mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-26 14:54:25 +00:00
926ee14909
GNU readelf isn't available as consistently as I assumed across SunOS, and it can be named different things on different illumos distros (some call it readelf, others call it greadelf, etc), so it's safer to use elfdump. This is a fairly easy fix, just kind of annoying.
160 lines
5.5 KiB
Python
160 lines
5.5 KiB
Python
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
'''Given a library, dependentlibs.py prints the list of libraries it depends
|
|
upon that are in the same directory, followed by the library itself.
|
|
'''
|
|
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import mozpack.path as mozpath
|
|
from collections import OrderedDict
|
|
from mozpack.executables import (
|
|
get_type,
|
|
ELF,
|
|
MACHO,
|
|
)
|
|
from buildconfig import substs
|
|
|
|
def dependentlibs_dumpbin(lib):
|
|
'''Returns the list of dependencies declared in the given DLL'''
|
|
try:
|
|
proc = subprocess.Popen(['dumpbin', '-dependents', lib], stdout = subprocess.PIPE)
|
|
except OSError:
|
|
# dumpbin is missing, probably mingw compilation. Try using objdump.
|
|
return dependentlibs_mingw_objdump(lib)
|
|
deps = []
|
|
for line in proc.stdout:
|
|
# Each line containing an imported library name starts with 4 spaces
|
|
match = re.match(' (\S+)', line)
|
|
if match:
|
|
deps.append(match.group(1))
|
|
elif len(deps):
|
|
# There may be several groups of library names, but only the
|
|
# first one is interesting. The second one is for delayload-ed
|
|
# libraries.
|
|
break
|
|
proc.wait()
|
|
return deps
|
|
|
|
def dependentlibs_mingw_objdump(lib):
|
|
proc = subprocess.Popen(['objdump', '-x', lib], stdout = subprocess.PIPE)
|
|
deps = []
|
|
for line in proc.stdout:
|
|
match = re.match('\tDLL Name: (\S+)', line)
|
|
if match:
|
|
deps.append(match.group(1))
|
|
proc.wait()
|
|
return deps
|
|
|
|
def dependentlibs_elfdump(lib):
|
|
'''Returns the list of dependencies declared in the given ELF .so'''
|
|
proc = subprocess.Popen(['elfdump', '-N', '.dynamic', lib], stdout = subprocess.PIPE)
|
|
deps = []
|
|
for line in proc.stdout:
|
|
# Each line has the following format:
|
|
# index TYPE tag value
|
|
tmp = line
|
|
if len(tmp) > 3 and 'NEEDED' in tmp:
|
|
# NEEDED lines look like:
|
|
# [1] NEEDED 0x0000001 libname
|
|
match = re.search(r'(lib\w+.so.*)', tmp)
|
|
if match:
|
|
deps.append(match.group(1))
|
|
proc.wait()
|
|
return deps
|
|
|
|
def dependentlibs_readelf(lib):
|
|
'''Returns the list of dependencies declared in the given ELF .so'''
|
|
proc = subprocess.Popen([substs.get('TOOLCHAIN_PREFIX', '') + 'readelf', '-d', lib], stdout = subprocess.PIPE)
|
|
deps = []
|
|
for line in proc.stdout:
|
|
# Each line has the following format:
|
|
# tag (TYPE) value
|
|
# or with BSD readelf:
|
|
# tag TYPE value
|
|
# Looking for NEEDED type entries
|
|
tmp = line.split(' ', 3)
|
|
if len(tmp) > 3 and 'NEEDED' in tmp[2]:
|
|
# NEEDED lines look like:
|
|
# 0x00000001 (NEEDED) Shared library: [libname]
|
|
# or with BSD readelf:
|
|
# 0x00000001 NEEDED Shared library: [libname]
|
|
match = re.search('\[(.*)\]', tmp[3])
|
|
if match:
|
|
deps.append(match.group(1))
|
|
proc.wait()
|
|
return deps
|
|
|
|
def dependentlibs_otool(lib):
|
|
'''Returns the list of dependencies declared in the given MACH-O dylib'''
|
|
proc = subprocess.Popen([substs['OTOOL'], '-l', lib], stdout = subprocess.PIPE)
|
|
deps= []
|
|
cmd = None
|
|
for line in proc.stdout:
|
|
# otool -l output contains many different things. The interesting data
|
|
# is under "Load command n" sections, with the content:
|
|
# cmd LC_LOAD_DYLIB
|
|
# cmdsize 56
|
|
# name libname (offset 24)
|
|
tmp = line.split()
|
|
if len(tmp) < 2:
|
|
continue
|
|
if tmp[0] == 'cmd':
|
|
cmd = tmp[1]
|
|
elif cmd == 'LC_LOAD_DYLIB' and tmp[0] == 'name':
|
|
deps.append(re.sub('^@executable_path/','',tmp[1]))
|
|
proc.wait()
|
|
return deps
|
|
|
|
def dependentlibs(lib, libpaths, func):
|
|
'''For a given library, returns the list of recursive dependencies that can
|
|
be found in the given list of paths, followed by the library itself.'''
|
|
assert(libpaths)
|
|
assert(isinstance(libpaths, list))
|
|
deps = OrderedDict()
|
|
for dep in func(lib):
|
|
if dep in deps or os.path.isabs(dep):
|
|
continue
|
|
for dir in libpaths:
|
|
deppath = os.path.join(dir, dep)
|
|
if os.path.exists(deppath):
|
|
deps.update(dependentlibs(deppath, libpaths, func))
|
|
# Black list the ICU data DLL because preloading it at startup
|
|
# leads to startup performance problems because of its excessive
|
|
# size (around 10MB).
|
|
if not dep.startswith("icu"):
|
|
deps[dep] = deppath
|
|
break
|
|
|
|
return deps
|
|
|
|
def gen_list(output, lib):
|
|
libpaths = [os.path.join(substs['DIST'], 'bin')]
|
|
binary_type = get_type(lib)
|
|
if substs['OS_ARCH'] == 'SunOS':
|
|
# If we're on SunOS, we're using ELF, but can't rely on readelf.
|
|
func = dependentlibs_elfdump
|
|
elif binary_type == ELF:
|
|
func = dependentlibs_readelf
|
|
elif binary_type == MACHO:
|
|
func = dependentlibs_otool
|
|
else:
|
|
ext = os.path.splitext(lib)[1]
|
|
assert(ext == '.dll')
|
|
func = dependentlibs_dumpbin
|
|
|
|
deps = dependentlibs(lib, libpaths, func)
|
|
deps[lib] = mozpath.join(libpaths[0], lib)
|
|
output.write('\n'.join(deps.keys()) + '\n')
|
|
return set(deps.values())
|
|
|
|
def main():
|
|
gen_list(sys.stdout, sys.argv[1])
|
|
|
|
if __name__ == '__main__':
|
|
main()
|