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