mirror of https://github.com/Shizmob/smol
fix smoldd
This commit is contained in:
parent
5c38b05c85
commit
7e384a44a6
167
smoldd.py
167
smoldd.py
|
@ -1,167 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import glob
|
|
||||||
import os.path
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import struct
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from smolshared import *
|
|
||||||
|
|
||||||
def readbyte(blob, off):
|
|
||||||
return struct.unpack('<B', blob[off:off+1])[0], (off+1)
|
|
||||||
|
|
||||||
def readint(blob, off):
|
|
||||||
return struct.unpack('<I', blob[off:off+4])[0], (off+4)
|
|
||||||
|
|
||||||
def readlong(blob, off):
|
|
||||||
return struct.unpack('<Q', blob[off:off+8])[0], (off+8)
|
|
||||||
|
|
||||||
def readstr(blob, off):
|
|
||||||
text = bytearray()
|
|
||||||
while True:
|
|
||||||
char, off = readbyte(blob, off)
|
|
||||||
if char == 0:
|
|
||||||
break
|
|
||||||
|
|
||||||
text.append(char)
|
|
||||||
|
|
||||||
return text.decode('utf-8'), off
|
|
||||||
|
|
||||||
def get_def_libpaths(cc_bin, is32bit):
|
|
||||||
if is32bit:
|
|
||||||
return ['/usr/lib32/','/lib32/']
|
|
||||||
|
|
||||||
out = subprocess.check_output([cc_bin, '-print-search-dirs'],
|
|
||||||
stderr=subprocess.DEVNULL)
|
|
||||||
|
|
||||||
stuff = dict({})
|
|
||||||
for l in out.decode('utf-8').splitlines():
|
|
||||||
blah = l.split(': ')
|
|
||||||
stuff[blah[0]] = blah[1].lstrip('=').split(':')
|
|
||||||
|
|
||||||
return stuff["libraries"]
|
|
||||||
|
|
||||||
def find_libs(bits, deflibs, libname):
|
|
||||||
dirs = os.environ['LD_LIBRARY_PATH'].split(':') + deflibs
|
|
||||||
|
|
||||||
for d in dirs:
|
|
||||||
for f in glob.glob(glob.escape(d + libname) + '*'):
|
|
||||||
yield f
|
|
||||||
|
|
||||||
def build_hashtab(scanelf_bin, lib):
|
|
||||||
out = subprocess.check_output([scanelf_bin, '-B', '-F', '%s', '-s', '%pd%*', lib],
|
|
||||||
stderr=subprocess.DEVNULL)
|
|
||||||
|
|
||||||
blah = set(out.decode('utf-8').split('\n'))
|
|
||||||
ret = dict({})
|
|
||||||
|
|
||||||
for x in blah:
|
|
||||||
y = x.split()
|
|
||||||
if len(y) != 7:
|
|
||||||
continue
|
|
||||||
ret[hash_djb2(y[6])] = y[6]
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument('input', type=argparse.FileType('rb'),
|
|
||||||
default=sys.stdin.buffer, help="input file")
|
|
||||||
parser.add_argument('--cc',
|
|
||||||
default=shutil.which('cc'), help="C compiler binary")
|
|
||||||
parser.add_argument('--scanelf',
|
|
||||||
default=shutil.which('scanelf'), help="scanelf binary")
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
blob = args.input.read()
|
|
||||||
|
|
||||||
machnum = struct.unpack('<H', blob[18:18+2])[0]
|
|
||||||
|
|
||||||
is32bit = machnum == archmagic['i386']
|
|
||||||
|
|
||||||
deflibs = get_def_libpaths(args.cc, is32bit)
|
|
||||||
|
|
||||||
phoff, phsz, phnum = 0, 0, 0
|
|
||||||
if is32bit:
|
|
||||||
phoff = struct.unpack('<I', blob[28:28+4])[0]
|
|
||||||
phsz = struct.unpack('<H', blob[42:42+2])[0]
|
|
||||||
phnum = struct.unpack('<H', blob[44:52+2])[0]
|
|
||||||
elif machnum == archmagic['x86_64']:
|
|
||||||
phoff = struct.unpack('<Q', blob[32:32+8])[0]
|
|
||||||
phsz = struct.unpack('<H', blob[54:54+2])[0]
|
|
||||||
phnum = struct.unpack('<H', blob[56:56+2])[0]
|
|
||||||
else:
|
|
||||||
eprintf("Unknown architecture " + str(machnum))
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
for i in range(phnum):
|
|
||||||
off = phoff + i * phsz
|
|
||||||
#print(hex(off))
|
|
||||||
|
|
||||||
ptyp, poff, pva, ppa, pfsz, pmsz, pfl, pal = 0,0,0,0,0,0,0,0
|
|
||||||
if is32bit:
|
|
||||||
ptyp, poff, pva, ppa, pfsz, pmsz, pfl, pal = \
|
|
||||||
struct.unpack('<ILLLIIII', blob[off:off+phsz])
|
|
||||||
else:
|
|
||||||
ptyp, pfl, poff, pva, ppa, pfsz, pmsz, pal = \
|
|
||||||
struct.unpack('<IIQQQQQQ', blob[off:off+phsz])
|
|
||||||
|
|
||||||
if ptyp != 2: # PT_DYNAMIC
|
|
||||||
continue
|
|
||||||
|
|
||||||
#print(hex(poff))
|
|
||||||
|
|
||||||
# right after the dynamic section, the smol 'symtab'/'hashtab' is found
|
|
||||||
#
|
|
||||||
# note that on i386, every lib name is followed by an E9 byte
|
|
||||||
# if the next libname/first byte of the hash is null, the table has
|
|
||||||
# come to an end.
|
|
||||||
|
|
||||||
if is32bit:
|
|
||||||
j = poff
|
|
||||||
strtaboff = 0
|
|
||||||
while j < poff + pfsz:
|
|
||||||
tag, j = readint(blob, j)
|
|
||||||
ptr, j = readint(blob, j)
|
|
||||||
|
|
||||||
if tag == 5: # DT_STRTAB
|
|
||||||
strtaboff = ptr
|
|
||||||
elif tag == 1: # DT_NEEDED
|
|
||||||
bakoff = j
|
|
||||||
|
|
||||||
smoltaboff = strtaboff + ptr - (pva - poff)
|
|
||||||
j = smoltaboff
|
|
||||||
|
|
||||||
libname, j = readstr(blob, j)
|
|
||||||
if len(libname) == 0:
|
|
||||||
break
|
|
||||||
|
|
||||||
sys.stdout.write("* " + libname)
|
|
||||||
|
|
||||||
libs = list(find_libs(32, deflibs, libname))
|
|
||||||
print(" -> NOT FOUND" if len(libs) == 0 else (" -> " + libs[0]))
|
|
||||||
ht = dict({}) if len(libs) == 0 else build_hashtab(args.scanelf, libs[0])
|
|
||||||
|
|
||||||
while True:
|
|
||||||
hashv, j = readint(blob, j)
|
|
||||||
|
|
||||||
if (hashv & 0xFF) == 0:
|
|
||||||
break
|
|
||||||
|
|
||||||
sys.stdout.write(" * " + hex(hashv))
|
|
||||||
print(" -> NOT FOUND" if hashv not in ht else (" -> " + ht[hashv]))
|
|
||||||
|
|
||||||
j = bakoff
|
|
||||||
|
|
||||||
break
|
|
||||||
else: # 64-bit
|
|
||||||
eprintf("Currently unsuppored, sorry.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
|
|
@ -0,0 +1,153 @@
|
||||||
|
|
||||||
|
# stolen from the contrib folder in https://github.com/blackle/LZMA-Vizualizer
|
||||||
|
# (i.e. I'm stealing it from myself)
|
||||||
|
|
||||||
|
# custom elf parser because a standard one wouldn't be trustable because the
|
||||||
|
# ELFs we're parsing will be a bit wonky anyway
|
||||||
|
|
||||||
|
from struct import unpack
|
||||||
|
from typing import *
|
||||||
|
|
||||||
|
ELFCLASS32 = 1
|
||||||
|
ELFCLASS64 = 2
|
||||||
|
|
||||||
|
EM_386 = 3
|
||||||
|
EM_X86_64 = 62
|
||||||
|
|
||||||
|
PT_NULL = 0
|
||||||
|
PT_LOAD = 1
|
||||||
|
PT_DYNAMIC = 2
|
||||||
|
PT_INTERP = 3
|
||||||
|
|
||||||
|
DT_NULL = 0
|
||||||
|
DT_NEEDED = 1
|
||||||
|
DT_STRTAB = 5
|
||||||
|
DT_SYMTAB = 6
|
||||||
|
|
||||||
|
class Phdr(NamedTuple):
|
||||||
|
ptype: int
|
||||||
|
off : int
|
||||||
|
vaddr: int
|
||||||
|
paddr: int
|
||||||
|
filesz: int
|
||||||
|
memsz: int
|
||||||
|
flags: int
|
||||||
|
align: int
|
||||||
|
|
||||||
|
class Dyn(NamedTuple):
|
||||||
|
tag: int
|
||||||
|
val: int
|
||||||
|
|
||||||
|
class ELF(NamedTuple):
|
||||||
|
data : bytes
|
||||||
|
ident : bytes
|
||||||
|
eclass: int
|
||||||
|
mach : int
|
||||||
|
entry : int
|
||||||
|
phdrs : Sequence[Phdr]
|
||||||
|
dyn : Sequence[Dyn]
|
||||||
|
is32bit: bool
|
||||||
|
|
||||||
|
# yeah, there's some code duplication here
|
||||||
|
# idgaf
|
||||||
|
|
||||||
|
def parse_phdr32(data: bytes, phoff:int, phentsz:int, phnum:int) -> Sequence[Phdr]:
|
||||||
|
ps = []
|
||||||
|
for off in range(phoff, phoff+phentsz*phnum, phentsz):
|
||||||
|
ptype, off, vaddr, paddr, filesz, memsz, flags, align = \
|
||||||
|
unpack('<IIIIIIII', data[off:off+8*4])
|
||||||
|
p = Phdr(ptype, off, vaddr, paddr, filesz, memsz, flags, align)
|
||||||
|
ps.append(p)
|
||||||
|
|
||||||
|
return ps
|
||||||
|
|
||||||
|
def parse_dyn32(data: bytes, dynp: Phdr) -> Dyn:
|
||||||
|
ds = []
|
||||||
|
|
||||||
|
off = dynp.off
|
||||||
|
while True:
|
||||||
|
tag, val = unpack('<II', data[off:off+2*4])
|
||||||
|
ds.append(Dyn(tag, val))
|
||||||
|
|
||||||
|
if tag == DT_NULL: break
|
||||||
|
off = off + 2*4
|
||||||
|
|
||||||
|
return ds
|
||||||
|
|
||||||
|
def parse_32(data: bytes) -> ELF:
|
||||||
|
ident = data[:16]
|
||||||
|
eclass = data[4]
|
||||||
|
mach = unpack('<H', data[18:18+2])[0]
|
||||||
|
entry = unpack('<I', data[24:24+4])[0]
|
||||||
|
|
||||||
|
phoff = unpack('<I', data[28:28+4])[0]
|
||||||
|
phentsz = unpack('<H', data[42:42+2])[0]
|
||||||
|
phnum = unpack('<H', data[44:44+2])[0]
|
||||||
|
|
||||||
|
phdrs = parse_phdr32(data, phoff, phentsz, phnum)
|
||||||
|
dyn = None
|
||||||
|
|
||||||
|
for p in phdrs:
|
||||||
|
if p.ptype == PT_DYNAMIC:
|
||||||
|
dyn = parse_dyn32(data, p)
|
||||||
|
break
|
||||||
|
|
||||||
|
return ELF(data, ident, eclass, mach, entry, phdrs, dyn, True)
|
||||||
|
|
||||||
|
def parse_phdr64(data: bytes, phoff:int, phentsz:int, phnum:int) -> Sequence[Phdr]:
|
||||||
|
ps = []
|
||||||
|
for off in range(phoff, phoff+phentsz*phnum, phentsz):
|
||||||
|
# TODO
|
||||||
|
ptype, flags, off, vaddr, paddr, filesz, memsz, align = \
|
||||||
|
unpack('<IIQQQQQQ', data[off:off+2*4+6*8])
|
||||||
|
p = Phdr(ptype, off, vaddr, paddr, filesz, memsz, flags, align)
|
||||||
|
ps.append(p)
|
||||||
|
|
||||||
|
return ps
|
||||||
|
|
||||||
|
def parse_dyn64(data: bytes, dynp: Phdr) -> Dyn:
|
||||||
|
ds = []
|
||||||
|
|
||||||
|
off = dynp.off
|
||||||
|
while True:
|
||||||
|
tag, val = unpack('<QQ', data[off:off+2*8])
|
||||||
|
ds.append(Dyn(tag, val))
|
||||||
|
|
||||||
|
if tag == DT_NULL: break
|
||||||
|
off = off + 2*8
|
||||||
|
|
||||||
|
return ds
|
||||||
|
|
||||||
|
def parse_64(data: bytes) -> ELF:
|
||||||
|
ident = data[:16]
|
||||||
|
eclass = data[4]
|
||||||
|
mach = unpack('<H', data[18:18+2])[0]
|
||||||
|
entry = unpack('<Q', data[24:24+8])[0]
|
||||||
|
|
||||||
|
phoff = unpack('<Q', data[32:32+8])[0]
|
||||||
|
phentsz = unpack('<H', data[54:54+2])[0]
|
||||||
|
phnum = unpack('<H', data[56:56+2])[0]
|
||||||
|
|
||||||
|
phdrs = parse_phdr64(data, phoff, phentsz, phnum)
|
||||||
|
dyn = None
|
||||||
|
|
||||||
|
for p in phdrs:
|
||||||
|
if p.ptype == PT_DYNAMIC:
|
||||||
|
dyn = parse_dyn64(data, p)
|
||||||
|
break
|
||||||
|
|
||||||
|
return ELF(data, ident, eclass, mach, entry, phdrs, dyn, False)
|
||||||
|
|
||||||
|
def parse(data: bytes) -> ELF:
|
||||||
|
assert data[:4] == b'\x7FELF', "Not a valid ELF file" # good enough
|
||||||
|
|
||||||
|
ecls = data[4]
|
||||||
|
if ecls == ELFCLASS32: return parse_32(data)
|
||||||
|
elif ecls == ELFCLASS64: return parse_64(data)
|
||||||
|
else:
|
||||||
|
emch = unpack('<H', data[18:18+2])[0]
|
||||||
|
if emch == EM_386: return parse_32(data)
|
||||||
|
elif emch == EM_X86_64: return parse_64(data)
|
||||||
|
|
||||||
|
assert False, "bad E_CLASS %d" % ecls
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
|
||||||
|
# stolen from the contrib folder in https://github.com/blackle/LZMA-Vizualizer
|
||||||
|
# (i.e. I'm stealing it from myself)
|
||||||
|
|
||||||
|
import re
|
||||||
|
from typing import *
|
||||||
|
|
||||||
|
class CommonSym(NamedTuple):
|
||||||
|
name: str
|
||||||
|
size: int
|
||||||
|
file: str
|
||||||
|
class Discard(NamedTuple):
|
||||||
|
name: str
|
||||||
|
size: int
|
||||||
|
file: str
|
||||||
|
class MemCfg(NamedTuple):
|
||||||
|
name: str
|
||||||
|
org : int
|
||||||
|
size: int
|
||||||
|
class MMap(NamedTuple):
|
||||||
|
sect: str
|
||||||
|
org : int
|
||||||
|
#size: int
|
||||||
|
sym : str
|
||||||
|
file: str
|
||||||
|
class XRef(NamedTuple):
|
||||||
|
name: str
|
||||||
|
deff: str
|
||||||
|
reff: Sequence[str]
|
||||||
|
|
||||||
|
class LinkMap(NamedTuple):
|
||||||
|
common : Sequence[CommonSym]
|
||||||
|
discard: Sequence[Discard]
|
||||||
|
memcfg : Sequence[MemCfg]
|
||||||
|
mmap : Sequence[MMap]
|
||||||
|
xref : Sequence[XRef]
|
||||||
|
|
||||||
|
def parse_common( ls: Sequence[str]) -> Sequence[CommonSym]: return [] # TODO
|
||||||
|
def parse_discard(ls: Sequence[str]) -> Sequence[Discard ]: return [] # TODO
|
||||||
|
def parse_memcfg( ls: Sequence[str]) -> Sequence[MemCfg ]: return [] # TODO
|
||||||
|
def parse_xref( ls: Sequence[str]) -> Sequence[XRef ]: return [] # TODO
|
||||||
|
|
||||||
|
def parse_mmap(ls: Sequence[str]) -> Sequence[MMap]:
|
||||||
|
rrr = []
|
||||||
|
|
||||||
|
bigsect = None
|
||||||
|
section = None
|
||||||
|
curfile = None
|
||||||
|
#size = -1
|
||||||
|
|
||||||
|
for l in ls:
|
||||||
|
def matcher(mobj):
|
||||||
|
return mobj.group(0).replace(' ', '_')
|
||||||
|
l = re.sub(r"\*\(.*\)", matcher, l)
|
||||||
|
#print(repr(l))
|
||||||
|
s = l.strip(); w = s.split()
|
||||||
|
|
||||||
|
if s.startswith('LOAD ') or s.startswith('OUTPUT('): continue#break
|
||||||
|
|
||||||
|
if l[0] != ' ':
|
||||||
|
bigsect = w[0]
|
||||||
|
del w[0]
|
||||||
|
elif l[1] != ' ':
|
||||||
|
section = w[0]
|
||||||
|
del w[0]
|
||||||
|
|
||||||
|
if len(w) == 0 or w[0] == "[!provide]":
|
||||||
|
continue # addr placed on next line for prettyprinting reasons
|
||||||
|
|
||||||
|
#print(repr(l), w[0])
|
||||||
|
assert w[0].startswith("0x"), "welp, bad symbol addr"
|
||||||
|
|
||||||
|
addr = int(w[0], 16)
|
||||||
|
|
||||||
|
size = -1
|
||||||
|
symn = ""
|
||||||
|
if w[1].startswith("0x"): # filename will prolly follow
|
||||||
|
size = int(w[1], 16)
|
||||||
|
curfile = w[2] if len(w) > 2 else ""
|
||||||
|
else: symn = w[1]
|
||||||
|
|
||||||
|
if len(symn) > 0:
|
||||||
|
rrr.append(MMap(section, addr, symn, curfile))
|
||||||
|
|
||||||
|
return rrr
|
||||||
|
|
||||||
|
def parse(s: str) -> LinkMap:
|
||||||
|
COMMON = 0
|
||||||
|
DISCARD = 1
|
||||||
|
MEMCFG = 2
|
||||||
|
MMAP = 3
|
||||||
|
XREF = 4
|
||||||
|
|
||||||
|
curpt = -1
|
||||||
|
|
||||||
|
commonl, discardl, memcfgl, mmapl, xrefl = [], [], [], [], []
|
||||||
|
|
||||||
|
for l in s.split('\n'):
|
||||||
|
if len(l.strip()) == 0: continue
|
||||||
|
|
||||||
|
ls = l.strip()
|
||||||
|
if ls == "Allocating common symbols": curpt = COMMON
|
||||||
|
elif ls == "Discarded input sections": curpt = DISCARD
|
||||||
|
elif ls == "Memory Configuration": curpt = MEMCFG
|
||||||
|
elif ls == "Linker script and memory map": curpt = MMAP
|
||||||
|
elif ls == 'Cross Reference Table': curpt = XREF
|
||||||
|
elif curpt == COMMON : commonl.append(l)
|
||||||
|
elif curpt == DISCARD: discardl.append(l)
|
||||||
|
elif curpt == MEMCFG : memcfgl.append(l)
|
||||||
|
elif curpt == MMAP : mmapl.append(l)
|
||||||
|
elif curpt == XREF : xrefl.append(l)
|
||||||
|
else:
|
||||||
|
assert False, "bad line %s" % ls
|
||||||
|
|
||||||
|
return LinkMap(parse_common(commonl), parse_discard(discardl), \
|
||||||
|
parse_memcfg(memcfgl), parse_mmap(mmapl), parse_xref(xrefl))
|
||||||
|
|
|
@ -0,0 +1,175 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os.path, struct, sys
|
||||||
|
import argparse, glob, shutil, subprocess
|
||||||
|
|
||||||
|
import hackyelf, linkmap
|
||||||
|
from smolshared import *
|
||||||
|
from smolparse import *
|
||||||
|
|
||||||
|
def readbyte(blob, off): return struct.unpack('<B', blob[off:off+1])[0], (off+1)
|
||||||
|
def readint(blob, off): return struct.unpack('<I', blob[off:off+4])[0], (off+4)
|
||||||
|
def readlong(blob, off): return struct.unpack('<Q', blob[off:off+8])[0], (off+8)
|
||||||
|
def readstr(blob, off):
|
||||||
|
text = bytearray()
|
||||||
|
while True:
|
||||||
|
char, off = readbyte(blob, off)
|
||||||
|
if char == 0: break
|
||||||
|
|
||||||
|
text.append(char)
|
||||||
|
|
||||||
|
return text.decode('utf-8'), off
|
||||||
|
|
||||||
|
def get_def_libpaths(cc_bin, is32bit):
|
||||||
|
# FIXME: HACK
|
||||||
|
if is32bit: return ['/usr/lib32/','/lib32/']
|
||||||
|
|
||||||
|
return get_cc_paths(cc_bin)['libraries']
|
||||||
|
|
||||||
|
def find_libs(deflibs, libname):
|
||||||
|
dirs = os.environ['LD_LIBRARY_PATH'].split(':') + deflibs
|
||||||
|
|
||||||
|
for d in dirs:
|
||||||
|
for f in glob.glob(glob.escape(d + '/' + libname) + '*'): yield f
|
||||||
|
|
||||||
|
def build_hashtab(scanelf_bin, lib):
|
||||||
|
out = subprocess.check_output([scanelf_bin, '-B', '-F', '%s', '-s', '%pd%*', lib],
|
||||||
|
stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
blah = set(out.decode('utf-8').split('\n'))
|
||||||
|
ret = dict({})
|
||||||
|
|
||||||
|
for x in blah:
|
||||||
|
y = x.split()
|
||||||
|
if len(y) != 7: continue
|
||||||
|
ret[hash_djb2(y[6])] = y[6]
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def addr2off(elf, addr):
|
||||||
|
for x in elf.phdrs:
|
||||||
|
if x.ptype != hackyelf.PT_LOAD: continue
|
||||||
|
|
||||||
|
if addr >= x.vaddr and addr < x.vaddr + x.memsz:
|
||||||
|
aoff = addr - x.vaddr
|
||||||
|
assert aoff < x.filesz, ".bss address!"
|
||||||
|
return aoff + x.off
|
||||||
|
|
||||||
|
assert False, "Address %08x not in the static address range!" % addr
|
||||||
|
|
||||||
|
def get_needed_libs(elf, blob):
|
||||||
|
assert elf.dyn is not None, "No DYNAMIC table present in the ELF file!"
|
||||||
|
|
||||||
|
strtabs = [x.val for x in elf.dyn if x.tag == hackyelf.DT_STRTAB]
|
||||||
|
assert len(strtabs) == 1, "Only one DT_STRTAB may be present in an ELF file."
|
||||||
|
strtab = strtabs[0]
|
||||||
|
|
||||||
|
return [readstr(blob,addr2off(elf, strtab+x.val))[0]
|
||||||
|
for x in elf.dyn if x.tag == hackyelf.DT_NEEDED]
|
||||||
|
|
||||||
|
def get_hashtbl(elf, blob, args):
|
||||||
|
htaddr = None
|
||||||
|
if args.map is not None:
|
||||||
|
lmap = linkmap.parse(args.map.read())
|
||||||
|
tabs = [x for x in lmap.mmap if x.sym == '_symbols']
|
||||||
|
assert len(tabs) == 1, "One '_symbols' symbol must be present."
|
||||||
|
htaddr = tabs[0].org
|
||||||
|
elif elf.is32bit:
|
||||||
|
txtoff = addr2off(elf, elf.entry)
|
||||||
|
# scan for 'push IMM32'
|
||||||
|
while blob[txtoff] != 0x68:
|
||||||
|
txtoff = txtoff + 1
|
||||||
|
assert txtoff < len(blob), "wtf??? (can't find a push IMM32 instruction which pushes the hashtable address)"
|
||||||
|
txtoff = txtoff + 1
|
||||||
|
|
||||||
|
htaddr = struct.unpack('<I', blob[txtoff:txtoff+4])[0]
|
||||||
|
else: # 64-bit
|
||||||
|
txtoff = addr2off(elf, elf.entry)
|
||||||
|
# scan for 'push IMM32'
|
||||||
|
# but the first one we'll encounter pushes the entrypoint addr!
|
||||||
|
while blob[txtoff] != 0x68:
|
||||||
|
txtoff = txtoff + 1
|
||||||
|
assert txtoff < len(blob), "wtf??? (can't find a push IMM32 instruction which pushes the entrypoint address)"
|
||||||
|
txtoff = txtoff + 1
|
||||||
|
# now we can look for the interesting address
|
||||||
|
while blob[txtoff] != 0x68:
|
||||||
|
txtoff = txtoff + 1
|
||||||
|
assert txtoff < len(blob), "wtf??? (can't find a push IMM32 instruction which pushes the hashtable address)"
|
||||||
|
txtoff = txtoff + 1
|
||||||
|
|
||||||
|
htaddr = struct.unpack('<I', blob[txtoff:txtoff+4])[0]
|
||||||
|
|
||||||
|
assert htaddr is not None, "wtf? (no hashtable address)"
|
||||||
|
print("Hash table address: 0x%08x" % htaddr)
|
||||||
|
htoff = addr2off(elf, htaddr)
|
||||||
|
|
||||||
|
tbl = []
|
||||||
|
while True:
|
||||||
|
if len(blob)-htoff < 4:
|
||||||
|
if struct.unpack('<B', blob[htoff:htoff+1])[0] == 0:
|
||||||
|
break
|
||||||
|
val = struct.unpack('<I', blob[htoff:htoff+4])[0]
|
||||||
|
if (val & 0xFF) == 0: break
|
||||||
|
tbl.append(val)
|
||||||
|
htoff = htoff + (4 if elf.is32bit else 8)
|
||||||
|
|
||||||
|
return tbl
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('input', type=argparse.FileType('rb'),
|
||||||
|
default=sys.stdin.buffer, help="input file")
|
||||||
|
parser.add_argument('--cc',
|
||||||
|
default=shutil.which('cc'), help="C compiler binary")
|
||||||
|
parser.add_argument('--scanelf',
|
||||||
|
default=shutil.which('scanelf'), help="scanelf binary")
|
||||||
|
parser.add_argument('--map', type=argparse.FileType('r'), help=\
|
||||||
|
"Get the address of the symbol hash table from the "+\
|
||||||
|
"linker map output instead of attempting to parse the"+\
|
||||||
|
" binary.")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
blob = args.input.read()
|
||||||
|
elf = hackyelf.parse(blob)
|
||||||
|
|
||||||
|
deflibs = get_def_libpaths(args.cc, elf.is32bit)
|
||||||
|
needed = get_needed_libs(elf, blob)
|
||||||
|
neededpaths = dict((l,list(find_libs(deflibs, l))[0]) for l in needed)
|
||||||
|
|
||||||
|
htbl = get_hashtbl(elf, blob, args)
|
||||||
|
|
||||||
|
libhashes = dict((l, build_hashtab(args.scanelf, neededpaths[l])) for l in needed)
|
||||||
|
|
||||||
|
hashresolves = dict({})
|
||||||
|
noresolves = []
|
||||||
|
# TODO: group by libs
|
||||||
|
for x in htbl:
|
||||||
|
done = False
|
||||||
|
for l in libhashes.keys():
|
||||||
|
v = libhashes[l]
|
||||||
|
if x in v:
|
||||||
|
if l not in hashresolves: hashresolves[l] = dict({})
|
||||||
|
hashresolves[l][x] = v[x]
|
||||||
|
done = True
|
||||||
|
break
|
||||||
|
if not done: noresolves.append(x)
|
||||||
|
|
||||||
|
for l in hashresolves.keys():
|
||||||
|
print("%s:" % l)
|
||||||
|
v = hashresolves[l]
|
||||||
|
for x in v.keys():
|
||||||
|
print("\t%08x -> %s" % (x, v[x]))
|
||||||
|
|
||||||
|
if len(noresolves) > 0:
|
||||||
|
print("UNRESOLVED:")
|
||||||
|
for x in noresolves: print("\t%08x" % x)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
rv = main()
|
||||||
|
if rv is None: pass
|
||||||
|
else:
|
||||||
|
try: sys.exit(int(rv))
|
||||||
|
except: sys.exit(1)
|
||||||
|
|
|
@ -105,7 +105,7 @@ def get_cc_paths(cc_bin):
|
||||||
|
|
||||||
return paths
|
return paths
|
||||||
|
|
||||||
def is_valid_elf(f):
|
def is_valid_elf(f): # Good Enough(tm)
|
||||||
with open(f, 'rb') as ff: return ff.read(4) == b'\x7FELF'
|
with open(f, 'rb') as ff: return ff.read(4) == b'\x7FELF'
|
||||||
|
|
||||||
def find_lib(spaths, wanted):
|
def find_lib(spaths, wanted):
|
||||||
|
|
Loading…
Reference in New Issue