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