1# 2# Copyright OpenEmbedded Contributors 3# 4# SPDX-License-Identifier: MIT 5# 6 7CHRPATH_BIN ?= "chrpath" 8PREPROCESS_RELOCATE_DIRS ?= "" 9 10def process_file_linux(cmd, fpath, rootdir, baseprefix, tmpdir, d, break_hardlinks = False): 11 import subprocess, oe.qa 12 13 with oe.qa.ELFFile(fpath) as elf: 14 try: 15 elf.open() 16 except oe.qa.NotELFFileError: 17 return 18 19 try: 20 out = subprocess.check_output([cmd, "-l", fpath], universal_newlines=True) 21 except subprocess.CalledProcessError: 22 return 23 24 # Handle RUNPATH as well as RPATH 25 out = out.replace("RUNPATH=","RPATH=") 26 # Throw away everything other than the rpath list 27 curr_rpath = out.partition("RPATH=")[2] 28 #bb.note("Current rpath for %s is %s" % (fpath, curr_rpath.strip())) 29 rpaths = curr_rpath.strip().split(":") 30 new_rpaths = [] 31 modified = False 32 for rpath in rpaths: 33 # If rpath is already dynamic copy it to new_rpath and continue 34 if rpath.find("$ORIGIN") != -1: 35 new_rpaths.append(rpath) 36 continue 37 rpath = os.path.normpath(rpath) 38 if baseprefix not in rpath and tmpdir not in rpath: 39 # Skip standard search paths 40 if rpath in ['/lib', '/usr/lib', '/lib64/', '/usr/lib64']: 41 bb.warn("Skipping RPATH %s as is a standard search path for %s" % (rpath, fpath)) 42 modified = True 43 continue 44 new_rpaths.append(rpath) 45 continue 46 new_rpaths.append("$ORIGIN/" + os.path.relpath(rpath, os.path.dirname(fpath.replace(rootdir, "/")))) 47 modified = True 48 49 # if we have modified some rpaths call chrpath to update the binary 50 if modified: 51 if break_hardlinks: 52 bb.utils.break_hardlinks(fpath) 53 54 args = ":".join(new_rpaths) 55 #bb.note("Setting rpath for %s to %s" %(fpath, args)) 56 try: 57 subprocess.check_output([cmd, "-r", args, fpath], 58 stderr=subprocess.PIPE, universal_newlines=True) 59 except subprocess.CalledProcessError as e: 60 bb.fatal("chrpath command failed with exit code %d:\n%s\n%s" % (e.returncode, e.stdout, e.stderr)) 61 62def process_file_darwin(cmd, fpath, rootdir, baseprefix, tmpdir, d, break_hardlinks = False): 63 import subprocess as sub 64 65 p = sub.Popen([d.expand("${HOST_PREFIX}otool"), '-L', fpath],stdout=sub.PIPE,stderr=sub.PIPE, text=True) 66 out, err = p.communicate() 67 # If returned successfully, process stdout for results 68 if p.returncode != 0: 69 return 70 for l in out.split("\n"): 71 if "(compatibility" not in l: 72 continue 73 rpath = l.partition("(compatibility")[0].strip() 74 if baseprefix not in rpath: 75 continue 76 77 if break_hardlinks: 78 bb.utils.break_hardlinks(fpath) 79 80 newpath = "@loader_path/" + os.path.relpath(rpath, os.path.dirname(fpath.replace(rootdir, "/"))) 81 p = sub.Popen([d.expand("${HOST_PREFIX}install_name_tool"), '-change', rpath, newpath, fpath],stdout=sub.PIPE,stderr=sub.PIPE) 82 out, err = p.communicate() 83 84def process_dir(rootdir, directory, d, break_hardlinks = False): 85 bb.debug(2, "Checking %s for binaries to process" % directory) 86 if not os.path.exists(directory): 87 return 88 89 import stat 90 91 rootdir = os.path.normpath(rootdir) 92 cmd = d.expand('${CHRPATH_BIN}') 93 tmpdir = os.path.normpath(d.getVar('TMPDIR', False)) 94 baseprefix = os.path.normpath(d.expand('${base_prefix}')) 95 hostos = d.getVar("HOST_OS") 96 97 if "linux" in hostos: 98 process_file = process_file_linux 99 elif "darwin" in hostos: 100 process_file = process_file_darwin 101 else: 102 # Relocations not supported 103 return 104 105 dirs = os.listdir(directory) 106 for file in dirs: 107 fpath = directory + "/" + file 108 fpath = os.path.normpath(fpath) 109 if os.path.islink(fpath): 110 # Skip symlinks 111 continue 112 113 if os.path.isdir(fpath): 114 process_dir(rootdir, fpath, d, break_hardlinks = break_hardlinks) 115 else: 116 #bb.note("Testing %s for relocatability" % fpath) 117 118 # We need read and write permissions for chrpath, if we don't have 119 # them then set them temporarily. Take a copy of the files 120 # permissions so that we can restore them afterwards. 121 perms = os.stat(fpath)[stat.ST_MODE] 122 if os.access(fpath, os.W_OK|os.R_OK): 123 perms = None 124 else: 125 # Temporarily make the file writeable so we can chrpath it 126 os.chmod(fpath, perms|stat.S_IRWXU) 127 128 process_file(cmd, fpath, rootdir, baseprefix, tmpdir, d, break_hardlinks = break_hardlinks) 129 130 if perms: 131 os.chmod(fpath, perms) 132 133def rpath_replace (path, d): 134 bindirs = d.expand("${bindir} ${sbindir} ${base_sbindir} ${base_bindir} ${libdir} ${base_libdir} ${libexecdir} ${PREPROCESS_RELOCATE_DIRS}").split() 135 136 for bindir in bindirs: 137 #bb.note ("Processing directory " + bindir) 138 directory = path + "/" + bindir 139 process_dir (path, directory, d) 140 141