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