mirror of https://github.com/Shizmob/smol
reorganize everything in a single invocation (take 2)
This commit is contained in:
parent
09acda915d
commit
51db8f8714
83
Makefile
83
Makefile
|
@ -1,10 +1,10 @@
|
||||||
OBJDIR := obj
|
OBJDIR := obj
|
||||||
BINDIR := bin
|
BINDIR := bin
|
||||||
SRCDIR := rt
|
SRCDIR := rt
|
||||||
PYDIR := src
|
|
||||||
LDDIR := ld
|
|
||||||
TESTDIR:= test
|
TESTDIR:= test
|
||||||
|
|
||||||
|
NASM ?= nasm
|
||||||
|
|
||||||
BITS ?= $(shell getconf LONG_BIT)
|
BITS ?= $(shell getconf LONG_BIT)
|
||||||
|
|
||||||
# -mpreferred-stack-boundary=3 messes up the stack and kills SSE!
|
# -mpreferred-stack-boundary=3 messes up the stack and kills SSE!
|
||||||
|
@ -23,58 +23,38 @@ CXXOPTFLAGS=$(COPTFLAGS) -fno-exceptions \
|
||||||
CFLAGS=-Wall -Wextra -Wpedantic -std=gnu11 -nostartfiles -fno-PIC $(COPTFLAGS) #-DUSE_DL_FINI
|
CFLAGS=-Wall -Wextra -Wpedantic -std=gnu11 -nostartfiles -fno-PIC $(COPTFLAGS) #-DUSE_DL_FINI
|
||||||
CXXFLAGS=-Wall -Wextra -Wpedantic -std=c++11 $(CXXOPTFLAGS) -nostartfiles -fno-PIC
|
CXXFLAGS=-Wall -Wextra -Wpedantic -std=c++11 $(CXXOPTFLAGS) -nostartfiles -fno-PIC
|
||||||
|
|
||||||
ASFLAGS=-I $(SRCDIR)/
|
|
||||||
LDFLAGS_ :=
|
|
||||||
ifeq ($(BITS),32)
|
|
||||||
# I think prescott is basically nocona but 32-bit only, althought I'm not sure
|
|
||||||
# if this one is optimal
|
|
||||||
CFLAGS += -m32 -march=prescott
|
|
||||||
LDFLAGS += -m32
|
|
||||||
ASFLAGS += -f elf32
|
|
||||||
LDFLAGS_ := -m32
|
|
||||||
else
|
|
||||||
# I've heard nocona gets slightly smaller binaries than core2
|
|
||||||
CFLAGS += -m64 -march=nocona
|
|
||||||
LDFLAGS += -m64
|
|
||||||
ASFLAGS += -f elf64
|
|
||||||
LDFLAGS_ := -m64
|
|
||||||
endif
|
|
||||||
LDFLAGS += -nostartfiles -nostdlib
|
|
||||||
LDFLAGS_ := $(LDFLAGS_) -T $(LDDIR)/link.ld -Wl,--oformat=binary $(LDFLAGS)
|
|
||||||
|
|
||||||
CFLAGS += -m$(BITS) $(shell pkg-config --cflags sdl2)
|
CFLAGS += -m$(BITS) $(shell pkg-config --cflags sdl2)
|
||||||
CXXFLAGS += -m$(BITS) $(shell pkg-config --cflags sdl2)
|
CXXFLAGS += -m$(BITS) $(shell pkg-config --cflags sdl2)
|
||||||
|
|
||||||
LIBS=-lc
|
ifeq ($(BITS),32)
|
||||||
|
# I think prescott is basically nocona but 32-bit only, althought I'm not sure
|
||||||
|
# if this one is optimal
|
||||||
|
CFLAGS += -march=prescott
|
||||||
|
else
|
||||||
|
# I've heard nocona gets slightly smaller binaries than core2
|
||||||
|
CFLAGS += -march=nocona
|
||||||
|
endif
|
||||||
|
|
||||||
SMOLFLAGS +=
|
LIBS = $(filter-out -pthread,$(shell pkg-config --libs sdl2)) -lX11 -lc #-lGL
|
||||||
ASFLAGS += -DALIGN_STACK -DUSE_INTERP #-DUSE_DNLOAD_LOADER
|
|
||||||
#-DUSE_DNLOAD_LOADER #-DUSE_DT_DEBUG #-DUSE_DL_FINI #-DNO_START_ARG #-DUNSAFE_DYNAMIC
|
PWD ?= .
|
||||||
|
|
||||||
|
SMOLFLAGS = --smolrt "$(PWD)/rt" --smolld "$(PWD)/ld" \
|
||||||
|
-falign-stack -fuse-interp \
|
||||||
|
--verbose #--keeptmp
|
||||||
|
# -fuse-dnload-loader -fskip-zero-value -fuse-nx -fskip-entries -fuse-dt-debug
|
||||||
|
# -fuse-dl-fini -fno-start-arg -funsafe-dynamic
|
||||||
|
|
||||||
NASM ?= nasm
|
|
||||||
PYTHON3 ?= python3
|
PYTHON3 ?= python3
|
||||||
|
|
||||||
all: $(BINDIR)/hello-crt $(BINDIR)/sdl-crt $(BINDIR)/flag $(BINDIR)/hello-_start
|
all: $(BINDIR)/hello-crt $(BINDIR)/sdl-crt $(BINDIR)/flag $(BINDIR)/hello-_start
|
||||||
|
|
||||||
LIBS += $(filter-out -pthread,$(shell pkg-config --libs sdl2)) -lX11 #-lGL
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@$(RM) -vrf $(OBJDIR) $(BINDIR)
|
@$(RM) -vrf $(OBJDIR) $(BINDIR)
|
||||||
|
|
||||||
%/:
|
%/:
|
||||||
@mkdir -vp "$@"
|
@mkdir -vp "$@"
|
||||||
|
|
||||||
# TODO: handle this in a more graceful and future-proof way!
|
|
||||||
ifneq ($(findstring (GCC) 9,$(shell $(CC) --version)),)
|
|
||||||
INCLINKOPT := -flinker-output=nolto-rel
|
|
||||||
else
|
|
||||||
ifneq ($(findstring (GCC) 10,$(shell $(CC) --version)),)
|
|
||||||
INCLINKOPT := -flinker-output=nolto-rel
|
|
||||||
else
|
|
||||||
INCLINKOPT :=
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
.SECONDARY:
|
.SECONDARY:
|
||||||
|
|
||||||
$(OBJDIR)/%.lto.o: $(SRCDIR)/%.c $(OBJDIR)/
|
$(OBJDIR)/%.lto.o: $(SRCDIR)/%.c $(OBJDIR)/
|
||||||
|
@ -87,26 +67,13 @@ $(OBJDIR)/%.o: $(SRCDIR)/%.c $(OBJDIR)/
|
||||||
$(OBJDIR)/%.o: $(TESTDIR)/%.c $(OBJDIR)/
|
$(OBJDIR)/%.o: $(TESTDIR)/%.c $(OBJDIR)/
|
||||||
$(CC) $(CFLAGS) -c "$<" -o "$@"
|
$(CC) $(CFLAGS) -c "$<" -o "$@"
|
||||||
|
|
||||||
$(OBJDIR)/%.start.o: $(OBJDIR)/%.lto.o $(OBJDIR)/crt1.lto.o
|
$(BINDIR)/%: $(OBJDIR)/%.o $(BINDIR)/
|
||||||
$(CC) $(LDFLAGS) -r $(INCLINKOPT) -o "$@" $^
|
$(PYTHON3) ./smold.py $(SMOLFLAGS) $(LIBS) "$<" "$@"
|
||||||
|
$(PYTHON3) ./smoltrunc.py "$@" "$(OBJDIR)/$(notdir $@)" && mv "$(OBJDIR)/$(notdir $@)" "$@" && chmod +x "$@"
|
||||||
|
|
||||||
$(OBJDIR)/symbols.%.asm: $(OBJDIR)/%.o
|
$(BINDIR)/%-crt: $(OBJDIR)/%.lto.o $(OBJDIR)/crt1.lto.o $(BINDIR)/
|
||||||
$(PYTHON3) $(PYDIR)/smol.py $(SMOLFLAGS) $(LIBS) "$<" "$@"
|
$(PYTHON3) ./smold.py $(SMOLFLAGS) --ldflags=-Wl,-Map=$(BINDIR)/$*-crt.map $(LIBS) "$<" $(OBJDIR)/crt1.lto.o "$@"
|
||||||
|
$(PYTHON3) ./smoltrunc.py "$@" "$(OBJDIR)/$(notdir $@)" && mv "$(OBJDIR)/$(notdir $@)" "$@" && chmod +x "$@"
|
||||||
$(OBJDIR)/stub.%.o: $(OBJDIR)/symbols.%.asm $(SRCDIR)/header32.asm \
|
|
||||||
$(SRCDIR)/loader32.asm
|
|
||||||
$(NASM) $(ASFLAGS) $< -o $@
|
|
||||||
|
|
||||||
$(OBJDIR)/stub.%.start.o: $(OBJDIR)/symbols.%.start.asm $(SRCDIR)/header32.asm \
|
|
||||||
$(SRCDIR)/loader32.asm
|
|
||||||
$(NASM) $(ASFLAGS) $< -o $@
|
|
||||||
|
|
||||||
$(BINDIR)/%: $(OBJDIR)/%.o $(OBJDIR)/stub.%.o $(BINDIR)/
|
|
||||||
$(CC) -Wl,-Map=$(BINDIR)/$*.map $(LDFLAGS_) $(OBJDIR)/$*.o $(OBJDIR)/stub.$*.o -o "$@"
|
|
||||||
./rmtrailzero.py "$@" "$(OBJDIR)/$(notdir $@)" && mv "$(OBJDIR)/$(notdir $@)" "$@" && chmod +x "$@"
|
|
||||||
|
|
||||||
$(BINDIR)/%-crt: $(OBJDIR)/%.start.o $(OBJDIR)/stub.%.start.o $(BINDIR)/
|
|
||||||
$(CC) -Wl,-Map=$(BINDIR)/$*-crt.map $(LDFLAGS_) $(OBJDIR)/$*.start.o $(OBJDIR)/stub.$*.start.o -o "$@"
|
|
||||||
|
|
||||||
.PHONY: all clean
|
.PHONY: all clean
|
||||||
|
|
||||||
|
|
99
README.md
99
README.md
|
@ -6,56 +6,33 @@ PoC by Shiz, bugfixing and 64-bit version by PoroCYon.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
* A C compiler (preferably GCC), GNU ld, binutils, GNU make, ...
|
* GCC (not clang, as the latter doesn't support `nolto-rel` output), GNU ld,
|
||||||
|
binutils, GNU make, ...
|
||||||
* nasm 2.13 or newer
|
* nasm 2.13 or newer
|
||||||
* scanelf from pax-utils
|
* `scanelf` from `pax-utils`
|
||||||
* Python 3
|
* Python 3
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
***NOTE***: Your entrypoint (`_start`) ***must*** be in a section called
|
||||||
|
`.text.startup._start`! Otherwise, the linker script will fail silently, and
|
||||||
|
the smol startup/symbol resolving code will jump to an undefined location.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./smol.py -lfoo -lbar input.o... smol-output.asm
|
./smold.py --use_interp --align_stack [--opts...] -lfoo -lbar input.o... output.elf
|
||||||
nasm -I src/ [-Doption ...] -o nasm-output.o smol-output.asm
|
|
||||||
ld -T ld/link.ld --oformat=binary -o output.elf nasm-output.o input.o...
|
|
||||||
# or cc -T ld/link.ld -Wl,--oformat=binary -o output.elf nasm-output.o input.o...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
* `USE_INTERP`: Include an interp segment in the output ELF file. If not, the
|
|
||||||
dynamic linker **must** be invoked *explicitely*! (You probably want to
|
|
||||||
enable this.) Costs the size of a phdr plus the size of the interp string.
|
|
||||||
* `ALIGN_STACK`: *64-bit only*: realign the stack so that SSE instructions
|
|
||||||
won't segfault. Costs 1 byte.
|
|
||||||
* `USE_NX`: Don't use `RWE` segments at all. Not very well tested. Costs the
|
|
||||||
size of 1 phdr, plus some extra stuff on `i386`. Don't forget to pass `-n`
|
|
||||||
to `smol.py` as well.
|
|
||||||
* `USE_DL_FINI`: keep track of the `_dl_fini` function and pass it to your
|
|
||||||
`_start`. Costs 2 bytes, plus maybe a few more depending on how it's passed
|
|
||||||
to `__libc_start_main`.
|
|
||||||
* `USE_DT_DEBUG`: retrieve the `struct link_map` from the `r_debug` linker
|
|
||||||
data (which is placed at `DT_DEBUG` at startup) instead of exploiting data
|
|
||||||
leakage from `_dt_start_user`. Might be more compatible and compressable, but
|
|
||||||
strictly worse size-wise by 10 (i386) or 3 (x86_64) bytes.
|
|
||||||
* `SKIP_ENTRIES`: skip the first two entries of the `struct link_map`, which
|
|
||||||
represent the main binary and the vDSO. Costs around 5 bytes.
|
|
||||||
* `USE_DNLOAD_LOADER`: use the symbol loading mechanism as used in dnload (i.e.
|
|
||||||
traverse the symtab of the imported libraries). Slightly larger, but probably
|
|
||||||
better compressable and more compatible with other libcs and future versions
|
|
||||||
of glibc.
|
|
||||||
* `NO_START_ARG`: *don't* pass the stack pointer to `_start` as the first arg.
|
|
||||||
Will make it unable to read argc/argv/environ, but gives you 3 bytes.
|
|
||||||
* `SKIP_ZERO_VALUE`: skip a `Sym` with a `st_value` field of `0`. If this isn't
|
|
||||||
enabled, weak symbols etc. might be imported instead of the real ones,
|
|
||||||
causing breakage. Many libraries don't have weak symbols at all, though.
|
|
||||||
Costs 4 (i386) or 5 (x86_64) bytes.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
usage: smol.py [-h] [-m TARGET] [-l LIB] [-L DIR] [--nasm NASM] [--cc CC]
|
usage: smold.py [-h] [-m TARGET] [-l LIB] [-L DIR] [-s] [-n] [-d] [-fuse-interp] [-falign-stack] [-fuse-nx]
|
||||||
[--scanelf SCANELF] [--readelf READELF]
|
[-fuse-dnload-loader] [-fskip-zero-value] [-fuse-dt-debug] [-fuse-dl-fini] [-fskip-entries]
|
||||||
input [input ...] output
|
[-fno-start-arg] [-funsafe-dynamic] [--nasm NASM] [--cc CC] [--scanelf SCANELF] [--readelf READELF]
|
||||||
|
[--cflags CFLAGS] [--asflags ASFLAGS] [--ldflags LDFLAGS] [--smolrt SMOLRT] [--smolld SMOLLD]
|
||||||
|
[--verbose] [--keeptmp]
|
||||||
|
input [input ...] output
|
||||||
|
|
||||||
positional arguments:
|
positional arguments:
|
||||||
input input object file
|
input input object file
|
||||||
output output nasm file
|
output output binary
|
||||||
|
|
||||||
optional arguments:
|
optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
|
@ -64,14 +41,45 @@ optional arguments:
|
||||||
-l LIB, --library LIB
|
-l LIB, --library LIB
|
||||||
libraries to link against
|
libraries to link against
|
||||||
-L DIR, --libdir DIR directories to search libraries in
|
-L DIR, --libdir DIR directories to search libraries in
|
||||||
|
-s, --hash16 Use 16-bit (BSD) hashes instead of 32-bit djb2 hashes. Implies -fuse-dnload-loader
|
||||||
|
-n, --nx Use NX (i.e. don't use RWE pages). Costs the size of one phdr, plus some extra bytes on
|
||||||
|
i386.
|
||||||
|
-d, --det Make the order of imports deterministic (default: just use whatever binutils throws at us)
|
||||||
|
-fuse-interp Include a program interpreter header (PT_INTERP). If not enabled, ld.so has to be invoked
|
||||||
|
manually by the end user.
|
||||||
|
-falign-stack Align the stack before running user code (_start). If not enabled, this has to be done
|
||||||
|
manually. Costs 1 byte.
|
||||||
|
-fuse-nx Don't use one big RWE segment, but use separate RW and RE ones. Use this to keep strict
|
||||||
|
kernels (PaX/grsec) happy. Costs at least the size of one program header entry.
|
||||||
|
-fuse-dnload-loader Use a dnload-style loader for resolving symbols, which doesn't depend on
|
||||||
|
nonstandard/undocumented ELF and ld.so features, but is slightly larger. If not enabled, a
|
||||||
|
smaller custom loader is used which assumes glibc.
|
||||||
|
-fskip-zero-value Skip an ELF symbol with a zero address (a weak symbol) when parsing libraries at runtime.
|
||||||
|
Try enabling this if you're experiencing sudden breakage. However, many libraries don't use
|
||||||
|
weak symbols, so this doesn't often pose a problem. Costs ~5 bytes.
|
||||||
|
-fuse-dt-debug Use the DT_DEBUG Dyn header to access the link_map, which doesn't depend on
|
||||||
|
nonstandard/undocumented ELF and ld.so features. If not enabled, the link_map is accessed
|
||||||
|
using data leaked to the entrypoint by ld.so, which assumes glibc. Costs ~10 bytes.
|
||||||
|
-fuse-dl-fini Pass _dl_fini to the user entrypoint, which should be done to properly comply with all
|
||||||
|
standards, but is very often not needed at all. Costs 2 bytes.
|
||||||
|
-fskip-entries Skip the first two entries in the link map (resp. ld.so and the vDSO). Speeds up symbol
|
||||||
|
resolving, but costs ~5 bytes.
|
||||||
|
-fno-start-arg Don't pass a pointer to argc/argv/envp to the entrypoint using the standard calling
|
||||||
|
convention. This means you need to read these yourself in assembly if you want to use them!
|
||||||
|
(envp is a preprequisite for X11, because it needs $DISPLAY.) Frees 3 bytes.
|
||||||
|
-funsafe-dynamic Don't end the ELF Dyn table with a DT_NULL entry. This might cause ld.so to interpret the
|
||||||
|
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
|
--cc CC which cc binary to use (MUST BE GCC!)
|
||||||
--scanelf SCANELF which scanelf binary to use
|
--scanelf SCANELF which scanelf binary to use
|
||||||
--readelf READELF which readelf binary to use
|
--readelf READELF which readelf binary to use
|
||||||
-n, --nx Use NX (i.e. don't use RWE pages). Costs the size of
|
--cflags CFLAGS Flags to pass to the C compiler for the relinking step
|
||||||
one phdr, plus some extra bytes on i386. Don't forget
|
--asflags ASFLAGS Flags to pass to the assembler when creating the ELF header and runtime startup code
|
||||||
to pass -DUSE_NX to the assembly loader as well!
|
--ldflags LDFLAGS Flags to pass to the linker for the final linking step
|
||||||
|
--smolrt SMOLRT Directory containing the smol runtime sources
|
||||||
|
--smolld SMOLLD Directory containing the smol linker scripts
|
||||||
|
--verbose Be verbose about what happens and which subcommands are invoked
|
||||||
|
--keeptmp Keep temp files (only useful for debugging)
|
||||||
```
|
```
|
||||||
|
|
||||||
A minimal crt (and `_start` funcion) are provided in case you want to use `main`.
|
A minimal crt (and `_start` funcion) are provided in case you want to use `main`.
|
||||||
|
@ -83,9 +91,6 @@ imported by a `smol`-ified binary. This can thus be used to detect user mistakes
|
||||||
during dynamic linking. (Think of it as an equivalent of `ldd`, except that it
|
during dynamic linking. (Think of it as an equivalent of `ldd`, except that it
|
||||||
also checks whether the imported functions are present as well.)
|
also checks whether the imported functions are present as well.)
|
||||||
|
|
||||||
***NOTE***: `smoldd.py` currently doesn't support 64-bit binaries anymore, as
|
|
||||||
there's currently no (good) way of retrieving the symbol hash table anymore.
|
|
||||||
|
|
||||||
## Internal workings
|
## Internal workings
|
||||||
|
|
||||||
`smol.py` inspects the input object files for needed library files and symbols.
|
`smol.py` inspects the input object files for needed library files and symbols.
|
||||||
|
@ -99,7 +104,7 @@ works for glibc): on both i386 and x86_64, the linker startup code
|
||||||
(`_dl_start_user`) leaks the global `struct link_map` to the user code:
|
(`_dl_start_user`) leaks the global `struct link_map` to the user code:
|
||||||
on i386, a pointer to it is passed directly through `eax`:
|
on i386, a pointer to it is passed directly through `eax`:
|
||||||
|
|
||||||
```s
|
```asm
|
||||||
# (eax, edx, ecx, esi) = (_dl_loaded, argc, argv, envp)
|
# (eax, edx, ecx, esi) = (_dl_loaded, argc, argv, envp)
|
||||||
movl _rtld_local@GOTOFF(%ebx), %eax
|
movl _rtld_local@GOTOFF(%ebx), %eax
|
||||||
## [ boring stuff... ]
|
## [ boring stuff... ]
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from .parse import *
|
||||||
|
from .shared import eprintf
|
||||||
|
|
||||||
|
def cc_relink_objs(verbose, cc_bin, arch, inputs, output, cflags):
|
||||||
|
archflag = '-m64' if arch == "x86_64" else '-m32'
|
||||||
|
|
||||||
|
cctyp, ccver = get_cc_version(cc_bin)
|
||||||
|
assert cctyp == "gcc", "A GCC compiler is needed for relinking objects!"
|
||||||
|
relink_arg = "-flinker-output=rel" if ccver < (9,0) else "-flinker-output=nolto-rel"
|
||||||
|
|
||||||
|
args = [cc_bin, archflag, '-nostartfiles', '-nostdlib', \
|
||||||
|
'-r', relink_arg, '-o', output] + cflags + inputs
|
||||||
|
|
||||||
|
if verbose: eprintf("cc: %s" % repr(args))
|
||||||
|
subprocess.check_call(args, stdout=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
def nasm_assemble_elfhdr(verbose, nasm_bin, arch, rtdir, intbl, output, asflags):
|
||||||
|
if rtdir[-1] != '/': rtdir = rtdir + '/'
|
||||||
|
archflag = 'elf64' if arch == "x86_64" else 'elf32'
|
||||||
|
|
||||||
|
args = [nasm_bin, '-I', rtdir, '-f', archflag] + asflags + [intbl, '-o', output]
|
||||||
|
|
||||||
|
if verbose: eprintf("nasm: %s" % repr(args))
|
||||||
|
subprocess.check_call(args, stdout=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
def ld_link_final(verbose, cc_bin, arch, lddir, inobjs, output, ldflags):
|
||||||
|
archflag = '-m64' if arch == "x86_64" else '-m32'
|
||||||
|
|
||||||
|
args = [cc_bin, archflag, '-T', lddir+'/link.ld', \
|
||||||
|
'-Wl,--oformat=binary', '-nostartfiles', '-nostdlib', \
|
||||||
|
'-o', output] + inobjs + ldflags
|
||||||
|
|
||||||
|
if verbose: eprintf("ld: %s" % repr(args))
|
||||||
|
subprocess.check_call(args, stdout=subprocess.DEVNULL)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import sys
|
import sys
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from smolshared import *
|
from .shared import *
|
||||||
|
|
||||||
def sort_imports(libraries, hashfn):
|
def sort_imports(libraries, hashfn):
|
||||||
#eprintf("in: " + str(libraries))
|
#eprintf("in: " + str(libraries))
|
||||||
|
@ -34,9 +34,8 @@ def output_x86(libraries, nx, h16, outf, det):
|
||||||
for sym, reloc in symrels: usedrelocs.add(reloc)
|
for sym, reloc in symrels: usedrelocs.add(reloc)
|
||||||
|
|
||||||
if not(nx) and 'R_386_PC32' in usedrelocs and 'R_386_GOT32X' in usedrelocs:
|
if not(nx) and 'R_386_PC32' in usedrelocs and 'R_386_GOT32X' in usedrelocs:
|
||||||
eprintf("Using a mix of R_386_PC32 and R_386_GOT32X relocations! "+\
|
error("Using a mix of R_386_PC32 and R_386_GOT32X relocations! "+\
|
||||||
"Please change a few C compiler flags and recompile your code.")
|
"Please change a few C compiler flags and recompile your code.")
|
||||||
exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
use_jmp_bytes = not nx and 'R_386_PC32' in usedrelocs
|
use_jmp_bytes = not nx and 'R_386_PC32' in usedrelocs
|
||||||
|
@ -111,8 +110,7 @@ global {name}
|
||||||
|
|
||||||
def output_amd64(libraries, nx, h16, outf, det):
|
def output_amd64(libraries, nx, h16, outf, det):
|
||||||
if h16:
|
if h16:
|
||||||
eprintf("--hash16 not supported yet for x86_64 outputs.")
|
error("--hash16 not supported yet for x86_64 outputs.")
|
||||||
exit(1)
|
|
||||||
|
|
||||||
if nx: outf.write('%define USE_NX 1\n')
|
if nx: outf.write('%define USE_NX 1\n')
|
||||||
# if h16: outf.write('%define USE_HASH16 1\n')
|
# if h16: outf.write('%define USE_HASH16 1\n')
|
||||||
|
@ -155,8 +153,7 @@ dynamic.end:
|
||||||
for sym, reloc in symrels:
|
for sym, reloc in symrels:
|
||||||
if reloc not in ['R_X86_64_PLT32', 'R_X86_64_GOTPCRELX', \
|
if reloc not in ['R_X86_64_PLT32', 'R_X86_64_GOTPCRELX', \
|
||||||
'R_X86_64_REX_GOTPCRELX', 'R_X86_64_GOTPCREL']:
|
'R_X86_64_REX_GOTPCRELX', 'R_X86_64_GOTPCREL']:
|
||||||
eprintf('Relocation type ' + reloc + ' of symbol ' + sym + ' unsupported!')
|
error('Relocation type ' + reloc + ' of symbol ' + sym + ' unsupported!')
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if reloc in ['R_X86_64_GOTPCRELX', 'R_X86_64_REX_GOTPCRELX', \
|
if reloc in ['R_X86_64_GOTPCRELX', 'R_X86_64_REX_GOTPCRELX', \
|
||||||
'R_X86_64_GOTPCREL']:
|
'R_X86_64_GOTPCREL']:
|
||||||
|
@ -192,6 +189,5 @@ def output(arch, libraries, nx, h16, outf, det):
|
||||||
if arch == 'i386': output_x86(libraries, nx, h16, outf, det)
|
if arch == 'i386': output_x86(libraries, nx, h16, outf, det)
|
||||||
elif arch == 'x86_64': output_amd64(libraries, nx, h16, outf, det)
|
elif arch == 'x86_64': output_amd64(libraries, nx, h16, outf, det)
|
||||||
else:
|
else:
|
||||||
eprintf("E: cannot emit for arch '" + str(arch) + "'")
|
error("E: cannot emit for arch '" + str(arch) + "'")
|
||||||
sys.exit(1)
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import subprocess
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from smolshared import *
|
from .shared import *
|
||||||
|
|
||||||
def decide_arch(inpfiles):
|
def decide_arch(inpfiles):
|
||||||
archs=set({})
|
archs=set({})
|
||||||
|
@ -20,8 +20,7 @@ def decide_arch(inpfiles):
|
||||||
archs.add(machnum)
|
archs.add(machnum)
|
||||||
|
|
||||||
if len(archs) != 1:
|
if len(archs) != 1:
|
||||||
eprintf("Input files have multiple architectures, can't link this...")
|
error("Input files have multiple architectures, can't link this...")
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
archn = list(archs)[0]
|
archn = list(archs)[0]
|
||||||
|
|
||||||
|
@ -48,24 +47,41 @@ def build_reloc_typ_table(reo):
|
||||||
|
|
||||||
return relocs
|
return relocs
|
||||||
|
|
||||||
def get_needed_syms(readelf_bin, inpfiles):
|
def has_lto_object(readelf_bin, files):
|
||||||
output = subprocess.check_output([readelf_bin, '-s', '-W']+inpfiles,
|
for x in files:
|
||||||
|
with open(x,'rb') as f:
|
||||||
|
if f.read(2) == b'BC': # LLVM bitcode! --> clang -flto
|
||||||
|
return True
|
||||||
|
|
||||||
|
output = subprocess.check_output([readelf_bin, '-s', '-W'] + files,
|
||||||
stderr=subprocess.DEVNULL)
|
stderr=subprocess.DEVNULL)
|
||||||
outrel = subprocess.check_output([readelf_bin, '-r', '-W']+inpfiles,
|
|
||||||
|
curfile = files[0]
|
||||||
|
for entry in output.decode('utf-8').splitlines():
|
||||||
|
stuff = entry.split()
|
||||||
|
if len(stuff)<2: continue
|
||||||
|
if stuff[0] == "File:": curfile = stuff[1]
|
||||||
|
if "__gnu_lto_" in entry or ".gnu.lto" in entry: # assuming nobody uses a symbol called "__gnu_lto_" ...
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_needed_syms(readelf_bin, inpfile):
|
||||||
|
output = subprocess.check_output([readelf_bin, '-s', '-W',inpfile],
|
||||||
|
stderr=subprocess.DEVNULL)
|
||||||
|
outrel = subprocess.check_output([readelf_bin, '-r', '-W',inpfile],
|
||||||
stderr=subprocess.DEVNULL)
|
stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
relocs = build_reloc_typ_table(outrel)
|
relocs = build_reloc_typ_table(outrel)
|
||||||
|
|
||||||
curfile = inpfiles[0]
|
curfile = inpfile
|
||||||
syms=set({})
|
syms=set({})
|
||||||
for entry in output.decode('utf-8').splitlines():
|
for entry in output.decode('utf-8').splitlines():
|
||||||
stuff = entry.split()
|
stuff = entry.split()
|
||||||
if len(stuff)<2: continue
|
if len(stuff)<2: continue
|
||||||
if stuff[0] == "File:": curfile = stuff[1]
|
if stuff[0] == "File:": curfile = stuff[1]
|
||||||
if len(stuff)<8: continue
|
if len(stuff)<8: continue
|
||||||
if stuff[7].startswith("__gnu_lto_"): # yikes, an LTO object
|
#if stuff[7].startswith("__gnu_lto_"): # yikes, an LTO object
|
||||||
eprintf("{} is an LTO object file, can't use this!".format(curfile))
|
# error("{} is an LTO object file, can't use this!".format(curfile))
|
||||||
exit(1)
|
|
||||||
if stuff[4] == "GLOBAL" and stuff[6] == "UND" and len(stuff[7])>0 \
|
if stuff[4] == "GLOBAL" and stuff[6] == "UND" and len(stuff[7])>0 \
|
||||||
and stuff[7] in relocs:
|
and stuff[7] in relocs:
|
||||||
syms.add((stuff[7], relocs[stuff[7]]))
|
syms.add((stuff[7], relocs[stuff[7]]))
|
||||||
|
@ -105,6 +121,21 @@ def get_cc_paths(cc_bin):
|
||||||
|
|
||||||
return paths
|
return paths
|
||||||
|
|
||||||
|
def get_cc_version(cc_bin):
|
||||||
|
bak = os.environ.copy()
|
||||||
|
os.environ['LANG'] = "C" # DON'T output localized search dirs!
|
||||||
|
output = subprocess.check_output([cc_bin, '--version'],
|
||||||
|
stderr=subprocess.DEVNULL)
|
||||||
|
os.environ = bak
|
||||||
|
|
||||||
|
lines = output.decode('utf-8').splitlines()
|
||||||
|
if "Free Software Foundation" in lines[1]: # GCC
|
||||||
|
verstr = lines[0].split()[-1]
|
||||||
|
return ("gcc", tuple(map(int, verstr.split('.'))))
|
||||||
|
else: # assume clang
|
||||||
|
verstr = lines[0].split()[-1]
|
||||||
|
return ("clang", tuple(map(int, verstr.split('.'))))
|
||||||
|
|
||||||
def is_valid_elf(f): # Good Enough(tm)
|
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'
|
||||||
|
|
||||||
|
@ -117,8 +148,7 @@ def find_lib(spaths, wanted):
|
||||||
#for f in glob.glob(glob.escape(p) + '/lib' + wanted + '.a' ): return f
|
#for f in glob.glob(glob.escape(p) + '/lib' + wanted + '.a' ): return f
|
||||||
#for f in glob.glob(glob.escape(p) + '/' + wanted + '.a' ): return f
|
#for f in glob.glob(glob.escape(p) + '/' + wanted + '.a' ): return f
|
||||||
|
|
||||||
eprintf("E: couldn't find library '" + wanted + "'.")
|
error("E: couldn't find library '" + wanted + "'.")
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def find_libs(spaths, wanted): return map(lambda l: find_lib(spaths, l), wanted)
|
def find_libs(spaths, wanted): return map(lambda l: find_lib(spaths, l), wanted)
|
||||||
|
|
|
@ -21,3 +21,7 @@ def hash_djb2(s):
|
||||||
|
|
||||||
def eprintf(*args, **kwargs): print(*args, file=sys.stderr, **kwargs)
|
def eprintf(*args, **kwargs): print(*args, file=sys.stderr, **kwargs)
|
||||||
|
|
||||||
|
def error(*args, **kwargs):
|
||||||
|
eprintf(*args, **kwargs)
|
||||||
|
sys.exit(1)
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import glob
|
||||||
|
import itertools
|
||||||
|
import os, os.path
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
from smol.shared import *
|
||||||
|
from smol.parse import *
|
||||||
|
from smol.emit import *
|
||||||
|
from smol.cnl import *
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('-m', '--target', default='', \
|
||||||
|
help='architecture to generate asm code for (default: auto)')
|
||||||
|
parser.add_argument('-l', '--library', default=[], metavar='LIB', action='append', \
|
||||||
|
help='libraries to link against')
|
||||||
|
parser.add_argument('-L', '--libdir', default=[], metavar='DIR', action='append', \
|
||||||
|
help="directories to search libraries in")
|
||||||
|
|
||||||
|
parser.add_argument('-s', '--hash16', default=False, action='store_true', \
|
||||||
|
help="Use 16-bit (BSD) hashes instead of 32-bit djb2 hashes. "+\
|
||||||
|
"Implies -fuse-dnload-loader")
|
||||||
|
parser.add_argument('-n', '--nx', default=False, action='store_true', \
|
||||||
|
help="Use NX (i.e. don't use RWE pages). Costs the size of one phdr, "+\
|
||||||
|
"plus some extra bytes on i386.")
|
||||||
|
parser.add_argument('-d', '--det', default=False, action='store_true', \
|
||||||
|
help="Make the order of imports deterministic (default: just use " + \
|
||||||
|
"whatever binutils throws at us)")
|
||||||
|
|
||||||
|
parser.add_argument('-fuse-interp', default=False, action='store_true', \
|
||||||
|
help="Include a program interpreter header (PT_INTERP). If not " +\
|
||||||
|
"enabled, ld.so has to be invoked manually by the end user.")
|
||||||
|
parser.add_argument('-falign-stack', default=False, action='store_true', \
|
||||||
|
help="Align the stack before running user code (_start). If not " + \
|
||||||
|
"enabled, this has to be done manually. Costs 1 byte.")
|
||||||
|
parser.add_argument('-fuse-nx', default=False, action='store_true', \
|
||||||
|
help="Don't use one big RWE segment, but use separate RW and RE ones."+\
|
||||||
|
" Use this to keep strict kernels (PaX/grsec) happy. Costs at "+\
|
||||||
|
"least the size of one program header entry.")
|
||||||
|
parser.add_argument('-fuse-dnload-loader', default=False, action='store_true', \
|
||||||
|
help="Use a dnload-style loader for resolving symbols, which doesn't "+\
|
||||||
|
"depend on nonstandard/undocumented ELF and ld.so features, but "+\
|
||||||
|
"is slightly larger. If not enabled, a smaller custom loader is "+\
|
||||||
|
"used which assumes glibc.")
|
||||||
|
parser.add_argument('-fskip-zero-value', default=False, action='store_true', \
|
||||||
|
help="Skip an ELF symbol with a zero address (a weak symbol) when "+\
|
||||||
|
"parsing libraries at runtime. Try enabling this if you're "+\
|
||||||
|
"experiencing sudden breakage. However, many libraries don't use "+\
|
||||||
|
"weak symbols, so this doesn't often pose a problem. Costs ~5 bytes.")
|
||||||
|
parser.add_argument('-fuse-dt-debug', default=False, action='store_true', \
|
||||||
|
help="Use the DT_DEBUG Dyn header to access the link_map, which doesn't"+\
|
||||||
|
" depend on nonstandard/undocumented ELF and ld.so features. If "+\
|
||||||
|
"not enabled, the link_map is accessed using data leaked to the "+\
|
||||||
|
"entrypoint by ld.so, which assumes glibc. Costs ~10 bytes.")
|
||||||
|
parser.add_argument('-fuse-dl-fini', default=False, action='store_true', \
|
||||||
|
help="Pass _dl_fini to the user entrypoint, which should be done to "+\
|
||||||
|
"properly comply with all standards, but is very often not "+\
|
||||||
|
"needed at all. Costs 2 bytes.")
|
||||||
|
parser.add_argument('-fskip-entries', default=False, action='store_true', \
|
||||||
|
help="Skip the first two entries in the link map (resp. ld.so and "+\
|
||||||
|
"the vDSO). Speeds up symbol resolving, but costs ~5 bytes.")
|
||||||
|
parser.add_argument('-fno-start-arg', default=False, action='store_true', \
|
||||||
|
help="Don't pass a pointer to argc/argv/envp to the entrypoint using "+\
|
||||||
|
"the standard calling convention. This means you need to read "+\
|
||||||
|
"these yourself in assembly if you want to use them! (envp is "+\
|
||||||
|
"a preprequisite for X11, because it needs $DISPLAY.) Frees 3 bytes.")
|
||||||
|
parser.add_argument('-funsafe-dynamic', default=False, action='store_true', \
|
||||||
|
help="Don't end the ELF Dyn table with a DT_NULL entry. This might "+\
|
||||||
|
"cause ld.so to interpret the entire binary as the Dyn table, "+\
|
||||||
|
"so only enable this if you're sure this won't break things!")
|
||||||
|
|
||||||
|
parser.add_argument('--nasm', default=os.getenv('NASM') or shutil.which('nasm'), \
|
||||||
|
help="which nasm binary to use")
|
||||||
|
parser.add_argument('--cc', default=os.getenv('CC') or shutil.which('cc'), \
|
||||||
|
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'), \
|
||||||
|
help="which readelf binary to use")
|
||||||
|
|
||||||
|
parser.add_argument('--cflags', default=[], metavar='CFLAGS', action='append',
|
||||||
|
help="Flags to pass to the C compiler for the relinking step")
|
||||||
|
parser.add_argument('--asflags', default=[], metavar='ASFLAGS', action='append',
|
||||||
|
help="Flags to pass to the assembler when creating the ELF header and runtime startup code")
|
||||||
|
parser.add_argument('--ldflags', default=[], metavar='LDFLAGS', action='append',
|
||||||
|
help="Flags to pass to the linker for the final linking step")
|
||||||
|
parser.add_argument('--smolrt', default=os.getcwd()+"/rt",
|
||||||
|
help="Directory containing the smol runtime sources")
|
||||||
|
parser.add_argument('--smolld', default=os.getcwd()+"/ld",
|
||||||
|
help="Directory containing the smol linker scripts")
|
||||||
|
|
||||||
|
parser.add_argument('--verbose', default=False, action='store_true', \
|
||||||
|
help="Be verbose about what happens and which subcommands are invoked")
|
||||||
|
parser.add_argument('--keeptmp', default=False, action='store_true', \
|
||||||
|
help="Keep temp files (only useful for debugging)")
|
||||||
|
|
||||||
|
parser.add_argument('input', nargs='+', help="input object file")
|
||||||
|
parser.add_argument('output', type=str, help="output binary")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.hash16:
|
||||||
|
args.fuse_dnload_loader = True
|
||||||
|
|
||||||
|
if args.fskip_zero_value: args.asflags.insert(0, "-DSKIP_ZERO_VALUE")
|
||||||
|
if args.fuse_nx: args.asflags.insert(0, "-DUSE_NX")
|
||||||
|
if args.fskip_entries: args.asflags.insert(0, "-DSKIP_ENTRIES")
|
||||||
|
if args.funsafe_dynamic: args.asflags.insert(0, "-DUNSAFE_DYNAMIC")
|
||||||
|
if args.fno_start_arg: args.asflags.insert(0, "-DNO_START_ARG")
|
||||||
|
if args.fuse_dl_fini: args.asflags.insert(0, "-DUSE_DL_FINI")
|
||||||
|
if args.fuse_dt_debug: args.asflags.insert(0, "-DUSE_DT_DEBUG")
|
||||||
|
if args.fuse_dnload_loader: args.asflags.insert(0, "-DUSE_DNLOAD_LOADER")
|
||||||
|
if args.fuse_interp: args.asflags.insert(0, "-DUSE_INTERP")
|
||||||
|
if args.falign_stack: args.asflags.insert(0, "-DALIGN_STACK")
|
||||||
|
|
||||||
|
for x in ['nasm','cc','scanelf','readelf']:
|
||||||
|
val = args.__dict__[x]
|
||||||
|
if val is None or not os.path.isfile(val):
|
||||||
|
error("'" + x + "' binary" + (" " if val is None
|
||||||
|
else " ('" + val + "')") + " not found")
|
||||||
|
|
||||||
|
arch = args.target.tolower() if len(args.target) != 0 else decide_arch(args.input)
|
||||||
|
if arch not in archmagic:
|
||||||
|
error("Unknown/unsupported architecture '" + str(arch) + "'")
|
||||||
|
if args.verbose: eprintf("arch: %s" % arch)
|
||||||
|
|
||||||
|
objinput = None
|
||||||
|
objinputistemp = False
|
||||||
|
tmp_asm_file = tempfile.mkstemp(prefix='smoltab',suffix='.asm',text=True)
|
||||||
|
tmp_asm_fd = tmp_asm_file[0]
|
||||||
|
tmp_asm_file = tmp_asm_file[1]
|
||||||
|
tmp_elf_file = tempfile.mkstemp(prefix='smolout',suffix='.o')
|
||||||
|
os.close(tmp_elf_file[0])
|
||||||
|
tmp_elf_file = tmp_elf_file[1]
|
||||||
|
try:
|
||||||
|
# if >1 input OR input is LTO object:
|
||||||
|
if len(args.input) > 1 or has_lto_object(args.readelf, args.input):
|
||||||
|
fd, objinput = tempfile.mkstemp(prefix='smolin',suffix='.o')
|
||||||
|
os.close(fd)
|
||||||
|
cc_relink_objs(args.verbose, args.cc, arch, args.input, objinput, args.cflags)
|
||||||
|
else: objinput = args.input[0]
|
||||||
|
|
||||||
|
# generate smol hashtab
|
||||||
|
cc_paths = get_cc_paths(args.cc)
|
||||||
|
syms = get_needed_syms(args.readelf, objinput)
|
||||||
|
spaths = args.libdir + cc_paths['libraries']
|
||||||
|
libraries = cc_paths['libraries']
|
||||||
|
libs = list(find_libs(spaths, args.library))
|
||||||
|
if args.verbose: eprintf("libs = " + str(libs))
|
||||||
|
symbols = {}
|
||||||
|
for symbol, reloc in syms:
|
||||||
|
library = find_symbol(args.scanelf, libs, args.library, symbol)
|
||||||
|
if not library:
|
||||||
|
error("could not find symbol: {}".format(symbol))
|
||||||
|
symbols.setdefault(library, [])
|
||||||
|
symbols[library].append((symbol, reloc))
|
||||||
|
|
||||||
|
with os.fdopen(tmp_asm_fd, mode='w') as taf:
|
||||||
|
output(arch, symbols, args.nx, args.hash16, taf, args.det)
|
||||||
|
if args.verbose:
|
||||||
|
eprintf("wrote symtab to %s" % tmp_asm_file)
|
||||||
|
|
||||||
|
# assemble hash table/ELF header
|
||||||
|
nasm_assemble_elfhdr(args.verbose, args.nasm, arch, args.smolrt,
|
||||||
|
tmp_asm_file, tmp_elf_file, args.asflags)
|
||||||
|
|
||||||
|
# link with LD into the final executable, w/ special linker script
|
||||||
|
ld_link_final(args.verbose, args.cc, arch, args.smolld, [objinput, tmp_elf_file],
|
||||||
|
args.output, args.ldflags)
|
||||||
|
finally:
|
||||||
|
if not args.keeptmp:
|
||||||
|
if objinputistemp: os.remove(objinput)
|
||||||
|
os.remove(tmp_asm_file)
|
||||||
|
os.remove(tmp_elf_file)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
rv = main()
|
||||||
|
if rv is None: pass
|
||||||
|
else:
|
||||||
|
try: sys.exit(int(rv))
|
||||||
|
except: sys.exit(1)
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
import os.path, struct, sys
|
import os.path, struct, sys
|
||||||
import argparse, glob, shutil, subprocess
|
import argparse, glob, shutil, subprocess
|
||||||
|
|
||||||
import hackyelf, linkmap
|
import smol.hackyelf as hackyelf
|
||||||
from smolshared import *
|
import smol.linkmap as linkmap
|
||||||
from smolparse import *
|
from smol.shared import *
|
||||||
|
from smol.parse import *
|
||||||
|
|
||||||
def readbyte(blob, off): return struct.unpack('<B', blob[off:off+1])[0], (off+1)
|
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 readint(blob, off): return struct.unpack('<I', blob[off:off+4])[0], (off+4)
|
||||||
|
@ -55,7 +56,7 @@ def addr2off(elf, addr):
|
||||||
assert aoff < x.filesz, ".bss address!"
|
assert aoff < x.filesz, ".bss address!"
|
||||||
return aoff + x.off
|
return aoff + x.off
|
||||||
|
|
||||||
assert False, "Address %08x not in the static address range!" % addr
|
error("E: Address %08x not in the static address range!" % addr)
|
||||||
|
|
||||||
def get_needed_libs(elf, blob):
|
def get_needed_libs(elf, blob):
|
||||||
assert elf.dyn is not None, "No DYNAMIC table present in the ELF file!"
|
assert elf.dyn is not None, "No DYNAMIC table present in the ELF file!"
|
||||||
|
@ -100,7 +101,7 @@ def get_hashtbl(elf, blob, args):
|
||||||
htaddr = struct.unpack('<I', blob[txtoff:txtoff+4])[0]
|
htaddr = struct.unpack('<I', blob[txtoff:txtoff+4])[0]
|
||||||
|
|
||||||
assert htaddr is not None, "wtf? (no hashtable address)"
|
assert htaddr is not None, "wtf? (no hashtable address)"
|
||||||
print("Hash table address: 0x%08x" % htaddr)
|
#print("Hash table address: 0x%08x" % htaddr)
|
||||||
htoff = addr2off(elf, htaddr)
|
htoff = addr2off(elf, htaddr)
|
||||||
|
|
||||||
tbl = []
|
tbl = []
|
|
@ -5,6 +5,7 @@ import sys
|
||||||
|
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
|
# TODO: output null -> in-place!
|
||||||
parser.add_argument('input', type=argparse.FileType('rb'), \
|
parser.add_argument('input', type=argparse.FileType('rb'), \
|
||||||
help="input file to truncate")
|
help="input file to truncate")
|
||||||
parser.add_argument('output', type=argparse.FileType('wb'), \
|
parser.add_argument('output', type=argparse.FileType('wb'), \
|
86
src/smol.py
86
src/smol.py
|
@ -1,86 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import glob
|
|
||||||
import itertools
|
|
||||||
import os.path
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from smolshared import *
|
|
||||||
from smolparse import *
|
|
||||||
from smolemit import *
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument('-m', '--target', default='', \
|
|
||||||
help='architecture to generate asm code for (default: auto)')
|
|
||||||
parser.add_argument('-l', '--library', metavar='LIB', action='append', \
|
|
||||||
help='libraries to link against')
|
|
||||||
parser.add_argument('-L', '--libdir', metavar='DIR', action='append', \
|
|
||||||
help="directories to search libraries in")
|
|
||||||
|
|
||||||
parser.add_argument('--nasm', default=shutil.which('nasm'), \
|
|
||||||
help="which nasm binary to use")
|
|
||||||
parser.add_argument('--cc', default=shutil.which('cc'), \
|
|
||||||
help="which cc binary to use")
|
|
||||||
parser.add_argument('--scanelf', default=shutil.which('scanelf'), \
|
|
||||||
help="which scanelf binary to use")
|
|
||||||
parser.add_argument('--readelf', default=shutil.which('readelf'), \
|
|
||||||
help="which readelf binary to use")
|
|
||||||
|
|
||||||
parser.add_argument('-s', '--hash16', default=False, action='store_true', \
|
|
||||||
help="Use 16-bit (BSD) hashes instead of 32-bit djb2 hashes. "\
|
|
||||||
+"Must be used with -DUSE_DNLOAD_LOADER")
|
|
||||||
parser.add_argument('-n', '--nx', default=False, action='store_true', \
|
|
||||||
help="Use NX (i.e. don't use RWE pages). Costs the size of one phdr, "\
|
|
||||||
+"plus some extra bytes on i386.")
|
|
||||||
parser.add_argument('-d', '--det', default=False, action='store_true', \
|
|
||||||
help="Make the order of imports deterministic (default: just use on "+\
|
|
||||||
"whatever binutils throws at us)")
|
|
||||||
|
|
||||||
parser.add_argument('input', nargs='+', help="input object file")
|
|
||||||
parser.add_argument('output', type=argparse.FileType('w'), \
|
|
||||||
help="output nasm file", default=sys.stdout)
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
for x in ['nasm','cc','scanelf','readelf']:
|
|
||||||
val = args.__dict__[x]
|
|
||||||
if val is None or not os.path.isfile(val):
|
|
||||||
eprintf("'" + x + "' binary" + (" " if val is None
|
|
||||||
else " ('" + val + "')") + " not found")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if args.libdir is None: args.libdir = []
|
|
||||||
arch = args.target.tolower() if len(args.target)!=0 \
|
|
||||||
else decide_arch(args.input)
|
|
||||||
if arch not in archmagic:
|
|
||||||
eprintf("Unknown architecture '" + str(arch) + "'")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
syms = get_needed_syms(args.readelf, args.input)
|
|
||||||
|
|
||||||
paths = get_cc_paths(args.cc)
|
|
||||||
|
|
||||||
spaths = args.libdir + paths['libraries']
|
|
||||||
libraries=paths['libraries']
|
|
||||||
libnames = args.library
|
|
||||||
libs = list(find_libs(spaths, libnames))
|
|
||||||
symbols = {}
|
|
||||||
#print("libs = " + str(libs))
|
|
||||||
|
|
||||||
for symbol, reloc in syms:
|
|
||||||
library = find_symbol(args.scanelf, libs, libnames, symbol)
|
|
||||||
if not library:
|
|
||||||
eprintf("could not find symbol: {}".format(symbol))
|
|
||||||
sys.exit(1)
|
|
||||||
symbols.setdefault(library, [])
|
|
||||||
symbols[library].append((symbol, reloc))
|
|
||||||
|
|
||||||
output(arch, symbols, args.nx, args.hash16, args.output, args.det)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
|
Loading…
Reference in New Issue