From 6081557aa2ed54312131f4c2d80694598af4b947 Mon Sep 17 00:00:00 2001 From: Shiz Date: Thu, 12 Apr 2018 00:14:36 +0200 Subject: [PATCH] epoch --- .gitignore | 2 + Makefile | 44 +++++++++++++++++ ldr/header.s | 78 +++++++++++++++++++++++++++++++ ldr/link.ld | 24 ++++++++++ ldr/loader.s | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++ ldr/mksyms | 80 +++++++++++++++++++++++++++++++ smol | 68 +++++++++++++++++++++++++++ src/hello.c | 9 ++++ src/sdl.c | 9 ++++ 9 files changed, 444 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 ldr/header.s create mode 100644 ldr/link.ld create mode 100644 ldr/loader.s create mode 100755 ldr/mksyms create mode 100755 smol create mode 100644 src/hello.c create mode 100644 src/sdl.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c7473d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/bin +/obj diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..191d8b6 --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +LDRDIR = ldr +OBJDIR = obj +BINDIR = bin +SRCDIR = src +DATADIR = data + +COPTFLAGS=-Os -fvisibility=hidden -mpreferred-stack-boundary=2 -fwhole-program \ + -ffast-math -funsafe-math-optimizations -fno-stack-protector -fomit-frame-pointer \ + -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables +CXXOPTFLAGS=$(COPTFLAGS) \ + -fno-rtti -fno-enforce-eh-specs -fnothrow-opt -fno-use-cxa-get-exception-ptr \ + -fno-implicit-templates -fno-threadsafe-statics -fno-use-cxa-atexit + +ASFLAGS=-f elf -I $(LDRDIR)/ +CFLAGS=-Wall -Wextra -Wpedantic -std=c99 $(COPTFLAGS) -nostartfiles -fno-PIC +CXXFLAGS=-Wall -Wextra -Wpedantic -std=c++11 $(CXXOPTFLAGS) -nostartfiles -fno-PIC +LIBS=-lGL -ldl -lc +LDFLAGS=--oformat=binary -T ldr/link.ld + + +.PHONY: all +all: $(BINDIR)/test + +.PHONY: clean +clean: + rm -rf $(OBJDIR)/* $(BINDIR)/* + + +.SECONDARY: + +$(OBJDIR)/%.o: $(SRCDIR)/%.c + $(CC) $(CFLAGS) -c $^ -o $@ + +$(OBJDIR)/%.o.syms: $(OBJDIR)/%.o + readelf -s $^ | grep UND | sed 1d | awk '{ print $$8 }' > $@ + +$(OBJDIR)/symbols.%.s: $(OBJDIR)/%.o.syms + $(LDRDIR)/mksyms $(LIBS) $$(cat $^) > $@ + +$(OBJDIR)/header.%.o: $(OBJDIR)/symbols.%.s $(LDRDIR)/header.s $(LDRDIR)/loader.s + nasm $(ASFLAGS) $< -o $@ + +$(BINDIR)/%: $(OBJDIR)/%.o $(OBJDIR)/header.%.o + $(LD) $(LDFLAGS) $^ -o $@ diff --git a/ldr/header.s b/ldr/header.s new file mode 100644 index 0000000..3a2c4ef --- /dev/null +++ b/ldr/header.s @@ -0,0 +1,78 @@ +%define ORIGIN 0x400000 + +extern _size +[section .header] + +header: + ; e_ident + db 0x7F, "ELF" ; EI_MAG0-EI_MAG3 + db 1 ; EI_CLASS: 1 = 32-bit + db 1 ; EI_DATA: 1 = LSB + db 1 ; EI_VERSION + db 3 ; EI_OSABI: 3 = Linux + db 1 ; EI_OSABIVERSION + times 7 db 0 ; EI_PAD, ld.so is a busta and won't let us use our leet group tags for padding bytes :( + ; e_type: 2 = executable + dw 2 + ; e_machine: 3 = x86 + dw 3 + ; e_version + dd 1 + ; e_entry + dd _start + ; e_phoff + dd (.segments - header) + ; e_shoff + dd 0 + ; e_flags + dd 0 + ; e_ehsize + dw (.segments - header) + ; e_phentsize + dw (.segments.load - .segments.dynamic) +.segments: +.segments.dynamic: + ; {e_phnum: 2, e_shentsize: 0}, p_type: 2 = PT_DYNAMIC + dd 2 + ; {e_shnum: , e_shstrnd: }, p_offset + dd (.dynamic - header) + ; p_vaddr + dd .dynamic + ; p_paddr + dd 0 + ; p_filesz + dd (.dynamic.end - .dynamic) + ; p_memsz + dd (.dynamic.end - .dynamic) + ; p_flags, p_align + dq 0 +.segments.load: + ; p_type: 1 = PT_LOAD + dd 1 + ; p_offset + dd 0 + ; p_vaddr + dd ORIGIN + ; p_paddr + dd 0 + ; p_filesz + dd _size + ; p_memsz + dd _size + ; p_flags: 1 = execute, 4 = read + dd (1 | 2 | 4) + ; p_align + dd 0x1000 +.segments.end: +.dynamic: +.dynamic.strtab: + ; d_tag: 5 = DT_STRTAB + dd 5 + ; d_un.d_ptr + dd _symbols +.dynamic.symtab: + ; this is required to be present or ld.so will crash, but it can be bogus + ; d_tag: 6 = DT_SYMTAB + dd 6 + ; d_un.d_ptr + dd 0 diff --git a/ldr/link.ld b/ldr/link.ld new file mode 100644 index 0000000..03e23aa --- /dev/null +++ b/ldr/link.ld @@ -0,0 +1,24 @@ +OUTPUT_FORMAT(binary) + +SECTIONS { + . = 0x400000; + + .header : { *(.header) } + + .text : { + *(.text .text.* .rdata .rdata.* .rodata .rodata.*) + } + + .data : { + *(.data .data.* .bss .bss.* .tdata .tdata.* .tbss .tbss.*) + } + + .dynamic : { *(.dynamic) } :all :dyn + .dynstuff : { *(.symtab .strtab .shstrtab .rel.text .got.plt .gnu.linkonce.* .plt .plt.got .interp) } :all + + /DISCARD/ : { + *(.*) + } + + _size = . - 0x400000; +} diff --git a/ldr/loader.s b/ldr/loader.s new file mode 100644 index 0000000..46be9ab --- /dev/null +++ b/ldr/loader.s @@ -0,0 +1,130 @@ +[section .text] + +%define LM_NAME_OFFSET 0x4 +%define LM_NEXT_OFFSET 0xC +%define LM_ADDR_OFFSET 0 +%define LM_INFO_OFFSET 0x20 +%define LM_NBUCKETS_OFFSET 0x178 +%define LM_GNU_BUCKETS_OFFSET 0x188 +%define LM_GNU_CHAIN_ZERO_OFFSET 0x18C + +%define DT_VALUE_OFFSET 0x4 +%define DYN_PTR_OFFSET 0x4 + +%define DT_SYMTAB 0x6 +%define DT_SYMSIZE_SHIFT 4 + + +strcmp: ; (const char *s1 (esi), const char *s2 (edi)) + push esi + push edi +.cmp: lodsb + or al, al + jz .done + sub al, [edi] + jnz .done + inc edi + jmp .cmp +.done: pop edi + pop esi + ret + + +basename: ; (const char *s (esi)) + push esi + push edi + mov edi, esi +.cmp: lodsb + or al, al + jz .done + cmp al, 47 ; '/' + cmove edi, esi + jmp .cmp +.done: mov eax, edi + pop edi + pop esi + ret + + +link_symbol: ; (struct link_map *entry, uint32_t *h) + mov ecx, esi + + ; eax = *h % entry->l_nbuckets + mov eax, [ecx] + xor edx, edx + mov ebx, [ebp + LM_NBUCKETS_OFFSET] + div ebx + ; eax = entry->l_gnu_buckets[eax] + mov eax, [ebp + LM_GNU_BUCKETS_OFFSET] + mov eax, [eax + edx * 4] + ; *h |= 1 + or word [ecx], 1 +.check_bucket: ; edx = entry->l_gnu_chain_zero[eax] | 1 + mov edx, [ebp + LM_GNU_CHAIN_ZERO_OFFSET] + mov edx, [edx + eax * 4] + or edx, 1 + ; check if this is our symbol + cmp edx, [ecx] + je .found + inc eax + jmp .check_bucket +.found: ; it is! edx = entry->l_info[DT_SYMTAB]->d_un.d_ptr + mov edx, [ebp + LM_INFO_OFFSET + DT_SYMTAB * 4] + mov edx, [edx + DYN_PTR_OFFSET] + ; edx = edx[eax].dt_value + entry->l_addr + shl eax, DT_SYMSIZE_SHIFT + mov edx, [edx + eax + DT_VALUE_OFFSET] + add edx, [ebp + LM_ADDR_OFFSET] + sub edx, ecx + sub edx, 4 + ; finally, write it back! + mov [ecx], edx + ret + + +link: ; (struct link_map *root, char *symtable) + mov eax, [esp+4] + mov esi, [esp+8] +.do_library: ; null library name means end of symbol table, we're done + cmp byte [esi], 0 + jz .done + ; setup start of map again + mov ebp, eax + push eax +.find_map_entry: ; compare basename(entry->l_name) to lib name, if so we got a match + push esi + mov esi, [ebp + LM_NAME_OFFSET] + call basename + mov edi, eax + pop esi + call strcmp + jz .process_map_entry + ; no match, next entry it is! + mov ebp, [ebp + LM_NEXT_OFFSET] + jmp .find_map_entry +.process_map_entry: ; skip past the name in the symbol table now to get to the symbols + lodsb + or al, al + jnz .process_map_entry + +.do_symbols: ; null byte means end of symbols for this library! + cmp byte [esi], 0 + jz .next_library + inc esi + call link_symbol + add esi, 4 + jmp .do_symbols +.next_library: pop eax + inc esi + jmp .do_library +.done: ret + + +extern main +_start: + push _symbols + push eax + call link + + call main + int3 diff --git a/ldr/mksyms b/ldr/mksyms new file mode 100755 index 0000000..305afe4 --- /dev/null +++ b/ldr/mksyms @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +import sys +import os.path +import subprocess +import itertools +import argparse + + +def hash_djb2(s): + h = 5381 + for c in s: + h = (h * 33 + ord(c)) & 0xFFFFFFFF + return h + + +def output_x86(libraries): + shorts = { l: l.split('.', 1)[0].lower() for l in libraries } + + print('%include "header.s"') + print('.dynamic.needed:') + for library in libraries: + print('dd 1') + print('dd (_symbols.{} - _symbols)'.format(shorts[library])) + print('.dynamic.end:') + print('_symbols:') + for library, symbols in libraries.items(): + print('\t_symbols.{}: db "{}",0'.format(shorts[library], library)) + + for sym in symbols: + hash = hash_djb2(sym) + print(""" +\t\tglobal {name} +\t\t{name}: db 0xE9 +\t\t dd 0x{hash:x} +""".strip('\n').format(name=sym, hash=hash)) + + print('\tdb 0') + print('db 0') + print('%include "loader.s"') + + +def get_cc_paths(): + output = subprocess.check_output(['cc', '-print-search-dirs'], stderr=subprocess.DEVNULL) + paths = {} + for entry in output.decode('utf-8').splitlines(): + category, path = entry.split(': ', 1) + path = path.lstrip('=') + paths[category] = list(set(os.path.realpath(p) for p in path.split(':') if os.path.isdir(p))) + return paths + +def find_symbol(paths, libraries, symbol): + output = subprocess.check_output(['scanelf', '-B', '-F' '%s %S', '-s', '+{}'.format(symbol)] + paths, 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(l) for l in libraries): + return soname + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-a', '--architecture', default='x86', help='architecture to generate asm code for') + parser.add_argument('-l', '--library', metavar='LIB', default=['c'], action='append', help='libraries to link against') + parser.add_argument('symbols', metavar='SYM', nargs='+', help='symbol to search for') + args = parser.parse_args() + + paths = get_cc_paths() + symbols = {} + libraries = ['lib{}.so'.format(l) for l in args.library] + + for symbol in args.symbols: + library = find_symbol(paths['libraries'], libraries, symbol) + if not library: + sys.stderr.write('could not find symbol: {}\n'.format(symbol)) + sys.exit(1) + symbols.setdefault(library, []) + symbols[library].append(symbol) + + output_x86(symbols) + +if __name__ == '__main__': + main() diff --git a/smol b/smol new file mode 100755 index 0000000..6cefe88 --- /dev/null +++ b/smol @@ -0,0 +1,68 @@ +#!/bin/sh +set -e + +COPTFLAGS=$(cat <<'EOF' +-Os -fvisibility=hidden -mpreferred-stack-boundary=2 -fwhole-program +-ffast-math -funsafe-math-optimizations -fno-stack-protector -fomit-frame-pointer +-fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables +EOF +) +CXXOPTFLAGS=$(cat < $1.syms + symfiles="$symfiles $1.syms" +} + +for a; do + if test -z "$cleared"; then + set --; cleared=1 + fi + case "$a" in + -o) output="$a";; + -l*) mksym_args="$mksym_args $a";; + -*) set -- "$@" "$a";; + *.c) + add_out + $CC $CFLAGS "$@" -c "$a" -o "$out" + add_syms "$out";; + *.cxx|*.cc|*.cpp) + add_out + $CXX $CXXFLAGS "$@" -c "$a" -o "$out" + add_syms "$out";; + *) printf "not sure what to do with input file: %s, bailing\n" "$a" &>2; exit 1;; + esac +done + +./ldr/mksyms $mksym_args $(cat $symfiles) > obj/$output.syms.s +nasm $ASFLAGS obj/$output.syms.s -o obj/$output.header.o +ld $LDFLAGS obj/$output.header.o $files -o bin/$output diff --git a/src/hello.c b/src/hello.c new file mode 100644 index 0000000..52c72c1 --- /dev/null +++ b/src/hello.c @@ -0,0 +1,9 @@ +#include +#include + +const char *f = "foo"; + +int main(void) { + printf("hello world %s\n", f); + exit(42); +} diff --git a/src/sdl.c b/src/sdl.c new file mode 100644 index 0000000..e5ad0ab --- /dev/null +++ b/src/sdl.c @@ -0,0 +1,9 @@ +#include + +int main(void) { + SDL_Init(SDL_INIT_VIDEO); + SDL_Window *w = SDL_CreateWindow("nice", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 720, SDL_WINDOW_OPENGL); + SDL_Delay(3000); + SDL_DestroyWindow(w); + return 0; +}