mirror of https://github.com/Shizmob/smol
Remove dependency on scanelf by using readelf -s
This commit is contained in:
parent
d7135e3fe9
commit
405d8ebf1e
|
@ -9,7 +9,6 @@ PoC by Shiz, bugfixing and 64-bit version by PoroCYon.
|
||||||
* GCC (not clang, as the latter doesn't support `nolto-rel` output), GNU ld,
|
* GCC (not clang, as the latter doesn't support `nolto-rel` output), GNU ld,
|
||||||
binutils, GNU make, ...
|
binutils, GNU make, ...
|
||||||
* nasm 2.13 or newer
|
* nasm 2.13 or newer
|
||||||
* `scanelf` from `pax-utils`
|
|
||||||
* Python 3
|
* Python 3
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
@ -25,7 +24,7 @@ the smol startup/symbol resolving code will jump to an undefined location.
|
||||||
```
|
```
|
||||||
usage: smold.py [-h] [-m TARGET] [-l LIB] [-L DIR] [-s] [-n] [-d] [-fuse-interp] [-falign-stack] [-fuse-nx]
|
usage: smold.py [-h] [-m TARGET] [-l LIB] [-L DIR] [-s] [-n] [-d] [-fuse-interp] [-falign-stack] [-fuse-nx]
|
||||||
[-fuse-dnload-loader] [-fskip-zero-value] [-fuse-dt-debug] [-fuse-dl-fini] [-fskip-entries]
|
[-fuse-dnload-loader] [-fskip-zero-value] [-fuse-dt-debug] [-fuse-dl-fini] [-fskip-entries]
|
||||||
[-fno-start-arg] [-funsafe-dynamic] [--nasm NASM] [--cc CC] [--scanelf SCANELF] [--readelf READELF]
|
[-fno-start-arg] [-funsafe-dynamic] [--nasm NASM] [--cc CC] [--readelf READELF]
|
||||||
[--cflags CFLAGS] [--asflags ASFLAGS] [--ldflags LDFLAGS] [--smolrt SMOLRT] [--smolld SMOLLD]
|
[--cflags CFLAGS] [--asflags ASFLAGS] [--ldflags LDFLAGS] [--smolrt SMOLRT] [--smolld SMOLLD]
|
||||||
[--verbose] [--keeptmp]
|
[--verbose] [--keeptmp]
|
||||||
input [input ...] output
|
input [input ...] output
|
||||||
|
@ -71,7 +70,6 @@ optional arguments:
|
||||||
entire binary as the Dyn table, so only enable this if you're sure this won't break things!
|
entire binary as the Dyn table, so only enable this if you're sure this won't break things!
|
||||||
--nasm NASM which nasm binary to use
|
--nasm NASM which nasm binary to use
|
||||||
--cc CC which cc binary to use (MUST BE GCC!)
|
--cc CC which cc binary to use (MUST BE GCC!)
|
||||||
--scanelf SCANELF which scanelf binary to use
|
|
||||||
--readelf READELF which readelf binary to use
|
--readelf READELF which readelf binary to use
|
||||||
--cflags CFLAGS Flags to pass to the C compiler for the relinking step
|
--cflags CFLAGS Flags to pass to the C compiler for the relinking step
|
||||||
--asflags ASFLAGS Flags to pass to the assembler when creating the ELF header and runtime startup code
|
--asflags ASFLAGS Flags to pass to the assembler when creating the ELF header and runtime startup code
|
||||||
|
|
|
@ -4,6 +4,7 @@ import os.path
|
||||||
import subprocess
|
import subprocess
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
|
import re
|
||||||
|
|
||||||
from .shared import *
|
from .shared import *
|
||||||
|
|
||||||
|
@ -150,14 +151,38 @@ def find_lib(spaths, wanted):
|
||||||
|
|
||||||
error("E: couldn't find library '" + wanted + "'.")
|
error("E: couldn't find library '" + wanted + "'.")
|
||||||
|
|
||||||
def find_libs(spaths, wanted): return map(lambda l: find_lib(spaths, l), wanted)
|
def find_libs(spaths, wanted):
|
||||||
|
return [find_lib(spaths, l) for l in wanted]
|
||||||
|
|
||||||
def find_symbol(scanelf_bin, libraries, libnames, symbol):
|
def list_symbols(readelf_bin, lib):
|
||||||
output = subprocess.check_output([scanelf_bin, '-B', '-F' '%s %S', '-s', \
|
out = subprocess.check_output([readelf_bin, '-sW', lib], stderr=subprocess.DEVNULL)
|
||||||
'+{}'.format(symbol)] + libraries, stderr=subprocess.DEVNULL)
|
|
||||||
for entry in output.decode('utf-8').splitlines():
|
|
||||||
sym, soname, path = entry.split(' ', 2)
|
|
||||||
if symbol in sym.split(',') and \
|
|
||||||
any(soname.startswith('lib'+l) for l in libnames):
|
|
||||||
return soname
|
|
||||||
|
|
||||||
|
lines = set(out.decode('utf-8').split('\n'))
|
||||||
|
symbols = []
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
fields = re.split(r"\s+", line)
|
||||||
|
if len(fields) != 9:
|
||||||
|
continue
|
||||||
|
|
||||||
|
vis, ndx, symbol = fields[6:9]
|
||||||
|
if vis != "DEFAULT" or ndx == "UND":
|
||||||
|
continue
|
||||||
|
|
||||||
|
# strip away GNU versions
|
||||||
|
symbol = re.sub(r"@@.*$", "", symbol)
|
||||||
|
symbols.append(symbol)
|
||||||
|
|
||||||
|
return symbols
|
||||||
|
|
||||||
|
def build_symbol_map(readelf_bin, libraries):
|
||||||
|
# create dictionary that maps symbols to libraries that provide them
|
||||||
|
symbol_map = {}
|
||||||
|
for lib in libraries:
|
||||||
|
symbols = list_symbols(readelf_bin, lib)
|
||||||
|
for symbol in symbols:
|
||||||
|
if symbol not in symbol_map:
|
||||||
|
symbol_map[symbol] = []
|
||||||
|
soname = lib.split("/")[-1]
|
||||||
|
symbol_map[symbol].append(soname)
|
||||||
|
return symbol_map
|
||||||
|
|
14
smold.py
14
smold.py
|
@ -85,8 +85,6 @@ def main():
|
||||||
help="which nasm binary to use")
|
help="which nasm binary to use")
|
||||||
parser.add_argument('--cc', default=os.getenv('CC') or shutil.which('cc'), \
|
parser.add_argument('--cc', default=os.getenv('CC') or shutil.which('cc'), \
|
||||||
help="which cc binary to use (MUST BE GCC!)")
|
help="which cc binary to use (MUST BE GCC!)")
|
||||||
parser.add_argument('--scanelf', default=os.getenv('SCANELF') or shutil.which('scanelf'), \
|
|
||||||
help="which scanelf binary to use")
|
|
||||||
parser.add_argument('--readelf', default=os.getenv('READELF') or shutil.which('readelf'), \
|
parser.add_argument('--readelf', default=os.getenv('READELF') or shutil.which('readelf'), \
|
||||||
help="which readelf binary to use")
|
help="which readelf binary to use")
|
||||||
|
|
||||||
|
@ -134,7 +132,7 @@ def main():
|
||||||
if args.fifunc_support: args.asflags.insert(0, "-DIFUNC_SUPPORT")
|
if args.fifunc_support: args.asflags.insert(0, "-DIFUNC_SUPPORT")
|
||||||
if args.fifunc_strict_cconv: args.asflags.insert(0, "-DIFUNC_CORRECT_CCONV")
|
if args.fifunc_strict_cconv: args.asflags.insert(0, "-DIFUNC_CORRECT_CCONV")
|
||||||
|
|
||||||
for x in ['nasm','cc','scanelf','readelf']:
|
for x in ['nasm','cc','readelf']:
|
||||||
val = args.__dict__[x]
|
val = args.__dict__[x]
|
||||||
if val is None or not os.path.isfile(val):
|
if val is None or not os.path.isfile(val):
|
||||||
error("'%s' binary%s not found" %
|
error("'%s' binary%s not found" %
|
||||||
|
@ -169,13 +167,17 @@ def main():
|
||||||
syms = get_needed_syms(args.readelf, objinput)
|
syms = get_needed_syms(args.readelf, objinput)
|
||||||
spaths = args.libdir + cc_paths['libraries']
|
spaths = args.libdir + cc_paths['libraries']
|
||||||
libraries = cc_paths['libraries']
|
libraries = cc_paths['libraries']
|
||||||
libs = list(find_libs(spaths, args.library))
|
libs = find_libs(spaths, args.library)
|
||||||
if args.verbose: eprintf("libs = %s" % str(libs))
|
if args.verbose: eprintf("libs = %s" % str(libs))
|
||||||
|
libs_symbol_map = build_symbol_map(args.readelf, libs)
|
||||||
symbols = {}
|
symbols = {}
|
||||||
for symbol, reloc in syms:
|
for symbol, reloc in syms:
|
||||||
library = find_symbol(args.scanelf, libs, args.library, symbol)
|
if symbol not in libs_symbol_map:
|
||||||
if not library:
|
|
||||||
error("could not find symbol: {}".format(symbol))
|
error("could not find symbol: {}".format(symbol))
|
||||||
|
libs_for_symbol = libs_symbol_map[symbol]
|
||||||
|
if len(libs_for_symbol) > 1:
|
||||||
|
error("E: the symbol '" + symbol + "' is provided by more than one library: " + str(libs_for_symbol))
|
||||||
|
library = libs_for_symbol[0]
|
||||||
symbols.setdefault(library, [])
|
symbols.setdefault(library, [])
|
||||||
symbols[library].append((symbol, reloc))
|
symbols[library].append((symbol, reloc))
|
||||||
|
|
||||||
|
|
21
smoldd.py
21
smoldd.py
|
@ -36,19 +36,10 @@ def find_libs(deflibs, libname):
|
||||||
for d in dirs:
|
for d in dirs:
|
||||||
for f in glob.glob(glob.escape(d + '/' + libname) + '*'): yield f
|
for f in glob.glob(glob.escape(d + '/' + libname) + '*'): yield f
|
||||||
|
|
||||||
def build_hashtab(scanelf_bin, lib):
|
def build_hashtab(readelf_bin, lib):
|
||||||
out = subprocess.check_output([scanelf_bin, '-B', '-F', '%s', '-s', '%pd%*', lib],
|
symbols = list_symbols(readelf_bin, lib)
|
||||||
stderr=subprocess.DEVNULL)
|
|
||||||
|
|
||||||
blah = set(out.decode('utf-8').split('\n'))
|
return { hash_djb2(symbol):symbol for symbol in symbols }
|
||||||
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):
|
def addr2off(elf, addr):
|
||||||
for x in elf.phdrs:
|
for x in elf.phdrs:
|
||||||
|
@ -147,8 +138,8 @@ def main():
|
||||||
default=sys.stdin.buffer, help="input file")
|
default=sys.stdin.buffer, help="input file")
|
||||||
parser.add_argument('--cc',
|
parser.add_argument('--cc',
|
||||||
default=shutil.which('cc'), help="C compiler binary")
|
default=shutil.which('cc'), help="C compiler binary")
|
||||||
parser.add_argument('--scanelf',
|
parser.add_argument('--readelf',
|
||||||
default=shutil.which('scanelf'), help="scanelf binary")
|
default=shutil.which('readelf'), help="readelf binary")
|
||||||
parser.add_argument('--map', type=argparse.FileType('r'), help=\
|
parser.add_argument('--map', type=argparse.FileType('r'), help=\
|
||||||
"Get the address of the symbol hash table from the "+\
|
"Get the address of the symbol hash table from the "+\
|
||||||
"linker map output instead of attempting to parse the"+\
|
"linker map output instead of attempting to parse the"+\
|
||||||
|
@ -164,7 +155,7 @@ def main():
|
||||||
|
|
||||||
htbl = get_hashtbl(elf, blob, args)
|
htbl = get_hashtbl(elf, blob, args)
|
||||||
|
|
||||||
libhashes = dict((l, build_hashtab(args.scanelf, neededpaths[l])) for l in needed)
|
libhashes = dict((l, build_hashtab(args.readelf, neededpaths[l])) for l in needed)
|
||||||
|
|
||||||
hashresolves = dict({})
|
hashresolves = dict({})
|
||||||
noresolves = []
|
noresolves = []
|
||||||
|
|
Loading…
Reference in New Issue