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