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 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 45def 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 65def 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 95 if p_filesz == 0: 96 break 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 106def change_dl_sysdirs(elf_file_name): 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) 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) 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) 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) 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 204if 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 209if sys.version_info < (3,): 210 new_prefix = sys.argv[1] 211 new_dl_path = sys.argv[2] 212else: 213 new_prefix = sys.argv[1].encode() 214 new_dl_path = sys.argv[2].encode() 215 216executables_list = sys.argv[3:] 217 218for 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) 244 change_dl_sysdirs(e) 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 256