summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@chromium.org>2014-07-30 00:40:04 -0400
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-08-01 06:34:43 +0000
commit33eff0822830feb5bd10216467051ea573a08dc2 (patch)
tree148234d55958ea9269ceba62811ce56b9f2b7d84
parent4469472c1437904d6f8dceed301fc7c9847cbe91 (diff)
downloadchromite-33eff0822830feb5bd10216467051ea573a08dc2.tar.gz
lddtree: resolve library symlinks against the selected root
When we hit a library that is an symlink with an absolute path, we currently dereference it directly which means its pointing to / and not the sysroot we care about. Make sure we resolve it relative to the root first. In this case, binutils has symlinks for its libs which are absolute: $ readlink /build/stumpy_moblab/usr/x86_64-cros-linux-gnu/lib/libbfd-2.24.so /usr/lib64/binutils/x86_64-cros-linux-gnu/2.24/libbfd-2.24.so Since lddtree then checks for that path and find that it doesn't exist, it skips the lib and ends up not being able to find it. When the host binutils is the same version, it happens to work because the host has its own copy of binutils installed at that path. BUG=chromium:398098 BUG=chromium:393018 TEST=`./build_image --board=stumpy_moblab` passes w/binutils-2.24 (host is still binutils-2.22) TEST=`cbuildbot arm-generic-full` passes TEST=`./generate_au_zip.py && ./test_au_zip.py` passes (outside of chroot) Change-Id: I0c73455ce16d65303bf647568e7a3cdd229b58f1 Reviewed-on: https://chromium-review.googlesource.com/210353 Reviewed-by: Mike Frysinger <vapier@chromium.org> Commit-Queue: Mike Frysinger <vapier@chromium.org> Tested-by: Mike Frysinger <vapier@chromium.org>
-rw-r--r--third_party/lddtree.py90
1 files changed, 68 insertions, 22 deletions
diff --git a/third_party/lddtree.py b/third_party/lddtree.py
index 41776988e..69cb70475 100644
--- a/third_party/lddtree.py
+++ b/third_party/lddtree.py
@@ -4,7 +4,7 @@
# Copyright 2012-2014 The Chromium OS Authors
# Use of this source code is governed by a BSD-style license (BSD-3)
# pylint: disable=C0301
-# $Header: /var/cvsroot/gentoo-projects/pax-utils/lddtree.py,v 1.48 2014/07/30 04:28:41 vapier Exp $
+# $Header: /var/cvsroot/gentoo-projects/pax-utils/lddtree.py,v 1.49 2014/07/30 04:34:09 vapier Exp $
# TODO: Handle symlinks.
@@ -60,6 +60,34 @@ def normpath(path):
return os.path.normpath(path).replace('//', '/')
+def readlink(path, root, prefixed=False):
+ """Like os.readlink(), but relative to a |root|
+
+ This does not currently handle the pathological case:
+ /lib/foo.so -> ../../../../../../../foo.so
+ This relies on the .. entries in / to point to itself.
+
+ Args:
+ path: The symlink to read
+ root: The path to use for resolving absolute symlinks
+ prefixed: When False, the |path| must not have |root| prefixed to it, nor
+ will the return value have |root| prefixed. When True, |path|
+ must have |root| prefixed, and the return value will have |root|
+ added.
+
+ Returns:
+ A fully resolved symlink path
+ """
+ root = root.rstrip('/')
+ if prefixed:
+ path = path[len(root):]
+
+ while os.path.islink(root + path):
+ path = os.path.join(os.path.dirname(path), os.readlink(root + path))
+
+ return normpath((root + path) if prefixed else path)
+
+
def makedirs(path):
"""Like os.makedirs(), but ignore EEXIST errors"""
try:
@@ -249,32 +277,40 @@ def CompatibleELFs(elf1, elf2):
elf1.header['e_machine'] == elf2.header['e_machine'])
-def FindLib(elf, lib, ldpaths, debug=False):
+def FindLib(elf, lib, ldpaths, root='/', debug=False):
"""Try to locate a |lib| that is compatible to |elf| in the given |ldpaths|
Args:
elf: The elf which the library should be compatible with (ELF wise)
lib: The library (basename) to search for
ldpaths: A list of paths to search
+ root: The root path to resolve symlinks
debug: Enable debug output
Returns:
- the full path to the desired library
+ Tuple of the full path to the desired library and the real path to it
"""
dbg(debug, ' FindLib(%s)' % lib)
+
for ldpath in ldpaths:
path = os.path.join(ldpath, lib)
- dbg(debug, ' checking:', path)
- if os.path.exists(path):
- with open(path, 'rb') as f:
+ target = readlink(path, root, prefixed=True)
+ if path != target:
+ dbg(debug, ' checking: %s -> %s' % (path, target))
+ else:
+ dbg(debug, ' checking:', path)
+
+ if os.path.exists(target):
+ with open(target, 'rb') as f:
libelf = ELFFile(f)
if CompatibleELFs(elf, libelf):
- return path
- return None
+ return (target, path)
+
+ return (None, None)
def ParseELF(path, root='/', prefix='', ldpaths={'conf':[], 'env':[], 'interp':[]},
- debug=False, _first=True, _all_libs={}):
+ display=None, debug=False, _first=True, _all_libs={}):
"""Parse the ELF dependency tree of the specified file
Args:
@@ -284,6 +320,7 @@ def ParseELF(path, root='/', prefix='', ldpaths={'conf':[], 'env':[], 'interp':[
prefix: The path under |root| to search
ldpaths: dict containing library paths to search; should have the keys:
conf, env, interp
+ display: The path to show rather than |path|
debug: Enable debug output
_first: Recursive use only; is this the first ELF ?
_all_libs: Recursive use only; dict of all libs we've seen
@@ -310,7 +347,8 @@ def ParseELF(path, root='/', prefix='', ldpaths={'conf':[], 'env':[], 'interp':[
ldpaths = ldpaths.copy()
ret = {
'interp': None,
- 'path': path,
+ 'path': path if display is None else display,
+ 'realpath': path,
'needed': [],
'rpath': [],
'runpath': [],
@@ -333,6 +371,7 @@ def ParseELF(path, root='/', prefix='', ldpaths={'conf':[], 'env':[], 'interp':[
ret['interp'] = normpath(root + interp)
ret['libs'][os.path.basename(interp)] = {
'path': ret['interp'],
+ 'realpath': readlink(ret['interp'], root, prefixed=True),
'needed': [],
}
# XXX: Should read it and scan for /lib paths.
@@ -383,13 +422,15 @@ def ParseELF(path, root='/', prefix='', ldpaths={'conf':[], 'env':[], 'interp':[
continue
if all_ldpaths is None:
all_ldpaths = rpaths + ldpaths['rpath'] + ldpaths['env'] + runpaths + ldpaths['runpath'] + ldpaths['conf'] + ldpaths['interp']
- fullpath = FindLib(elf, lib, all_ldpaths, debug=debug)
+ realpath, fullpath = FindLib(elf, lib, all_ldpaths, root, debug=debug)
_all_libs[lib] = {
+ 'realpath': realpath,
'path': fullpath,
'needed': [],
}
if fullpath:
- lret = ParseELF(fullpath, root, prefix, ldpaths, debug, False, _all_libs)
+ lret = ParseELF(realpath, root, prefix, ldpaths, display=fullpath,
+ debug=debug, _first=False, _all_libs=_all_libs)
_all_libs[lib]['needed'] = lret['needed']
del elf
@@ -402,7 +443,7 @@ def _NormalizePath(option, _opt, value, parser):
def _ShowVersion(_option, _opt, _value, _parser):
- d = '$Id: lddtree.py,v 1.48 2014/07/30 04:28:41 vapier Exp $'.split()
+ d = '$Id: lddtree.py,v 1.49 2014/07/30 04:34:09 vapier Exp $'.split()
print('%s-%s %s %s' % (d[1].split('.')[0], d[2], d[3], d[4]))
sys.exit(0)
@@ -451,8 +492,9 @@ def _ActionCopy(options, elf):
def _StripRoot(path):
return path[len(options.root) - 1:]
- def _copy(src, striproot=True, wrapit=False, libpaths=(), outdir=None):
- if src is None:
+ def _copy(realsrc, src, striproot=True, wrapit=False, libpaths=(),
+ outdir=None):
+ if realsrc is None:
return
if wrapit:
@@ -471,7 +513,7 @@ def _ActionCopy(options, elf):
try:
# See if they're the same file.
nstat = os.stat(dst + ('.elf' if wrapit else ''))
- ostat = os.stat(src)
+ ostat = os.stat(realsrc)
for field in ('mode', 'mtime', 'size'):
if getattr(ostat, 'st_' + field) != \
getattr(nstat, 'st_' + field):
@@ -487,10 +529,10 @@ def _ActionCopy(options, elf):
makedirs(os.path.dirname(dst))
try:
- shutil.copy2(src, dst)
+ shutil.copy2(realsrc, dst)
except IOError:
os.unlink(dst)
- shutil.copy2(src, dst)
+ shutil.copy2(realsrc, dst)
if wrapit:
if options.verbose:
@@ -509,10 +551,11 @@ def _ActionCopy(options, elf):
# for known libs that get loaded (e.g. curl will dlopen(libresolv)).
libpaths = set()
for lib in elf['libs']:
- path = elf['libs'][lib]['path']
+ libdata = elf['libs'][lib]
+ path = libdata['realpath']
if not options.libdir:
libpaths.add(_StripRoot(os.path.dirname(path)))
- _copy(path, outdir=options.libdir)
+ _copy(path, libdata['path'], outdir=options.libdir)
if not options.libdir:
libpaths = list(libpaths)
@@ -523,8 +566,10 @@ def _ActionCopy(options, elf):
else:
libpaths.add(options.libdir)
- _copy(elf['interp'], outdir=options.libdir)
- _copy(elf['path'], striproot=options.auto_root,
+ # We don't bother to copy this as ParseElf adds the interp to the 'libs',
+ # so it was already copied in the libs loop above.
+ #_copy(elf['interp'], outdir=options.libdir)
+ _copy(elf['realpath'], elf['path'], striproot=options.auto_root,
wrapit=options.generate_wrappers, libpaths=libpaths,
outdir=options.bindir)
@@ -667,6 +712,7 @@ they need will be placed into /foo/lib/ only.""")
'runpath': [],
'rpath': [],
'path': p,
+ 'realpath': p,
}
_ActionCopy(options, elf)
continue