├── tests ├── simple_collector.rs ├── linker │ ├── asm-sec │ │ ├── Makefile │ │ ├── garbage.asm │ │ └── main.asm │ ├── c-common │ │ ├── Makefile │ │ ├── mdata.c │ │ ├── crt1.lo │ │ ├── .12-data.c.swp │ │ ├── zzz-libmusl.a │ │ ├── minit.c │ │ └── main.c │ ├── c-simple │ │ ├── Makefile │ │ ├── 0-crt1.lo │ │ ├── 999-libmusl.a │ │ └── 10-main.c │ ├── asm-common │ │ ├── Makefile │ │ ├── main.asm │ │ └── init_common.asm │ ├── asm-simple │ │ ├── Makefile │ │ └── main.asm │ ├── c-explode │ │ ├── Makefile │ │ ├── _Exit.lo │ │ ├── exit.lo │ │ ├── libc.lo │ │ ├── ofl.lo │ │ ├── uname.lo │ │ ├── 0-crt1.lo │ │ ├── __lock.lo │ │ ├── __wait.lo │ │ ├── frexpl.lo │ │ ├── fwrite.lo │ │ ├── memchr.lo │ │ ├── memcpy.lo │ │ ├── memset.lo │ │ ├── printf.lo │ │ ├── stdout.lo │ │ ├── strnlen.lo │ │ ├── wcrtomb.lo │ │ ├── wctomb.lo │ │ ├── __environ.lo │ │ ├── __init_tls.lo │ │ ├── __lctrans.lo │ │ ├── __lockfile.lo │ │ ├── __signbitl.lo │ │ ├── __towrite.lo │ │ ├── strerror.lo │ │ ├── vfprintf.lo │ │ ├── __stdio_exit.lo │ │ ├── __stdio_seek.lo │ │ ├── syscall_ret.lo │ │ ├── __fpclassifyl.lo │ │ ├── __stdio_close.lo │ │ ├── __stdio_write.lo │ │ ├── __stdout_write.lo │ │ ├── __errno_location.lo │ │ ├── __libc_start_main.lo │ │ ├── __set_thread_area.lo │ │ ├── __stack_chk_fail.lo │ │ └── 10-main.c │ ├── c-init-array │ │ ├── Makefile │ │ ├── 0-crt1.lo │ │ ├── 999-libmusl.a │ │ └── 10-main.c │ ├── no-undef-reloc │ │ ├── Makefile │ │ ├── crt1.lo │ │ ├── zzz-libmusl.a │ │ └── main.c │ ├── reloc-01-X86_64_64 │ │ ├── Makefile │ │ ├── data.asm │ │ └── dx_main.asm │ ├── reloc-04-X86_64_PLT32 │ │ ├── Makefile │ │ ├── hello-fn.asm │ │ ├── data.asm │ │ └── main.asm │ ├── reloc-19-X86_64_TLSGD │ │ ├── Makefile │ │ ├── 0-crt1.lo │ │ ├── 999-libmusl.a │ │ └── 10-main.c │ ├── reloc-09-X86_64_GOTPCREL │ │ ├── Makefile │ │ ├── data.asm │ │ └── main.asm │ ├── gnucompat-tolerate-archive-conflicts │ │ ├── Makefile │ │ ├── 0-crt1.lo │ │ ├── 998-libmusl.a │ │ ├── 999-libmusl.a │ │ └── 10-main.c │ ├── reloc-20-X86_64_TLSLD │ │ ├── 0-crt1.lo │ │ ├── Makefile │ │ ├── 999-libmusl.a │ │ └── 10-main.c │ ├── reloc-22-X86_64_GOTTPOFF │ │ ├── 0-crt1.lo │ │ ├── Makefile │ │ ├── 999-libmusl.a │ │ └── 10-main.c │ ├── reloc-21-R_X86_64_DTPOFF32 │ │ ├── 0-crt1.lo │ │ ├── Makefile │ │ ├── 999-libmusl.a │ │ └── 10-main.c │ ├── Makefile │ └── common.makefile ├── ld.so └── layout.rs ├── .gitignore ├── bin ├── readelf-screenshot.png ├── linktree.rs ├── ldd.rs └── readelf.rs ├── bolter ├── args.rs ├── relocations.rs ├── main.rs └── ld.rs ├── .travis.yml ├── src ├── filetype.rs ├── lib.rs ├── error.rs ├── segment.rs ├── strtab.rs ├── dynamic.rs ├── header.rs ├── utils.rs ├── relocation.rs ├── symbol.rs ├── section.rs ├── loader.rs └── symbolic_linker.rs ├── Cargo.toml ├── LICENSE ├── NOTES.md └── README.md /tests/simple_collector.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target 3 | -------------------------------------------------------------------------------- /tests/linker/asm-sec/Makefile: -------------------------------------------------------------------------------- 1 | ../common.makefile -------------------------------------------------------------------------------- /tests/linker/c-common/Makefile: -------------------------------------------------------------------------------- 1 | ../common.makefile -------------------------------------------------------------------------------- /tests/linker/c-simple/Makefile: -------------------------------------------------------------------------------- 1 | ../common.makefile -------------------------------------------------------------------------------- /tests/linker/asm-common/Makefile: -------------------------------------------------------------------------------- 1 | ../common.makefile -------------------------------------------------------------------------------- /tests/linker/asm-simple/Makefile: -------------------------------------------------------------------------------- 1 | ../common.makefile -------------------------------------------------------------------------------- /tests/linker/c-explode/Makefile: -------------------------------------------------------------------------------- 1 | ../common.makefile -------------------------------------------------------------------------------- /tests/linker/c-init-array/Makefile: -------------------------------------------------------------------------------- 1 | ../common.makefile -------------------------------------------------------------------------------- /tests/linker/c-common/mdata.c: -------------------------------------------------------------------------------- 1 | char *hello ="derp"; 2 | -------------------------------------------------------------------------------- /tests/linker/no-undef-reloc/Makefile: -------------------------------------------------------------------------------- 1 | ../common.makefile -------------------------------------------------------------------------------- /tests/linker/reloc-01-X86_64_64/Makefile: -------------------------------------------------------------------------------- 1 | ../common.makefile -------------------------------------------------------------------------------- /tests/linker/reloc-04-X86_64_PLT32/Makefile: -------------------------------------------------------------------------------- 1 | ../common.makefile -------------------------------------------------------------------------------- /tests/linker/reloc-19-X86_64_TLSGD/Makefile: -------------------------------------------------------------------------------- 1 | ../common.makefile -------------------------------------------------------------------------------- /tests/ld.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/ld.so -------------------------------------------------------------------------------- /tests/linker/reloc-09-X86_64_GOTPCREL/Makefile: -------------------------------------------------------------------------------- 1 | ../common.makefile -------------------------------------------------------------------------------- /tests/linker/gnucompat-tolerate-archive-conflicts/Makefile: -------------------------------------------------------------------------------- 1 | ../common.makefile -------------------------------------------------------------------------------- /bin/readelf-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/bin/readelf-screenshot.png -------------------------------------------------------------------------------- /tests/linker/c-common/crt1.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-common/crt1.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/_Exit.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/_Exit.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/exit.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/exit.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/libc.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/libc.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/ofl.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/ofl.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/uname.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/uname.lo -------------------------------------------------------------------------------- /tests/linker/c-simple/0-crt1.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-simple/0-crt1.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/0-crt1.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/0-crt1.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/__lock.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__lock.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/__wait.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__wait.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/frexpl.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/frexpl.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/fwrite.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/fwrite.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/memchr.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/memchr.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/memcpy.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/memcpy.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/memset.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/memset.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/printf.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/printf.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/stdout.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/stdout.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/strnlen.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/strnlen.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/wcrtomb.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/wcrtomb.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/wctomb.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/wctomb.lo -------------------------------------------------------------------------------- /tests/linker/c-common/.12-data.c.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-common/.12-data.c.swp -------------------------------------------------------------------------------- /tests/linker/c-common/zzz-libmusl.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-common/zzz-libmusl.a -------------------------------------------------------------------------------- /tests/linker/c-explode/__environ.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__environ.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/__init_tls.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__init_tls.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/__lctrans.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__lctrans.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/__lockfile.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__lockfile.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/__signbitl.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__signbitl.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/__towrite.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__towrite.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/strerror.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/strerror.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/vfprintf.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/vfprintf.lo -------------------------------------------------------------------------------- /tests/linker/c-init-array/0-crt1.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-init-array/0-crt1.lo -------------------------------------------------------------------------------- /tests/linker/c-simple/999-libmusl.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-simple/999-libmusl.a -------------------------------------------------------------------------------- /tests/linker/no-undef-reloc/crt1.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/no-undef-reloc/crt1.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/__stdio_exit.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__stdio_exit.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/__stdio_seek.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__stdio_seek.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/syscall_ret.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/syscall_ret.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/__fpclassifyl.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__fpclassifyl.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/__stdio_close.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__stdio_close.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/__stdio_write.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__stdio_write.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/__stdout_write.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__stdout_write.lo -------------------------------------------------------------------------------- /tests/linker/c-init-array/999-libmusl.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-init-array/999-libmusl.a -------------------------------------------------------------------------------- /tests/linker/no-undef-reloc/zzz-libmusl.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/no-undef-reloc/zzz-libmusl.a -------------------------------------------------------------------------------- /tests/linker/c-explode/__errno_location.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__errno_location.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/__libc_start_main.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__libc_start_main.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/__set_thread_area.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__set_thread_area.lo -------------------------------------------------------------------------------- /tests/linker/c-explode/__stack_chk_fail.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/c-explode/__stack_chk_fail.lo -------------------------------------------------------------------------------- /tests/linker/c-common/minit.c: -------------------------------------------------------------------------------- 1 | char *hello; 2 | 3 | void init(){ 4 | hello = "The quick brown fox jumps over the lazy dog"; 5 | } 6 | -------------------------------------------------------------------------------- /tests/linker/reloc-19-X86_64_TLSGD/0-crt1.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/reloc-19-X86_64_TLSGD/0-crt1.lo -------------------------------------------------------------------------------- /tests/linker/reloc-20-X86_64_TLSLD/0-crt1.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/reloc-20-X86_64_TLSLD/0-crt1.lo -------------------------------------------------------------------------------- /tests/linker/reloc-20-X86_64_TLSLD/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS+=-ftls-model=local-dynamic 2 | LDFLAGS=--no-relax 3 | include ../common.makefile 4 | -------------------------------------------------------------------------------- /tests/linker/reloc-19-X86_64_TLSGD/999-libmusl.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/reloc-19-X86_64_TLSGD/999-libmusl.a -------------------------------------------------------------------------------- /tests/linker/reloc-20-X86_64_TLSLD/999-libmusl.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/reloc-20-X86_64_TLSLD/999-libmusl.a -------------------------------------------------------------------------------- /tests/linker/reloc-22-X86_64_GOTTPOFF/0-crt1.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/reloc-22-X86_64_GOTTPOFF/0-crt1.lo -------------------------------------------------------------------------------- /tests/linker/reloc-22-X86_64_GOTTPOFF/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS+=-O0 -ftls-model=initial-exec 2 | LDFLAGS=--no-relax 3 | include ../common.makefile 4 | -------------------------------------------------------------------------------- /tests/linker/reloc-21-R_X86_64_DTPOFF32/0-crt1.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/reloc-21-R_X86_64_DTPOFF32/0-crt1.lo -------------------------------------------------------------------------------- /tests/linker/reloc-21-R_X86_64_DTPOFF32/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS+=-O0 -ftls-model=local-dynamic 2 | LDFLAGS=--no-relax 3 | include ../common.makefile 4 | -------------------------------------------------------------------------------- /tests/linker/reloc-22-X86_64_GOTTPOFF/999-libmusl.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/reloc-22-X86_64_GOTTPOFF/999-libmusl.a -------------------------------------------------------------------------------- /tests/linker/asm-sec/garbage.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | global garbage: 3 | blurp db "yayaya" 4 | garbage db "hello, world!" 5 | 6 | 7 | -------------------------------------------------------------------------------- /tests/linker/reloc-21-R_X86_64_DTPOFF32/999-libmusl.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/reloc-21-R_X86_64_DTPOFF32/999-libmusl.a -------------------------------------------------------------------------------- /tests/linker/gnucompat-tolerate-archive-conflicts/0-crt1.lo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/gnucompat-tolerate-archive-conflicts/0-crt1.lo -------------------------------------------------------------------------------- /tests/linker/gnucompat-tolerate-archive-conflicts/998-libmusl.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/gnucompat-tolerate-archive-conflicts/998-libmusl.a -------------------------------------------------------------------------------- /tests/linker/gnucompat-tolerate-archive-conflicts/999-libmusl.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aep/elfkit/HEAD/tests/linker/gnucompat-tolerate-archive-conflicts/999-libmusl.a -------------------------------------------------------------------------------- /bolter/args.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | use std::env; 3 | use ::fail; 4 | use elfkit::*; 5 | use std::fs::OpenOptions; 6 | use ::goblin; 7 | use std::io::{Read, Cursor}; 8 | use colored::*; 9 | 10 | 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | addons: 5 | apt: 6 | packages: 7 | - nasm 8 | script: 9 | - cargo build --verbose --all 10 | - cargo test 11 | - cd $TRAVIS_BUILD_DIR/tests/linker/ && make travis && make test 12 | -------------------------------------------------------------------------------- /tests/linker/reloc-04-X86_64_PLT32/hello-fn.asm: -------------------------------------------------------------------------------- 1 | extern msg2; 2 | 3 | section .text 4 | global hello:func 5 | hello: 6 | mov rax, 1 7 | mov rdi, 1 8 | mov rsi, msg2 9 | mov rdx, 43 10 | syscall 11 | ret 12 | 13 | -------------------------------------------------------------------------------- /tests/linker/c-simple/10-main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char**argv){ 5 | struct utsname unameData; 6 | uname(&unameData); 7 | printf("The quick brown fox jumps over the lazy dog"); 8 | return 42; 9 | } 10 | -------------------------------------------------------------------------------- /tests/linker/c-explode/10-main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char**argv){ 5 | struct utsname unameData; 6 | uname(&unameData); 7 | printf("The quick brown fox jumps over the lazy dog"); 8 | return 42; 9 | } 10 | -------------------------------------------------------------------------------- /tests/linker/no-undef-reloc/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int __attribute__((weak)) never(); 4 | 5 | int main(int argc, char**argv){ 6 | if (&never) { 7 | never(); 8 | } 9 | printf("The quick brown fox jumps over the lazy dog"); 10 | return 42; 11 | } 12 | -------------------------------------------------------------------------------- /tests/linker/reloc-01-X86_64_64/data.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | 3 | global msg1:data (msg1.end - msg1) 4 | msg1 db "hello, world!" 5 | .end: 6 | 7 | global msg2:data (msg2.end - msg2) 8 | msg2 db "The quick brown fox jumps over the lazy dog" 9 | .end: 10 | -------------------------------------------------------------------------------- /tests/linker/reloc-04-X86_64_PLT32/data.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | 3 | global msg1:data (msg1.end - msg1) 4 | msg1 db "hello, world!" 5 | .end: 6 | 7 | global msg2:data (msg2.end - msg2) 8 | msg2 db "The quick brown fox jumps over the lazy dog" 9 | .end: 10 | -------------------------------------------------------------------------------- /tests/linker/reloc-09-X86_64_GOTPCREL/data.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | 3 | global msg1:data (msg1.end - msg1) 4 | msg1 db "hello, world!" 5 | .end: 6 | 7 | global msg2:data (msg2.end - msg2) 8 | msg2 db "The quick brown fox jumps over the lazy dog" 9 | .end: 10 | -------------------------------------------------------------------------------- /tests/linker/gnucompat-tolerate-archive-conflicts/10-main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char**argv){ 5 | struct utsname unameData; 6 | uname(&unameData); 7 | printf("The quick brown fox jumps over the lazy dog"); 8 | return 42; 9 | } 10 | -------------------------------------------------------------------------------- /tests/linker/reloc-04-X86_64_PLT32/main.asm: -------------------------------------------------------------------------------- 1 | extern hello; 2 | 3 | section .text 4 | global _start 5 | _start: 6 | push rbp 7 | call hello WRT ..plt 8 | pop rbp 9 | mov rax, 60 10 | mov rdi, 10 11 | syscall 12 | global _useless: 13 | 14 | _useless: 15 | nop 16 | -------------------------------------------------------------------------------- /tests/linker/asm-common/main.asm: -------------------------------------------------------------------------------- 1 | common msg2 43; 2 | extern init; 3 | 4 | section .text 5 | global _start 6 | _start: 7 | call init 8 | mov rax, 1 9 | mov rdi, 1 10 | mov rsi, msg2 11 | mov rdx, 43 12 | syscall 13 | mov rax, 60 14 | mov rdi, 10 15 | syscall 16 | -------------------------------------------------------------------------------- /tests/linker/c-init-array/10-main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | char *x = 0; 4 | __attribute__((constructor)) void xinit() { 5 | x = "The quick brown fox jumps over the lazy dog"; 6 | } 7 | 8 | __attribute__((destructor)) void xfini() { 9 | printf(x); 10 | } 11 | 12 | int main(int argc, char**argv) { 13 | return 42; 14 | } 15 | -------------------------------------------------------------------------------- /tests/linker/c-common/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | void init(); 6 | 7 | //this will be emitted as COMMON 8 | char *hello; 9 | 10 | int main(int argc, char**argv){ 11 | init(); 12 | 13 | struct utsname unameData; 14 | uname(&unameData); 15 | printf(hello); 16 | return 42; 17 | } 18 | -------------------------------------------------------------------------------- /tests/linker/reloc-01-X86_64_64/dx_main.asm: -------------------------------------------------------------------------------- 1 | extern msg2; 2 | 3 | section .text 4 | global _start 5 | _start: 6 | mov rax, 1 7 | mov rdi, 1 8 | mov rsi, msg2 9 | mov rdx, 43 10 | syscall 11 | mov rax, 60 12 | mov rdi, 10 13 | syscall 14 | 15 | global _useless: 16 | _useless: 17 | nop 18 | -------------------------------------------------------------------------------- /tests/linker/reloc-09-X86_64_GOTPCREL/main.asm: -------------------------------------------------------------------------------- 1 | extern msg2; 2 | 3 | section .text 4 | global _start 5 | _start: 6 | mov rsi, [rel msg2 wrt ..got] 7 | mov rax, 1 8 | mov rdi, 1 9 | mov rsi, [rel msg2 wrt ..got] 10 | mov rdx, 43 11 | syscall 12 | mov rax, 60 13 | mov rdi, 10 14 | syscall 15 | 16 | global _useless: 17 | _useless: 18 | nop 19 | -------------------------------------------------------------------------------- /tests/linker/asm-simple/main.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | msg1 db "hello, world!" 3 | msg2 db "The quick brown fox jumps over the lazy dog" 4 | 5 | section .text 6 | global _start 7 | _start: 8 | mov rax, 1 9 | mov rdi, 1 10 | mov rsi, msg2 11 | mov rdx, 43 12 | syscall 13 | mov rax, 60 14 | mov rdi, 10 15 | syscall 16 | 17 | global _useless: 18 | _useless: 19 | nop 20 | -------------------------------------------------------------------------------- /tests/linker/asm-sec/main.asm: -------------------------------------------------------------------------------- 1 | extern garbage; 2 | 3 | section .data 4 | msg1: db "hello, world!" 5 | msg2: db "The quick brown fox jumps over the lazy dog" 6 | 7 | section .text 8 | global _start 9 | _start: 10 | mov rax, 1 11 | mov rdi, 1 12 | mov rsi, msg2 13 | mov rdx, 43 14 | syscall 15 | mov rax, 60 16 | mov rdi, 10 17 | syscall 18 | 19 | global _useless: 20 | _useless: 21 | nop 22 | mov rsi, garbage 23 | -------------------------------------------------------------------------------- /tests/linker/Makefile: -------------------------------------------------------------------------------- 1 | SUBDIRS=$(shell find * -maxdepth 1 -type d) 2 | 3 | .PHONY: test 4 | test: 5 | for dir in $(SUBDIRS); do \ 6 | if test -e $$dir/Makefile; \ 7 | then \ 8 | $(MAKE) -C $$dir test || exit; \ 9 | fi; \ 10 | done 11 | @echo -e "\n======\nPASS" 12 | 13 | .PHONY: clean 14 | clean: 15 | for dir in $(SUBDIRS); do \ 16 | if test -e $$dir/Makefile; \ 17 | then \ 18 | $(MAKE) -C $$dir clean; \ 19 | fi; \ 20 | done 21 | 22 | 23 | # travis is ubuntu, which is so hopless garbage, i can't be bothered 24 | travis: 25 | rm -rf cpp-hello 26 | -------------------------------------------------------------------------------- /tests/linker/asm-common/init_common.asm: -------------------------------------------------------------------------------- 1 | DEFAULT REL; 2 | common msg2 43; 3 | 4 | section .data 5 | msg1 db "The quick brown fox jumps over the lazy dog" 6 | 7 | 8 | section .text 9 | global init:func 10 | init: 11 | mov rdi, [msg1] 12 | mov [msg2], rdi 13 | mov rdi, [msg1+8] 14 | mov [msg2+8], rdi 15 | mov rdi, [msg1+16] 16 | mov [msg2+16], rdi 17 | mov rdi, [msg1+24] 18 | mov [msg2+24], rdi 19 | mov rdi, [msg1+32] 20 | mov [msg2+32], rdi 21 | mov rdi, [msg1+40] 22 | mov [msg2+40], rdi 23 | ret 24 | -------------------------------------------------------------------------------- /tests/linker/reloc-19-X86_64_TLSGD/10-main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | __thread char bla[] = "this is just some random crap to test alingment and to make an incorrect offset 0 fail"; 6 | __thread char x[] = "The quick brown fox jumps over the lazy dog"; 7 | __thread char bla2[] = "more crap at the end of tdata"; 8 | 9 | // this will not affect x, because tr gets a thread local copy of x 10 | void *tr(void*_) { 11 | memcpy(&x, "nope\0", 5); 12 | } 13 | 14 | int main(int argc, char**argv) { 15 | pthread_t t; 16 | pthread_create(&t, 0, &tr, 0); 17 | pthread_join(t, 0); 18 | 19 | printf(x); 20 | return 42; 21 | } 22 | -------------------------------------------------------------------------------- /tests/linker/reloc-20-X86_64_TLSLD/10-main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | __thread char bla[] = "this is just some random crap to test alingment and to make an incorrect offset 0 fail"; 6 | __thread char x[] = "The quick brown fox jumps over the lazy dog"; 7 | __thread char blurp[] = "more funny things at the end of tdata"; 8 | 9 | 10 | // this will not affect x, because tr gets a thread local copy of x 11 | void *tr(void*_) { 12 | memcpy(&x, "nope\0", 5); 13 | } 14 | 15 | int main(int argc, char**argv) { 16 | pthread_t t; 17 | pthread_create(&t, 0, &tr, 0); 18 | pthread_join(t, 0); 19 | 20 | printf(x); 21 | return 42; 22 | } 23 | -------------------------------------------------------------------------------- /tests/linker/common.makefile: -------------------------------------------------------------------------------- 1 | OUTPUTS=ld.out ek.out 2 | INPUTS=$(sort $(patsubst %.c,%.o,$(wildcard *.c)) \ 3 | $(patsubst %.asm,%.o,$(wildcard *.asm)) \ 4 | $(patsubst %.cpp,%.o,$(wildcard *.cpp)) \ 5 | $(wildcard *.o) \ 6 | $(wildcard *.a) \ 7 | $(wildcard *.lo)) 8 | 9 | all: $(OUTPUTS) 10 | clean: 11 | rm -f $(OUTPUTS) 12 | 13 | CFLAGS+=-fPIC -g 14 | CXXFLAGS+=-fPIC -g 15 | LDFLAGS+=--emit-relocs -pie -dynamic-linker /lib64/ld-linux-x86-64.so.2 16 | 17 | 18 | %.o: %.asm 19 | nasm -g -f elf64 -o $@ $^ 20 | 21 | ld.out: $(INPUTS) 22 | ld -g -o $@ $(LDFLAGS) $^ 23 | 24 | ek.out: $(INPUTS) 25 | cargo run --bin ld -- -o $@ $(LDFLAGS) $^ 26 | 27 | .PHONY: test 28 | test: all 29 | test "$$(./ld.out)" = "$$(./ek.out)" && echo PASS 30 | 31 | -------------------------------------------------------------------------------- /src/filetype.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Read, Result, Seek, SeekFrom}; 2 | 3 | pub enum FileType { 4 | Archive, 5 | Elf, 6 | Unknown, 7 | } 8 | 9 | 10 | pub fn filetype(io: &mut T) -> Result 11 | where 12 | T: Read + Seek, 13 | { 14 | io.seek(SeekFrom::Start(0))?; 15 | let mut magic = [0; 8]; 16 | io.read(&mut magic)?; 17 | io.seek(SeekFrom::Start(0))?; 18 | 19 | if magic[0..4] == [0x7F, 'E' as u8, 'L' as u8, 'F' as u8] { 20 | return Ok(FileType::Elf); 21 | } 22 | 23 | if magic 24 | == [ 25 | '!' as u8, 26 | '<' as u8, 27 | 'a' as u8, 28 | 'r' as u8, 29 | 'c' as u8, 30 | 'h' as u8, 31 | '>' as u8, 32 | 0x0A, 33 | ] { 34 | return Ok(FileType::Archive); 35 | } 36 | 37 | return Ok(FileType::Unknown); 38 | } 39 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate bitflags; 2 | extern crate byteorder; 3 | #[macro_use] extern crate enum_primitive_derive; 4 | extern crate num_traits; 5 | extern crate indexmap; 6 | #[macro_use] extern crate log; 7 | 8 | #[macro_use] pub mod utils; 9 | pub mod dynamic; 10 | pub mod elf; 11 | pub mod error; 12 | pub mod filetype; 13 | pub mod header; 14 | pub mod loader; 15 | pub mod symbolic_linker; 16 | pub mod relocation; 17 | pub mod section; 18 | pub mod segment; 19 | pub mod strtab; 20 | pub mod symbol; 21 | pub mod types; 22 | 23 | pub use dynamic::{Dynamic, DynamicContent}; 24 | pub use elf::Elf; 25 | pub use error::Error; 26 | pub use header::Header; 27 | pub use symbolic_linker::{SymbolicLinker}; 28 | pub use relocation::Relocation; 29 | pub use section::{Section, SectionContent, SectionHeader}; 30 | pub use segment::SegmentHeader; 31 | pub use strtab::Strtab; 32 | pub use symbol::{Symbol, SymbolSectionIndex}; 33 | -------------------------------------------------------------------------------- /tests/linker/reloc-22-X86_64_GOTTPOFF/10-main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | __thread char x1[] = "The quick brown "; 7 | __thread char bla[] = "this is just some random crap to test alingment"; 8 | __thread char x2[] = "jumps over the "; 9 | __thread char bla4[] = "this is just some random crap to test alingment"; 10 | __thread char bla3[] = "this is just some random crap to test alingment"; 11 | __thread char x3[] = "lazy dog"; 12 | __thread char bla2[] = "this is just some random crap to test alingment"; 13 | 14 | 15 | // this will not affect x, because tr gets a thread local copy of x 16 | void *tr(void*_) { 17 | memcpy(&x1, "nope\0", 5); 18 | memcpy(&x2, "nope\0", 5); 19 | } 20 | 21 | int main(int argc, char**argv) { 22 | pthread_t t; 23 | pthread_create(&t, 0, &tr, 0); 24 | pthread_join(t, 0); 25 | 26 | printf("%s%s%s", x1,x2,x3); 27 | return 42; 28 | } 29 | -------------------------------------------------------------------------------- /tests/linker/reloc-21-R_X86_64_DTPOFF32/10-main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | __thread char derp[] = "this is just some random crap to test alingment"; 7 | 8 | typedef struct { 9 | char x1[32]; 10 | char x2[32]; 11 | char x3[32]; 12 | } blarp; 13 | 14 | __thread blarp bla; 15 | __thread char ferp[] = "this is just some random crap to test alingment"; 16 | 17 | 18 | // this will not affect x, because tr gets a thread local copy of x 19 | void *tr(void*_) { 20 | memcpy(&bla.x1, "nope\0", 5); 21 | memcpy(&bla.x2, "nope\0", 5); 22 | } 23 | 24 | int main(int argc, char**argv) { 25 | blarp *b = &bla; 26 | strcpy(b->x1, "The quick brown "); 27 | strcpy(b->x2, "fox jumps over the "); 28 | strcpy(b->x3, "lazy dog"); 29 | 30 | pthread_t t; 31 | pthread_create(&t, 0, &tr, 0); 32 | pthread_join(t, 0); 33 | 34 | printf("%s%s%s", bla.x1,bla.x2,bla.x3); 35 | return 42; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "elfkit" 3 | version = "0.0.6" 4 | authors = ["Arvid E. Picciani "] 5 | repository = "https://github.com/aep/elfkit" 6 | homepage = "https://github.com/aep/elfkit" 7 | documentation = "https://docs.rs/elfkit" 8 | readme = "README.md" 9 | license = "MIT/Apache-2.0" 10 | description = """ 11 | an elf parser and manipulation library in pure rust 12 | """ 13 | 14 | [dependencies] 15 | byteorder = "1" 16 | enum-primitive-derive = "0.1" 17 | num-traits = "0.2.5" 18 | bitflags = "1.0.0" 19 | itertools = "0.7.8" 20 | rayon = "1.0.1" 21 | bit-vec = "0.5.0" 22 | bloom = "0.3.2" 23 | fnv = "1.0.5" 24 | indexmap = "1.0.1" 25 | log = "0.4" 26 | env_logger = "0.5.10" 27 | pretty_env_logger = "0.2.3" 28 | clap = "2.28.0" 29 | glob = "0.2.11" 30 | ar = "0.6.0" 31 | 32 | 33 | ## bin dependencies 34 | colored = "1" 35 | tempfile = "3.0.2" 36 | sha2 = "0.7.1" 37 | 38 | 39 | [[bin]] 40 | name="readelf" 41 | path="bin/readelf.rs" 42 | 43 | [[bin]] 44 | name="linktree" 45 | path="bin/linktree.rs" 46 | 47 | [[bin]] 48 | name="ld" 49 | path="bin/ld.rs" 50 | 51 | [[bin]] 52 | name="ldd" 53 | path="bin/ldd.rs" 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Arvid E. Picciani 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use types; 2 | use section::SectionContent; 3 | 4 | #[derive(Debug)] 5 | pub enum Error { 6 | Io(::std::io::Error), 7 | InvalidMagic, 8 | InvalidIdentClass(u8), 9 | InvalidEndianness(u8), 10 | InvalidIdentVersion(u8), 11 | InvalidVersion(u32), 12 | InvalidAbi(u8), 13 | InvalidElfType(u16), 14 | InvalidMachineType(u16), 15 | InvalidHeaderFlags(u32), 16 | InvalidSectionFlags(u64), 17 | InvalidSegmentType(u32), 18 | InvalidSectionType(u32), 19 | UnsupportedMachineTypeForRelocation(types::Machine), 20 | InvalidSymbolType(u8), 21 | InvalidSymbolBind(u8), 22 | InvalidSymbolVis(u8), 23 | InvalidDynamicType(u64), 24 | MissingShstrtabSection, 25 | LinkedSectionIsNotStrtab{ 26 | during: &'static str, 27 | link: Option, 28 | }, 29 | InvalidDynamicFlags1(u64), 30 | FirstSectionOffsetCanNotBeLargerThanAddress, 31 | MissingSymtabSection, 32 | LinkedSectionIsNotSymtab, 33 | UnexpectedSectionContent, 34 | InvalidSymbolShndx(String, u16), 35 | DynsymInStaticLibrary, 36 | SymbolSectionIndexExtendedCannotBeWritten, 37 | WritingNotSynced, 38 | SyncingUnloadedSection, 39 | WritingUnloadedSection, 40 | NoSymbolsInObject, 41 | MultipleSymbolSections, 42 | ConflictingSymbol{ 43 | sym: String, 44 | obj1_name: String, 45 | obj2_name: String, 46 | obj1_hash: String, 47 | obj2_hash: String, 48 | }, 49 | UndefinedReference{ 50 | sym: String, 51 | obj: String, 52 | }, 53 | MovingLockedSection{ 54 | sec: String, 55 | old_addr: u64, 56 | new_addr: u64, 57 | cause: String, 58 | } 59 | } 60 | 61 | impl From<::std::io::Error> for Error { 62 | fn from(error: ::std::io::Error) -> Self { 63 | Error::Io(error) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /bin/linktree.rs: -------------------------------------------------------------------------------- 1 | extern crate elfkit; 2 | 3 | use std::env; 4 | use elfkit::{ Header, types, symbol, SymbolicLinker, loader}; 5 | use std::fs::File; 6 | use std::io::Write; 7 | 8 | fn main() { 9 | let mut loader: Vec = env::args().skip(2).map(|s| loader::State::Path{name:s}).collect(); 10 | 11 | let rootsym = env::args().nth(1).unwrap().into_bytes(); 12 | loader.push(loader::State::Object{ 13 | hash: String::from("___linker_entry"), 14 | name: String::from("___linker_entry"), 15 | symbols: vec![symbol::Symbol{ 16 | stype: types::SymbolType::FUNC, 17 | size: 0, 18 | value: 0, 19 | bind: types::SymbolBind::GLOBAL, 20 | vis: types::SymbolVis::DEFAULT, 21 | shndx: symbol::SymbolSectionIndex::Undefined, 22 | name: rootsym.to_vec(), 23 | _name: 0, 24 | }], 25 | header: Header::default(), 26 | sections: Vec::new(), 27 | }); 28 | 29 | let mut linker = SymbolicLinker::default(); 30 | linker.link(loader).unwrap(); 31 | println!("lookup complete: {} objects are required", linker.objects.len()); 32 | linker.gc(); 33 | println!("after gc : {}", linker.objects.len()); 34 | 35 | 36 | let mut file = File::create("link.dot").unwrap(); 37 | writeln!(&mut file, "digraph link{{").unwrap(); 38 | writeln!(&mut file, " node[shape=record]").unwrap(); 39 | writeln!(&mut file, " 40 | Legend [pos=\"1,1\", pin=true, shape=none, margin=0, label=< 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
Legend
RedUNDEF
Regular lineGLOBAL
Dashed lineWEAK
Dotted lineCOMMON
62 | >]; 63 | ").unwrap(); 64 | 65 | linker.write_graphviz(&mut file).unwrap(); 66 | 67 | writeln!(&mut file, "}}").unwrap(); 68 | 69 | 70 | //for link in linker.symtab { 71 | // println!("{:?}", link.sym); 72 | //} 73 | 74 | } 75 | -------------------------------------------------------------------------------- /bolter/relocations.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | use elfkit::{ 3 | Elf, Header 4 | }; 5 | 6 | 7 | /// given value and addr as 64bit address relative to BASE 8 | /// at runtime write the absolute value into addr 9 | pub const len_bootstrap_abs64 : usize = 3 + 4 + 3 + 4; 10 | pub fn write_bootstrap_abs64(eh: &Header, codeoff: u64, code: &mut Vec, value: u64, addr: u64) { 11 | 12 | // the value is given as absolute value from BASE, 13 | // undo the rip relative so it's redone at runtime 14 | // to prododuce the absolute value 15 | // note that rip is the _next_ instruction 16 | let mut rip = codeoff + code.len() as u64 + 3 + 4; 17 | let io = code; 18 | let relative_value = ((value as i64) - (rip as i64)) as i32; 19 | 20 | // lea -> %rax 21 | io.write(&[0x48,0x8d,0x05]); 22 | elf_write_u32!(&eh, io, relative_value as u32); 23 | 24 | rip += 3 + 4; 25 | // the write address is given as absolute too. again, undo the rip relative 26 | let relative_address = ((addr as i64) - (rip as i64)) as i32; 27 | 28 | //mov %rax, ..(%rip) 29 | io.write(&[0x48,0x89,0x05]); 30 | elf_write_u32!(&eh, io, relative_address as u32); 31 | } 32 | 33 | /// write exact absolute value (usually not an address) to addr 34 | pub const len_bootstrap_val64 : usize = 2 + 8 + 3 + 4; 35 | pub fn write_bootstrap_val64(eh: &Header, codeoff: u64, code: &mut Vec, value: u64, addr: u64) { 36 | 37 | let mut rip = codeoff + code.len() as u64 + 3 + 4; 38 | let io = code; 39 | let relative_value = ((value as i64) - (rip as i64)) as i32; 40 | 41 | // movabs value -> %rax 42 | io.write(&[0x48,0xb8]); 43 | elf_write_u64!(&eh, io, value); 44 | 45 | rip += 2 + 8; 46 | // the write address is given as absolute, undo the rip relative 47 | let relative_address = ((addr as i64) - (rip as i64)) as i32; 48 | 49 | //mov %rax, ..(%rip) 50 | io.write(&[0x48,0x89,0x05]); 51 | elf_write_u32!(&eh, io, relative_address as u32); 52 | } 53 | 54 | /// given value and addr as 64bit address relative to BASE 55 | /// at runtime write the value relative to addr into addr 56 | pub const len_bootstrap_rel32 : usize = 2 + 4 + 4; 57 | pub fn write_bootstrap_rel32(eh: &Header, codeoff: u64, code: &mut Vec, value: u64, addr: u64) { 58 | let mut rip = codeoff + code.len() as u64 + 2 + 4 + 4; 59 | let io = code; 60 | let relative_address = ((addr as i64) - (rip as i64)) as i32; 61 | let relative_value = ((value as i64) - (addr as i64)) as i32; 62 | 63 | // movl relative_value,relative_address(%rip) 64 | io.write(&[0xc7,0x05]); 65 | elf_write_u32!(&eh, io, relative_address as u32); 66 | elf_write_u32!(&eh, io, relative_value as u32); 67 | } 68 | 69 | pub const len_reljumpto : usize = 1+4; 70 | pub fn write_reljumpto(eh: &Header, codeoff: u64, code: &mut Vec, targetaddr: u64) { 71 | let pc = codeoff + code.len() as u64 + 1 + 4; 72 | let io = code; 73 | let rel = ((targetaddr as i64) - (pc as i64)) as i32; 74 | // jmpq 75 | io.write(&[0xe9]); 76 | elf_write_u32!(&eh, io, rel as u32); 77 | } 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /src/segment.rs: -------------------------------------------------------------------------------- 1 | use error::Error; 2 | use types; 3 | use header::Header; 4 | 5 | use std::io::BufWriter; 6 | use std::io::{Read, Write}; 7 | use num_traits::{FromPrimitive, ToPrimitive}; 8 | 9 | #[derive(Default, Debug, Clone)] 10 | pub struct SegmentHeader { 11 | pub phtype: types::SegmentType, 12 | pub flags: types::SegmentFlags, 13 | pub offset: u64, 14 | pub vaddr: u64, 15 | pub paddr: u64, 16 | pub filesz: u64, 17 | pub memsz: u64, 18 | pub align: u64, 19 | } 20 | 21 | impl SegmentHeader { 22 | pub fn entsize(eh: &Header) -> usize { 23 | match eh.ident_class { 24 | types::Class::Class64 => 4 + 4 + 6 * 8, 25 | types::Class::Class32 => 4 + 4 + 6 * 4, 26 | } 27 | } 28 | 29 | pub fn from_reader(io: &mut R, eh: &Header) -> Result 30 | where 31 | R: Read, 32 | { 33 | let mut r = SegmentHeader::default(); 34 | let reb = elf_read_u32!(eh, io)?; 35 | r.phtype = match types::SegmentType::from_u32(reb) { 36 | Some(v) => v, 37 | None => return Err(Error::InvalidSegmentType(reb)), 38 | }; 39 | 40 | match eh.ident_class { 41 | types::Class::Class64 => { 42 | r.flags = types::SegmentFlags::from_bits_truncate(elf_read_u32!(eh, io)? as u64); 43 | r.offset = elf_read_u64!(eh, io)?; 44 | r.vaddr = elf_read_u64!(eh, io)?; 45 | r.paddr = elf_read_u64!(eh, io)?; 46 | r.filesz = elf_read_u64!(eh, io)?; 47 | r.memsz = elf_read_u64!(eh, io)?; 48 | r.align = elf_read_u64!(eh, io)?; 49 | } 50 | types::Class::Class32 => { 51 | r.offset = elf_read_u32!(eh, io)? as u64; 52 | r.vaddr = elf_read_u32!(eh, io)? as u64; 53 | r.paddr = elf_read_u32!(eh, io)? as u64; 54 | r.filesz = elf_read_u32!(eh, io)? as u64; 55 | r.memsz = elf_read_u32!(eh, io)? as u64; 56 | r.flags = types::SegmentFlags::from_bits_truncate(elf_read_u32!(eh, io)? as u64); 57 | r.align = elf_read_u32!(eh, io)? as u64; 58 | } 59 | }; 60 | Ok(r) 61 | } 62 | pub fn to_writer(&self, eh: &Header, io: &mut R) -> Result<(), Error> 63 | where 64 | R: Write, 65 | { 66 | let mut w = BufWriter::new(io); 67 | elf_write_u32!(eh, w, self.phtype.to_u32().unwrap())?; 68 | match eh.ident_class { 69 | types::Class::Class64 => { 70 | elf_write_u32!(eh, w, self.flags.bits() as u32)?; 71 | elf_write_u64!(eh, w, self.offset)?; 72 | elf_write_u64!(eh, w, self.vaddr)?; 73 | elf_write_u64!(eh, w, self.paddr)?; 74 | elf_write_u64!(eh, w, self.filesz)?; 75 | elf_write_u64!(eh, w, self.memsz)?; 76 | elf_write_u64!(eh, w, self.align)?; 77 | } 78 | types::Class::Class32 => { 79 | elf_write_u32!(eh, w, self.offset as u32)?; 80 | elf_write_u32!(eh, w, self.vaddr as u32)?; 81 | elf_write_u32!(eh, w, self.paddr as u32)?; 82 | elf_write_u32!(eh, w, self.filesz as u32)?; 83 | elf_write_u32!(eh, w, self.memsz as u32)?; 84 | elf_write_u32!(eh, w, self.flags.bits() as u32)?; 85 | elf_write_u32!(eh, w, self.align as u32)?; 86 | } 87 | }; 88 | Ok(()) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/strtab.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Read, Write}; 2 | use {Error, Header, SectionContent}; 3 | 4 | #[derive(Debug, Default, Clone)] 5 | pub struct Strtab { 6 | data: Vec, 7 | } 8 | 9 | impl Strtab { 10 | pub fn len(&self, _: &Header) -> usize { 11 | self.data.len() 12 | } 13 | pub fn entsize(_: &Header) -> usize { 14 | 1 15 | } 16 | pub fn from_reader( 17 | mut io: impl Read, 18 | _: Option<&SectionContent>, 19 | _: &Header, 20 | ) -> Result { 21 | let mut data = Vec::new(); 22 | io.read_to_end(&mut data)?; 23 | Ok(SectionContent::Strtab(Strtab { data })) 24 | } 25 | 26 | pub fn to_writer(&self, mut io: impl Write, _: &Header) -> Result { 27 | Ok(io.write(&self.data)?) 28 | } 29 | 30 | pub fn get(&self, i: usize) -> Vec { 31 | if i >= self.data.len() { 32 | println!("pointer {} into strtab extends beyond section size", i); 33 | return b"".to_vec(); 34 | } 35 | self.data[i..] 36 | .split(|&c| c == 0) 37 | .next() 38 | .unwrap_or(&[0; 0]) 39 | .to_vec() 40 | } 41 | 42 | pub fn insert(&mut self, ns: &[u8]) -> usize { 43 | // If ns is already in our data, it takes up (ns.len() + 1) bytes. 44 | if let Some(max_start) = self.data.len().checked_sub(ns.len() + 1) { 45 | for start in 0..=max_start { 46 | // We first check for ns, then check for the null terminator. 47 | if self.data[start..].starts_with(ns) { 48 | if self.data[start + ns.len()] == 0 { 49 | return start; 50 | } 51 | } 52 | } 53 | } 54 | 55 | // No spot for ns, insert it (and the null terminator) at the end. 56 | let i = self.data.len(); 57 | self.data.extend(ns); 58 | self.data.push(0); 59 | i 60 | } 61 | } 62 | 63 | #[cfg(test)] 64 | mod tests { 65 | use super::*; 66 | 67 | #[test] 68 | fn test_insert_then_get() { 69 | let ns1: &[u8] = b".text"; 70 | let ns2: &[u8] = b".data"; 71 | let mut strtab = Strtab { data: Vec::new() }; 72 | 73 | assert_eq!(strtab.insert(&[]), 0); 74 | assert_eq!(strtab.insert(ns1), 1); 75 | assert_eq!(strtab.insert(ns2), 2 + ns1.len()); 76 | 77 | assert_eq!(strtab.get(0), &[]); 78 | assert_eq!(strtab.get(1), ns1); 79 | assert_eq!(strtab.get(2 + ns1.len()), ns2); 80 | } 81 | 82 | #[test] 83 | fn test_insert_suffix() { 84 | let mut strtab = Strtab { data: Vec::new() }; 85 | assert_eq!(strtab.insert(b".text"), 0); 86 | 87 | // inserting an existing suffix should not grow the data size 88 | let old_size = strtab.data.len(); 89 | assert_eq!(strtab.insert(b"xt"), 3); 90 | assert_eq!(strtab.data.len(), old_size); 91 | } 92 | 93 | #[test] 94 | fn test_starting_data() { 95 | let ns = b".text"; 96 | // Have the data initially be [NULL, ".text"] 97 | let mut data = vec![0]; 98 | data.extend(ns); 99 | data.push(0); 100 | 101 | let mut strtab = Strtab { data }; 102 | assert_eq!(strtab.get(1), ns); 103 | assert_eq!(strtab.insert(b".data"), 2 + ns.len()); 104 | } 105 | 106 | #[test] 107 | fn test_only_data() { 108 | let ns: &[u8] = b".text"; 109 | // Have the data initially just be ".text" 110 | let mut data = vec![]; 111 | data.extend(ns); 112 | data.push(0); 113 | let mut strtab = Strtab { data }; 114 | // The only value should be ".text" 115 | assert_eq!(strtab.get(0), ns); 116 | // Inserting the value again should change nothing. 117 | assert_eq!(strtab.insert(ns), 0); 118 | assert_eq!(strtab.get(0), ns); 119 | } 120 | 121 | #[test] 122 | fn test_insert_prefix() { 123 | // Have the data initially just be ".text" 124 | let mut data = vec![]; 125 | data.extend(b".text"); 126 | data.push(0); 127 | 128 | let mut strtab = Strtab { data }; 129 | // Inserting a prefix should not save any space 130 | assert_eq!(strtab.insert(b".tex"), 6); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /NOTES.md: -------------------------------------------------------------------------------- 1 | notepad for useful learnings 2 | =========================== 3 | 4 | 5 | segment headers must come before sections 6 | ----------------------------------------- 7 | 8 | A 'bug' in linux causes it to pass an incorrect pointer to PHDR to ld via auxv, 9 | if there are holes between the program header and the base load address. 10 | 11 | As a result, the segment headers must be written before the section content, so 12 | that the first LOAD segment contains both. 13 | 14 | This prevents usecases which would require to rewrite the program header of existing binaries, 15 | since you can't create additional room for more program headers by moving them to the end of the file. 16 | A commonly applied hack seems to be to move some sections from the front to the back instead. 17 | 18 | 19 | DT_RELA 20 | ------------ 21 | 22 | DT_RELA points into .rela.dyn for relocations to be applied by the dynloader (the thin in .interp) 23 | DT_RELACOUNT counts ONLY R_X86_64_RELATIVE which are executed before loading libraries 24 | while DT_RELASZ is the size of the full section, potentially containing more reloc types 25 | 26 | according to the glibc code (untested) these additional relocations should work: 27 | - R_X86_64_SIZE64 28 | - R_X86_64_SIZE32 29 | - R_X86_64_GLOB_DAT 30 | - R_X86_64_DTPMOD64 31 | - R_X86_64_DTPOFF64 32 | - R_X86_64_TLSDESC 33 | - R_X86_64_TPOFF64 34 | - R_X86_64_64 35 | - R_X86_64_SIZE32 36 | - R_X86_64_32 37 | - R_X86_64_PC32 38 | - R_X86_64_COPY 39 | - R_X86_64_IRELATIVE 40 | 41 | 42 | there's also DT_TEXTREL, which may contain even more relocation types. it seems to be a count offset into DT_RELA 43 | 44 | X86_64 RIP Relative Relocations 45 | ------------------------------- 46 | 47 | so on x86_64 there's a thing called RIP. i'm not sure if that's an actual register, 48 | it sort of is a pointer to the next instruction, but RELATIVE to the program load address. 49 | so if program loads at 0xwhatever, it'll still be 0x1 for the first instruction executed. 50 | I still have no idea how that even works, because how does the cpu know where the thing was loaded? 51 | anyway.. 52 | 53 | Together with for exmaple X86_64_GOTPCREL the compiler emits a LEA instruction with an offset from RIP, so for example: 54 | 55 | ``` 56 | 0x0: "hello" 57 | 0x5: 48 8d 35 06 00 00 00 lea -0xb(%rip),%rsi 58 | 0xb: ... 59 | ``` 60 | 61 | which means "add -0xb to the position of the next instruction and store that POSITION in rsi" 62 | that's different from mov, which would store the value at that position in rsi 63 | 64 | in this case X86_64_GOTPCREL has 65 | - offset=0x9 (the address offset part of lea) 66 | - addend=-0x4 (i am currently assuming this corrects for %rip being the NEXT instruction) 67 | - symbol=something pointing at 0x0 hello 68 | 69 | if the linker knows the address of hello, it can simply write that at reloc.offset. 70 | Otherwise, it's supposed to 71 | 72 | - change the instruction from lea to mov 73 | - emit a Global Offset Table (.got) section with 8 bytes zeros 74 | - write the address to that into reloc.offset 75 | - emit something like X86_64_GLOB_DAT which will at runtime copy the address of hello to .got 76 | 77 | so the linked executable will look like: 78 | 79 | ``` 80 | 0x0: 00 00 00 00 81 | 0x5: 48 8d 35 06 00 00 00 mov -0xb(%rip),%rsi 82 | 0xb: ... 83 | ``` 84 | 85 | when loading, the dynloader then puts the address of hello in there 86 | 87 | ``` 88 | 0x0: 00 00 00 0f 89 | 0x5: 48 8d 35 06 00 00 00 mov -0xb(%rip),%rsi 90 | 0xb: ... 91 | 0xf: "hello" 92 | ``` 93 | 94 | the mov instruction will load (unlike lea) the value from 0x0, apply the rip offset to get an absolute address, 95 | and store it in rsi. There doesn't seem to be any immediate benefit from doing this, and in most cases there isn't. 96 | To the best of my current knowledge, a GOT is only nessesary if the thing we want to address is located so far away that 97 | a 32bit signed int can't express the distance. 98 | 99 | This happens rather often when we load another library at runtime. 100 | Thanks to the Memory Mapping Unit of the CPU, using the entire 64bit for addressing doesn't relly come with any cost, 101 | so ld.so regularily places libraries far apart from each other. This is why we need a GOT to have ld.so tell us the absolute address 102 | of a symbol in another library at runtime in 64bit space. 103 | 104 | 105 | useful debugging help 106 | ------------------------ 107 | 108 | make ld show the aux vector: 109 | 110 | LD_SHOW_AUXV=1 /bin/something 111 | 112 | 113 | show memory map in gdb: 114 | 115 | info proc mappings 116 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/aep/elfkit.svg?branch=master)](https://travis-ci.org/aep/elfkit) 2 | [![crates.io](http://meritbadge.herokuapp.com/elfkit)](https://crates.io/crates/elfkit) 3 | [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE-MIT) 4 | [![docs](https://docs.rs/elfkit/badge.svg)](https://docs.rs/elfkit) 5 | 6 | DED 7 | ========= 8 | 9 | please note that i no longer work with rust and this is not maintained. 10 | If anyone has interest in maintaining this crate, feel free to open a github issue. 11 | 12 | 13 | 14 | Elfkit 15 | ========= 16 | 17 | An elf read and manipulation library in pure Rust (written from scratch, no bfd, no gnu code, no license infections), 18 | intended to be used in binary manipulation utils such as strip, chrpath, objcopy and ld. 19 | The end goal is to build a well designed library that facilitates all sorts of binary manipulation magic. 20 | 21 | elfkit can now link elfkit, so it's reasonably complete for x86_64. But it's definitely not stable yet and might produce incorrect code. 22 | 23 | 24 | Using the linker 25 | --------------------- 26 | 27 | The quickest way to use elfkit with Rust is with [korhal/stasis](https://github.com/korhalio/stasis). 28 | 29 | You can also either build from source or download binaries. 30 | GCC does not have an option to use a foreign linker, so we need to pretend we're ld.gold, like so: 31 | 32 | ```sh 33 | curl -L https://github.com/aep/elfkit/releases/download/0.0.4/elfkit-0.0.4.tar.xz | tar xvjf - 34 | export PATH="$PWD/elfkit-0.0.4/:$PATH" 35 | musl-gcc -fuse-ld=gold main.c 36 | ``` 37 | 38 | For using elfkit for compiling Rust code, add the following to `~/.cargo/config`: 39 | 40 | ```toml 41 | [target.x86_64-unknown-linux-musl] 42 | rustflags = [ 43 | "-C", "link-arg=-fuse-ld=gold", 44 | "-C", "link-arg=-Wl,-dynamic-linker,/usr/local/musl/lib/libc.so", 45 | ] 46 | ``` 47 | 48 | When compiling from source, create the ld.gold symlink manually: 49 | 50 | ```sh 51 | cargo build --release --bin ld 52 | ln -s "$PWD/target/release/ld" /usr/local/bin/ld.gold 53 | ``` 54 | 55 | 56 | Other binutils 57 | --------------------- 58 | 59 | readelf: 60 | ![screenshot](/bin/readelf-screenshot.png?raw=true) 61 | 62 | 63 | implementation status 64 | --------------------- 65 | 66 | binutils 67 | 68 | | type | status | gnu compatible | 69 | |--------------|-----------|----------------| 70 | | ldd | done | no | 71 | | readelf | done | no | 72 | | ld | wip | wip | 73 | | objdump | - | - | 74 | | ar | - | - | 75 | | as | - | - | 76 | | nm | - | - | 77 | | strip | - | - | 78 | 79 | section parsers 80 | 81 | | type | read | write | 82 | |--------------|---------|---------| 83 | | symbols | done | done | 84 | | strtab | done | done | 85 | | relocations | done | done | 86 | | dynamic | done | done | 87 | | note | - | - | 88 | | gnu_hash | - | - | 89 | | hash | - | mvp | 90 | | versym | - | - | 91 | | verneed | - | - | 92 | 93 | architectures 94 | 95 | | abi | parser | linker | 96 | |--------------|---------|--------| 97 | | x86_64 | done | wip | 98 | | mips32r2 o32 | done | | 99 | | arm eabi | done | | 100 | 101 | 102 | modular linker toolkit 103 | --------------------- 104 | 105 | - Loader: loads elf objects from disk 106 | - Linker: produces a link graph of sections from a loader 107 | - Collector: bakes multiple sections into a single object 108 | - Relocator: applies relocations to a combined object 109 | 110 | alternatives 111 | ---------------- 112 | 113 | - [goblin](https://crates.io/crates/goblin) mach-o and archive support, no-std support, very low level 114 | - [elf](https://crates.io/crates/elf) most popular, most generic use case, no writing, no section parsing 115 | - [xmas-elf](https://github.com/nrc/xmas-elf) zero alloc (good for writing an OS), read only 116 | 117 | 118 | references 119 | --------------------- 120 | - https://en.wikipedia.org/wiki/Executable_and_Linkable_Format 121 | - https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-r252.pdf 122 | - https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf 123 | - http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044f/IHI0044F_aaelf.pdf 124 | - https://dmz-portal.imgtec.com/wiki/MIPS_ABI_Project 125 | - https://dmz-portal.imgtec.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking 126 | - http://www.mindfruit.co.uk/2012/06/relocations-relocations.html#reloc_types_table 127 | - https://www.akkadia.org/drepper/tls.pdf 128 | -------------------------------------------------------------------------------- /src/dynamic.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Read, Write}; 2 | use {Error, Header, SectionContent}; 3 | use types; 4 | use num_traits::{FromPrimitive, ToPrimitive}; 5 | 6 | #[derive(Debug, Clone)] 7 | pub enum DynamicContent { 8 | None, 9 | String((Vec,Option)), 10 | Address(u64), 11 | Flags1(types::DynamicFlags1), 12 | } 13 | 14 | impl Default for DynamicContent{ 15 | fn default() -> Self { 16 | DynamicContent::None 17 | } 18 | } 19 | 20 | #[derive(Debug, Clone, Default)] 21 | pub struct Dynamic { 22 | pub dhtype: types::DynamicType, 23 | pub content: DynamicContent, 24 | } 25 | 26 | impl Dynamic { 27 | pub fn entsize(eh: &Header) -> usize { 28 | match eh.ident_class { 29 | types::Class::Class64 => 16, 30 | types::Class::Class32 => 8, 31 | } 32 | } 33 | 34 | pub fn from_reader( 35 | mut io: R, 36 | linked: Option<&SectionContent>, 37 | eh: &Header, 38 | ) -> Result 39 | where 40 | R: Read, 41 | { 42 | let strtab = match linked { 43 | None => None, 44 | Some(&SectionContent::Strtab(ref s)) => Some(s), 45 | any => return Err(Error::LinkedSectionIsNotStrtab{ 46 | during: "reading dynamic", 47 | link: any.map(|v|v.clone()), 48 | }), 49 | }; 50 | 51 | let mut r = Vec::new(); 52 | 53 | while let Ok(tag) = elf_read_uclass!(eh, io) { 54 | let val = elf_read_uclass!(eh, io)?; 55 | 56 | match types::DynamicType::from_u64(tag) { 57 | None => return Err(Error::InvalidDynamicType(tag)), 58 | Some(types::DynamicType::NULL) => { 59 | r.push(Dynamic { 60 | dhtype: types::DynamicType::NULL, 61 | content: DynamicContent::None, 62 | }); 63 | break; 64 | }, 65 | Some(types::DynamicType::RPATH) => { 66 | r.push(Dynamic { 67 | dhtype: types::DynamicType::RPATH, 68 | content: DynamicContent::String(match strtab { 69 | None => (Vec::default(),None), 70 | Some(s) => (s.get(val as usize), Some(val)), 71 | }), 72 | }); 73 | }, 74 | Some(types::DynamicType::NEEDED) => { 75 | r.push(Dynamic { 76 | dhtype: types::DynamicType::NEEDED, 77 | content: DynamicContent::String(match strtab { 78 | None => (Vec::default(),None), 79 | Some(s) => (s.get(val as usize), Some(val)), 80 | }), 81 | }); 82 | }, 83 | Some(types::DynamicType::FLAGS_1) => { 84 | r.push(Dynamic { 85 | dhtype: types::DynamicType::FLAGS_1, 86 | content: DynamicContent::Flags1( 87 | match types::DynamicFlags1::from_bits(val) { 88 | Some(v) => v, 89 | None => return Err(Error::InvalidDynamicFlags1(val)), 90 | }, 91 | ), 92 | }); 93 | }, 94 | Some(x) => { 95 | r.push(Dynamic { 96 | dhtype: x, 97 | content: DynamicContent::Address(val), 98 | }); 99 | } 100 | }; 101 | } 102 | 103 | Ok(SectionContent::Dynamic(r)) 104 | } 105 | pub fn to_writer( 106 | &self, 107 | mut io: W, 108 | eh: &Header, 109 | ) -> Result<(usize), Error> 110 | where 111 | W: Write, 112 | { 113 | elf_write_uclass!(eh, io, self.dhtype.to_u64().unwrap())?; 114 | 115 | match self.content { 116 | DynamicContent::None => { 117 | elf_write_uclass!(eh, io, 0)?; 118 | } 119 | DynamicContent::String(ref s) => match s.1 { 120 | Some(val) => elf_write_uclass!(eh, io, val)?, 121 | None => return Err(Error::WritingNotSynced), 122 | }, 123 | DynamicContent::Address(ref v) => { 124 | elf_write_uclass!(eh, io, *v)?; 125 | } 126 | DynamicContent::Flags1(ref v) => { 127 | elf_write_uclass!(eh, io, v.bits())?; 128 | } 129 | } 130 | Ok(Dynamic::entsize(eh)) 131 | } 132 | 133 | pub fn sync(&mut self, linked: Option<&mut SectionContent>, _: &Header) -> Result<(), Error> { 134 | match self.content { 135 | DynamicContent::String(ref mut s) => match linked { 136 | Some(&mut SectionContent::Strtab(ref mut strtab)) => { 137 | s.1 = Some(strtab.insert(&s.0) as u64); 138 | } 139 | any => return Err(Error::LinkedSectionIsNotStrtab{ 140 | during: "syncing dynamic", 141 | link: any.map(|v|v.clone()), 142 | }), 143 | }, 144 | DynamicContent::None => {} 145 | DynamicContent::Address(_) => {} 146 | DynamicContent::Flags1(_) => {} 147 | } 148 | Ok(()) 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /bin/ldd.rs: -------------------------------------------------------------------------------- 1 | extern crate glob; 2 | extern crate clap; 3 | extern crate elfkit; 4 | 5 | use std::fs::{File}; 6 | use elfkit::Elf; 7 | use std::path::Path; 8 | use std::collections::HashSet; 9 | use std::io::{self}; 10 | use std::io::BufReader; 11 | use std::io::BufRead; 12 | use glob::glob; 13 | 14 | struct Ldd { 15 | sysroot: String, 16 | lpaths: Vec, 17 | visited: HashSet, 18 | } 19 | 20 | fn join_paths(a: &str, b: &str) -> String { 21 | if b.len() < 1 { 22 | return String::from(a); 23 | } 24 | let mut a = String::from(a); 25 | if a.len() < 1 { 26 | return a; 27 | } 28 | if a.chars().last().unwrap() != '/' { 29 | a.push('/'); 30 | } 31 | 32 | if b.chars().nth(0).unwrap() == '/' { 33 | return a + &b[1..]; 34 | } 35 | return a + b; 36 | } 37 | 38 | impl Ldd { 39 | fn recurse(&mut self, path: &str) { 40 | let mut f = File::open(path).unwrap(); 41 | let mut elf = Elf::from_reader(&mut f).unwrap(); 42 | 43 | let mut deps = Vec::new(); 44 | for shndx in 0..elf.sections.len() { 45 | if elf.sections[shndx].header.shtype == elfkit::types::SectionType::DYNAMIC { 46 | elf.load(shndx, &mut f).unwrap(); 47 | let dynamic = elf.sections[shndx].content.as_dynamic().unwrap(); 48 | 49 | for dyn in dynamic.iter() { 50 | if dyn.dhtype == elfkit::types::DynamicType::RPATH{ 51 | if let elfkit::dynamic::DynamicContent::String(ref name) = dyn.content { 52 | self.lpaths.push(join_paths( 53 | &self.sysroot, &String::from_utf8_lossy(&name.0).into_owned())) 54 | } 55 | } 56 | if dyn.dhtype == elfkit::types::DynamicType::NEEDED { 57 | if let elfkit::dynamic::DynamicContent::String(ref name) = dyn.content { 58 | deps.push(String::from_utf8_lossy(&name.0).into_owned()); 59 | } 60 | } 61 | } 62 | } 63 | } 64 | 65 | for dep in &mut deps { 66 | let mut found = false; 67 | for lpath in self.lpaths.iter() { 68 | let joined = join_paths(&lpath, &dep); 69 | let joined = Path::new(&joined); 70 | if joined.exists() { 71 | *dep = joined.to_string_lossy().into_owned(); 72 | found = true; 73 | break; 74 | } 75 | } 76 | if found { 77 | if self.visited.insert(dep.clone()) { 78 | println!("{}", dep); 79 | self.recurse(dep); 80 | } 81 | } else { 82 | panic!("unable to find dependcy {} in {:?}", dep, self.lpaths); 83 | } 84 | } 85 | } 86 | } 87 | 88 | fn parse_ld_so_conf(path: &str) -> io::Result> { 89 | let mut paths = Vec::new(); 90 | 91 | let f = File::open(path)?; 92 | let f = BufReader::new(&f); 93 | for line in f.lines() { 94 | let line = line?; 95 | let line = line.trim(); 96 | if line.starts_with("#") { 97 | continue; 98 | } 99 | if line == "" { 100 | continue; 101 | } 102 | 103 | if line.contains(" ") { 104 | if line.starts_with("include ") { 105 | for entry in glob(line.split(" ").last().unwrap()).expect("Failed to read glob pattern") { 106 | paths.extend(parse_ld_so_conf(&entry.unwrap().to_string_lossy().into_owned())?); 107 | } 108 | 109 | } 110 | } else { 111 | paths.push(line.to_owned()); 112 | } 113 | } 114 | Ok(paths) 115 | } 116 | 117 | fn main() { 118 | let matches = clap::App::new("elfkit-ldd") 119 | .setting(clap::AppSettings::ArgRequiredElseHelp) 120 | .setting(clap::AppSettings::UnifiedHelpMessage) 121 | .setting(clap::AppSettings::DisableHelpSubcommand) 122 | .version("0.5") 123 | .arg(clap::Arg::with_name("file") 124 | .required(true) 125 | .help("path to binary to inspect") 126 | .takes_value(true) 127 | .index(1) 128 | ) 129 | .arg(clap::Arg::with_name("library-path") 130 | .short("L") 131 | .long("library-path") 132 | .takes_value(true) 133 | .multiple(true) 134 | .help("library lookup path, ignores $SYSROOT/etc/ld.so.conf") 135 | ) 136 | .arg(clap::Arg::with_name("sysroot") 137 | .short("R") 138 | .long("sysroot") 139 | .takes_value(true) 140 | .help("specify sysroot to look up dependencies in, instead of /") 141 | ) 142 | .get_matches(); 143 | 144 | 145 | 146 | let sysroot = matches.value_of("sysroot").unwrap_or("/").to_owned(); 147 | 148 | let lpaths = match matches.values_of("library-path") { 149 | None => { 150 | match parse_ld_so_conf(&(sysroot.clone() + "/etc/ld.so.conf")) { 151 | Ok(l) => l.into_iter().map(|l| join_paths(&sysroot, &l)).collect(), 152 | Err(_) => vec![join_paths(&sysroot, "/lib"), join_paths(&sysroot, "/usr/lib")], 153 | } 154 | }, 155 | Some(vals) => { 156 | vals.map(|v|v.to_owned()).collect() 157 | } 158 | }; 159 | 160 | 161 | let mut ldd = Ldd{ 162 | sysroot: sysroot, 163 | lpaths: lpaths, 164 | visited: HashSet::new(), 165 | }; 166 | 167 | let path = matches.value_of("file").unwrap(); 168 | ldd.recurse(&path); 169 | } 170 | 171 | 172 | -------------------------------------------------------------------------------- /src/header.rs: -------------------------------------------------------------------------------- 1 | use num_traits::{FromPrimitive, ToPrimitive}; 2 | use std::io::{Read, Write}; 3 | use std::io::BufWriter; 4 | use error::Error; 5 | use types; 6 | 7 | #[derive(Debug,Clone)] 8 | pub struct Header { 9 | pub ident_magic: [u8; 4], 10 | pub ident_class: types::Class, 11 | pub ident_endianness: types::Endianness, 12 | pub ident_version: u8, // 1 13 | pub ident_abi: types::Abi, 14 | pub ident_abiversion: u8, 15 | 16 | pub etype: types::ElfType, 17 | pub machine: types::Machine, 18 | pub version: u32, //1 19 | pub entry: u64, //program counter starts here 20 | pub phoff: u64, //offset of program header table 21 | pub shoff: u64, //offset of section header table 22 | pub flags: types::HeaderFlags, 23 | pub ehsize: u16, //size of this header (who cares?) 24 | pub phentsize: u16, //the size of a program header table entry 25 | pub phnum: u16, //the number of entries in the program header table 26 | pub shentsize: u16, //the size of a section header table entry 27 | pub shnum: u16, //the number of entries in the section header table 28 | pub shstrndx: u16, //where to find section names 29 | } 30 | 31 | impl Default for Header { 32 | fn default() -> Self { 33 | Header { 34 | ident_magic: [0x7F, 0x45, 0x4c, 0x46], 35 | ident_class: types::Class::Class64, 36 | ident_endianness: types::Endianness::LittleEndian, 37 | ident_version: 1, 38 | ident_abi: types::Abi::SYSV, 39 | ident_abiversion: 0, 40 | etype: types::ElfType::default(), 41 | machine: types::Machine::default(), 42 | version: 1, 43 | entry: 0, 44 | phoff: 0, 45 | shoff: 0, 46 | flags: types::HeaderFlags::default(), 47 | ehsize: 0, 48 | phentsize: 0, 49 | phnum: 0, 50 | shentsize: 0, 51 | shnum: 0, 52 | shstrndx: 0, 53 | } 54 | } 55 | } 56 | 57 | 58 | impl Header { 59 | pub fn from_reader(io: &mut R) -> Result 60 | where 61 | R: Read, 62 | { 63 | let mut r = Header::default(); 64 | let mut b = [0; 16]; 65 | if let Err(_) = io.read_exact(&mut b) { 66 | return Err(Error::InvalidMagic); 67 | } 68 | r.ident_magic.clone_from_slice(&b[0..4]); 69 | 70 | if r.ident_magic != [0x7F, 0x45, 0x4c, 0x46] { 71 | return Err(Error::InvalidMagic); 72 | } 73 | 74 | r.ident_class = match types::Class::from_u8(b[4]) { 75 | Some(v) => v, 76 | None => return Err(Error::InvalidIdentClass(b[4])), 77 | }; 78 | 79 | r.ident_endianness = match types::Endianness::from_u8(b[5]) { 80 | Some(v) => v, 81 | None => return Err(Error::InvalidEndianness(b[5])), 82 | }; 83 | 84 | r.ident_version = b[6]; 85 | if r.ident_version != 1 { 86 | return Err(Error::InvalidIdentVersion(b[6])); 87 | } 88 | 89 | r.ident_abi = match types::Abi::from_u8(b[7]) { 90 | Some(v) => v, 91 | None => return Err(Error::InvalidAbi(b[7])), 92 | }; 93 | 94 | r.ident_abiversion = b[8]; 95 | 96 | elf_dispatch_endianness!(r => { 97 | 98 | let reb = read_u16(io)?; 99 | r.etype = match types::ElfType::from_u16(reb) { 100 | Some(v) => v, 101 | None => return Err(Error::InvalidElfType(reb)), 102 | }; 103 | 104 | let reb = read_u16(io)?; 105 | r.machine = match types::Machine::from_u16(reb) { 106 | Some(v) => v, 107 | None => return Err(Error::InvalidMachineType(reb)), 108 | }; 109 | 110 | r.version = read_u32(io)?; 111 | if r.version != 1 { 112 | return Err(Error::InvalidVersion(r.version)); 113 | } 114 | 115 | 116 | elf_dispatch_uclass!(r => { 117 | r.entry = read_uclass(io)?; 118 | r.phoff = read_uclass(io)?; 119 | r.shoff = read_uclass(io)?; 120 | }); 121 | 122 | let reb = io.read_u32::()?; 123 | r.flags = types::HeaderFlags::from_bits_truncate(reb); 124 | //r.flags = match types::HeaderFlags::from_bits(reb) { 125 | // Some(v) => v, 126 | // None => return Err(Error::InvalidHeaderFlags(reb)), 127 | //}; 128 | 129 | r.ehsize = read_u16(io)?; 130 | r.phentsize = read_u16(io)?; 131 | r.phnum = read_u16(io)?; 132 | r.shentsize = read_u16(io)?; 133 | r.shnum = read_u16(io)?; 134 | r.shstrndx = read_u16(io)?; 135 | }); 136 | 137 | Ok(r) 138 | } 139 | 140 | pub fn to_writer(&self, io: &mut R) -> Result<(), Error> 141 | where 142 | R: Write, 143 | { 144 | let mut w = BufWriter::new(io); 145 | w.write(&self.ident_magic)?; 146 | w.write(&[self.ident_class.to_u8().unwrap()])?; 147 | w.write(&[self.ident_endianness.to_u8().unwrap()])?; 148 | w.write(&[self.ident_version.to_u8().unwrap()])?; 149 | w.write(&[self.ident_abi.to_u8().unwrap()])?; 150 | w.write(&[0; 8])?; 151 | 152 | elf_write_u16!(self, w, self.etype.to_u16().unwrap())?; 153 | elf_write_u16!(self, w, self.machine.to_u16().unwrap())?; 154 | elf_write_u32!(self, w, self.version.to_u32().unwrap())?; 155 | elf_write_uclass!(self, w, self.entry.to_u64().unwrap())?; 156 | elf_write_uclass!(self, w, self.phoff.to_u64().unwrap())?; 157 | elf_write_uclass!(self, w, self.shoff.to_u64().unwrap())?; 158 | elf_write_u32!(self, w, self.flags.bits())?; 159 | elf_write_u16!(self, w, self.ehsize.to_u16().unwrap())?; 160 | elf_write_u16!(self, w, self.phentsize.to_u16().unwrap())?; 161 | elf_write_u16!(self, w, self.phnum.to_u16().unwrap())?; 162 | elf_write_u16!(self, w, self.shentsize.to_u16().unwrap())?; 163 | elf_write_u16!(self, w, self.shnum.to_u16().unwrap())?; 164 | elf_write_u16!(self, w, self.shstrndx.to_u16().unwrap())?; 165 | 166 | Ok(()) 167 | } 168 | 169 | pub fn size(&self) -> usize { 170 | 16 + 2 + 2 + 4 + match self.ident_class { 171 | types::Class::Class32 => 4 + 4 + 4, 172 | types::Class::Class64 => 8 + 8 + 8, 173 | } + 4 + 2 + 2 + 2 + 2 + 2 + 2 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Read, Result}; 2 | use Header; 3 | use types; 4 | use std; 5 | 6 | pub trait ElfEndianReadExt: Read { 7 | fn elf_read_u16(&mut self, eh: &Header) -> Result { 8 | use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; 9 | match eh.ident_endianness { 10 | types::Endianness::LittleEndian => self.read_u16::(), 11 | types::Endianness::BigEndian => self.read_u16::(), 12 | } 13 | } 14 | fn elf_read_u32(&mut self, eh: &Header) -> Result { 15 | use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; 16 | match eh.ident_endianness { 17 | types::Endianness::LittleEndian => self.read_u32::(), 18 | types::Endianness::BigEndian => self.read_u32::(), 19 | } 20 | } 21 | } 22 | impl ElfEndianReadExt for R {} 23 | 24 | 25 | //adapted from https://github.com/cole14/rust-elf/blob/master/src/utils.rs 26 | 27 | #[macro_export] 28 | macro_rules! elf_read_u16 { 29 | ($header:expr, $io:ident) => ({ 30 | use byteorder::{LittleEndian, BigEndian, ReadBytesExt}; 31 | use types; 32 | match $header.ident_endianness { 33 | types::Endianness::LittleEndian => $io.read_u16::(), 34 | types::Endianness::BigEndian => $io.read_u16::(), 35 | } 36 | }); 37 | } 38 | 39 | #[macro_export] 40 | macro_rules! elf_read_u32 { 41 | ($header:expr, $io:ident) => ({ 42 | use byteorder::{LittleEndian, BigEndian, ReadBytesExt}; 43 | use types; 44 | match $header.ident_endianness { 45 | types::Endianness::LittleEndian => $io.read_u32::(), 46 | types::Endianness::BigEndian => $io.read_u32::(), 47 | } 48 | }); 49 | } 50 | 51 | #[macro_export] 52 | macro_rules! elf_read_u64 { 53 | ($header:expr, $io:ident) => ({ 54 | use byteorder::{LittleEndian, BigEndian, ReadBytesExt}; 55 | use types; 56 | match $header.ident_endianness { 57 | types::Endianness::LittleEndian => $io.read_u64::(), 58 | types::Endianness::BigEndian => $io.read_u64::(), 59 | } 60 | }); 61 | } 62 | 63 | #[macro_export] 64 | macro_rules! elf_read_uclass { 65 | ($header:expr, $io:ident) => ({ 66 | use types; 67 | match $header.ident_class { 68 | types::Class::Class32 => match elf_read_u32!($header, $io) { 69 | Err(e) => Err(e), 70 | Ok(v) => Ok(v as u64), 71 | }, 72 | types::Class::Class64=> elf_read_u64!($header, $io), 73 | } 74 | }); 75 | } 76 | 77 | 78 | #[macro_export] 79 | macro_rules! elf_write_u16 { 80 | ($header:expr, $io:ident, $val:expr) => ({ 81 | use byteorder::{LittleEndian, BigEndian, WriteBytesExt}; 82 | use types; 83 | match $header.ident_endianness { 84 | types::Endianness::LittleEndian => $io.write_u16::($val), 85 | types::Endianness::BigEndian => $io.write_u16::($val), 86 | } 87 | }); 88 | } 89 | 90 | #[macro_export] 91 | macro_rules! elf_write_u32 { 92 | ($header:expr, $io:ident, $val:expr) => ({ 93 | use byteorder::{LittleEndian, BigEndian, WriteBytesExt}; 94 | use types; 95 | match $header.ident_endianness { 96 | types::Endianness::LittleEndian => $io.write_u32::($val), 97 | types::Endianness::BigEndian => $io.write_u32::($val), 98 | } 99 | }); 100 | } 101 | 102 | #[macro_export] 103 | macro_rules! elf_write_u64 { 104 | ($header:expr, $io:ident, $val:expr) => ({ 105 | use byteorder::{LittleEndian, BigEndian, WriteBytesExt}; 106 | use types; 107 | match $header.ident_endianness { 108 | types::Endianness::LittleEndian => $io.write_u64::($val), 109 | types::Endianness::BigEndian => $io.write_u64::($val), 110 | } 111 | }); 112 | } 113 | 114 | #[macro_export] 115 | macro_rules! elf_write_uclass { 116 | ($header:expr, $io:ident, $val:expr) => ({ 117 | use types; 118 | match $header.ident_class { 119 | types::Class::Class32 => elf_write_u32!($header, $io, $val as u32), 120 | types::Class::Class64 => elf_write_u64!($header, $io, $val), 121 | } 122 | }); 123 | } 124 | 125 | #[macro_export] 126 | macro_rules! elf_dispatch_endianness { 127 | ($header:expr => $block:expr) => ({ 128 | use byteorder::{self, ReadBytesExt}; 129 | use std; 130 | match $header.ident_endianness { 131 | types::Endianness::LittleEndian => { 132 | #[allow(dead_code)] 133 | type DispatchedEndian = byteorder::LittleEndian; 134 | #[allow(dead_code)] 135 | fn read_u16(r: &mut R) -> std::io::Result { 136 | r.read_u16::() 137 | } 138 | #[allow(dead_code)] 139 | fn read_u32(r: &mut R) -> std::io::Result { 140 | r.read_u32::() 141 | } 142 | #[allow(dead_code)] 143 | fn read_u64(r: &mut R) -> std::io::Result { 144 | r.read_u64::() 145 | } 146 | $block 147 | }, 148 | types::Endianness::BigEndian => { 149 | #[allow(dead_code)] 150 | type DispatchedEndian = byteorder::BigEndian; 151 | #[allow(dead_code)] 152 | fn read_u16(r: &mut R) -> std::io::Result { 153 | r.read_u16::() 154 | } 155 | #[allow(dead_code)] 156 | fn read_u32(r: &mut R) -> std::io::Result { 157 | r.read_u32::() 158 | } 159 | #[allow(dead_code)] 160 | fn read_u64(r: &mut R) -> std::io::Result { 161 | r.read_u64::() 162 | } 163 | $block 164 | }, 165 | } 166 | }) 167 | } 168 | 169 | #[macro_export] 170 | macro_rules! elf_dispatch_uclass { 171 | ($header:expr => $block:expr) => ({ 172 | use std; 173 | match $header.ident_class { 174 | types::Class::Class32 => { 175 | fn read_uclass(r: &mut R) -> std::io::Result { 176 | Ok(r.read_u32::()? as u64) 177 | } 178 | $block 179 | }, 180 | types::Class::Class64 => { 181 | fn read_uclass(r: &mut R) -> std::io::Result { 182 | r.read_u64::() 183 | } 184 | $block 185 | }, 186 | } 187 | }) 188 | } 189 | 190 | pub fn hextab(align: usize, s: S) -> String 191 | where 192 | S: std::fmt::LowerHex, 193 | { 194 | let s = format!("{:.align$x}", s, align = align); 195 | let pad: String = vec!['0'; align - s.len()].into_iter().collect(); 196 | format!("\x1b[90m{}\x1b[0;m{}", pad, s) 197 | } 198 | 199 | -------------------------------------------------------------------------------- /src/relocation.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Read, Write}; 2 | use {Error, Header, SectionContent}; 3 | use types; 4 | use num_traits::{FromPrimitive, ToPrimitive}; 5 | 6 | /* 7 | A Represents the addend used to compute the value of the relocatable field. 8 | 9 | B Represents the base address at which a shared object has been loaded into memory 10 | during execution. Generally, a shared object is built with a 0 base virtual 11 | address, but the execution address will be different. 12 | 13 | G Represents the offset into the global offset table at which the relocation entry’s 14 | symbol will reside during execution. 15 | 16 | GOT Represents the address of the global offset table. 17 | 18 | L Represents the place (section offset or address) of the Procedure Linkage Table 19 | entry for a symbol. 20 | 21 | P Represents the place (section offset or address) of the storage unit being relocated 22 | -> that is the relocations offset in loaded memory, so for example a relocation at offset 0x3 in 23 | .text which is loaded at 0x100 will have P = 0x103 24 | 25 | S Represents the value of the symbol whose index resides in the relocation entry. 26 | The AMD64 ABI architectures uses only Elf64_Rela relocation entries 27 | with explicit addends. The r_addend member serves as the relocation addend. 28 | */ 29 | #[allow(non_camel_case_types)] 30 | #[derive(Debug, Primitive, PartialEq, Clone)] 31 | pub enum RelocationType { 32 | R_X86_64_NONE = 0, // none none 33 | R_X86_64_64 = 1, // word64 S + A 34 | R_X86_64_PC32 = 2, // word32 S + A - P 35 | R_X86_64_GOT32 = 3, // word32 G + A 36 | R_X86_64_PLT32 = 4, // word32 L + A - P 37 | R_X86_64_COPY = 5, // none none 38 | R_X86_64_GLOB_DAT = 6, // wordclass S 39 | R_X86_64_JUMP_SLOT = 7, // wordclass S 40 | R_X86_64_RELATIVE = 8, // wordclass B + A 41 | R_X86_64_GOTPCREL = 9, // word32 G + GOT + A - P 42 | R_X86_64_32 = 10, // word32 S + A 43 | R_X86_64_32S = 11, // word32 S + A 44 | R_X86_64_16 = 12, // word16 S + A 45 | R_X86_64_PC16 = 13, // word16 S + A - P 46 | R_X86_64_8 = 14, // word8 S + A 47 | R_X86_64_PC8 = 15, // word8 S + A - P 48 | 49 | /// First part of the tls_index structure: ID of module containing symbol 50 | /// writes the module id at this location 51 | /// in an executable this is always exactly 1, 52 | /// so this reloc is only emitted for DYN where the dynamic linker 53 | /// needs to give the module an id 54 | R_X86_64_DTPMOD64 = 16, // word64 55 | 56 | /// Second part of tls_index: The Offset of the symbol in the TLS Block 57 | /// this is written into the GOT of _this_ unit by the dynamic linker, 58 | /// and the offset is into the TLS block of some other unit that actually 59 | /// defines that symbol 60 | R_X86_64_DTPOFF64 = 17, // word64 61 | 62 | /// Offset in initial TLS Block in initial exec model 63 | /// no idea why this needs a different reloc type, this appears to be identical 64 | /// to R_X86_64_DTPOFF64 65 | R_X86_64_TPOFF64 = 18, // word64 66 | 67 | /// PC Relative address to the tls_index structure in the GOT 68 | /// in general dynamic model 69 | R_X86_64_TLSGD = 19, // word32 70 | 71 | /// PC Relative address to the tls_index structure in the GOT 72 | /// in local dynamic model. that index only contains the module id, 73 | /// since the offset is known at link time and will be accessed via 74 | /// R_X86_64_DTPOFF32 75 | R_X86_64_TLSLD = 20, // word32 76 | 77 | /// Offset of the symbol in TLS Block (local dynamic model) 78 | R_X86_64_DTPOFF32 = 21, // word32 79 | 80 | 81 | /// in initial exec model, this is a PC Relative offset to a GOT entry 82 | /// which contains the 32bit offset into the thread local block. 83 | /// this is emitted by the compiler when the thread local var will definately 84 | /// be inside the executable. 85 | R_X86_64_GOTTPOFF = 22, // word32 86 | 87 | /// for initial exec model, this is the reloc on the GOT entry. 88 | R_X86_64_TPOFF32 = 23, // word32 89 | 90 | R_X86_64_PC64 = 24, // word64 S + A - P 91 | R_X86_64_GOTOFF64 = 25, // word64 S + A - GOT 92 | R_X86_64_GOTPC32 = 26, // word32 GOT + A - P 93 | R_X86_64_SIZE32 = 32, // word32 Z + A 94 | R_X86_64_SIZE64 = 33, // word64 Z + A 95 | R_X86_64_GOTPC32_TLSDESC = 34, // word32 96 | R_X86_64_TLSDESC_CALL = 35, // none 97 | R_X86_64_TLSDESC = 36, // word64×2 98 | R_X86_64_IRELATIVE = 37, // wordclass indirect (B + A) 99 | R_X86_64_RELATIVE64 = 38, // word64 B + A 100 | 101 | //not documented. hopefully these are ok to be treated as R_X86_64_GOTPCREL 102 | R_X86_64_GOTPCRELX = 41, // word32 G + GOT + A - P 103 | R_X86_64_REX_GOTPCRELX = 42, //word32 G + GOT + A - P 104 | } 105 | impl Default for RelocationType { 106 | fn default() -> Self { 107 | RelocationType::R_X86_64_NONE 108 | } 109 | } 110 | 111 | #[derive(Default, Debug, Clone)] 112 | pub struct Relocation { 113 | pub addr: u64, 114 | pub sym: u32, 115 | pub rtype: RelocationType, 116 | pub addend: i64, 117 | } 118 | 119 | impl Relocation { 120 | pub fn entsize(eh: &Header) -> usize { 121 | match eh.machine { 122 | types::Machine::X86_64 => 3 * 8, 123 | _ => panic!("relocs for machine '{:?}' not implemented", eh.machine), 124 | } 125 | } 126 | 127 | pub fn from_reader( 128 | mut io: R, 129 | _: Option<&SectionContent>, 130 | eh: &Header, 131 | ) -> Result 132 | where 133 | R: Read, 134 | { 135 | if eh.machine != types::Machine::X86_64 { 136 | return Err(Error::UnsupportedMachineTypeForRelocation( 137 | eh.machine.clone(), 138 | )); 139 | } 140 | 141 | let mut r = Vec::new(); 142 | 143 | while let Ok(addr) = elf_read_u64!(eh, io) { 144 | let info = match elf_read_u64!(eh, io) { 145 | Ok(v) => v, 146 | _ => break, 147 | }; 148 | 149 | let sym = (info >> 32) as u32; 150 | let rtype = (info & 0xffffffff) as u32; 151 | let rtype = match RelocationType::from_u32(rtype) { 152 | Some(v) => v, 153 | None => { 154 | println!( 155 | "warning: unknown relocation type {} skipped while reading", 156 | rtype 157 | ); 158 | elf_read_u64!(eh, io)?; 159 | continue; 160 | } 161 | }; 162 | 163 | let addend = elf_read_u64!(eh, io)?; 164 | 165 | r.push(Relocation { 166 | addr: addr, 167 | sym: sym, 168 | rtype: rtype, 169 | addend: addend as i64, 170 | }); 171 | } 172 | 173 | Ok(SectionContent::Relocations(r)) 174 | } 175 | 176 | pub fn to_writer( 177 | &self, 178 | mut io: W, 179 | eh: &Header, 180 | ) -> Result<(usize), Error> 181 | where 182 | W: Write, 183 | { 184 | elf_write_u64!(eh, io, self.addr)?; 185 | 186 | let info = (self.sym.to_u64().unwrap() << 32) + self.rtype.to_u64().unwrap(); 187 | elf_write_u64!(eh, io, info)?; 188 | 189 | elf_write_u64!(eh, io, self.addend as u64)?; 190 | 191 | Ok(8+8+8) 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/symbol.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Read, Write}; 2 | use {types, Error, Header, SectionContent}; 3 | use utils::hextab; 4 | use num_traits::{FromPrimitive, ToPrimitive}; 5 | use strtab::Strtab; 6 | use section::{Section, SectionHeader}; 7 | use std::fmt; 8 | 9 | #[derive(Debug, Clone, Eq, PartialEq)] 10 | pub enum SymbolSectionIndex { 11 | Section(u16), // 1-6551 12 | Undefined, // 0 13 | Absolute, // 65521, 14 | Common, // 6552, 15 | } 16 | impl Default for SymbolSectionIndex { 17 | fn default() -> SymbolSectionIndex { 18 | SymbolSectionIndex::Undefined 19 | } 20 | } 21 | 22 | #[derive(Default, Clone)] 23 | pub struct Symbol { 24 | pub shndx: SymbolSectionIndex, 25 | pub value: u64, 26 | pub size: u64, 27 | 28 | pub name: Vec, 29 | pub stype: types::SymbolType, 30 | pub bind: types::SymbolBind, 31 | pub vis: types::SymbolVis, 32 | 33 | pub _name: u32, 34 | } 35 | 36 | impl fmt::Debug for Symbol { 37 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 38 | write!(f, 39 | " {} {:>5.5} {:<7.7} {:<6.6} {:<8.8} {:<3.3} {} ", 40 | hextab(16, self.value), 41 | self.size, 42 | format!("{:?}", self.stype), 43 | format!("{:?}", self.bind), 44 | format!("{:?}", self.vis), 45 | match self.shndx { 46 | SymbolSectionIndex::Undefined => String::from("UND"), 47 | SymbolSectionIndex::Absolute => String::from("ABS"), 48 | SymbolSectionIndex::Common => String::from("COM"), 49 | SymbolSectionIndex::Section(i) => format!("{}", i), 50 | }, 51 | String::from_utf8_lossy(&self.name) 52 | ) 53 | } 54 | } 55 | 56 | impl Symbol { 57 | fn from_val( 58 | tab: Option<&Strtab>, 59 | _name: u32, 60 | info: u8, 61 | other: u8, 62 | shndx: u16, 63 | value: u64, 64 | size: u64, 65 | ) -> Result { 66 | let name = match tab { 67 | Some(tab) => tab.get(_name as usize), 68 | None => Vec::default(), 69 | }; 70 | 71 | let shndx = match shndx { 72 | 0 => SymbolSectionIndex::Undefined, 73 | 65521 => SymbolSectionIndex::Absolute, 74 | 65522 => SymbolSectionIndex::Common, 75 | _ if shndx > 0 && shndx < 6552 => SymbolSectionIndex::Section(shndx), 76 | _ => return Err(Error::InvalidSymbolShndx(String::from_utf8_lossy(&name).into_owned(), shndx)), 77 | }; 78 | 79 | let reb = info & 0xf; 80 | let stype = match types::SymbolType::from_u8(reb) { 81 | Some(v) => v, 82 | None => return Err(Error::InvalidSymbolType(reb)), 83 | }; 84 | 85 | let reb = info >> 4; 86 | let bind = match types::SymbolBind::from_u8(reb) { 87 | Some(v) => v, 88 | None => return Err(Error::InvalidSymbolBind(reb)), 89 | }; 90 | 91 | let reb = other & 0x3; 92 | let vis = match types::SymbolVis::from_u8(reb) { 93 | Some(v) => v, 94 | None => return Err(Error::InvalidSymbolVis(reb)), 95 | }; 96 | 97 | Ok(Symbol { 98 | shndx: shndx, 99 | value: value, 100 | size: size, 101 | 102 | name: name, 103 | stype: stype, 104 | bind: bind, 105 | vis: vis, 106 | 107 | _name: _name, 108 | }) 109 | } 110 | 111 | pub fn entsize(eh: &Header) -> usize { 112 | match eh.ident_class { 113 | types::Class::Class64 => 24, 114 | types::Class::Class32 => 16, 115 | } 116 | } 117 | 118 | pub fn from_reader( 119 | mut io: R, 120 | linked: Option<&SectionContent>, 121 | eh: &Header, 122 | ) -> Result 123 | where 124 | R: Read, 125 | { 126 | let tab = match linked { 127 | None => None, 128 | Some(&SectionContent::Strtab(ref s)) => Some(s), 129 | any => return Err(Error::LinkedSectionIsNotStrtab{ 130 | during: "reading symbols", 131 | link: any.map(|v|v.clone()), 132 | }), 133 | }; 134 | 135 | let mut r = Vec::new(); 136 | let mut b = vec![0; Self::entsize(eh)]; 137 | while io.read(&mut b)? > 0 { 138 | let mut br = &b[..]; 139 | elf_dispatch_endianness!(eh => { 140 | let _name = read_u32(&mut br)?; 141 | r.push(match eh.ident_class { 142 | types::Class::Class64 => { 143 | let info = b[4]; 144 | let other = b[5]; 145 | br = &b[6..]; 146 | let shndx = read_u16(&mut br)?; 147 | let value = read_u64(&mut br)?; 148 | let size = read_u64(&mut br)?; 149 | 150 | Symbol::from_val(tab, _name, info, other, shndx, value, size)? 151 | } 152 | types::Class::Class32 => { 153 | let value = read_u32(&mut br)?; 154 | let size = read_u32(&mut br)?; 155 | let info = b[12]; 156 | let other = b[13]; 157 | br = &b[14..]; 158 | let shndx = read_u16(&mut br)?; 159 | 160 | Symbol::from_val(tab, _name, info, other, shndx, value as u64, size as u64)? 161 | } 162 | }) 163 | }) 164 | } 165 | 166 | Ok(SectionContent::Symbols(r)) 167 | } 168 | 169 | pub fn to_writer( 170 | &self, 171 | mut io: W, 172 | eh: &Header, 173 | ) -> Result<(usize), Error> 174 | where 175 | W: Write, 176 | { 177 | let info = (self.bind.to_u8().unwrap() << 4) + (self.stype.to_u8().unwrap() & 0xf); 178 | let other = self.vis.to_u8().unwrap(); 179 | 180 | let shndx = match self.shndx { 181 | SymbolSectionIndex::Section(i) => i, 182 | SymbolSectionIndex::Undefined => 0, 183 | SymbolSectionIndex::Absolute => 65521, 184 | SymbolSectionIndex::Common => 65522, 185 | }; 186 | 187 | elf_write_u32!(eh, io, self._name)?; 188 | 189 | Ok(match eh.ident_class { 190 | types::Class::Class64 => { 191 | io.write(&[info, other])?; 192 | elf_write_u16!(eh, io, shndx)?; 193 | elf_write_u64!(eh, io, self.value)?; 194 | elf_write_u64!(eh, io, self.size)?; 195 | 196 | 2+2+8+8 197 | } 198 | types::Class::Class32 => { 199 | elf_write_u32!(eh, io, self.value as u32)?; 200 | elf_write_u32!(eh, io, self.size as u32)?; 201 | io.write(&[info, other])?; 202 | elf_write_u16!(eh, io, shndx)?; 203 | 204 | 4+4+2+2 205 | } 206 | }) 207 | } 208 | 209 | pub fn sync(&mut self, linked: Option<&mut SectionContent>, _: &Header) -> Result<(), Error> { 210 | match linked { 211 | Some(&mut SectionContent::Strtab(ref mut strtab)) => { 212 | self._name = strtab.insert(&self.name) as u32; 213 | } 214 | any => return Err(Error::LinkedSectionIsNotStrtab{ 215 | during: "syncing symbols", 216 | link: any.map(|v|v.clone()), 217 | }), 218 | } 219 | Ok(()) 220 | } 221 | } 222 | 223 | pub fn sysv_hash(s: &String) -> u64 { 224 | let mut h: u64 = 0; 225 | let mut g: u64; 226 | 227 | for byte in s.bytes() { 228 | h = (h << 4) + byte as u64; 229 | g = h & 0xf0000000; 230 | if g > 0 { 231 | h ^= g >> 24; 232 | } 233 | h &= !g; 234 | } 235 | return h; 236 | } 237 | 238 | 239 | pub fn symhash(eh: &Header, symbols: &Vec, link: u32) -> Result { 240 | assert!(symbols.len() > 0); 241 | //TODO i'm too lazy to do this correctly now, so we'll just emit a hashtable with nbuckets == 1 242 | let mut b = Vec::new(); 243 | { 244 | let io = &mut b; 245 | elf_write_uclass!(eh, io, 1)?; //nbuckets 246 | elf_write_uclass!(eh, io, symbols.len() as u64)?; //nchains 247 | 248 | elf_write_uclass!(eh, io, 1)?; //the bucket. pointing at symbol 1 249 | 250 | elf_write_uclass!(eh, io, 0)?; //symbol 0 251 | 252 | //the chains. every symbol just points at the next, because nbuckets == 1 253 | for i in 1..symbols.len() - 1 { 254 | elf_write_uclass!(eh, io, i as u64 + 1)?; 255 | } 256 | 257 | //except the last one 258 | elf_write_uclass!(eh, io, 0)?; 259 | } 260 | 261 | Ok(Section { 262 | name: b".hash".to_vec(), 263 | header: SectionHeader { 264 | name: 0, 265 | shtype: types::SectionType::HASH, 266 | flags: types::SectionFlags::ALLOC, 267 | addr: 0, 268 | offset: 0, 269 | size: b.len() as u64, 270 | link: link, 271 | info: 0, 272 | addralign: 0, 273 | entsize: 8, // or 4 for CLass32 274 | }, 275 | content: SectionContent::Raw(b), 276 | addrlock: false, 277 | }) 278 | } 279 | -------------------------------------------------------------------------------- /src/section.rs: -------------------------------------------------------------------------------- 1 | use error::Error; 2 | use header::Header; 3 | use relocation::Relocation; 4 | use dynamic::Dynamic; 5 | use symbol::Symbol; 6 | use strtab::Strtab; 7 | use types; 8 | 9 | use std::io::{Read, Seek, SeekFrom, Write}; 10 | use std::io::BufWriter; 11 | 12 | #[derive(Default, Debug, Clone)] 13 | pub struct SectionHeader { 14 | pub name: u32, 15 | pub shtype: types::SectionType, 16 | pub flags: types::SectionFlags, 17 | pub addr: u64, 18 | pub offset: u64, 19 | pub size: u64, 20 | pub link: u32, 21 | pub info: u32, 22 | pub addralign: u64, 23 | pub entsize: u64, 24 | } 25 | 26 | impl SectionHeader { 27 | pub fn entsize(eh: &Header) -> usize { 28 | 4 + 4 + match eh.ident_class { 29 | types::Class::Class64 => 6 * 8, 30 | types::Class::Class32 => 6 * 4, 31 | } + 4 + 4 32 | } 33 | 34 | pub fn from_reader(io: &mut R, eh: &Header) -> Result 35 | where 36 | R: Read, 37 | { 38 | elf_dispatch_endianness!(eh => { 39 | let mut r = SectionHeader::default(); 40 | r.name = read_u32(io)?; 41 | let reb = read_u32(io)?; 42 | r.shtype = types::SectionType(reb); 43 | 44 | elf_dispatch_uclass!(eh => { 45 | let reb = read_uclass(io)?; 46 | r.flags = match types::SectionFlags::from_bits(reb) { 47 | Some(v) => v, 48 | None => return Err(Error::InvalidSectionFlags(reb)), 49 | }; 50 | r.addr = read_uclass(io)?; 51 | r.offset = read_uclass(io)?; 52 | r.size = read_uclass(io)?; 53 | r.link = read_u32(io)?; 54 | r.info = read_u32(io)?; 55 | r.addralign = read_uclass(io)?; 56 | r.entsize = read_uclass(io)?; 57 | Ok(r) 58 | }) 59 | }) 60 | } 61 | 62 | pub fn to_writer(&self, eh: &Header, io: &mut R) -> Result<(), Error> 63 | where 64 | R: Write, 65 | { 66 | let mut w = BufWriter::new(io); 67 | elf_write_u32!(eh, w, self.name)?; 68 | elf_write_u32!(eh, w, self.shtype.to_u32())?; 69 | elf_write_uclass!(eh, w, self.flags.bits())?; 70 | 71 | elf_write_uclass!(eh, w, self.addr)?; 72 | elf_write_uclass!(eh, w, self.offset)?; 73 | elf_write_uclass!(eh, w, self.size)?; 74 | elf_write_u32!(eh, w, self.link)?; 75 | elf_write_u32!(eh, w, self.info)?; 76 | elf_write_uclass!(eh, w, self.addralign)?; 77 | elf_write_uclass!(eh, w, self.entsize)?; 78 | Ok(()) 79 | } 80 | } 81 | 82 | #[derive(Debug, Clone)] 83 | pub enum SectionContent { 84 | None, 85 | Unloaded, 86 | Raw(Vec), 87 | Relocations(Vec), 88 | Symbols(Vec), 89 | Dynamic(Vec), 90 | Strtab(Strtab), 91 | } 92 | 93 | impl Default for SectionContent { 94 | fn default() -> Self { 95 | SectionContent::None 96 | } 97 | } 98 | impl SectionContent { 99 | pub fn as_dynamic_mut(&mut self) -> Option<&mut Vec> { 100 | match self { 101 | &mut SectionContent::Dynamic(ref mut v) => Some(v), 102 | _ => None, 103 | } 104 | } 105 | pub fn as_dynamic(&self) -> Option<&Vec> { 106 | match self { 107 | &SectionContent::Dynamic(ref v) => Some(v), 108 | _ => None, 109 | } 110 | } 111 | pub fn into_dynamic(self) -> Option> { 112 | match self { 113 | SectionContent::Dynamic(v) => Some(v), 114 | _ => None, 115 | } 116 | } 117 | pub fn as_strtab_mut(&mut self) -> Option<&mut Strtab> { 118 | match self { 119 | &mut SectionContent::Strtab(ref mut v) => Some(v), 120 | _ => None, 121 | } 122 | } 123 | pub fn as_symbols(&self) -> Option<&Vec> { 124 | match self { 125 | &SectionContent::Symbols(ref v) => Some(v), 126 | _ => None, 127 | } 128 | } 129 | pub fn as_symbols_mut(&mut self) -> Option<&mut Vec> { 130 | match self { 131 | &mut SectionContent::Symbols(ref mut v) => Some(v), 132 | _ => None, 133 | } 134 | } 135 | pub fn into_symbols(self) -> Option> { 136 | match self { 137 | SectionContent::Symbols(v) => Some(v), 138 | _ => None, 139 | } 140 | } 141 | pub fn as_relocations(&self) -> Option<&Vec> { 142 | match self { 143 | &SectionContent::Relocations(ref v) => Some(v), 144 | _ => None, 145 | } 146 | } 147 | pub fn as_relocations_mut(&mut self) -> Option<&mut Vec> { 148 | match self { 149 | &mut SectionContent::Relocations(ref mut v) => Some(v), 150 | _ => None, 151 | } 152 | } 153 | pub fn into_relocations(self) -> Option> { 154 | match self { 155 | SectionContent::Relocations(v) => Some(v), 156 | _ => None, 157 | } 158 | } 159 | pub fn as_raw(&self) -> Option<&Vec> { 160 | match self { 161 | &SectionContent::Raw(ref v) => Some(v), 162 | _ => None, 163 | } 164 | } 165 | pub fn as_raw_mut(&mut self) -> Option<&mut Vec> { 166 | match self { 167 | &mut SectionContent::Raw(ref mut v) => Some(v), 168 | _ => None, 169 | } 170 | } 171 | pub fn into_raw(self) -> Option> { 172 | match self { 173 | SectionContent::Raw(v) => Some(v), 174 | _ => None, 175 | } 176 | } 177 | pub fn size(&self, eh: &Header) -> usize { 178 | match self { 179 | &SectionContent::Unloaded => panic!("cannot size unloaded section"), 180 | &SectionContent::None => 0, 181 | &SectionContent::Raw(ref v) => v.len(), 182 | &SectionContent::Dynamic(ref v) => v.len() * Dynamic::entsize(eh), 183 | &SectionContent::Strtab(ref v) => v.len(eh), 184 | &SectionContent::Symbols(ref v) => v.len() * Symbol::entsize(eh), 185 | &SectionContent::Relocations(ref v) => v.len() * Relocation::entsize(eh), 186 | } 187 | } 188 | } 189 | 190 | #[derive(Debug, Default, Clone)] 191 | pub struct Section { 192 | pub header: SectionHeader, 193 | pub name: Vec, 194 | pub content: SectionContent, 195 | pub addrlock: bool, 196 | } 197 | 198 | 199 | impl Section { 200 | pub fn size(&self, eh: &Header) -> usize { 201 | self.content.size(eh) 202 | } 203 | pub fn new( 204 | name: Vec, 205 | shtype: types::SectionType, 206 | flags: types::SectionFlags, 207 | content: SectionContent, 208 | link: u32, 209 | info: u32, 210 | ) -> Section { 211 | Section { 212 | name: name, 213 | header: SectionHeader { 214 | name: 0, 215 | shtype: shtype, 216 | flags: flags, 217 | addr: 0, 218 | offset: 0, 219 | size: 0, 220 | link: link, 221 | info: info, 222 | addralign: 0, 223 | entsize: 0, 224 | }, 225 | content: content, 226 | addrlock: false, 227 | } 228 | } 229 | 230 | pub fn sync( 231 | &mut self, 232 | eh: &Header, 233 | mut linked: Option<&mut SectionContent>, 234 | ) -> Result<(), Error> { 235 | match self.content { 236 | SectionContent::Unloaded => { 237 | return Err(Error::SyncingUnloadedSection); 238 | }, 239 | SectionContent::Relocations(_) => { 240 | self.header.entsize = Relocation::entsize(eh) as u64; 241 | } 242 | SectionContent::Symbols(ref mut vv) => { 243 | for (i, sym) in vv.iter().enumerate() { 244 | if sym.bind == types::SymbolBind::GLOBAL { 245 | self.header.info = i as u32; 246 | break; 247 | } 248 | } 249 | for v in vv { 250 | v.sync(linked.as_mut().map(|r| &mut **r), eh)?; 251 | } 252 | self.header.entsize = Symbol::entsize(eh) as u64; 253 | } 254 | SectionContent::Dynamic(ref mut vv) => { 255 | for v in vv { 256 | v.sync(linked.as_mut().map(|r| &mut **r), eh)?; 257 | } 258 | self.header.entsize = Dynamic::entsize(eh) as u64; 259 | } 260 | SectionContent::Strtab(_) => { 261 | self.header.entsize = Strtab::entsize(eh) as u64; 262 | } 263 | SectionContent::None | SectionContent::Raw(_) => {} 264 | } 265 | if self.header.shtype != types::SectionType::NOBITS { 266 | self.header.size = self.size(eh) as u64; 267 | } 268 | Ok(()) 269 | } 270 | 271 | pub fn from_reader( 272 | &mut self, 273 | mut io: T, 274 | linked: Option<&Section>, 275 | eh: &Header, 276 | ) -> Result<(), Error> where T: Read + Seek { 277 | match self.content { 278 | SectionContent::Unloaded => {}, 279 | _ => return Ok(()), 280 | }; 281 | if self.header.shtype == types::SectionType::NOBITS { 282 | self.content = SectionContent::None; 283 | return Ok(()); 284 | }; 285 | io.seek(SeekFrom::Start(self.header.offset))?; 286 | let mut bb = vec![0; self.header.size as usize]; 287 | io.read_exact(&mut bb)?; 288 | let linked = linked.map(|s|&s.content); 289 | self.content = match self.header.shtype { 290 | types::SectionType::NOBITS => { 291 | unreachable!(); 292 | }, 293 | types::SectionType::STRTAB => { 294 | let io = bb.as_slice(); 295 | Strtab::from_reader(io, linked, eh)? 296 | } 297 | types::SectionType::RELA => { 298 | let io = bb.as_slice(); 299 | Relocation::from_reader(io, linked, eh)? 300 | } 301 | types::SectionType::SYMTAB | types::SectionType::DYNSYM => { 302 | let io = bb.as_slice(); 303 | Symbol::from_reader(io, linked, eh)? 304 | } 305 | types::SectionType::DYNAMIC => { 306 | let io = bb.as_slice(); 307 | Dynamic::from_reader(io, linked, eh)? 308 | } 309 | _ => { 310 | SectionContent::Raw(bb) 311 | } 312 | }; 313 | Ok(()) 314 | } 315 | 316 | 317 | pub fn to_writer( 318 | &self, 319 | mut io: R, 320 | eh: &Header, 321 | ) -> Result<(), Error> where R: Write + Seek { 322 | match self.content { 323 | SectionContent::Unloaded => return Ok(()), 324 | _ => {}, 325 | }; 326 | io.seek(SeekFrom::Start(self.header.offset))?; 327 | 328 | let rs = match &self.content { 329 | &SectionContent::Unloaded => { 330 | return Err(Error::WritingUnloadedSection); 331 | }, 332 | &SectionContent::Relocations(ref vv) => { 333 | let mut rs = 0; 334 | for v in vv { 335 | rs += v.to_writer(&mut io, eh)?; 336 | } 337 | rs 338 | } 339 | &SectionContent::Symbols(ref vv) => { 340 | let mut rs = 0; 341 | for v in vv { 342 | rs += v.to_writer(&mut io, eh)?; 343 | } 344 | rs 345 | } 346 | &SectionContent::Dynamic(ref vv) => { 347 | let mut rs = 0; 348 | for v in vv { 349 | rs += v.to_writer(&mut io, eh)?; 350 | } 351 | rs 352 | } 353 | &SectionContent::Strtab(ref v) => { 354 | v.to_writer(&mut io, eh)? 355 | } 356 | &SectionContent::None => { 357 | 0 358 | }, 359 | &SectionContent::Raw(ref raw) => { 360 | io.write(&raw)? 361 | } 362 | }; 363 | 364 | assert_eq!( 365 | io.seek(SeekFrom::Current(0))?, 366 | self.header.offset + self.content.size(eh) as u64, 367 | "writing {} with header.size {} and content.size {} returned a written size {}", 368 | String::from_utf8_lossy(&self.name), 369 | self.content.size(eh), 370 | self.header.size, 371 | rs 372 | ); 373 | 374 | Ok(()) 375 | } 376 | 377 | } 378 | -------------------------------------------------------------------------------- /src/loader.rs: -------------------------------------------------------------------------------- 1 | extern crate ar; 2 | extern crate bit_vec; 3 | extern crate fnv; 4 | extern crate core; 5 | extern crate rayon; 6 | 7 | use {types, Header, Elf, Error, symbol, filetype, relocation, section}; 8 | use std; 9 | use std::io::{Read, Seek, Cursor}; 10 | use std::hash::{Hash,Hasher}; 11 | use std::fs::{File}; 12 | use std::collections::HashMap; 13 | use std::cell::RefCell; 14 | use self::rayon::prelude::*; 15 | use self::fnv::FnvHasher; 16 | use self::bit_vec::BitVec; 17 | use self::ar::Archive; 18 | use std::collections::hash_map::DefaultHasher; 19 | 20 | pub trait ReadSeekSend : Read + Seek + Send {} 21 | impl ReadSeekSend for T where T: Read + Seek + Send{} 22 | 23 | pub enum State { 24 | Error{ 25 | name: String, 26 | error: Error 27 | }, 28 | Path{ 29 | name: String 30 | }, 31 | Archive{ 32 | name: String, 33 | archive: Archive, 34 | }, 35 | Elf{ 36 | hash: String, 37 | name: String, 38 | elf: Elf, 39 | read: RefCell>, 40 | bloom: BloomFilter, 41 | symbols: Vec, 42 | }, 43 | Object{ 44 | hash: String, 45 | name: String, 46 | symbols: Vec, 47 | header: Header, 48 | sections: Vec<(usize, section::Section, Vec)>, 49 | }, 50 | } 51 | 52 | pub trait Loader { 53 | fn load_all(self, e: &E) -> Vec 54 | where E: Fn(Error, String) -> Vec + Sync; 55 | fn load_if(self, needles: &Vec<&[u8]>, e: &E) -> (Vec,Vec) 56 | where E: Fn(Error, String) -> Vec + Sync; 57 | } 58 | 59 | impl Loader for Vec { 60 | fn load_all(self, e: &E) -> Vec 61 | where E: Fn(Error, String) -> Vec + Sync 62 | { 63 | self.into_par_iter() 64 | .flat_map(|l| l.load(e)) 65 | .flat_map(|l| l.load(e)) 66 | .flat_map(|l| l.load(e)) 67 | .flat_map(|l| l.load(e)) 68 | .collect() 69 | } 70 | fn load_if(self, needles: &Vec<&[u8]>, e: &E) -> (Vec,Vec) 71 | where E: Fn(Error, String) -> Vec + Sync 72 | { 73 | self.into_par_iter().flat_map(|l| l.load_if(needles, e)) 74 | .partition(|o| if let &State::Object{..} = o {false} else {true}) 75 | } 76 | } 77 | 78 | 79 | impl State { 80 | pub fn load_if (mut self, needles: &Vec<&[u8]>, e: &E) -> Vec 81 | where E: Fn(Error, String) -> Vec + Sync 82 | { 83 | if needles.iter().map(|needle|self.contains(needle, BloomFilter::hash(needle))).any(|e|e==true) { 84 | self.load(e).into_par_iter().flat_map(|s|s.load_if(needles, e)).collect() 85 | } else { 86 | vec![self] 87 | } 88 | } 89 | 90 | pub fn contains(&mut self, needle: &[u8], needle_hash: [u64;2]) -> bool { 91 | match self { 92 | &mut State::Error{..} => true, 93 | &mut State::Path {..} => true, 94 | &mut State::Archive{ref mut archive, ..} => { 95 | let symbols = match archive.symbols() { 96 | Ok(v) => v, 97 | Err(_) => return true, 98 | }; 99 | for symbol in symbols { 100 | if symbol == needle { 101 | return true; 102 | } 103 | } 104 | return false; 105 | }, 106 | &mut State::Elf{ref bloom, ref symbols, ..} => { 107 | if bloom.contains(&needle_hash) { 108 | for sym in symbols.iter() { 109 | match sym.bind { 110 | types::SymbolBind::GLOBAL | types::SymbolBind::WEAK => { 111 | match sym.shndx { 112 | symbol::SymbolSectionIndex::Undefined | 113 | symbol::SymbolSectionIndex::Absolute => {}, 114 | _ => { 115 | if sym.name == needle { 116 | return true; 117 | } 118 | }, 119 | } 120 | }, 121 | _ => {}, 122 | } 123 | } 124 | } 125 | return false; 126 | }, 127 | &mut State::Object{..} => false, 128 | } 129 | } 130 | 131 | pub fn load(self, e: &E) -> Vec where E: Fn(Error, String) -> Vec { 132 | match self { 133 | State::Error{name,error} => e(error,name), 134 | State::Path{name} => { 135 | let mut f = match File::open(&name) { 136 | Err(e) => return vec![State::Error{ 137 | error: Error::from(e), 138 | name: name 139 | }], 140 | Ok(f) => f, 141 | }; 142 | match filetype::filetype(&mut f) { 143 | Ok(filetype::FileType::Unknown) => { 144 | return vec![State::Error{ 145 | error: Error::InvalidMagic, 146 | name: name, 147 | }]; 148 | }, 149 | Ok(filetype::FileType::Elf) => { 150 | vec![match State::make_object(name.clone(), RefCell::new(Box::new(f))) { 151 | Err(e) => State::Error{ 152 | error: e, 153 | name: name, 154 | }, 155 | Ok(v) => v, 156 | }] 157 | }, 158 | Ok(filetype::FileType::Archive) => { 159 | vec![State::Archive{ 160 | name: name, 161 | archive: Archive::new(f), 162 | }] 163 | }, 164 | Err(e) => vec![ 165 | State::Error{ 166 | error: Error::from(e), 167 | name: name, 168 | } 169 | ], 170 | } 171 | }, 172 | State::Archive{name, mut archive} => { 173 | let mut r = Vec::new(); 174 | while let Some(entry) = archive.next_entry() { 175 | let mut name = name.clone(); 176 | match &entry { 177 | &Ok(ref entry) => name += 178 | &(String::from("::") + 179 | &String::from_utf8_lossy(&entry.header().identifier())), 180 | _ => {}, 181 | }; 182 | 183 | r.push(match State::make_object_ar(name.clone(), entry) { 184 | Err(e) => State::Error{ 185 | error: e, 186 | name: name, 187 | }, 188 | Ok(v) => v, 189 | }) 190 | } 191 | r 192 | }, 193 | State::Elf{name, hash, mut elf, read, symbols, ..} => { 194 | if let Err(e) = elf.load_all(&mut *read.borrow_mut()) { 195 | return vec![State::Error{ 196 | name: name, 197 | error: e, 198 | }] 199 | } 200 | 201 | let mut relocs : HashMap> = HashMap::new(); 202 | for sec in elf.sections.iter_mut() { 203 | if sec.header.shtype == types::SectionType::RELA { 204 | relocs.insert(sec.header.info as usize, 205 | std::mem::replace(sec, section::Section::default()) 206 | .content.into_relocations().unwrap() 207 | ); 208 | } 209 | } 210 | 211 | let mut sections = Vec::new(); 212 | for (i, sec) in elf.sections.into_iter().enumerate() { 213 | match sec.header.shtype { 214 | types::SectionType::NULL | types::SectionType::STRTAB => {}, 215 | _ => { 216 | sections.push((i, sec, relocs.remove(&i).unwrap_or_else(||Vec::new()))); 217 | }, 218 | } 219 | } 220 | 221 | vec![State::Object{ 222 | hash: hash, 223 | name: name, 224 | symbols: symbols, 225 | header: elf.header, 226 | sections: sections, 227 | }] 228 | }, 229 | any => vec![any], 230 | } 231 | } 232 | 233 | fn make_object(name: String, io: RefCell>) -> Result { 234 | 235 | let mut elf = Elf::from_reader(&mut *io.borrow_mut())?; 236 | 237 | let mut hasher = DefaultHasher::new(); 238 | let mut num_symbols = 0; 239 | 240 | for i in 0..elf.sections.len() { 241 | match elf.sections[i].header.shtype { 242 | types::SectionType::SYMTAB | 243 | types::SectionType::DYNSYM => { 244 | elf.load(i, &mut *io.borrow_mut())?; 245 | let symbols = elf.sections[i].content.as_symbols().unwrap(); 246 | for sym in symbols { 247 | hasher.write(&sym.name); 248 | } 249 | num_symbols += symbols.len(); 250 | }, 251 | _ => {} 252 | } 253 | } 254 | 255 | if num_symbols == 0 { 256 | return Err(Error::NoSymbolsInObject); 257 | } 258 | 259 | let mut bloom = BloomFilter::new(num_symbols); 260 | 261 | let mut symbols = None; 262 | for i in 0..elf.sections.len() { 263 | if (elf.sections[i].header.shtype == types::SectionType::SYMTAB && 264 | elf.header.etype == types::ElfType::REL) || 265 | (elf.sections[i].header.shtype == types::SectionType::DYNSYM && 266 | elf.header.etype == types::ElfType::DYN) { 267 | 268 | if let Some(_) = symbols { 269 | return Err(Error::MultipleSymbolSections); 270 | } 271 | 272 | let syms = std::mem::replace(&mut elf.sections[i], section::Section::default()) 273 | .content.into_symbols().unwrap(); 274 | 275 | for sym in syms.iter() { 276 | match sym.bind { 277 | types::SymbolBind::GLOBAL | types::SymbolBind::WEAK => { 278 | match sym.shndx { 279 | symbol::SymbolSectionIndex::Undefined | 280 | symbol::SymbolSectionIndex::Absolute => {}, 281 | _ => { 282 | bloom.insert(&BloomFilter::hash(&sym.name)); 283 | }, 284 | } 285 | }, 286 | _ => {}, 287 | 288 | } 289 | } 290 | symbols = Some(syms); 291 | } 292 | } 293 | 294 | if let None = symbols { 295 | return Err(Error::MissingSymtabSection); 296 | } 297 | 298 | Ok(State::Elf{ 299 | hash: format!("{}>>{:x}!", name.split("::").last().unwrap(), hasher.finish()), 300 | name: name, 301 | elf: elf, 302 | read: io, 303 | bloom: bloom, 304 | symbols: symbols.unwrap(), 305 | }) 306 | } 307 | 308 | fn make_object_ar(name: String, entry: std::io::Result>) -> Result { 309 | let mut entry = entry?; 310 | let mut buf = Vec::with_capacity(entry.header().size() as usize); 311 | entry.read_to_end(&mut buf)?; 312 | let io = Cursor::new(buf); 313 | 314 | State::make_object(name, 315 | RefCell::new(Box::new(io))) 316 | } 317 | 318 | } 319 | 320 | 321 | 322 | pub struct BloomFilter { 323 | bits: BitVec, 324 | } 325 | 326 | impl BloomFilter { 327 | 328 | fn new(num_items: usize) -> BloomFilter { 329 | BloomFilter { 330 | bits: BitVec::from_elem(Self::needed_bits(0.001, num_items as u32), false), 331 | } 332 | } 333 | 334 | fn hash(n:&[u8]) -> [u64;2] { 335 | let mut a1 = FnvHasher::with_key(0xcbf29ce484222325); 336 | let mut a2 = FnvHasher::with_key(0x84222325b444f000); 337 | n.hash(&mut a1); 338 | n.hash(&mut a2); 339 | [a1.finish(),a2.finish()] 340 | } 341 | 342 | fn needed_bits(false_pos_rate: f32, num_items: u32) -> usize { 343 | let ln22 = core::f32::consts::LN_2 * core::f32::consts::LN_2; 344 | (num_items as f32 * ((1.0/false_pos_rate).ln() / ln22)).round() as usize 345 | } 346 | 347 | fn insert(&mut self, nh: &[u64;2]) { 348 | let len = self.bits.len(); 349 | self.bits.set(nh[0] as usize % len, true); 350 | self.bits.set(nh[1] as usize % len, true); 351 | } 352 | 353 | fn contains(&self, nh: &[u64;2]) -> bool { 354 | self.bits[nh[0] as usize % self.bits.len()] && 355 | self.bits[nh[1] as usize % self.bits.len()] 356 | } 357 | } 358 | 359 | -------------------------------------------------------------------------------- /bin/readelf.rs: -------------------------------------------------------------------------------- 1 | extern crate colored; 2 | extern crate elfkit; 3 | 4 | use std::env; 5 | use std::fs::File; 6 | use elfkit::{types, DynamicContent, Elf, SectionContent}; 7 | use elfkit::relocation::RelocationType; 8 | use elfkit::symbol::SymbolSectionIndex; 9 | use colored::*; 10 | 11 | fn hextab(align: usize, s: S) -> String 12 | where 13 | S: std::fmt::LowerHex, 14 | { 15 | let s = format!("{:.align$x}", s, align = align); 16 | let pad: String = vec!['0'; align - s.len()].into_iter().collect(); 17 | format!("\x1b[90m{}\x1b[0;m{}", pad, s) 18 | } 19 | 20 | fn main() { 21 | let filename = env::args().nth(1).unwrap(); 22 | let mut file = File::open(filename).unwrap(); 23 | let mut elf = Elf::from_reader(&mut file).unwrap(); 24 | elf.load_all(&mut file).unwrap(); 25 | 26 | println!("{}", "ELF Header:".bold()); 27 | println!( 28 | " Magic: {:?}", 29 | elf.header.ident_magic 30 | ); 31 | println!( 32 | " Class: {:?}", 33 | elf.header.ident_class 34 | ); 35 | println!( 36 | " Data: {:?}", 37 | elf.header.ident_endianness 38 | ); 39 | println!( 40 | " Version: {}", 41 | elf.header.ident_version 42 | ); 43 | println!( 44 | " OS/ABI: {:?}", 45 | elf.header.ident_abi 46 | ); 47 | println!( 48 | " ABI Version: {:?}", 49 | elf.header.ident_abiversion 50 | ); 51 | println!( 52 | " Type: {:?}", 53 | elf.header.etype 54 | ); 55 | println!( 56 | " Machine: {:?}", 57 | elf.header.machine 58 | ); 59 | println!( 60 | " Version: {:?}", 61 | elf.header.version 62 | ); 63 | println!( 64 | " Entry point address: 0x{:x}", 65 | elf.header.entry 66 | ); 67 | println!( 68 | " Start of program headers: {} (bytes into file)", 69 | elf.header.phoff 70 | ); 71 | println!( 72 | " Start of section headers: {} (bytes into file)", 73 | elf.header.shoff 74 | ); 75 | println!( 76 | " Flags: 0x{:x} {:?}", 77 | elf.header.flags, 78 | elf.header.flags 79 | ); 80 | println!( 81 | " Size of this header: {} (bytes)", 82 | elf.header.ehsize 83 | ); 84 | println!( 85 | " Size of program headers: {} (bytes)", 86 | elf.header.phentsize 87 | ); 88 | println!(" Number of program headers: {}", elf.header.phnum); 89 | println!( 90 | " Size of section headers: {} (bytes)", 91 | elf.header.shentsize 92 | ); 93 | println!(" Number of section headers: {}", elf.header.shnum); 94 | println!( 95 | " Section header string table index: {}", 96 | elf.header.shstrndx 97 | ); 98 | println!(""); 99 | println!( 100 | "{} at offset 0x{:x}:", 101 | "Section Headers".bold(), 102 | elf.header.shoff 103 | ); 104 | println!( 105 | " [Nr] Name Type Address Offset Size EntS \ 106 | Flg Lnk Inf Al" 107 | ); 108 | 109 | for (i, section) in elf.sections.iter().enumerate() { 110 | println!( 111 | " [{:>2}] {:<16.16} {} {} {} {} {} {:<3} {:<3.3} {:<3} {:<2.2}", 112 | i, 113 | String::from_utf8_lossy(§ion.name).bold(), 114 | match section.header.shtype.typename(&elf.header) { 115 | Some(s) => format!("{:<14.14}", s), 116 | None => hextab(14, section.header.shtype.to_u32()), 117 | }, 118 | hextab(16, section.header.addr), 119 | hextab(8, section.header.offset), 120 | hextab(8, section.header.size), 121 | hextab(4, section.header.entsize), 122 | section.header.flags, 123 | section.header.link, 124 | section.header.info, 125 | section.header.addralign 126 | ); 127 | } 128 | 129 | println!("",); 130 | println!("{}", "Legend for Flags:".bold()); 131 | println!( 132 | " {}: write, {}: alloc, {}: execute, {}: merge, {}: strings, {}: info, 133 | {}: link order, {}: extra OS processing required, {}: group, {}: TLS, 134 | {}: compressed, {}: OS specific, {}: exclude, {}: large, 135 | {}: mips: global data area", 136 | "W".bold(), 137 | "A".bold(), 138 | "X".bold(), 139 | "M".bold(), 140 | "S".bold(), 141 | "I".bold(), 142 | "L".bold(), 143 | "O".bold(), 144 | "G".bold(), 145 | "T".bold(), 146 | "C".bold(), 147 | "o".bold(), 148 | "E".bold(), 149 | "l".bold(), 150 | "g".bold() 151 | ); 152 | 153 | if elf.segments.len() > 0 { 154 | println!(""); 155 | println!( 156 | "{} at offset 0x{:x}:", 157 | "Program Headers (Segments)".bold(), 158 | elf.header.phoff 159 | ); 160 | println!(" Type Offset VirtAddr PhysAddr"); 161 | println!(" FileSiz MemSiz Flags Align"); 162 | 163 | for ph in &elf.segments { 164 | println!( 165 | " {:<14.14} 0x{} 0x{} 0x{}", 166 | format!("{:?}", ph.phtype), 167 | hextab(16, ph.offset), 168 | hextab(16, ph.vaddr), 169 | hextab(16, ph.paddr) 170 | ); 171 | 172 | println!( 173 | " 0x{} 0x{} {:<6} 0x{:x}", 174 | hextab(16, ph.filesz), 175 | hextab(16, ph.memsz), 176 | ph.flags, 177 | ph.align 178 | ); 179 | } 180 | } 181 | 182 | println!(""); 183 | println!("{}:", "Segment Layout".bold()); 184 | 185 | let mut fls = vec![ 186 | /* 187 | (String::from("elf header"), 0, elf.header.ehsize as u64), 188 | ( 189 | String::from("section headers"), 190 | elf.header.shoff, 191 | elf.header.shentsize as u64 * elf.header.shnum as u64, 192 | ), 193 | */ 194 | ]; 195 | if elf.header.phoff > 0 { 196 | fls.push(( 197 | String::from("segment headers"), 198 | elf.header.phoff, 199 | (elf.header.phentsize * elf.header.phnum) as u64, 200 | )); 201 | } 202 | 203 | for section in elf.sections.iter() { 204 | if section.header.size < 1 { 205 | continue; 206 | } 207 | fls.push(( 208 | String::from_utf8_lossy(§ion.name).into_owned(), 209 | section.header.addr, 210 | section.header.size, 211 | )); 212 | } 213 | 214 | fls.sort_by(|&(_, a, _), &(_, b, _)| a.cmp(&b)); 215 | 216 | println!( 217 | "{}", 218 | " addr size segment".bold() 219 | ); 220 | if elf.segments.len() > 0 { 221 | for n in 0..12 { 222 | let n = n; 223 | print!(" "); 224 | for segment in elf.segments.iter() { 225 | let name = format!("{:?}", segment.phtype); 226 | print!(" {} |", if name.len() > n { &name[n..n + 1] } else { " " }); 227 | } 228 | println!(""); 229 | } 230 | print!(" "); 231 | for _ in elf.segments.iter() { 232 | print!("---|") 233 | } 234 | println!(""); 235 | } 236 | 237 | 238 | 239 | let mut cfileoff = 0; 240 | let fls_intermediate = fls.drain(..).collect::>(); 241 | for (name, off, size) in fls_intermediate { 242 | if cfileoff < off { 243 | fls.push((String::default(), cfileoff, off - cfileoff)); 244 | } 245 | fls.push((name, off, size)); 246 | cfileoff = off + size; 247 | } 248 | 249 | if let Some(&(_, off, size)) = fls.last() { 250 | let filelen = file.metadata().unwrap().len(); 251 | if off + size < filelen { 252 | fls.push((String::default(), off + size, filelen - (off + size))); 253 | } 254 | } 255 | 256 | 257 | for (name, off, size) in fls { 258 | print!( 259 | " {} 0x{:<6.6x} 0x{:<6.6x} ", 260 | format!("{:<16.16}", name).bold(), 261 | off, 262 | size 263 | ); 264 | 265 | for segment in elf.segments.iter() { 266 | if off >= segment.vaddr && (off + size) <= (segment.vaddr + segment.memsz) { 267 | if segment.flags.contains(types::SegmentFlags::WRITABLE) { 268 | print!("{:^3}|", format!("{}", segment.flags).red()) 269 | } else if segment.flags.contains(types::SegmentFlags::EXECUTABLE) { 270 | print!("{:^3}|", format!("{}", segment.flags).green()) 271 | } else { 272 | print!("{:^3}|", format!("{}", segment.flags)); 273 | } 274 | } else { 275 | print!(" |"); 276 | } 277 | } 278 | println!(""); 279 | } 280 | 281 | 282 | 283 | for section in &elf.sections { 284 | match section.content { 285 | SectionContent::Relocations(ref relocs) => { 286 | println!(""); 287 | println!( 288 | "{} relocation section at offset 0x{:x}:", 289 | String::from_utf8_lossy(§ion.name).bold(), 290 | section.header.offset 291 | ); 292 | println!(" Offset Type Symbol Addend"); 293 | 294 | for reloc in relocs { 295 | print!( 296 | " {} {:<15.15} ", 297 | hextab(16, reloc.addr), 298 | &format!("{:?}", reloc.rtype)[2..] 299 | ); 300 | 301 | elf.sections 302 | .get(section.header.link as usize) 303 | .and_then(|sec| sec.content.as_symbols()) 304 | .and_then(|symbols| symbols.get(reloc.sym as usize)) 305 | .and_then(|symbol| if symbol.name.len() > 0 { 306 | print!("{: <20.20} ", String::from_utf8_lossy(&symbol.name)); 307 | Some(()) 308 | } else { 309 | None 310 | }) 311 | .unwrap_or_else(|| { 312 | print!("{: <20.20} ", reloc.sym); 313 | }); 314 | 315 | match reloc.rtype { 316 | RelocationType::R_X86_64_RELATIVE => { 317 | println!("0x{:<16.16x}", reloc.addend); 318 | } 319 | _ => { 320 | println!("{: <18.18}", reloc.addend); 321 | } 322 | } 323 | } 324 | } 325 | SectionContent::Symbols(ref symbols) => { 326 | println!(""); 327 | println!( 328 | "{} symbols section at offset 0x{:x}:", 329 | String::from_utf8_lossy(§ion.name).bold(), 330 | section.header.offset 331 | ); 332 | println!(" Num: Value Size Type Bind Vis Ndx Name"); 333 | 334 | for (i, symbol) in symbols.iter().enumerate() { 335 | println!( 336 | " {:>3}: {} {:>5.5} {:<7.7} {:<6.6} {:<8.8} {:<3.3} {} ", 337 | i, 338 | hextab(16, symbol.value), 339 | symbol.size, 340 | format!("{:?}", symbol.stype), 341 | format!("{:?}", symbol.bind), 342 | format!("{:?}", symbol.vis), 343 | match symbol.shndx { 344 | SymbolSectionIndex::Undefined => String::from("UND"), 345 | SymbolSectionIndex::Absolute => String::from("ABS"), 346 | SymbolSectionIndex::Common => String::from("COM"), 347 | SymbolSectionIndex::Section(i) => format!("{}", i), 348 | }, 349 | String::from_utf8_lossy(&symbol.name) 350 | ); 351 | } 352 | } 353 | SectionContent::Dynamic(ref dynamics) => { 354 | println!(""); 355 | println!( 356 | "{} dynamic linker section at offset 0x{:x}:", 357 | String::from_utf8_lossy(§ion.name).bold(), 358 | section.header.offset 359 | ); 360 | println!(" Tag Value"); 361 | 362 | for dyn in dynamics { 363 | println!( 364 | " {:<12} {}", 365 | format!("{:?}", dyn.dhtype), 366 | match dyn.content { 367 | DynamicContent::None => String::default(), 368 | DynamicContent::String(ref s) => String::from_utf8_lossy(&s.0).into_owned(), 369 | DynamicContent::Address(u) => hextab(16, u), 370 | DynamicContent::Flags1(v) => format!("{:?}", v), 371 | } 372 | ); 373 | } 374 | } 375 | SectionContent::Raw(ref s) => match section.name.as_slice() { 376 | b".interp" => { 377 | println!(""); 378 | println!( 379 | "{} program interpreter section at offset 0x{:x}:", 380 | String::from_utf8_lossy(§ion.name).bold(), 381 | section.header.offset 382 | ); 383 | println!(" {}", String::from_utf8_lossy(s)); 384 | } 385 | _ => {} 386 | }, 387 | _ => {} 388 | } 389 | } 390 | } 391 | -------------------------------------------------------------------------------- /tests/layout.rs: -------------------------------------------------------------------------------- 1 | extern crate elfkit; 2 | 3 | use elfkit::{Elf, Section, SectionHeader, SectionContent, types, dynamic, segment}; 4 | 5 | 6 | fn fixture_section_dynamic() -> Section { 7 | Section { 8 | name: b".dynamic".to_vec(), 9 | header: SectionHeader { 10 | name: 0, 11 | shtype: types::SectionType::PROGBITS, 12 | flags: types::SectionFlags::ALLOC, 13 | addr: 0x123, 14 | offset: 0x654, 15 | size: 0x200, 16 | link: 0, 17 | info: 0, 18 | addralign: 8, 19 | entsize: 0, 20 | }, 21 | content: SectionContent::Dynamic(vec![dynamic::Dynamic{ 22 | dhtype: types::DynamicType::NULL, 23 | content: dynamic::DynamicContent::None, 24 | }]), 25 | addrlock: false, 26 | } 27 | } 28 | 29 | fn fixture_section_interp() -> Section { 30 | Section { 31 | name: b".interp".to_vec(), 32 | header: SectionHeader { 33 | name: 0, 34 | shtype: types::SectionType::PROGBITS, 35 | flags: types::SectionFlags::ALLOC, 36 | addr: 0x923, 37 | offset: 0x54, 38 | size: 0x1283, 39 | link: 0, 40 | info: 0, 41 | addralign: 1, 42 | entsize: 0, 43 | }, 44 | content: SectionContent::Raw(b"/lib/asdfblurp.ld".to_vec()), 45 | addrlock: false, 46 | } 47 | } 48 | 49 | fn fixture_section_text() -> Section { 50 | Section { 51 | name: b".text".to_vec(), 52 | header: SectionHeader { 53 | name: 0, 54 | shtype: types::SectionType::PROGBITS, 55 | flags: types::SectionFlags::ALLOC | types::SectionFlags::EXECINSTR, 56 | addr: 0x123, 57 | offset: 0x654, 58 | size: 0x200, 59 | link: 0, 60 | info: 0, 61 | addralign: 16, 62 | entsize: 0, 63 | }, 64 | content: SectionContent::Raw(vec![0;149]), // this is a prime number to test alingment 65 | addrlock: false, 66 | } 67 | } 68 | fn fixture_section_rodata() -> Section { 69 | Section { 70 | name: b".rodata".to_vec(), 71 | header: SectionHeader { 72 | name: 0, 73 | shtype: types::SectionType::PROGBITS, 74 | flags: types::SectionFlags::ALLOC, 75 | addr: 0x123, 76 | offset: 0x654, 77 | size: 0x200, 78 | link: 0, 79 | info: 0, 80 | addralign: 16, 81 | entsize: 0, 82 | }, 83 | content: SectionContent::Raw(vec![0;149]), // this is a prime number to test alingment 84 | addrlock: false, 85 | } 86 | } 87 | 88 | fn fixture_section_data() -> Section { 89 | Section { 90 | name: b".data".to_vec(), 91 | header: SectionHeader { 92 | name: 0, 93 | shtype: types::SectionType::PROGBITS, 94 | flags: types::SectionFlags::ALLOC | types::SectionFlags::WRITE, 95 | addr: 0x123, 96 | offset: 0x654, 97 | size: 0x200, 98 | link: 0, 99 | info: 0, 100 | addralign: 16, 101 | entsize: 0, 102 | }, 103 | content: SectionContent::Raw(vec![0;149]), // this is a prime number to test alingment 104 | addrlock: false, 105 | } 106 | } 107 | 108 | fn fixture_section_bss() -> Section { 109 | Section { 110 | name: b".bss".to_vec(), 111 | header: SectionHeader { 112 | name: 0, 113 | shtype: types::SectionType::NOBITS, 114 | flags: types::SectionFlags::ALLOC | types::SectionFlags::WRITE, 115 | addr: 0x123, 116 | offset: 0x654, 117 | size: 0x27, 118 | link: 0, 119 | info: 0, 120 | addralign: 16, 121 | entsize: 0, 122 | }, 123 | content: SectionContent::None, 124 | addrlock: false, 125 | } 126 | } 127 | 128 | #[test] 129 | fn layout_just_text() { 130 | let mut elf = Elf::default(); 131 | elf.sections.push(Section::default()); 132 | elf.sections.push(fixture_section_text()); 133 | elf.layout().unwrap(); 134 | 135 | assert_eq!(elf.sections[1].name, b".text", 136 | ".text section must be at shndx 1"); 137 | assert_eq!(elf.sections[1].header.offset, elf.sections[1].header.addr, 138 | ".text section offset and address must be identical"); 139 | 140 | assert_eq!(elf.segments.len(), 2, 141 | "expect exactly two segments"); 142 | 143 | let phdr_segments :Vec<&segment::SegmentHeader> = elf.segments.iter().filter(|x| x.phtype == types::SegmentType::PHDR).collect(); 144 | assert_eq!(phdr_segments.len(), 1, 145 | "expecting exactly one phdr segment"); 146 | let phdr = phdr_segments.get(0).unwrap();; 147 | 148 | assert_eq!(phdr.offset, elf.header.phoff, 149 | "phdr.offset must be identical to elf header phoff"); 150 | 151 | assert_eq!(phdr.offset, elf.header.ehsize as u64, 152 | "(phdr.offset == elf.header.ehsize) phdr must follow exactly elf header"); 153 | 154 | let load_segments :Vec<&segment::SegmentHeader> = elf.segments.iter().filter(|x| x.phtype == types::SegmentType::LOAD).collect(); 155 | assert_eq!(load_segments.len(), 1, 156 | "expect exactly one load segment"); 157 | 158 | let segment = load_segments.get(0).unwrap();; 159 | assert_eq!(segment.offset,0, 160 | "first load segment must start at zero"); 161 | assert_eq!(segment.vaddr, 0, 162 | "first load segment must start at zero"); 163 | assert_eq!(segment.paddr, 0, 164 | "first load segment must start at zero"); 165 | 166 | assert!(segment.vaddr <= elf.header.phoff, 167 | "first load segment must contain phdr (because of a bug in the linux kernel"); 168 | assert_eq!(segment.vaddr, segment.paddr, 169 | "first load segment must have offset == addr (because of linux kernel stuff)"); 170 | assert_eq!(segment.vaddr, segment.offset, 171 | "first load segment must have offset == addr (because of linux kernel stuff)"); 172 | 173 | assert!(segment.vaddr <= elf.sections[1].header.addr, 174 | "first load segment must start at or before .text"); 175 | assert!(segment.offset <= elf.sections[1].header.offset, 176 | "first load segment must start at or before .text"); 177 | 178 | assert!(segment.vaddr + segment.memsz >= elf.sections[1].header.addr + elf.sections[1].header.size, 179 | "first load segment must not end before .text end"); 180 | assert!(segment.offset + segment.filesz >= elf.sections[1].header.offset + elf.sections[1].header.size, 181 | "first load segment must not end before .text end"); 182 | 183 | assert!(segment.flags.contains(types::SegmentFlags::EXECUTABLE), 184 | "first load segment must be executable"); 185 | 186 | assert!(!segment.flags.contains(types::SegmentFlags::WRITABLE), 187 | "first load segment must NOT be writable"); 188 | } 189 | 190 | /* 191 | #[test] 192 | fn layout_just_bss() { 193 | let mut elf = Elf::default(); 194 | elf.sections.push(Section::default()); 195 | elf.sections.push(fixture_section_bss()); 196 | elf.layout().unwrap(); 197 | 198 | assert_eq!(elf.sections[1].name, b".bss", 199 | ".bss section must be at shndx 1"); 200 | assert_eq!(elf.sections[1].header.offset, elf.sections[1].header.addr, 201 | ".bss section offset and address must be identical"); 202 | 203 | assert_eq!(elf.segments.len(), 2, 204 | "expect exactly two segments"); 205 | let load_segments :Vec<&segment::SegmentHeader> = elf.segments.iter().filter(|x| x.phtype == types::SegmentType::LOAD).collect(); 206 | assert_eq!(load_segments.len(), 1, 207 | "expect xactly one load segment"); 208 | let segment = load_segments.get(0).unwrap();; 209 | 210 | assert!(segment.vaddr <= elf.sections[1].header.addr, 211 | "first load segment must start at or before .bss"); 212 | assert!(segment.offset <= elf.sections[1].header.offset, 213 | "first load segment must start at or before .bss"); 214 | 215 | assert!(segment.vaddr + segment.memsz >= elf.sections[1].header.addr + elf.sections[1].header.size, 216 | "first load segment must not end before .bss end"); 217 | 218 | assert!(segment.offset + segment.filesz < elf.sections[1].header.offset + elf.sections[1].header.size, 219 | "first load segment must not include .bss size as physical size"); 220 | 221 | assert!(segment.filesz == segment.memsz - elf.sections[1].header.size, 222 | "first load segment filesz must be exactly memsz - .bss size"); 223 | 224 | assert!(!segment.flags.contains(types::SegmentFlags::EXECUTABLE), 225 | "first load segment must NOT be executable"); 226 | 227 | assert!(segment.flags.contains(types::SegmentFlags::WRITABLE), 228 | "first load segment must be writable"); 229 | } 230 | */ 231 | 232 | #[test] 233 | fn layout_text_and_bss_1() { 234 | let mut elf = Elf::default(); 235 | elf.sections.push(Section::default()); 236 | elf.sections.push(fixture_section_text()); 237 | elf.sections.push(fixture_section_bss()); 238 | elf.layout().unwrap(); 239 | 240 | for seg in &elf.segments { 241 | println!("{:?}", seg); 242 | } 243 | 244 | 245 | assert_eq!(elf.sections[1].name, b".text", 246 | ".text section must be at shndx 1"); 247 | assert_eq!(elf.sections[2].name, b".bss", 248 | ".bss section must be at shndx 2"); 249 | 250 | assert_eq!(elf.segments.len(), 3, 251 | "expect exactly 3 segments"); 252 | let load_segments :Vec<&segment::SegmentHeader> = elf.segments.iter().filter(|x| x.phtype == types::SegmentType::LOAD).collect(); 253 | assert_eq!(load_segments.len(), 2, 254 | "expect exactly 2 load segments"); 255 | let segment0 = load_segments.get(0).unwrap();; 256 | let segment1 = load_segments.get(1).unwrap();; 257 | 258 | assert_eq!(segment0.vaddr,0 , 259 | "first load segment must start at 0"); 260 | 261 | assert!(segment0.offset <= elf.sections[1].header.offset, 262 | "first load segment must start at or before first section"); 263 | 264 | assert!(segment1.vaddr + segment1.memsz >= elf.sections[2].header.addr + elf.sections[2].header.size, 265 | "second load segment must not end before last section"); 266 | 267 | assert!(segment0.offset + segment0.filesz < elf.sections[2].header.offset + elf.sections[2].header.size, 268 | "first load segment must not include .bss size as physical size"); 269 | 270 | assert!(segment0.flags.contains(types::SegmentFlags::EXECUTABLE), 271 | "first load segment must be executable"); 272 | 273 | assert!(!segment0.flags.contains(types::SegmentFlags::WRITABLE), 274 | "first load segment must NOT be writable"); 275 | } 276 | 277 | #[test] 278 | fn layout_text_and_bss_2() { 279 | let mut elf = Elf::default(); 280 | //seg 0 281 | elf.sections.push(Section::default()); 282 | //seg 1 283 | elf.sections.push(fixture_section_bss()); 284 | //seg 2 285 | elf.sections.push(fixture_section_text()); 286 | elf.layout().unwrap(); 287 | 288 | for seg in &elf.segments { 289 | println!("{:?}", seg); 290 | } 291 | 292 | for sec in &elf.sections{ 293 | println!("{:?}", sec); 294 | } 295 | 296 | assert_eq!(elf.sections[1].name, b".bss", 297 | ".bss section must be at shndx 2"); 298 | assert_eq!(elf.sections[2].name, b".text", 299 | ".text section must be at shndx 1"); 300 | 301 | assert_eq!(elf.segments.len(), 4, 302 | "expect exactly 4 segments"); 303 | let load_segments :Vec<&segment::SegmentHeader> = elf.segments.iter().filter(|x| x.phtype == types::SegmentType::LOAD).collect(); 304 | assert_eq!(load_segments.len(), 3, 305 | "expect exactly 3 load segments"); 306 | let segment0 = load_segments.get(0).unwrap();; 307 | let segment1 = load_segments.get(1).unwrap();; 308 | let segment2 = load_segments.get(2).unwrap();; 309 | 310 | assert_eq!(segment0.vaddr, 0, 311 | "first load segment must start at 0"); 312 | 313 | assert_eq!(segment0.offset + segment0.filesz, elf.sections[1].header.offset, 314 | "first load segment must before .bss"); 315 | 316 | assert!(segment0.vaddr + segment0.memsz < elf.sections[2].header.addr, 317 | "first load segment must end before .text starts"); 318 | 319 | assert!(segment0.filesz == segment0.memsz, 320 | "first load segment filesz must be exactly memsz "); 321 | 322 | assert_eq!(segment2.offset, segment2.offset + segment1.filesz, 323 | "third load segment must start exactly after second segment in file"); 324 | 325 | assert_eq!(segment2.filesz, elf.sections[2].header.size, 326 | "third load segment filesz must be exactly as big as .text"); 327 | 328 | assert_eq!(segment1.vaddr + segment1.memsz, elf.sections[1].header.addr + elf.sections[1].header.size, 329 | "second load segment memsz must be exactly .bss size"); 330 | 331 | assert!(!segment0.flags.contains(types::SegmentFlags::EXECUTABLE), 332 | "first load segment must not be executable"); 333 | 334 | assert!(segment1.flags.contains(types::SegmentFlags::EXECUTABLE), 335 | "second load segment must be executable"); 336 | 337 | assert!(!segment0.flags.contains(types::SegmentFlags::WRITABLE), 338 | "first load segment must NOT be writable"); 339 | } 340 | 341 | #[test] 342 | fn layout_dynamic_and_interp() { 343 | let mut elf = Elf::default(); 344 | elf.sections.push(Section::default()); 345 | elf.sections.push(fixture_section_interp()); 346 | elf.sections.push(fixture_section_text()); 347 | elf.sections.push(fixture_section_bss()); 348 | elf.sections.push(fixture_section_dynamic()); 349 | 350 | elf.layout().unwrap(); 351 | 352 | assert_eq!(elf.sections[4].name, b".dynamic", 353 | ".dynamic section must be at shndx 4"); 354 | 355 | assert_eq!(elf.segments.len(), 6, 356 | "expect exactly 6 segments"); 357 | let load_segments :Vec<&segment::SegmentHeader> = elf.segments.iter().filter(|x| x.phtype == types::SegmentType::LOAD).collect(); 358 | assert_eq!(load_segments.len(), 3, 359 | "expect exactly 3 load segments"); 360 | 361 | let dynamic:Vec<&segment::SegmentHeader> = elf.segments.iter().filter(|x| x.phtype == types::SegmentType::DYNAMIC).collect(); 362 | assert_eq!(dynamic.len(), 1, 363 | "expect exactly 1 dynamic segment"); 364 | let dynamic = dynamic.get(0).unwrap(); 365 | 366 | assert_eq!(dynamic.memsz, 16, 367 | "dynamic size must be 16"); 368 | } 369 | 370 | #[test] 371 | fn layout_align() { 372 | let mut elf = Elf::default(); 373 | elf.sections.push(Section::default()); 374 | elf.sections.push(fixture_section_text()); 375 | elf.sections.push(fixture_section_text()); 376 | elf.sections.push(fixture_section_text()); 377 | elf.sections.push(fixture_section_text()); 378 | elf.layout().unwrap(); 379 | 380 | assert_eq!(elf.segments.len(), 2, 381 | "expect exactly two segments"); 382 | 383 | assert!(elf.sections[1].header.addr % 16 == 0, 384 | "expect section 1 to be aligned by 16 bytes"); 385 | assert!(elf.sections[2].header.addr % 16 == 0, 386 | "expect section 2 to be aligned by 16 bytes"); 387 | assert!(elf.sections[3].header.addr % 16 == 0, 388 | "expect section 3 to be aligned by 16 bytes"); 389 | assert!(elf.sections[4].header.addr % 16 == 0, 390 | "expect section 4 to be aligned by 16 bytes"); 391 | } 392 | 393 | 394 | #[test] 395 | fn layout_stable() { 396 | let mut elf = Elf::default(); 397 | elf.sections.push(Section::default()); 398 | // seg 0 + interp 399 | elf.sections.push(fixture_section_interp()); 400 | elf.sections.push(fixture_section_rodata()); 401 | // seg 1 + dynamic 402 | elf.sections.push(fixture_section_dynamic()); 403 | elf.sections.push(fixture_section_bss()); 404 | elf.layout().unwrap(); 405 | 406 | for seg in &elf.segments { 407 | println!("{:?}", seg); 408 | } 409 | 410 | assert_eq!(elf.sections[0].header.addr, 0, 411 | "section 0 is always addr 0"); 412 | assert_eq!(elf.sections[0].header.size, 0, 413 | "section 0 is always size 0"); 414 | assert_eq!(elf.sections[0].header.offset , 0, 415 | "section 0 is always offset 0"); 416 | 417 | assert_eq!(elf.segments.len(), 5, 418 | "expect exactly 5 segments"); 419 | 420 | for i in 0..5 { 421 | elf.sections[i].addrlock = true; 422 | } 423 | 424 | elf.layout().unwrap(); 425 | } 426 | 427 | 428 | #[test] 429 | #[should_panic] 430 | fn layout_enforce_addrlock() { 431 | let mut elf = Elf::default(); 432 | elf.sections.push(Section::default()); 433 | elf.sections.push(fixture_section_text()); 434 | elf.layout().unwrap(); 435 | elf.sections[1].addrlock = true; 436 | elf.sections.insert(1,fixture_section_text()); 437 | elf.layout().unwrap(); 438 | } 439 | 440 | #[test] 441 | fn layout_many_bss() { 442 | let mut elf = Elf::default(); 443 | 444 | // load 0 445 | elf.sections.push(Section::default()); 446 | // load 1 447 | elf.sections.push(fixture_section_data()); 448 | elf.sections.push(fixture_section_data()); 449 | elf.sections.push(fixture_section_bss()); 450 | elf.sections.push(fixture_section_bss()); 451 | elf.sections.push(fixture_section_bss()); 452 | elf.sections.push(fixture_section_bss()); 453 | 454 | // load 2 455 | elf.sections.push(fixture_section_data()); 456 | elf.sections.push(fixture_section_bss()); 457 | elf.sections.push(fixture_section_bss()); 458 | 459 | // load 3 460 | elf.sections.push(fixture_section_text()); 461 | elf.layout().unwrap(); 462 | 463 | for seg in &elf.segments { 464 | println!("{:?}", seg); 465 | } 466 | 467 | assert_eq!(elf.segments.len(), 5, 468 | "expect exactly 5 segments"); 469 | 470 | let load_segments :Vec<&segment::SegmentHeader> = elf.segments.iter().filter(|x| x.phtype == types::SegmentType::LOAD).collect(); 471 | let segment0 = load_segments.get(0).unwrap();; 472 | assert!(!segment0.flags.contains(types::SegmentFlags::WRITABLE), 473 | "first load segment must NOT be writable"); 474 | } 475 | -------------------------------------------------------------------------------- /src/symbolic_linker.rs: -------------------------------------------------------------------------------- 1 | extern crate indexmap; 2 | 3 | use {Header, types, symbol, relocation, section, Error}; 4 | use std; 5 | use std::io::Write; 6 | use std::collections::hash_map::{self, HashMap}; 7 | use std::collections::HashSet; 8 | use loader::{self, Loader}; 9 | use std::sync::atomic::{self, AtomicUsize}; 10 | 11 | pub type LinkGlobalId = usize; 12 | 13 | pub struct LinkableSymbol { 14 | pub obj: LinkGlobalId, 15 | pub sym: symbol::Symbol, 16 | } 17 | 18 | pub struct Object { 19 | /// the link layout global id, assigned by the symbolic linker 20 | pub lid: LinkGlobalId, 21 | 22 | /// loader hash of the original object 23 | pub hash: String, 24 | 25 | /// name of the object + section name 26 | pub name: String, 27 | 28 | /// copy of the original objects elf Header 29 | pub header: Header, 30 | 31 | /// the actual section extracted from the object 32 | pub section: section::Section, 33 | 34 | /// relocations that need to be applied to this section 35 | /// reloc.sym points at SymbolicLinker.symtab 36 | pub relocs: Vec, 37 | 38 | // global source object id. this is used for debugging 39 | oid: LinkGlobalId, 40 | } 41 | 42 | #[derive(Default)] 43 | pub struct SymbolicLinker { 44 | pub objects: HashMap, 45 | pub symtab: Vec, 46 | 47 | lookup: HashMap, usize>, 48 | lid_counter: AtomicUsize, 49 | 50 | objects_seen: HashSet, 51 | } 52 | 53 | impl SymbolicLinker { 54 | pub fn link_all(&mut self, loader: Vec) -> Result<(), Error> { 55 | let loader = loader.load_all(&|e,name| { 56 | println!("elfkit::Linker {:?} while loading {}", e, name); 57 | Vec::with_capacity(0) 58 | }); 59 | self.objects.reserve(loader.len()); 60 | for ma in loader { 61 | if let loader::State::Object{name, hash, header, symbols, sections} = ma { 62 | if self.objects_seen.insert(hash.clone()) { 63 | self.insert_object(name, hash, header, symbols, sections)?; 64 | } 65 | } 66 | } 67 | Ok(()) 68 | } 69 | pub fn link(&mut self, mut loader: Vec) -> Result<(), Error> { 70 | loop { 71 | let (l2, matches) = self.link_iteration(loader); 72 | loader = l2; 73 | if matches.len() == 0 { 74 | for link in self.symtab.iter() { 75 | if link.sym.shndx == symbol::SymbolSectionIndex::Undefined && 76 | link.sym.bind == types::SymbolBind::GLOBAL { 77 | return Err(Error::UndefinedReference{ 78 | obj: self.objects[&link.obj].name.clone(), 79 | sym: String::from_utf8_lossy(&link.sym.name).into_owned(), 80 | }); 81 | } 82 | } 83 | break; 84 | } 85 | 86 | self.objects.reserve(matches.len()); 87 | for ma in matches { 88 | if let loader::State::Object{name, hash, header, symbols, sections} = ma { 89 | if self.objects_seen.insert(hash.clone()) { 90 | self.insert_object(name, hash, header, symbols, sections)?; 91 | } 92 | } 93 | } 94 | } 95 | Ok(()) 96 | } 97 | 98 | fn link_iteration(&mut self, loader: Vec) -> (Vec, Vec) { 99 | let (state2, matches) : (Vec, Vec) = { 100 | let undefined_refs = self.symtab.iter().filter_map(|link|{ 101 | match link.sym.shndx { 102 | symbol::SymbolSectionIndex::Undefined => { 103 | if link.sym.bind == types::SymbolBind::GLOBAL { 104 | Some(link.sym.name.as_ref()) 105 | } else { 106 | None 107 | } 108 | }, 109 | symbol::SymbolSectionIndex::Common => { 110 | //note that this will only pull in objects that have this symbol as global, 111 | //not those who merely also define it as common 112 | Some(link.sym.name.as_ref()) 113 | }, 114 | _ => None, 115 | } 116 | }).collect(); 117 | 118 | loader.load_if(&undefined_refs, &|e,name| { 119 | println!("elfkit::Linker {:?} while loading {}", e, name); 120 | Vec::with_capacity(0) 121 | }) 122 | }; 123 | (state2, matches) 124 | } 125 | 126 | fn insert_object(&mut self, name: String, hash:String, header: Header, symbols: Vec, 127 | sections: Vec<(usize, section::Section, Vec)>) 128 | -> Result<(), Error> { 129 | 130 | assert!((sections.len() as u16) <= header.shnum, 131 | "incoming object header.shnum is {} but loader gave us {} sections ", header.shnum, sections.len()); 132 | let lid_base = self.lid_counter.fetch_add(header.shnum as usize, atomic::Ordering::Acquire); 133 | 134 | let locations = match self.link_locations(lid_base, symbols) { 135 | Ok(v) => v, 136 | Err(Error::ConflictingSymbol{sym, obj1_name, obj1_hash, ..}) => { 137 | return Err(Error::ConflictingSymbol{sym, obj1_name, obj2_name:name, 138 | obj1_hash, obj2_hash: hash}); 139 | }, 140 | Err(e) => return Err(e), 141 | }; 142 | 143 | 144 | let name = name.split("/").last().unwrap().to_owned(); 145 | for (sec_shndx, sec, mut relocs) in sections { 146 | 147 | // point the relocs at the global symtab 148 | for reloc in &mut relocs { 149 | reloc.sym = locations[reloc.sym as usize] as u32; 150 | }; 151 | 152 | self.objects.insert(lid_base + sec_shndx as usize, Object { 153 | oid: lid_base, 154 | lid: lid_base + sec_shndx as usize, 155 | hash: hash.clone(), 156 | name: name.clone() + "("+ &String::from_utf8_lossy(&sec.name) + ")", 157 | header: header.clone(), 158 | section: sec, 159 | relocs: relocs, 160 | }); 161 | } 162 | 163 | // TODO insert a fake object at base + 0 so error messages 164 | // can correctly report the name of an object when a Needed 165 | // symbol isn't statisfied 166 | // this is a bit hackish tho, and we need to rely on gc() 167 | // to remove the crap object before layout 168 | 169 | self.objects.insert(lid_base, Object { 170 | oid: lid_base, 171 | lid: lid_base, 172 | hash: hash.clone(), 173 | name: name.clone(), 174 | header: header.clone(), 175 | section: section::Section::default(), 176 | relocs: Vec::new(), 177 | }); 178 | 179 | Ok(()) 180 | } 181 | 182 | fn link_locations(&mut self, lid_base: LinkGlobalId, symbols: Vec) 183 | -> Result, Error> { 184 | 185 | let mut locations = Vec::with_capacity(symbols.len()); 186 | for mut sym in symbols { 187 | match sym.shndx { 188 | symbol::SymbolSectionIndex::Undefined => { 189 | if sym.name == b"_GLOBAL_OFFSET_TABLE_" { 190 | //emit as not linkable, because nothing should relocate here 191 | //the symbol appears to be mainly a hint that the linker needs to 192 | //emit a GOT. which it knows from relocs anyway, so this appears to 193 | //be kinda useless. We could emit a fake symbol to statisy it, 194 | //but i want to ensure really nothing actually uses this symbol. 195 | //Absolute will show up as error when a reloc points to it. 196 | sym.shndx = symbol::SymbolSectionIndex::Absolute; 197 | } 198 | if sym.bind == types::SymbolBind::LOCAL { 199 | if sym.name.len() > 0 { 200 | panic!("local undefined symbol {:?}", sym); 201 | } 202 | } 203 | let gsi = match self.lookup.entry(sym.name.clone()) { 204 | hash_map::Entry::Occupied(e) => { 205 | *e.get() 206 | }, 207 | hash_map::Entry::Vacant(e) => { 208 | let i = self.symtab.len(); 209 | self.symtab.push(LinkableSymbol{sym: sym, obj: lid_base}); 210 | e.insert(i); 211 | i 212 | }, 213 | }; 214 | locations.push(gsi); 215 | }, 216 | symbol::SymbolSectionIndex::Common => { 217 | let gsi = match self.lookup.entry(sym.name.clone()) { 218 | hash_map::Entry::Occupied(e) => { 219 | let i = *e.get(); 220 | if let symbol::SymbolSectionIndex::Undefined = self.symtab[i].sym.shndx { 221 | self.symtab[i] = LinkableSymbol{sym: sym, obj: lid_base}; 222 | } else { 223 | //TODO check that the existing symbol is common with the same size 224 | } 225 | i 226 | }, 227 | hash_map::Entry::Vacant(e) => { 228 | let i = self.symtab.len(); 229 | self.symtab.push(LinkableSymbol{sym: sym, obj: lid_base}); 230 | e.insert(i); 231 | i 232 | }, 233 | }; 234 | locations.push(gsi); 235 | }, 236 | symbol::SymbolSectionIndex::Absolute => { 237 | locations.push(self.symtab.len()); 238 | self.symtab.push(LinkableSymbol{sym: sym, obj: lid_base}); 239 | }, 240 | symbol::SymbolSectionIndex::Section(shndx) => { 241 | match sym.bind { 242 | types::SymbolBind::GLOBAL => { 243 | let gsi = match self.lookup.entry(sym.name.clone()) { 244 | hash_map::Entry::Occupied(e) => { 245 | let i = *e.get(); 246 | if let symbol::SymbolSectionIndex::Section(_) = self.symtab[i].sym.shndx { 247 | if self.symtab[i].sym.bind != types::SymbolBind::WEAK { 248 | return Err(Error::ConflictingSymbol{ 249 | sym: String::from_utf8_lossy(&self.symtab[i].sym.name) 250 | .into_owned(), 251 | obj1_name: self.objects[&self.symtab[i].obj].name.clone(), 252 | obj1_hash: self.objects[&self.symtab[i].obj].hash.clone(), 253 | obj2_name: String::default(), 254 | obj2_hash: String::default(), 255 | }); 256 | } 257 | }; 258 | self.symtab[i] = LinkableSymbol{sym: sym, 259 | obj: lid_base + shndx as usize}; 260 | i 261 | }, 262 | hash_map::Entry::Vacant(e) => { 263 | let i = self.symtab.len(); 264 | self.symtab.push(LinkableSymbol{sym: sym, obj: lid_base + shndx as usize}); 265 | e.insert(i); 266 | i 267 | }, 268 | }; 269 | locations.push(gsi); 270 | }, 271 | types::SymbolBind::WEAK => { 272 | let gsi = match self.lookup.entry(sym.name.clone()) { 273 | hash_map::Entry::Occupied(e) => { 274 | let i = e.get(); 275 | if let symbol::SymbolSectionIndex::Undefined = self.symtab[*i].sym.shndx { 276 | self.symtab[*i] = LinkableSymbol{sym: sym, 277 | obj: lid_base + shndx as usize}; 278 | }; 279 | *i 280 | }, 281 | hash_map::Entry::Vacant(e) => { 282 | let i = self.symtab.len(); 283 | self.symtab.push(LinkableSymbol{sym: sym, 284 | obj: lid_base + shndx as usize}); 285 | e.insert(i); 286 | i 287 | }, 288 | }; 289 | locations.push(gsi); 290 | } 291 | _ => { 292 | locations.push(self.symtab.len()); 293 | self.symtab.push(LinkableSymbol{sym: sym, obj: lid_base + shndx as usize}); 294 | }, 295 | } 296 | }, 297 | } 298 | } 299 | Ok(locations) 300 | } 301 | 302 | //TODO: maybe too aggressive because stuff like .comment and .note.GNU-stack are culled? 303 | pub fn gc(&mut self) { 304 | 305 | let mut again = true; 306 | let mut symtab_remap : Vec> = vec![None;self.symtab.len()]; 307 | while again { 308 | symtab_remap = vec![None;self.symtab.len()]; 309 | let mut removelids = HashMap::new(); 310 | for (lid, obj) in &self.objects { 311 | 312 | //TODO yep yep, more hacks 313 | if obj.section.header.shtype == types::SectionType::INIT_ARRAY || 314 | obj.section.header.shtype == types::SectionType::FINI_ARRAY { 315 | continue; 316 | } 317 | removelids.insert(*lid, true); 318 | } 319 | 320 | for (lid, obj) in &self.objects { 321 | //TODO oh look, more hacks 322 | if obj.section.name.starts_with(b".debug_") { 323 | continue; 324 | } 325 | 326 | for reloc in &obj.relocs { 327 | symtab_remap[reloc.sym as usize] = Some(0); 328 | 329 | let link = &self.symtab[reloc.sym as usize]; 330 | 331 | if link.obj != *lid { 332 | if let symbol::SymbolSectionIndex::Section(_) = link.sym.shndx { 333 | removelids.insert(link.obj, false); 334 | } 335 | } 336 | } 337 | 338 | } 339 | 340 | //TODO this feels like a hack. I think we should be able to mark root nodes before gc 341 | if let Some(i) = self.lookup.get(&(b"_start".to_vec())) { 342 | removelids.insert(self.symtab[*i].obj, false); 343 | } 344 | 345 | 346 | again = false; 347 | for (lid, t) in removelids { 348 | if t { 349 | again = true; 350 | self.objects.remove(&lid); 351 | } else { 352 | for (i, sym) in self.symtab.iter().enumerate() { 353 | if sym.obj == lid { 354 | symtab_remap[i] = Some(0); 355 | } 356 | } 357 | } 358 | } 359 | } 360 | 361 | let mut symtab = Vec::new(); 362 | 363 | for (i, link) in self.symtab.drain(..).enumerate() { 364 | if link.sym.shndx == symbol::SymbolSectionIndex::Absolute { 365 | symtab_remap[i] = Some(0); 366 | } 367 | if let Some(_) = symtab_remap[i] { 368 | symtab_remap[i] = Some(symtab.len()); 369 | symtab.push(link); 370 | } 371 | } 372 | 373 | for (_, obj) in &mut self.objects { 374 | for reloc in &mut obj.relocs { 375 | reloc.sym = symtab_remap[reloc.sym as usize] 376 | .expect("bug in elfkit: dangling reloc after gc") as u32; 377 | } 378 | } 379 | 380 | self.symtab = symtab; 381 | 382 | } 383 | 384 | 385 | pub fn write_graphviz (&self, mut file: W) -> std::io::Result<()> { 386 | 387 | for (lid, object) in self.objects.iter() { 388 | 389 | writeln!(file, " o{}[group=g{}, label=\"{}| {}\"];", 390 | lid, object.oid, object.oid, object.name)?; 391 | 392 | 393 | for reloc in &object.relocs { 394 | let link = &self.symtab[reloc.sym as usize]; 395 | 396 | if link.obj != object.lid { 397 | let mut style = String::new(); 398 | let mut linkto = format!("o{}", link.obj); 399 | let label = String::from_utf8_lossy(&link.sym.name).to_owned(); 400 | 401 | if link.sym.bind == types::SymbolBind::WEAK { 402 | style = String::from(", style=\"dashed\""); 403 | }; 404 | if link.sym.shndx == symbol::SymbolSectionIndex::Common { 405 | writeln!(file, " common_{}[label=\"COMMON {}\", style=\"dotted\"];", 406 | String::from_utf8_lossy(&link.sym.name), 407 | String::from_utf8_lossy(&link.sym.name))?; 408 | 409 | style = String::from(", style=\"dotted\""); 410 | linkto = format!("common_{}", String::from_utf8_lossy(&link.sym.name)); 411 | } 412 | if link.sym.shndx == symbol::SymbolSectionIndex::Undefined { 413 | writeln!(file, " missing_{}[label=\"UNDEFINED {}\", color=\"red\", style=\"dashed\", fontcolor=\"red\"];", 414 | String::from_utf8_lossy(&link.sym.name), 415 | String::from_utf8_lossy(&link.sym.name) 416 | )?; 417 | style += ", color=\"red\""; 418 | linkto = format!("missing_{}", String::from_utf8_lossy(&link.sym.name)); 419 | } 420 | 421 | writeln!(file, " o{} -> {} [label=\"{}\" {}]", 422 | object.lid, linkto, label, style)?; 423 | } 424 | } 425 | } 426 | 427 | Ok(()) 428 | } 429 | } 430 | -------------------------------------------------------------------------------- /bolter/main.rs: -------------------------------------------------------------------------------- 1 | extern crate colored; 2 | #[macro_use] extern crate elfkit; 3 | extern crate byteorder; 4 | extern crate goblin; 5 | extern crate sha2; 6 | 7 | use elfkit::{ 8 | Elf, Header, types, SegmentHeader, Section, SectionContent, Error, 9 | SectionHeader, Dynamic, Symbol, Relocation, Strtab, SymbolSectionIndex}; 10 | 11 | use elfkit::filetype; 12 | use elfkit::linker; 13 | 14 | use std::fs::OpenOptions; 15 | use elfkit::dynamic::DynamicContent; 16 | use elfkit::relocation::RelocationType; 17 | use std::collections::HashMap; 18 | use std::os::unix::fs::PermissionsExt; 19 | use std::path::Path; 20 | use colored::*; 21 | use sha2::Digest; 22 | use std::io::Write; 23 | 24 | mod ld; 25 | use ld::*; 26 | mod args; 27 | use args::*; 28 | mod relocations; 29 | use relocations::*; 30 | 31 | pub fn fail(msg: String) -> ! { 32 | println!("{}", msg.red()); 33 | panic!("abort"); 34 | } 35 | 36 | fn main() { 37 | let ldoptions = parse_ld_options(); 38 | let mut elfs = load_elfs(ldoptions.object_paths); 39 | let mut lookup = Lookup::default(); 40 | 41 | let mut start = Symbol::default(); 42 | start.name = String::from("_start"); 43 | start.bind = types::SymbolBind::GLOBAL; 44 | let mut got = Symbol::default(); 45 | got.name = String::from("_GLOBAL_OFFSET_TABLE_"); //TODO 46 | got.shndx = SymbolSectionIndex::Global(0); 47 | got.bind = types::SymbolBind::GLOBAL; 48 | lookup.insert_unit(Unit::fake(String::from("exe"), LinkBehaviour::Static, vec![start, got])); 49 | 50 | lookup.link(elfs); 51 | // TODO garbage collect unused units 52 | 53 | println!("linking {} units into exe", lookup.units.len()); 54 | 55 | let mut out_file = OpenOptions::new().write(true).truncate(true).create(true).open(ldoptions.output_path).unwrap(); 56 | let mut out_elf = Elf::default(); 57 | out_elf.header.ident_class = types::Class::Class64; 58 | out_elf.header.ident_endianness = types::Endianness::LittleEndian; 59 | out_elf.header.ident_abi = types::Abi::SYSV; 60 | out_elf.header.etype = types::ElfType::DYN; 61 | out_elf.header.machine = types::Machine::X86_64; 62 | 63 | let mut sc_interp : Vec = ldoptions.dynamic_linker.trim().bytes().collect(); 64 | sc_interp.push(0); 65 | let mut sc_rela : Vec = Vec::new(); 66 | let mut sc_dynsym : Vec = vec![Symbol::default()]; 67 | let mut sc_dynamic : Vec = vec![ 68 | Dynamic{ 69 | dhtype: types::DynamicType::FLAGS_1, 70 | content: DynamicContent::Flags1(types::DynamicFlags1::PIE), 71 | }, 72 | ]; 73 | let mut sc_symtab : Vec = vec![Symbol::default()]; 74 | 75 | 76 | out_elf.sections.insert(0, Section::default()); 77 | if sc_interp.len() > 1 { 78 | out_elf.sections.push(Section::new(String::from(".interp"), types::SectionType::PROGBITS, 79 | types::SectionFlags::ALLOC, 80 | SectionContent::Raw(sc_interp), 0,0)); 81 | } 82 | 83 | //--------------------- prepare bootstrap section 84 | let boostrap_len = relocations::len_reljumpto + lookup.units.iter().fold(0, |acc, ref u| { 85 | acc + u.relocations.iter().fold(0, |acc, ref reloc|{ 86 | acc + match reloc.rtype { 87 | RelocationType::R_X86_64_64 => relocations::len_bootstrap_abs64, 88 | RelocationType::R_X86_64_GOTPCREL | 89 | RelocationType::R_X86_64_GOTPCRELX | 90 | RelocationType::R_X86_64_REX_GOTPCRELX => 91 | relocations::len_bootstrap_abs64 + 92 | relocations::len_bootstrap_rel32, 93 | RelocationType::R_X86_64_PC32 | 94 | RelocationType::R_X86_64_PLT32 => relocations::len_bootstrap_rel32, 95 | RelocationType::R_X86_64_TLSGD => relocations::len_bootstrap_val64 + 96 | relocations::len_bootstrap_abs64 + 97 | relocations::len_bootstrap_rel32, 98 | _ => 0, 99 | } 100 | }) 101 | }); 102 | let mut bootstrap = vec![0;boostrap_len]; 103 | let sh_index_bootstrap = out_elf.sections.len(); 104 | out_elf.sections.push(Section::new(String::from(".xo.bootstrap"), types::SectionType::PROGBITS, 105 | types::SectionFlags::ALLOC | 106 | types::SectionFlags::EXECINSTR, 107 | SectionContent::Raw(bootstrap), 108 | 0,0)); 109 | 110 | out_elf.sync_all().unwrap(); 111 | linker::relayout(&mut out_elf, 0x300).unwrap(); 112 | 113 | let blt_bootstrap_sym = Symbol{ 114 | shndx: SymbolSectionIndex::Section(sh_index_bootstrap as u16), 115 | value: out_elf.sections[sh_index_bootstrap].header.addr, 116 | size: out_elf.sections[sh_index_bootstrap].header.size, 117 | name: String::from("__blt_bootstrap"), 118 | stype: types::SymbolType::FUNC, 119 | bind: types::SymbolBind::LOCAL, 120 | vis: types::SymbolVis::DEFAULT, 121 | }; 122 | sc_symtab.push(blt_bootstrap_sym.clone()); 123 | 124 | 125 | 126 | //----------------------------layout 127 | let mut sc_relink = Vec::new(); 128 | let mut vaddr = out_elf.sections[sh_index_bootstrap].header.addr + 129 | out_elf.sections[sh_index_bootstrap].header.size; 130 | let mut sc_text = Vec::new(); 131 | let mut sc_data = Vec::new(); 132 | let mut sc_tls = Vec::new(); 133 | let mut sc_bss = 0; 134 | let mut unit_addresses = HashMap::new(); 135 | 136 | lookup.units.sort_unstable_by(|a,b| { 137 | a.segment.cmp(&b.segment) 138 | }); 139 | lookup.reindex(); 140 | 141 | for unit in &mut lookup.units { 142 | match unit.segment { 143 | UnitSegment::Executable => { 144 | sc_relink.push(sc_text.len() as u32); 145 | unit_addresses.insert(unit.global_id, vaddr); 146 | vaddr += unit.code.len() as u64; 147 | sc_text.append(&mut unit.code); 148 | }, 149 | UnitSegment::Data => { 150 | sc_relink.push(sc_data.len() as u32); 151 | unit_addresses.insert(unit.global_id, vaddr); 152 | vaddr += unit.code.len() as u64; 153 | sc_data.append(&mut unit.code); 154 | }, 155 | UnitSegment::Bss => { 156 | unit_addresses.insert(unit.global_id, vaddr); 157 | vaddr += unit.code.len() as u64; 158 | sc_bss += unit.code.len() as u64; 159 | }, 160 | UnitSegment::Tls => { 161 | sc_relink.push(sc_tls.len() as u32); 162 | unit_addresses.insert(unit.global_id, vaddr); 163 | vaddr += unit.code.len() as u64; 164 | sc_tls.append(&mut unit.code); 165 | } 166 | } 167 | } 168 | 169 | let sh_index_text = out_elf.sections.len(); 170 | out_elf.sections.push(Section::new(String::from(".xo.text"), 171 | types::SectionType::PROGBITS, 172 | types::SectionFlags::ALLOC | types::SectionFlags::WRITE | types::SectionFlags::EXECINSTR, 173 | SectionContent::Raw(sc_text), 0, 0)); 174 | 175 | let sh_index_data = out_elf.sections.len(); 176 | out_elf.sections.push(Section::new(String::from(".xo.data"), 177 | types::SectionType::PROGBITS, 178 | types::SectionFlags::ALLOC | types::SectionFlags::WRITE, 179 | SectionContent::Raw(sc_data), 0, 0)); 180 | 181 | let sh_index_bss = out_elf.sections.len(); 182 | if sc_bss > 0 { 183 | let mut bss = Section::new(String::from(".xo.bss"), 184 | types::SectionType::NOBITS, 185 | types::SectionFlags::ALLOC | types::SectionFlags::WRITE, 186 | SectionContent::None, 0, 0); 187 | bss.header.size = sc_bss; 188 | out_elf.sections.push(bss); 189 | } 190 | 191 | let sh_index_tls = out_elf.sections.len(); 192 | if sc_tls.len() > 0 { 193 | let mut tls = Section::new(String::from(".xo.tdata"), 194 | types::SectionType::PROGBITS, 195 | types::SectionFlags::ALLOC | types::SectionFlags::WRITE | types::SectionFlags::TLS, 196 | SectionContent::Raw(sc_tls), 0, 0); 197 | out_elf.sections.push(tls); 198 | } 199 | 200 | //reposition all the symbols 201 | for sym in &mut lookup.symbols { 202 | if let SymbolSectionIndex::Global(id) = sym.shndx { 203 | let unit = &lookup.units[lookup.by_id[&id]]; 204 | sym.shndx = SymbolSectionIndex::Section(match unit.segment { 205 | UnitSegment::Executable => sh_index_text, 206 | UnitSegment::Data => sh_index_data, 207 | UnitSegment::Bss => sh_index_bss, 208 | UnitSegment::Tls => sh_index_tls, 209 | } as u16); 210 | sym.value += unit_addresses[&unit.global_id]; 211 | } 212 | } 213 | out_elf.header.entry = lookup.get_by_name("_start").unwrap().value; 214 | 215 | //symtab 216 | for unit in &lookup.units { 217 | for mut sym in unit.symbols.iter().cloned() { 218 | if let SymbolSectionIndex::Global(id) = sym.shndx { 219 | let unit = &lookup.units[lookup.by_id[&id]]; 220 | sym.shndx = SymbolSectionIndex::Section(match unit.segment { 221 | UnitSegment::Executable => sh_index_text, 222 | UnitSegment::Data => sh_index_data, 223 | UnitSegment::Bss => sh_index_bss, 224 | UnitSegment::Tls => sh_index_tls, 225 | } as u16); 226 | sym.value += unit_addresses[&unit.global_id]; 227 | } 228 | sc_symtab.push(sym); 229 | } 230 | } 231 | 232 | //----------------------------------relocate 233 | let mut bootstrap : Vec = Vec::new(); 234 | let mut got_used : u64 = 0; 235 | 236 | //TODO: we emit a GOT for every reloc, although some relocs point to the same symbol 237 | for mut unit in std::mem::replace(&mut lookup.units, Vec::new()) { 238 | for mut reloc in unit.relocations { 239 | let mut sym = &unit.symbols[reloc.sym as usize]; 240 | let sym_addr = match sym.stype { 241 | types::SymbolType::SECTION => { 242 | if let SymbolSectionIndex::Global(id) = sym.shndx { 243 | unit_addresses[&id] 244 | } else { 245 | panic!("bug in elfkit linker: reloc against section that's not global. like what?"); 246 | } 247 | }, 248 | _ => { 249 | if sym.shndx == SymbolSectionIndex::Undefined { 250 | match lookup.get_by_name(&sym.name) { 251 | Some(s) => { 252 | assert!(s.name.len() > 0); 253 | s.value 254 | }, 255 | None => { 256 | panic!( 257 | "bug in elfkit linker: symbol no longer in lookup table while relocating: {:?} < {:?} < {}", 258 | sym, reloc, unit.name); 259 | } 260 | } 261 | } else if let SymbolSectionIndex::Global(id) = sym.shndx { 262 | unit_addresses[&id] + sym.value 263 | } else { 264 | panic!("bug in elfkit linker: symbol in reloc neither undefined nor global {:?}", sym); 265 | } 266 | } 267 | }; 268 | reloc.addr += unit_addresses[&unit.global_id]; 269 | 270 | if sym_addr == 0 { 271 | assert!(sym.bind == types::SymbolBind::WEAK); 272 | println!("undefined weak (this is usually ok) {:?} to {}", reloc.rtype, sym.name); 273 | } 274 | 275 | match reloc.rtype { 276 | RelocationType::R_X86_64_64 => { 277 | write_bootstrap_abs64(&out_elf.header, 278 | out_elf.sections[sh_index_bootstrap].header.addr, 279 | &mut bootstrap, 280 | (sym_addr as i64 + reloc.addend) as u64, 281 | reloc.addr, 282 | ); 283 | }, 284 | RelocationType::R_X86_64_PC32 | RelocationType::R_X86_64_PLT32 => { 285 | write_bootstrap_rel32(&out_elf.header, 286 | out_elf.sections[sh_index_bootstrap].header.addr, 287 | &mut bootstrap, 288 | (sym_addr as i64 + reloc.addend) as u64, 289 | reloc.addr, 290 | ); 291 | }, 292 | RelocationType::R_X86_64_GOTPCREL | RelocationType::R_X86_64_GOTPCRELX | RelocationType::R_X86_64_REX_GOTPCRELX => { 293 | 294 | let got_slot = vaddr; 295 | vaddr += 8; 296 | out_elf.sections[sh_index_bss].header.size += 8; 297 | 298 | write_bootstrap_rel32(&out_elf.header, 299 | out_elf.sections[sh_index_bootstrap].header.addr, 300 | &mut bootstrap, 301 | (got_slot as i64 + reloc.addend) as u64, 302 | reloc.addr, 303 | ); 304 | 305 | //this is is only really used for debugging 306 | sc_symtab.push(Symbol{ 307 | shndx: SymbolSectionIndex::Section(sh_index_bss as u16), 308 | value: got_slot, 309 | size: 8, 310 | name: sym.name.clone() + "__GOT", 311 | stype: types::SymbolType::OBJECT, 312 | bind: types::SymbolBind::LOCAL, 313 | vis: types::SymbolVis::DEFAULT, 314 | }); 315 | 316 | 317 | write_bootstrap_abs64(&out_elf.header, 318 | out_elf.sections[sh_index_bootstrap].header.addr, 319 | &mut bootstrap, 320 | sym_addr, 321 | got_slot, 322 | ); 323 | }, 324 | 325 | 326 | RelocationType::R_X86_64_32 | RelocationType::R_X86_64_32S => { 327 | fail(format!("unsupported relocation. maybe missing -fPIC ? {:?}", reloc)); 328 | }, 329 | 330 | // this is the "general model" 331 | RelocationType:: R_X86_64_TLSGD => { 332 | 333 | let got_slot_mod = vaddr; 334 | let got_slot_off = vaddr + 8; 335 | vaddr += 16; 336 | out_elf.sections[sh_index_bss].header.size += 16; 337 | 338 | write_bootstrap_val64(&out_elf.header, 339 | out_elf.sections[sh_index_bootstrap].header.addr, 340 | &mut bootstrap, 341 | 1, //module is always 1, 342 | got_slot_mod, 343 | ); 344 | 345 | write_bootstrap_abs64(&out_elf.header, 346 | out_elf.sections[sh_index_bootstrap].header.addr, 347 | &mut bootstrap, 348 | sym_addr, 349 | got_slot_off, 350 | ); 351 | //this is is only really used for debugging 352 | sc_symtab.push(Symbol{ 353 | shndx: SymbolSectionIndex::Section(sh_index_bss as u16), 354 | value: got_slot_mod, 355 | size: 8, 356 | name: sym.name.clone() + "__TLSGD", 357 | stype: types::SymbolType::OBJECT, 358 | bind: types::SymbolBind::LOCAL, 359 | vis: types::SymbolVis::DEFAULT, 360 | }); 361 | 362 | write_bootstrap_rel32(&out_elf.header, 363 | out_elf.sections[sh_index_bootstrap].header.addr, 364 | &mut bootstrap, 365 | (got_slot_mod as i64 + reloc.addend) as u64, 366 | reloc.addr, 367 | ); 368 | 369 | }, 370 | _ => { 371 | fail(format!("unsupported relocation {:?} to {:?}", reloc, sym)); 372 | }, 373 | } 374 | } 375 | } 376 | 377 | //sc_symtab.append(&mut lookup.symbols); 378 | 379 | //indirect _start via __blt_bootstrap 380 | write_reljumpto(&out_elf.header, 381 | out_elf.sections[sh_index_bootstrap].header.addr, 382 | &mut bootstrap, 383 | out_elf.header.entry, 384 | ); 385 | 386 | out_elf.header.entry = out_elf.sections[sh_index_bootstrap].header.addr; 387 | 388 | 389 | if bootstrap.len() < out_elf.sections[sh_index_bootstrap].header.size as usize { 390 | let more = out_elf.sections[sh_index_bootstrap].header.size as usize - bootstrap.len(); 391 | bootstrap.extend(vec![0;more]); 392 | } 393 | assert_eq!(bootstrap.len(), out_elf.sections[sh_index_bootstrap].header.size as usize); 394 | out_elf.sections[sh_index_bootstrap].content = SectionContent::Raw(bootstrap); 395 | 396 | 397 | let sh_index_dynstr = out_elf.sections.len(); 398 | out_elf.sections.push(Section::new(String::from(".dynstr"), types::SectionType::STRTAB, 399 | types::SectionFlags::ALLOC, 400 | SectionContent::Strtab(Strtab::default()), 0,0)); 401 | 402 | linker::relayout(&mut out_elf, 0x300).unwrap(); 403 | 404 | sc_dynamic.extend(linker::dynamic(&out_elf).unwrap()); 405 | out_elf.sections.push(Section::new(String::from(".dynamic"), types::SectionType::DYNAMIC, 406 | types::SectionFlags::ALLOC | types::SectionFlags::WRITE, 407 | SectionContent::Dynamic(sc_dynamic), sh_index_dynstr as u32,0)); 408 | 409 | let sh_index_strtab = out_elf.sections.len(); 410 | out_elf.sections.push(Section::new(String::from(".strtab"), types::SectionType::STRTAB, 411 | types::SectionFlags::empty(), 412 | SectionContent::Strtab(Strtab::default()), 0,0)); 413 | 414 | //sc_symtab.sort_unstable_by(|a,b| a.bind.cmp(&b.bind)); 415 | let first_global_symtab = sc_symtab.iter().enumerate() 416 | .find(|&(_,s)|s.bind == types::SymbolBind::GLOBAL).map(|(i,_)|i).unwrap_or(0);; 417 | out_elf.sections.push(Section::new(String::from(".symtab"), types::SectionType::SYMTAB, 418 | types::SectionFlags::empty(), 419 | SectionContent::Symbols(sc_symtab), 420 | sh_index_strtab as u32, first_global_symtab as u32)); 421 | 422 | out_elf.sections.push(Section::new(String::from(".shstrtab"), types::SectionType::STRTAB, 423 | types::SectionFlags::from_bits_truncate(0), 424 | SectionContent::Strtab(Strtab::default()), 425 | 0,0)); 426 | 427 | 428 | let mut b_relink = Vec::new(); 429 | for cut in sc_relink { 430 | let io = &mut b_relink; 431 | elf_write_u32!(&out_elf.header, io, cut); 432 | } 433 | 434 | out_elf.sections.push(Section::new(String::from(".relink"), types::SectionType::RELINKABLE, 435 | types::SectionFlags::from_bits_truncate(0), 436 | SectionContent::Raw(b_relink), 437 | 0,0)); 438 | 439 | out_elf.sync_all().unwrap(); 440 | linker::relayout(&mut out_elf, 0x300).unwrap(); 441 | out_elf.segments = linker::segments(&out_elf).unwrap(); 442 | out_elf.store_all().unwrap(); 443 | out_elf.to_writer(&mut out_file).unwrap(); 444 | 445 | let mut perms = out_file.metadata().unwrap().permissions(); 446 | perms.set_mode(0o755); 447 | out_file.set_permissions(perms).unwrap(); 448 | } 449 | -------------------------------------------------------------------------------- /bolter/ld.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::collections::HashSet; 3 | use std; 4 | use ::fail; 5 | use std::collections::hash_map::Entry; 6 | 7 | use elfkit::{ 8 | Elf, Header, types, SegmentHeader, Section, SectionContent, Error, 9 | SectionHeader, Dynamic, Symbol, Relocation, Strtab, SymbolSectionIndex}; 10 | 11 | #[derive(Clone, Debug, PartialEq, Eq)] 12 | pub enum LinkBehaviour { 13 | Static, 14 | Dynamic 15 | } 16 | impl Default for LinkBehaviour { 17 | fn default() -> LinkBehaviour { 18 | LinkBehaviour::Static 19 | } 20 | } 21 | 22 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] 23 | pub enum UnitSegment { 24 | Executable, 25 | Data, 26 | Bss, 27 | Tls, 28 | } 29 | impl Default for UnitSegment{ 30 | fn default() -> UnitSegment { 31 | UnitSegment::Executable 32 | } 33 | } 34 | 35 | #[derive(Default)] 36 | pub struct Unit { 37 | pub global_id: u64, 38 | pub name: String, 39 | pub behaviour: LinkBehaviour, 40 | pub segment: UnitSegment, 41 | pub code: Vec, 42 | pub symbols: Vec, 43 | pub relocations: Vec, 44 | pub deps: Vec, 45 | 46 | s_lookup: HashMap, 47 | } 48 | 49 | 50 | impl Unit { 51 | pub fn fake(name: String, behaviour: LinkBehaviour, symbols: Vec) -> Unit { 52 | 53 | let mut s_lookup = HashMap::new(); 54 | for (i, sym) in symbols.iter().enumerate() { 55 | s_lookup.insert(sym.name.clone(), i); 56 | } 57 | 58 | Unit { 59 | global_id: 0, 60 | name: name, 61 | behaviour: behaviour, 62 | segment: UnitSegment::Bss, 63 | code: vec![0;8], 64 | symbols: symbols, 65 | relocations:Vec::new(), 66 | s_lookup: s_lookup, 67 | deps: Vec::new(), 68 | } 69 | } 70 | 71 | 72 | pub fn lookup(&self, name: &str) -> Option<&Symbol> { 73 | match self.s_lookup.get(name) { 74 | None => None, 75 | Some(i) => self.symbols.get(*i), 76 | } 77 | } 78 | 79 | pub fn from_elf(name: String, mut elf: Elf, global_id_counter: &mut u64) -> Vec{ 80 | let behaviour = match elf.header.etype { 81 | types::ElfType::DYN => LinkBehaviour::Dynamic, 82 | _ => LinkBehaviour::Static, 83 | }; 84 | 85 | assert!(behaviour == LinkBehaviour::Static, 86 | format!("{}: linking to dynamic libraries is not implemented", name)); 87 | 88 | let mut sec2global = HashMap::new(); 89 | let mut units = HashMap::new(); 90 | let mut symbols = (0, Vec::new()); 91 | let mut relas = Vec::new(); 92 | 93 | for i in elf.sections.iter().enumerate().filter_map(|(i, ref sec)| { 94 | match sec.header.shtype { 95 | types::SectionType::SYMTAB | 96 | types::SectionType::DYNSYM | 97 | types::SectionType::RELA | 98 | types::SectionType::NOBITS | 99 | types::SectionType::PROGBITS => Some (i), 100 | _ => None, 101 | } 102 | }).collect::>().iter() { 103 | elf.load_at(*i).unwrap(); 104 | let sec = std::mem::replace(&mut elf.sections[*i], Section::default()); 105 | 106 | match sec.header.shtype { 107 | types::SectionType::PROGBITS | 108 | types::SectionType::NOBITS 109 | if sec.header.flags.contains(types::SectionFlags::ALLOC) => { 110 | *global_id_counter += 1; 111 | sec2global.insert(*i, *global_id_counter); 112 | 113 | units.insert(*i, Unit{ 114 | global_id: *global_id_counter, 115 | name: sec.name.clone() + "." + &name.clone(), 116 | behaviour: behaviour.clone(), 117 | segment: if sec.header.shtype == types::SectionType::NOBITS { 118 | UnitSegment::Bss 119 | } else if sec.header.flags.contains(types::SectionFlags::TLS) { 120 | UnitSegment::Tls 121 | } else if sec.header.flags.contains(types::SectionFlags::EXECINSTR) { 122 | UnitSegment::Executable 123 | } else { 124 | UnitSegment::Data 125 | }, 126 | code: if sec.header.shtype == types::SectionType::NOBITS { 127 | vec![0;sec.header.size as usize] 128 | } else { 129 | sec.content.into_raw().unwrap() 130 | }, 131 | symbols: Vec::new(), 132 | relocations: Vec::new(), 133 | s_lookup: HashMap::new(), 134 | deps: Vec::new(), 135 | }); 136 | }, 137 | types::SectionType::SYMTAB if behaviour == LinkBehaviour::Static => { 138 | symbols = (*i, sec.content.into_symbols().unwrap()); 139 | }, 140 | types::SectionType::DYNSYM if behaviour == LinkBehaviour::Dynamic => { 141 | symbols = (*i, sec.content.into_symbols().unwrap()); 142 | }, 143 | types::SectionType::RELA => { 144 | relas.push((sec.header, sec.content.into_relocations().unwrap())); 145 | }, 146 | _ => {}, 147 | } 148 | } 149 | 150 | for (obj_shndx, ref mut obj) in units.iter_mut() { 151 | let mut smap = HashMap::new(); 152 | 153 | // copy all symbols from symtab where .shndx is this obj 154 | for (i, sym) in symbols.1.iter().enumerate() { 155 | if sym.shndx == SymbolSectionIndex::Section(*obj_shndx as u16) { 156 | let mut sym = sym.clone(); 157 | sym.shndx = SymbolSectionIndex::Global(obj.global_id); 158 | smap.insert(i, obj.symbols.len()); 159 | obj.s_lookup.insert(sym.name.clone(), obj.symbols.len()); 160 | obj.symbols.push(sym); 161 | } 162 | } 163 | 164 | // for all refs where .info is this obj 165 | for &(ref header, ref relas) in &relas { 166 | if header.info == *obj_shndx as u32 { 167 | if header.link != symbols.0 as u32{ 168 | fail(format!("{}: reloc section {} references unexpected or duplicated symbols section", 169 | name, header.name)); 170 | } 171 | // also copy the rest of the symbols needed for reloc 172 | for rela in relas { 173 | if smap.get(&(rela.sym as usize)) == None { 174 | let mut sym = symbols.1[rela.sym as usize].clone(); 175 | 176 | // if it's a global or weak, undef it, 177 | // so it's actually looked up in other units 178 | 179 | if sym.bind != types::SymbolBind::LOCAL { 180 | sym.shndx = SymbolSectionIndex::Undefined; 181 | } else if let SymbolSectionIndex::Section(sx) = sym.shndx { 182 | sym.shndx = match sec2global.get(&(sx as usize)) { 183 | None => fail(format!("{} rela {:?} -> {:?} to section {} which is not allocated", 184 | obj.name, rela, sym, sx)), 185 | Some(id) => { 186 | obj.deps.push(*id); 187 | SymbolSectionIndex::Global(*id) 188 | }, 189 | }; 190 | }; 191 | smap.insert(rela.sym as usize, obj.symbols.len()); 192 | obj.s_lookup.insert(sym.name.clone(), obj.symbols.len()); 193 | obj.symbols.push(sym); 194 | } 195 | let mut rela = rela.clone(); 196 | rela.sym = *smap.get(&(rela.sym as usize)).unwrap() as u32; 197 | obj.relocations.push(rela); 198 | } 199 | } 200 | } 201 | 202 | } 203 | 204 | let mut units = units.into_iter().map(|(k,v)|v).collect::>(); 205 | 206 | 207 | //we can emit COMMON symbols as WEAK in a bss 208 | //because we're smart enough to only layout the bss that's actually used. 209 | for sym in symbols.1.iter() { 210 | if sym.shndx == SymbolSectionIndex::Common { 211 | *global_id_counter += 1; 212 | 213 | let symname = sym.name.clone(); 214 | let symsize = sym.size; 215 | let mut sym = sym.clone(); 216 | sym.value = 0; 217 | sym.shndx = SymbolSectionIndex::Global(*global_id_counter); 218 | sym.bind = types::SymbolBind::WEAK; 219 | let mut symbols = vec![sym]; 220 | let mut s_lookup = HashMap::new(); 221 | s_lookup.insert(symname.clone(), 0); 222 | 223 | assert!(behaviour == LinkBehaviour::Static, "cannot use common symbol with dynamic linking"); 224 | units.push(Unit{ 225 | global_id: *global_id_counter, 226 | name: String::from(".common.") + &symname, 227 | behaviour: behaviour.clone(), 228 | segment: UnitSegment::Bss, 229 | code: vec![0;symsize as usize], 230 | symbols: symbols, 231 | relocations: Vec::new(), 232 | s_lookup: s_lookup, 233 | deps: Vec::new(), 234 | }); 235 | } 236 | } 237 | 238 | //TODO we need to extract things like .init_array* , .preinit_array.* , ... 239 | //they have dependencies the opposite way. i.e a reloc on .init_array.bla will point into .bla, 240 | //because the function in .init_array.bla does stuff to memory in .bla 241 | 242 | 243 | 244 | // this is nessesary because coders assume that they can emit sections into objects which 245 | // will be made available for linking when some other section of that object gets linked in 246 | // this seems only relevant when that other section actually has symbols? hopefully 247 | // TODO: we can later remove those extra sections again should they not become part of the 248 | // link, but i'll delay implementing this until the lookup logic has a proper datastructure 249 | let deps: Vec = units.iter().filter_map(|unit|{ 250 | for sym in &unit.symbols { 251 | if sym.stype != types::SymbolType::SECTION && 252 | sym.shndx != SymbolSectionIndex::Undefined && 253 | sym.bind != types::SymbolBind::LOCAL { 254 | return Some(unit.global_id); 255 | } 256 | } 257 | None 258 | }).collect(); 259 | 260 | for unit in &mut units { 261 | unit.deps.extend(&deps); 262 | } 263 | 264 | units 265 | } 266 | } 267 | 268 | #[derive(Default)] 269 | pub struct Lookup { 270 | pub units: Vec, 271 | pub by_id: HashMap, 272 | 273 | pub symbols: Vec, 274 | pub by_name: HashMap, 275 | 276 | pub symbols2units: HashMap, 277 | } 278 | 279 | impl Lookup { 280 | pub fn get_by_name(&self, name: &str) -> Option<&Symbol> { 281 | match self.by_name.get(name) { 282 | Some(v) => self.symbols.get(*v), 283 | None => None, 284 | } 285 | } 286 | 287 | pub fn insert_unit(&mut self, unit: Unit) { 288 | let ui = self.units.len(); 289 | 290 | for sym in &unit.symbols { 291 | self.insert_symbol(sym.clone(), ui, &unit.name); 292 | } 293 | 294 | self.by_id.insert(unit.global_id.clone(), self.units.len()); 295 | self.units.push(unit); 296 | } 297 | 298 | pub fn reindex(&mut self) { 299 | self.by_id.clear(); 300 | for (i,unit) in self.units.iter().enumerate() { 301 | self.by_id.insert(unit.global_id.clone(), i); 302 | } 303 | } 304 | 305 | fn symbol_lookup_priority(s1: &Symbol, s2: &Symbol) -> usize { 306 | 307 | if s1.shndx == SymbolSectionIndex::Undefined { 308 | // we don't check if s2 is undefined here, because it won't matter. 309 | // if both are undefined, we can just pick a random one. 310 | return 2; 311 | } 312 | if s2.shndx == SymbolSectionIndex::Undefined { 313 | // we skip the rest of the checks here too. 314 | // any defition beats undefined 315 | return 1; 316 | } 317 | 318 | if s1.bind == types::SymbolBind::GLOBAL { 319 | if s2.bind == types::SymbolBind::GLOBAL { 320 | // can't have two defined global 321 | return 0; 322 | } else { 323 | return 1; 324 | } 325 | } 326 | 327 | // 1 wasn't global, so it can only be WEAK 328 | // 2 is either GLOBAL or WEAK, so it either beats 1 or it doesnt matter 329 | return 2; 330 | } 331 | 332 | 333 | fn insert_symbol(&mut self, sym: Symbol, unit_index: usize, obj_name: &str) -> usize { 334 | match sym.stype { 335 | types::SymbolType::NOTYPE | types::SymbolType::OBJECT | types::SymbolType::FUNC | types::SymbolType::TLS => { 336 | if sym.bind == types::SymbolBind::LOCAL { 337 | return 0; 338 | } 339 | match self.by_name.entry(sym.name.clone()) { 340 | Entry::Vacant(o) => { 341 | let i = self.symbols.len(); 342 | o.insert(i); 343 | self.symbols.push(sym); 344 | self.symbols2units.insert(i, unit_index); 345 | i 346 | }, 347 | Entry::Occupied(o) => { 348 | let sym2 = &mut self.symbols[*o.get()]; 349 | 350 | match Lookup::symbol_lookup_priority(&sym, sym2) { 351 | 1 => { 352 | std::mem::replace(sym2, sym); 353 | self.symbols2units.insert(*o.get(), unit_index); 354 | }, 355 | 2 => {}, 356 | _ => { 357 | fail(format!( 358 | "{}: re-export of symbol {:?} \n already defined in {} as {:?}", 359 | obj_name, sym, self.units[self.symbols2units[o.get()]].name, sym2)); 360 | } 361 | } 362 | *o.get() 363 | } 364 | } 365 | }, 366 | _ => {0}, 367 | } 368 | } 369 | 370 | 371 | pub fn link(&mut self, mut elfs: Vec<(String,Elf)>) { 372 | let mut global_id_counter = 10; 373 | let mut candidates = HashMap::new(); 374 | loop { 375 | println!("lookup iteration"); 376 | let missing = self.symbols.iter().enumerate().filter_map(|(i, ref sym)|{ 377 | if sym.shndx == SymbolSectionIndex::Undefined && sym.bind == types::SymbolBind::GLOBAL { 378 | Some(i) 379 | } else { 380 | None 381 | } 382 | }).collect::>(); 383 | 384 | if missing.len() < 1 { 385 | break; 386 | } 387 | 388 | for mi in missing { 389 | let mut found = None; 390 | let was_needed_by = self.units[self.symbols2units[&mi]].name.clone(); 391 | 392 | let mut cont = true; 393 | while cont { 394 | cont = false; 395 | for ei in 0..elfs.len() { 396 | let contains = match elfs[ei].1.contains_symbol(&self.symbols[mi].name) { 397 | Ok(v) => v, 398 | Err(e) => fail(format!("error in self in {} : {:?}", 399 | elfs[ei].0, e)), 400 | }; 401 | if contains { 402 | let elf = elfs.swap_remove(ei); 403 | for unit in Unit::from_elf(elf.0, elf.1, &mut global_id_counter) { 404 | candidates.insert(unit.global_id.clone(), unit); 405 | } 406 | cont = true; 407 | break; 408 | } 409 | } 410 | } 411 | 412 | 413 | for (id, candidate) in candidates.iter() { 414 | for sym in candidate.lookup(&self.symbols[mi].name) { 415 | if sym.shndx != SymbolSectionIndex::Undefined { 416 | found = Some(id.clone()); 417 | break; 418 | } 419 | } 420 | } 421 | 422 | if let Some(id) = found { 423 | let unit = candidates.remove(&id).unwrap(); 424 | self.resursive_insert(&mut candidates, unit, &mut HashSet::new()); 425 | 426 | println!(" - {} <= {} <= {} ", was_needed_by, 427 | &self.symbols[mi].name, 428 | self.units[self.symbols2units[&mi]].name, 429 | ); 430 | 431 | //integrity check 432 | if self.symbols[mi].shndx == SymbolSectionIndex::Undefined { 433 | panic!("BUG in elfkit lookup: symbol {:?} is still undefined after inserting unit where Unit:self() returned a defined symbol", 434 | self.symbols[mi]); 435 | } 436 | 437 | } else { 438 | let sym = &self.symbols[mi]; 439 | if sym.shndx == SymbolSectionIndex::Undefined && sym.bind == types::SymbolBind::GLOBAL { 440 | fail(format!("{}: undefined reference to {}", 441 | self.units[self.symbols2units[&mi]].name, 442 | self.symbols[mi].name)); 443 | } 444 | } 445 | } 446 | } 447 | } 448 | 449 | fn resursive_insert(&mut self, candidates: &mut HashMap, 450 | unit: Unit, promise_insert: &mut HashSet) { 451 | promise_insert.insert(unit.global_id); 452 | 453 | for id in &unit.deps { 454 | if *id != unit.global_id && !promise_insert.contains(id) && self.by_id.get(id) == None { 455 | match candidates.remove(&id) { 456 | Some(unit) => { 457 | self.resursive_insert(candidates, unit, promise_insert); 458 | }, 459 | None => panic!("bug in elfkit linker: {} dependant unit {} not found", unit.name, id), 460 | }; 461 | } 462 | } 463 | self.insert_unit(unit); 464 | } 465 | 466 | 467 | 468 | //FIXME this doesnt belong in Elf 469 | 470 | 471 | 472 | impl Elf { 473 | /// check if a global defined symbol is exported from the elf file. 474 | /// can be used to avoid load_all if a particular elf file doesn't 475 | /// contain the symbol you need anyway. 476 | /// It uses the cheapest possible method to determine the result 477 | /// which is currently loading symtab into a hashmap 478 | /// TODO should be replaced with checking HASH and GNU_HASH 479 | pub fn contains_symbol(&mut self, name: &str) -> Result { 480 | if None == self.s_lookup { 481 | let mut hm = HashSet::new(); 482 | 483 | for i in self.sections 484 | .iter() 485 | .enumerate() 486 | .filter_map(|(i, ref sec)| { 487 | if sec.header.shtype == types::SectionType::SYMTAB 488 | || sec.header.shtype == types::SectionType::DYNSYM 489 | { 490 | Some(i) 491 | } else { 492 | None 493 | } 494 | }) 495 | .collect::>() 496 | .iter() 497 | { 498 | self.load(*i)?; 499 | for sym in self.sections[*i].content.as_symbols().unwrap() { 500 | if sym.bind != types::SymbolBind::LOCAL 501 | && sym.shndx != SymbolSectionIndex::Undefined 502 | { 503 | hm.insert(sym.name.clone()); 504 | } 505 | } 506 | } 507 | 508 | self.s_lookup = Some(hm); 509 | } 510 | Ok(self.s_lookup.as_ref().unwrap().contains(name)) 511 | } 512 | } 513 | } 514 | 515 | 516 | 517 | --------------------------------------------------------------------------------