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