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