xref: /openbmc/openbmc/poky/scripts/relocate_sdk.py (revision 92b42cb35d755f8cfe6c17d403711a536e0f0721)
1eb8dc403SDave Cobbley#!/usr/bin/env python3
2eb8dc403SDave Cobbley#
3eb8dc403SDave Cobbley# Copyright (c) 2012 Intel Corporation
4eb8dc403SDave Cobbley#
5c342db35SBrad Bishop# SPDX-License-Identifier: GPL-2.0-only
6eb8dc403SDave Cobbley#
7eb8dc403SDave Cobbley# DESCRIPTION
8eb8dc403SDave Cobbley# This script is called by the SDK installer script. It replaces the dynamic
9eb8dc403SDave Cobbley# loader path in all binaries and also fixes the SYSDIR paths/lengths and the
10eb8dc403SDave Cobbley# location of ld.so.cache in the dynamic loader binary
11eb8dc403SDave Cobbley#
12eb8dc403SDave Cobbley# AUTHORS
13eb8dc403SDave Cobbley# Laurentiu Palcu <laurentiu.palcu@intel.com>
14eb8dc403SDave Cobbley#
15eb8dc403SDave Cobbley
16eb8dc403SDave Cobbleyimport struct
17eb8dc403SDave Cobbleyimport sys
18eb8dc403SDave Cobbleyimport stat
19eb8dc403SDave Cobbleyimport os
20eb8dc403SDave Cobbleyimport re
21eb8dc403SDave Cobbleyimport errno
22eb8dc403SDave Cobbley
23eb8dc403SDave Cobbleyif sys.version < '3':
24eb8dc403SDave Cobbley    def b(x):
25eb8dc403SDave Cobbley        return x
26eb8dc403SDave Cobbleyelse:
27eb8dc403SDave Cobbley    def b(x):
28eb8dc403SDave Cobbley        return x.encode(sys.getfilesystemencoding())
29eb8dc403SDave Cobbley
30eb8dc403SDave Cobbleyold_prefix = re.compile(b("##DEFAULT_INSTALL_DIR##"))
31eb8dc403SDave Cobbley
32eb8dc403SDave Cobbleydef get_arch():
33de0582f0SPatrick Williams    global endian_prefix
34eb8dc403SDave Cobbley    f.seek(0)
35eb8dc403SDave Cobbley    e_ident =f.read(16)
36de0582f0SPatrick Williams    ei_mag0,ei_mag1_3,ei_class,ei_data,ei_version = struct.unpack("<B3sBBB9x", e_ident)
37de0582f0SPatrick Williams
38de0582f0SPatrick Williams    # ei_data = 1 for little-endian & 0 for big-endian
39de0582f0SPatrick Williams    if ei_data == 1:
40de0582f0SPatrick Williams        endian_prefix = '<'
41de0582f0SPatrick Williams    else:
42de0582f0SPatrick Williams        endian_prefix = '>'
43eb8dc403SDave Cobbley
44eb8dc403SDave Cobbley    if (ei_mag0 != 0x7f and ei_mag1_3 != "ELF") or ei_class == 0:
45eb8dc403SDave Cobbley        return 0
46eb8dc403SDave Cobbley
47eb8dc403SDave Cobbley    if ei_class == 1:
48eb8dc403SDave Cobbley        return 32
49eb8dc403SDave Cobbley    elif ei_class == 2:
50eb8dc403SDave Cobbley        return 64
51eb8dc403SDave Cobbley
52eb8dc403SDave Cobbleydef parse_elf_header():
53eb8dc403SDave Cobbley    global e_type, e_machine, e_version, e_entry, e_phoff, e_shoff, e_flags,\
54eb8dc403SDave Cobbley           e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum, e_shstrndx
55eb8dc403SDave Cobbley
56eb8dc403SDave Cobbley    f.seek(0)
57eb8dc403SDave Cobbley    elf_header = f.read(64)
58eb8dc403SDave Cobbley
59eb8dc403SDave Cobbley    if arch == 32:
60eb8dc403SDave Cobbley        # 32bit
61de0582f0SPatrick Williams        hdr_fmt = endian_prefix + "HHILLLIHHHHHH"
62eb8dc403SDave Cobbley        hdr_size = 52
63eb8dc403SDave Cobbley    else:
64eb8dc403SDave Cobbley        # 64bit
65de0582f0SPatrick Williams        hdr_fmt = endian_prefix + "HHIQQQIHHHHHH"
66eb8dc403SDave Cobbley        hdr_size = 64
67eb8dc403SDave Cobbley
68eb8dc403SDave Cobbley    e_type, e_machine, e_version, e_entry, e_phoff, e_shoff, e_flags,\
69eb8dc403SDave Cobbley    e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum, e_shstrndx =\
70eb8dc403SDave Cobbley        struct.unpack(hdr_fmt, elf_header[16:hdr_size])
71eb8dc403SDave Cobbley
72eb8dc403SDave Cobbleydef change_interpreter(elf_file_name):
73eb8dc403SDave Cobbley    if arch == 32:
74de0582f0SPatrick Williams        ph_fmt = endian_prefix + "IIIIIIII"
75eb8dc403SDave Cobbley    else:
76de0582f0SPatrick Williams        ph_fmt = endian_prefix + "IIQQQQQQ"
77eb8dc403SDave Cobbley
78eb8dc403SDave Cobbley    """ look for PT_INTERP section """
79eb8dc403SDave Cobbley    for i in range(0,e_phnum):
80eb8dc403SDave Cobbley        f.seek(e_phoff + i * e_phentsize)
81eb8dc403SDave Cobbley        ph_hdr = f.read(e_phentsize)
82eb8dc403SDave Cobbley        if arch == 32:
83eb8dc403SDave Cobbley            # 32bit
84eb8dc403SDave Cobbley            p_type, p_offset, p_vaddr, p_paddr, p_filesz,\
85eb8dc403SDave Cobbley                p_memsz, p_flags, p_align = struct.unpack(ph_fmt, ph_hdr)
86eb8dc403SDave Cobbley        else:
87eb8dc403SDave Cobbley            # 64bit
88eb8dc403SDave Cobbley            p_type, p_flags, p_offset, p_vaddr, p_paddr, \
89eb8dc403SDave Cobbley            p_filesz, p_memsz, p_align = struct.unpack(ph_fmt, ph_hdr)
90eb8dc403SDave Cobbley
91eb8dc403SDave Cobbley        """ change interpreter """
92eb8dc403SDave Cobbley        if p_type == 3:
93eb8dc403SDave Cobbley            # PT_INTERP section
94eb8dc403SDave Cobbley            f.seek(p_offset)
95eb8dc403SDave Cobbley            # External SDKs with mixed pre-compiled binaries should not get
96eb8dc403SDave Cobbley            # relocated so look for some variant of /lib
97eb8dc403SDave Cobbley            fname = f.read(11)
98eb8dc403SDave Cobbley            if fname.startswith(b("/lib/")) or fname.startswith(b("/lib64/")) or \
99eb8dc403SDave Cobbley               fname.startswith(b("/lib32/")) or fname.startswith(b("/usr/lib32/")) or \
100eb8dc403SDave Cobbley               fname.startswith(b("/usr/lib32/")) or fname.startswith(b("/usr/lib64/")):
101eb8dc403SDave Cobbley                break
102eb8dc403SDave Cobbley            if p_filesz == 0:
103eb8dc403SDave Cobbley                break
104eb8dc403SDave Cobbley            if (len(new_dl_path) >= p_filesz):
105eb8dc403SDave Cobbley                print("ERROR: could not relocate %s, interp size = %i and %i is needed." \
106eb8dc403SDave Cobbley                    % (elf_file_name, p_memsz, len(new_dl_path) + 1))
107*92b42cb3SPatrick Williams                return False
108eb8dc403SDave Cobbley            dl_path = new_dl_path + b("\0") * (p_filesz - len(new_dl_path))
109eb8dc403SDave Cobbley            f.seek(p_offset)
110eb8dc403SDave Cobbley            f.write(dl_path)
111eb8dc403SDave Cobbley            break
112*92b42cb3SPatrick Williams    return True
113eb8dc403SDave Cobbley
114eb8dc403SDave Cobbleydef change_dl_sysdirs(elf_file_name):
115eb8dc403SDave Cobbley    if arch == 32:
116de0582f0SPatrick Williams        sh_fmt = endian_prefix + "IIIIIIIIII"
117eb8dc403SDave Cobbley    else:
118de0582f0SPatrick Williams        sh_fmt = endian_prefix + "IIQQQQIIQQ"
119eb8dc403SDave Cobbley
120eb8dc403SDave Cobbley    """ read section string table """
121eb8dc403SDave Cobbley    f.seek(e_shoff + e_shstrndx * e_shentsize)
122eb8dc403SDave Cobbley    sh_hdr = f.read(e_shentsize)
123eb8dc403SDave Cobbley    if arch == 32:
124de0582f0SPatrick Williams        sh_offset, sh_size = struct.unpack(endian_prefix + "16xII16x", sh_hdr)
125eb8dc403SDave Cobbley    else:
126de0582f0SPatrick Williams        sh_offset, sh_size = struct.unpack(endian_prefix + "24xQQ24x", sh_hdr)
127eb8dc403SDave Cobbley
128eb8dc403SDave Cobbley    f.seek(sh_offset)
129eb8dc403SDave Cobbley    sh_strtab = f.read(sh_size)
130eb8dc403SDave Cobbley
131eb8dc403SDave Cobbley    sysdirs = sysdirs_len = ""
132eb8dc403SDave Cobbley
133eb8dc403SDave Cobbley    """ change ld.so.cache path and default libs path for dynamic loader """
134eb8dc403SDave Cobbley    for i in range(0,e_shnum):
135eb8dc403SDave Cobbley        f.seek(e_shoff + i * e_shentsize)
136eb8dc403SDave Cobbley        sh_hdr = f.read(e_shentsize)
137eb8dc403SDave Cobbley
138eb8dc403SDave Cobbley        sh_name, sh_type, sh_flags, sh_addr, sh_offset, sh_size, sh_link,\
139eb8dc403SDave Cobbley            sh_info, sh_addralign, sh_entsize = struct.unpack(sh_fmt, sh_hdr)
140eb8dc403SDave Cobbley
141eb8dc403SDave Cobbley        name = sh_strtab[sh_name:sh_strtab.find(b("\0"), sh_name)]
142eb8dc403SDave Cobbley
143eb8dc403SDave Cobbley        """ look only into SHT_PROGBITS sections """
144eb8dc403SDave Cobbley        if sh_type == 1:
145eb8dc403SDave Cobbley            f.seek(sh_offset)
146eb8dc403SDave Cobbley            """ default library paths cannot be changed on the fly because  """
147eb8dc403SDave Cobbley            """ the string lengths have to be changed too.                  """
148eb8dc403SDave Cobbley            if name == b(".sysdirs"):
149eb8dc403SDave Cobbley                sysdirs = f.read(sh_size)
150eb8dc403SDave Cobbley                sysdirs_off = sh_offset
151eb8dc403SDave Cobbley                sysdirs_sect_size = sh_size
152eb8dc403SDave Cobbley            elif name == b(".sysdirslen"):
153eb8dc403SDave Cobbley                sysdirslen = f.read(sh_size)
154eb8dc403SDave Cobbley                sysdirslen_off = sh_offset
155eb8dc403SDave Cobbley            elif name == b(".ldsocache"):
156eb8dc403SDave Cobbley                ldsocache_path = f.read(sh_size)
157eb8dc403SDave Cobbley                new_ldsocache_path = old_prefix.sub(new_prefix, ldsocache_path)
158eb8dc403SDave Cobbley                new_ldsocache_path = new_ldsocache_path.rstrip(b("\0"))
159eb8dc403SDave Cobbley                if (len(new_ldsocache_path) >= sh_size):
160eb8dc403SDave Cobbley                    print("ERROR: could not relocate %s, .ldsocache section size = %i and %i is needed." \
161eb8dc403SDave Cobbley                    % (elf_file_name, sh_size, len(new_ldsocache_path)))
162eb8dc403SDave Cobbley                    sys.exit(-1)
163eb8dc403SDave Cobbley                # pad with zeros
164eb8dc403SDave Cobbley                new_ldsocache_path += b("\0") * (sh_size - len(new_ldsocache_path))
165eb8dc403SDave Cobbley                # write it back
166eb8dc403SDave Cobbley                f.seek(sh_offset)
167eb8dc403SDave Cobbley                f.write(new_ldsocache_path)
168eb8dc403SDave Cobbley            elif name == b(".gccrelocprefix"):
169eb8dc403SDave Cobbley                offset = 0
170eb8dc403SDave Cobbley                while (offset + 4096) <= sh_size:
171eb8dc403SDave Cobbley                    path = f.read(4096)
172eb8dc403SDave Cobbley                    new_path = old_prefix.sub(new_prefix, path)
173eb8dc403SDave Cobbley                    new_path = new_path.rstrip(b("\0"))
174eb8dc403SDave Cobbley                    if (len(new_path) >= 4096):
175eb8dc403SDave Cobbley                        print("ERROR: could not relocate %s, max path size = 4096 and %i is needed." \
176eb8dc403SDave Cobbley                        % (elf_file_name, len(new_path)))
177eb8dc403SDave Cobbley                        sys.exit(-1)
178eb8dc403SDave Cobbley                    # pad with zeros
179eb8dc403SDave Cobbley                    new_path += b("\0") * (4096 - len(new_path))
180eb8dc403SDave Cobbley                    #print "Changing %s to %s at %s" % (str(path), str(new_path), str(offset))
181eb8dc403SDave Cobbley                    # write it back
182eb8dc403SDave Cobbley                    f.seek(sh_offset + offset)
183eb8dc403SDave Cobbley                    f.write(new_path)
184eb8dc403SDave Cobbley                    offset = offset + 4096
185eb8dc403SDave Cobbley    if sysdirs != "" and sysdirslen != "":
186eb8dc403SDave Cobbley        paths = sysdirs.split(b("\0"))
187eb8dc403SDave Cobbley        sysdirs = b("")
188eb8dc403SDave Cobbley        sysdirslen = b("")
189eb8dc403SDave Cobbley        for path in paths:
190eb8dc403SDave Cobbley            """ exit the loop when we encounter first empty string """
191eb8dc403SDave Cobbley            if path == b(""):
192eb8dc403SDave Cobbley                break
193eb8dc403SDave Cobbley
194eb8dc403SDave Cobbley            new_path = old_prefix.sub(new_prefix, path)
195eb8dc403SDave Cobbley            sysdirs += new_path + b("\0")
196eb8dc403SDave Cobbley
197eb8dc403SDave Cobbley            if arch == 32:
198eb8dc403SDave Cobbley                sysdirslen += struct.pack("<L", len(new_path))
199eb8dc403SDave Cobbley            else:
200eb8dc403SDave Cobbley                sysdirslen += struct.pack("<Q", len(new_path))
201eb8dc403SDave Cobbley
202eb8dc403SDave Cobbley        """ pad with zeros """
203eb8dc403SDave Cobbley        sysdirs += b("\0") * (sysdirs_sect_size - len(sysdirs))
204eb8dc403SDave Cobbley
205eb8dc403SDave Cobbley        """ write the sections back """
206eb8dc403SDave Cobbley        f.seek(sysdirs_off)
207eb8dc403SDave Cobbley        f.write(sysdirs)
208eb8dc403SDave Cobbley        f.seek(sysdirslen_off)
209eb8dc403SDave Cobbley        f.write(sysdirslen)
210eb8dc403SDave Cobbley
211eb8dc403SDave Cobbley# MAIN
212eb8dc403SDave Cobbleyif len(sys.argv) < 4:
213eb8dc403SDave Cobbley    sys.exit(-1)
214eb8dc403SDave Cobbley
215eb8dc403SDave Cobbley# In python > 3, strings may also contain Unicode characters. So, convert
216eb8dc403SDave Cobbley# them to bytes
217eb8dc403SDave Cobbleyif sys.version_info < (3,):
218eb8dc403SDave Cobbley    new_prefix = sys.argv[1]
219eb8dc403SDave Cobbley    new_dl_path = sys.argv[2]
220eb8dc403SDave Cobbleyelse:
221eb8dc403SDave Cobbley    new_prefix = sys.argv[1].encode()
222eb8dc403SDave Cobbley    new_dl_path = sys.argv[2].encode()
223eb8dc403SDave Cobbley
224eb8dc403SDave Cobbleyexecutables_list = sys.argv[3:]
225eb8dc403SDave Cobbley
226*92b42cb3SPatrick Williamserrors = False
227eb8dc403SDave Cobbleyfor e in executables_list:
228eb8dc403SDave Cobbley    perms = os.stat(e)[stat.ST_MODE]
229eb8dc403SDave Cobbley    if os.access(e, os.W_OK|os.R_OK):
230eb8dc403SDave Cobbley        perms = None
231eb8dc403SDave Cobbley    else:
232eb8dc403SDave Cobbley        os.chmod(e, perms|stat.S_IRWXU)
233eb8dc403SDave Cobbley
234eb8dc403SDave Cobbley    try:
235eb8dc403SDave Cobbley        f = open(e, "r+b")
236eb8dc403SDave Cobbley    except IOError:
237eb8dc403SDave Cobbley        exctype, ioex = sys.exc_info()[:2]
238eb8dc403SDave Cobbley        if ioex.errno == errno.ETXTBSY:
239eb8dc403SDave Cobbley            print("Could not open %s. File used by another process.\nPlease "\
240eb8dc403SDave Cobbley                  "make sure you exit all processes that might use any SDK "\
241eb8dc403SDave Cobbley                  "binaries." % e)
242eb8dc403SDave Cobbley        else:
243eb8dc403SDave Cobbley            print("Could not open %s: %s(%d)" % (e, ioex.strerror, ioex.errno))
244eb8dc403SDave Cobbley        sys.exit(-1)
245eb8dc403SDave Cobbley
246eb8dc403SDave Cobbley    # Save old size and do a size check at the end. Just a safety measure.
247eb8dc403SDave Cobbley    old_size = os.path.getsize(e)
248eb8dc403SDave Cobbley    if old_size >= 64:
249eb8dc403SDave Cobbley        arch = get_arch()
250eb8dc403SDave Cobbley        if arch:
251eb8dc403SDave Cobbley            parse_elf_header()
252*92b42cb3SPatrick Williams            if not change_interpreter(e):
253*92b42cb3SPatrick Williams                errors = True
254eb8dc403SDave Cobbley            change_dl_sysdirs(e)
255eb8dc403SDave Cobbley
256eb8dc403SDave Cobbley    """ change permissions back """
257eb8dc403SDave Cobbley    if perms:
258eb8dc403SDave Cobbley        os.chmod(e, perms)
259eb8dc403SDave Cobbley
260eb8dc403SDave Cobbley    f.close()
261eb8dc403SDave Cobbley
262eb8dc403SDave Cobbley    if old_size != os.path.getsize(e):
263eb8dc403SDave Cobbley        print("New file size for %s is different. Looks like a relocation error!", e)
264eb8dc403SDave Cobbley        sys.exit(-1)
265eb8dc403SDave Cobbley
266*92b42cb3SPatrick Williamsif errors:
267*92b42cb3SPatrick Williams    print("Relocation of one or more executables failed.")
268*92b42cb3SPatrick Williams    sys.exit(-1)
269