xref: /OK3568_Linux_fs/yocto/poky/meta/classes/chrpath.bbclass (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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