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 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 61 hdr_fmt = endian_prefix + "HHILLLIHHHHHH" 62 hdr_size = 52 63 else: 64 # 64bit 65 hdr_fmt = endian_prefix + "HHIQQQIHHHHHH" 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 72def change_interpreter(elf_file_name): 73 if arch == 32: 74 ph_fmt = endian_prefix + "IIIIIIII" 75 else: 76 ph_fmt = endian_prefix + "IIQQQQQQ" 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 102 if p_filesz == 0: 103 break 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 return False 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 return True 113 114def change_dl_sysdirs(elf_file_name): 115 if arch == 32: 116 sh_fmt = endian_prefix + "IIIIIIIIII" 117 else: 118 sh_fmt = endian_prefix + "IIQQQQIIQQ" 119 120 """ read section string table """ 121 f.seek(e_shoff + e_shstrndx * e_shentsize) 122 sh_hdr = f.read(e_shentsize) 123 if arch == 32: 124 sh_offset, sh_size = struct.unpack(endian_prefix + "16xII16x", sh_hdr) 125 else: 126 sh_offset, sh_size = struct.unpack(endian_prefix + "24xQQ24x", sh_hdr) 127 128 f.seek(sh_offset) 129 sh_strtab = f.read(sh_size) 130 131 sysdirs = sysdirs_len = "" 132 133 """ change ld.so.cache path and default libs path for dynamic loader """ 134 for i in range(0,e_shnum): 135 f.seek(e_shoff + i * e_shentsize) 136 sh_hdr = f.read(e_shentsize) 137 138 sh_name, sh_type, sh_flags, sh_addr, sh_offset, sh_size, sh_link,\ 139 sh_info, sh_addralign, sh_entsize = struct.unpack(sh_fmt, sh_hdr) 140 141 name = sh_strtab[sh_name:sh_strtab.find(b("\0"), sh_name)] 142 143 """ look only into SHT_PROGBITS sections """ 144 if sh_type == 1: 145 f.seek(sh_offset) 146 """ default library paths cannot be changed on the fly because """ 147 """ the string lengths have to be changed too. """ 148 if name == b(".sysdirs"): 149 sysdirs = f.read(sh_size) 150 sysdirs_off = sh_offset 151 sysdirs_sect_size = sh_size 152 elif name == b(".sysdirslen"): 153 sysdirslen = f.read(sh_size) 154 sysdirslen_off = sh_offset 155 elif name == b(".ldsocache"): 156 ldsocache_path = f.read(sh_size) 157 new_ldsocache_path = old_prefix.sub(new_prefix, ldsocache_path) 158 new_ldsocache_path = new_ldsocache_path.rstrip(b("\0")) 159 if (len(new_ldsocache_path) >= sh_size): 160 print("ERROR: could not relocate %s, .ldsocache section size = %i and %i is needed." \ 161 % (elf_file_name, sh_size, len(new_ldsocache_path))) 162 sys.exit(-1) 163 # pad with zeros 164 new_ldsocache_path += b("\0") * (sh_size - len(new_ldsocache_path)) 165 # write it back 166 f.seek(sh_offset) 167 f.write(new_ldsocache_path) 168 elif name == b(".gccrelocprefix"): 169 offset = 0 170 while (offset + 4096) <= sh_size: 171 path = f.read(4096) 172 new_path = old_prefix.sub(new_prefix, path) 173 new_path = new_path.rstrip(b("\0")) 174 if (len(new_path) >= 4096): 175 print("ERROR: could not relocate %s, max path size = 4096 and %i is needed." \ 176 % (elf_file_name, len(new_path))) 177 sys.exit(-1) 178 # pad with zeros 179 new_path += b("\0") * (4096 - len(new_path)) 180 #print "Changing %s to %s at %s" % (str(path), str(new_path), str(offset)) 181 # write it back 182 f.seek(sh_offset + offset) 183 f.write(new_path) 184 offset = offset + 4096 185 if sysdirs != "" and sysdirslen != "": 186 paths = sysdirs.split(b("\0")) 187 sysdirs = b("") 188 sysdirslen = b("") 189 for path in paths: 190 """ exit the loop when we encounter first empty string """ 191 if path == b(""): 192 break 193 194 new_path = old_prefix.sub(new_prefix, path) 195 sysdirs += new_path + b("\0") 196 197 if arch == 32: 198 sysdirslen += struct.pack("<L", len(new_path)) 199 else: 200 sysdirslen += struct.pack("<Q", len(new_path)) 201 202 """ pad with zeros """ 203 sysdirs += b("\0") * (sysdirs_sect_size - len(sysdirs)) 204 205 """ write the sections back """ 206 f.seek(sysdirs_off) 207 f.write(sysdirs) 208 f.seek(sysdirslen_off) 209 f.write(sysdirslen) 210 211# MAIN 212if len(sys.argv) < 4: 213 sys.exit(-1) 214 215# In python > 3, strings may also contain Unicode characters. So, convert 216# them to bytes 217if sys.version_info < (3,): 218 new_prefix = sys.argv[1] 219 new_dl_path = sys.argv[2] 220else: 221 new_prefix = sys.argv[1].encode() 222 new_dl_path = sys.argv[2].encode() 223 224executables_list = sys.argv[3:] 225 226errors = False 227for e in executables_list: 228 perms = os.stat(e)[stat.ST_MODE] 229 if os.access(e, os.W_OK|os.R_OK): 230 perms = None 231 else: 232 os.chmod(e, perms|stat.S_IRWXU) 233 234 try: 235 f = open(e, "r+b") 236 except IOError: 237 exctype, ioex = sys.exc_info()[:2] 238 if ioex.errno == errno.ETXTBSY: 239 print("Could not open %s. File used by another process.\nPlease "\ 240 "make sure you exit all processes that might use any SDK "\ 241 "binaries." % e) 242 else: 243 print("Could not open %s: %s(%d)" % (e, ioex.strerror, ioex.errno)) 244 sys.exit(-1) 245 246 # Save old size and do a size check at the end. Just a safety measure. 247 old_size = os.path.getsize(e) 248 if old_size >= 64: 249 arch = get_arch() 250 if arch: 251 parse_elf_header() 252 if not change_interpreter(e): 253 errors = True 254 change_dl_sysdirs(e) 255 256 """ change permissions back """ 257 if perms: 258 os.chmod(e, perms) 259 260 f.close() 261 262 if old_size != os.path.getsize(e): 263 print("New file size for %s is different. Looks like a relocation error!", e) 264 sys.exit(-1) 265 266if errors: 267 print("Relocation of one or more executables failed.") 268 sys.exit(-1) 269