Add ELF symbol table support
ober
0cbce05f57d8f7a6a489dc4472afde88e200d275
--- a/Makefile +++ b/Makefile @@ -27,6 +27,7 @@ test-binary: binary $(SCHEME) --libdirs $(LIBDIRS) --script tests/write-sample-elf.ss ./.jasm-sample.o ./$(BINARY) headers ./.jasm-sample.o ./$(BINARY) sections ./.jasm-sample.o + ./$(BINARY) symbols ./.jasm-sample.o ./$(BINARY) object --section .text --syntax intel ./.jasm-sample.o rm -f ./.jasm-sample.o --- a/docs/plan.md +++ b/docs/plan.md @@ -41,10 +41,11 @@ reference material and parity targets. - Tests cover the initial decoder slice. - `(jasm object elf)` can parse ELF32/ELF64 headers and section tables from a bytevector or file, map supported machines to decoder architectures, discover - executable sections, and return section bytes. + executable sections, return section bytes, and parse `.symtab`/`.dynsym` + symbol entries. - The `jasm` binary has initial ELF-oriented `object`, `headers`, and - `sections` commands. `object` disassembles executable sections or a selected - section by name. + `sections`, and `symbols` commands. `object` disassembles executable sections + or a selected section by name. ## Core Architecture @@ -149,7 +150,7 @@ Initial commands: - `object` (implemented for initial ELF executable-section disassembly) - `headers` (implemented for initial ELF metadata) - `sections` (implemented for initial ELF section tables) -- `symbols` +- `symbols` (implemented for initial ELF symbol tables) - `relocs` Common options: @@ -202,7 +203,7 @@ Required: - Program header table. - String tables. - Section-name string table. Initial parser is implemented. -- Symbol tables: `.symtab`, `.dynsym`. +- Symbol tables: `.symtab`, `.dynsym`. Initial parser is implemented. - Relocations: - `SHT_REL` - `SHT_RELA` Binary files a/jasm and b/jasm differ --- a/lib/jasm/object/elf.ss +++ b/lib/jasm/object/elf.ss @@ -12,6 +12,7 @@ elf-object-architecture elf-object-entry elf-object-sections + elf-object-symbols elf-section? elf-section-index elf-section-name @@ -27,16 +28,26 @@ elf-section-executable? elf-section-by-name elf-executable-sections - elf-section-bytes) + elf-section-bytes + elf-symbol? + elf-symbol-index + elf-symbol-name + elf-symbol-value + elf-symbol-size + elf-symbol-binding + elf-symbol-type + elf-symbol-visibility + elf-symbol-section-index + elf-symbol-table-section) (import (chezscheme)) - (define (make-elf-object class endian machine entry sections path bytes) - (vector 'elf-object class endian machine entry sections path bytes)) + (define (make-elf-object class endian machine entry sections symbols path bytes) + (vector 'elf-object class endian machine entry sections symbols path bytes)) (define (elf-object? x) (and (vector? x) - (= (vector-length x) 8) + (= (vector-length x) 9) (eq? (vector-ref x 0) 'elf-object))) (define (elf-object-class obj) (vector-ref obj 1)) @@ -44,8 +55,9 @@ (define (elf-object-machine obj) (vector-ref obj 3)) (define (elf-object-entry obj) (vector-ref obj 4)) (define (elf-object-sections obj) (vector-ref obj 5)) - (define (elf-object-path obj) (vector-ref obj 6)) - (define (elf-object-bytes obj) (vector-ref obj 7)) + (define (elf-object-symbols obj) (vector-ref obj 6)) + (define (elf-object-path obj) (vector-ref obj 7)) + (define (elf-object-bytes obj) (vector-ref obj 8)) (define (make-elf-section index name type flags address offset size align entry-size link info) (vector 'elf-section index name type flags address offset size align entry-size link info)) @@ -67,6 +79,24 @@ (define (elf-section-link section) (vector-ref section 10)) (define (elf-section-info section) (vector-ref section 11)) + (define (make-elf-symbol index name value size binding type visibility section-index table-section) + (vector 'elf-symbol index name value size binding type visibility section-index table-section)) + + (define (elf-symbol? x) + (and (vector? x) + (= (vector-length x) 10) + (eq? (vector-ref x 0) 'elf-symbol))) + + (define (elf-symbol-index symbol) (vector-ref symbol 1)) + (define (elf-symbol-name symbol) (vector-ref symbol 2)) + (define (elf-symbol-value symbol) (vector-ref symbol 3)) + (define (elf-symbol-size symbol) (vector-ref symbol 4)) + (define (elf-symbol-binding symbol) (vector-ref symbol 5)) + (define (elf-symbol-type symbol) (vector-ref symbol 6)) + (define (elf-symbol-visibility symbol) (vector-ref symbol 7)) + (define (elf-symbol-section-index symbol) (vector-ref symbol 8)) + (define (elf-symbol-table-section symbol) (vector-ref symbol 9)) + (define (bv-len bv) (bytevector-length bv)) (define (require-range who bv offset size) @@ -256,6 +286,115 @@ (raw-section-info raw))]) (loop (+ index 1) (cdr raws) (cons section sections))))))) + (define (symbol-table-section? section) + (or (= (elf-section-type section) 2) + (= (elf-section-type section) 11))) + + (define (symbol-binding-name n) + (case n + [(0) 'local] + [(1) 'global] + [(2) 'weak] + [(10) 'loos] + [(12) 'hios] + [(13) 'loproc] + [(15) 'hiproc] + [else n])) + + (define (symbol-type-name n) + (case n + [(0) 'notype] + [(1) 'object] + [(2) 'func] + [(3) 'section] + [(4) 'file] + [(5) 'common] + [(6) 'tls] + [(10) 'loos] + [(12) 'hios] + [(13) 'loproc] + [(15) 'hiproc] + [else n])) + + (define (symbol-visibility-name n) + (case n + [(0) 'default] + [(1) 'internal] + [(2) 'hidden] + [(3) 'protected] + [else n])) + + (define (parse-symbol-entry class endian bv offset) + (if (eq? class 'elf64) + (let* ([name-offset (u32 bv offset endian)] + [info (u8 bv (+ offset 4))] + [other (u8 bv (+ offset 5))] + [section-index (u16 bv (+ offset 6) endian)] + [value (u64 bv (+ offset 8) endian)] + [size (u64 bv (+ offset 16) endian)]) + (list name-offset info other section-index value size)) + (let* ([name-offset (u32 bv offset endian)] + [value (u32 bv (+ offset 4) endian)] + [size (u32 bv (+ offset 8) endian)] + [info (u8 bv (+ offset 12))] + [other (u8 bv (+ offset 13))] + [section-index (u16 bv (+ offset 14) endian)]) + (list name-offset info other section-index value size)))) + + (define (parse-symbols-for-section class endian bv sections table-section) + (let* ([entry-size (if (zero? (elf-section-entry-size table-section)) + (if (eq? class 'elf64) 24 16) + (elf-section-entry-size table-section))] + [count (quotient (elf-section-size table-section) entry-size)] + [string-section (list-ref/default sections (elf-section-link table-section) #f)] + [string-bounds (if string-section + (cons (elf-section-offset string-section) + (+ (elf-section-offset string-section) + (elf-section-size string-section))) + (cons 0 0))]) + (let loop ([index 0] [symbols '()]) + (if (= index count) + (reverse symbols) + (let* ([entry-offset (+ (elf-section-offset table-section) + (* index entry-size))] + [entry (parse-symbol-entry class endian bv entry-offset)] + [name-offset (list-ref entry 0)] + [info (list-ref entry 1)] + [other (list-ref entry 2)] + [section-index (list-ref entry 3)] + [value (list-ref entry 4)] + [size (list-ref entry 5)] + [binding (symbol-binding-name (quotient info 16))] + [type (symbol-type-name (modulo info 16))] + [visibility (symbol-visibility-name (bitwise-and other #x3))] + [name (if (or (zero? name-offset) (not string-section)) + "" + (read-c-string bv + (+ (car string-bounds) name-offset) + (cdr string-bounds)))]) + (loop (+ index 1) + (cons (make-elf-symbol index + name + value + size + binding + type + visibility + section-index + (elf-section-name table-section)) + symbols))))))) + + (define (parse-symbols class endian bv sections) + (let ([all-sections sections]) + (let loop ([remaining sections] [symbols '()]) + (cond + [(null? remaining) (reverse symbols)] + [(symbol-table-section? (car remaining)) + (loop (cdr remaining) + (append (reverse (parse-symbols-for-section class endian bv all-sections (car remaining))) + symbols))] + [else (loop (cdr remaining) symbols)])))) + (define (read-elf-bytevector bv . maybe-path) (let ([path (if (null? maybe-path) #f (car maybe-path))]) (require-range 'read-elf-bytevector bv 0 64) @@ -275,8 +414,9 @@ [raw-sections (if (zero? shnum) '() (read-raw-section-headers class endian bv shoff shentsize shnum))] - [sections (materialize-sections bv raw-sections shstrndx)]) - (make-elf-object class endian machine entry sections path bv)))) + [sections (materialize-sections bv raw-sections shstrndx)] + [symbols (parse-symbols class endian bv sections)]) + (make-elf-object class endian machine entry sections symbols path bv)))) (define (read-elf-file path) (read-elf-bytevector (read-file-bytevector path) path)) --- a/main.ss +++ b/main.ss @@ -8,6 +8,7 @@ (display " jasm object [--arch <arch>] [--syntax att|intel] [--section name] <file>\n") (display " jasm headers <file>\n") (display " jasm sections <file>\n") + (display " jasm symbols <file>\n") (display "\nExamples:\n") (display " jasm raw --arch x86-64 --syntax intel --hex '55 48 89 e5 c3'\n") (display " jasm raw --arch aarch64 --hex '20 00 80 d2 c0 03 5f d6'\n") @@ -96,6 +97,34 @@ (define (print-elf-sections obj) (for-each print-section-row (elf-object-sections obj))) +(define (section-name-by-index obj index) + (if (and (>= index 0) (< index (length (elf-object-sections obj)))) + (elf-section-name (list-ref (elf-object-sections obj) index)) + (number->string index))) + +(define (print-symbol-row obj symbol) + (display "[") + (display (elf-symbol-index symbol)) + (display "] ") + (display (hex-number (elf-symbol-value symbol))) + (display "\tsize=") + (display (elf-symbol-size symbol)) + (display "\t") + (display (elf-symbol-binding symbol)) + (display "\t") + (display (elf-symbol-type symbol)) + (display "\t") + (display (elf-symbol-visibility symbol)) + (display "\t") + (display (section-name-by-index obj (elf-symbol-section-index symbol))) + (display "\t") + (display (elf-symbol-name symbol)) + (newline)) + +(define (print-elf-symbols obj) + (for-each (lambda (symbol) (print-symbol-row obj symbol)) + (elf-object-symbols obj))) + (define (one-file args command-name) (cond [(null? args) (error 'jasm (string-append command-name " requires a file"))] @@ -180,6 +209,9 @@ (define (parse-sections args) (print-elf-sections (read-elf-file (one-file args "sections")))) +(define (parse-symbols args) + (print-elf-symbols (read-elf-file (one-file args "symbols")))) + (define (main args) (guard (exn [#t (display "jasm: " (current-error-port)) @@ -201,6 +233,10 @@ (parse-headers (cdr args))] [(string=? (car args) "sections") (parse-sections (cdr args))] + [(or (string=? (car args) "symbols") + (string=? (car args) "--syms") + (string=? (car args) "-t")) + (parse-symbols (cdr args))] [else (usage) (exit 1)]))) --- a/tests/test-jasm.ss +++ b/tests/test-jasm.ss @@ -27,7 +27,7 @@ (instruction-text (car insns)))) (define (sample-elf64le) - (let ([bv (make-bytevector #x200 0)]) + (let ([bv (make-bytevector #x300 0)]) (define (put8! offset value) (bytevector-u8-set! bv offset (modulo value #x100))) (define (put16le! offset value) @@ -58,6 +58,13 @@ (put32le! (+ offset 44) info) (put64le! (+ offset 48) align) (put64le! (+ offset 56) entry-size)) + (define (put-sym64! offset name info other section-index value size) + (put32le! offset name) + (put8! (+ offset 4) info) + (put8! (+ offset 5) other) + (put16le! (+ offset 6) section-index) + (put64le! (+ offset 8) value) + (put64le! (+ offset 16) size)) (put8! 0 #x7f) (put-ascii! 1 "ELF") (put8! 4 2) @@ -74,13 +81,17 @@ (put16le! 54 0) (put16le! 56 0) (put16le! 58 64) - (put16le! 60 3) + (put16le! 60 5) (put16le! 62 2) (put-ascii! #x80 (string #\x55 #\xc3 #\x90 #\x90 #\xc3)) - (put-ascii! #x90 (string #\nul #\. #\t #\e #\x #\t #\nul #\. #\s #\h #\s #\t #\r #\t #\a #\b #\nul)) + (put-ascii! #x90 (string #\nul #\. #\t #\e #\x #\t #\nul #\. #\s #\h #\s #\t #\r #\t #\a #\b #\nul #\. #\s #\t #\r #\t #\a #\b #\nul #\. #\s #\y #\m #\t #\a #\b #\nul)) + (put-ascii! #xb0 (string #\nul #\f #\u #\n #\c #\nul)) + (put-sym64! #x238 1 #x12 0 1 #x1000 5) (put-shdr64! #xc0 0 0 0 0 0 0 0 0 0 0) (put-shdr64! #x100 1 1 #x6 #x1000 #x80 5 0 0 16 0) - (put-shdr64! #x140 7 3 0 0 #x90 17 0 0 1 0) + (put-shdr64! #x140 7 3 0 0 #x90 33 0 0 1 0) + (put-shdr64! #x180 17 3 0 0 #xb0 6 0 0 1 0) + (put-shdr64! #x1c0 25 2 0 0 #x220 48 3 1 8 24) bv)) (define (write-test-bytevector-file path bv) @@ -127,7 +138,7 @@ (elf-section-address text) (elf-section-executable? text) (map elf-section-name (elf-executable-sections obj)))) - '(3 #t 1 4096 #t (".text"))) + '(5 #t 1 4096 #t (".text"))) (test "elf section bytes" (let* ([obj (read-elf-bytevector (sample-elf64le))] @@ -135,6 +146,20 @@ (bytevector->u8-list (elf-section-bytes obj text))) '(85 195 144 144 195)) +(test "elf symbols" + (let* ([obj (read-elf-bytevector (sample-elf64le))] + [symbol (cadr (elf-object-symbols obj))]) + (list (length (elf-object-symbols obj)) + (elf-symbol? symbol) + (elf-symbol-name symbol) + (elf-symbol-binding symbol) + (elf-symbol-type symbol) + (elf-symbol-value symbol) + (elf-symbol-size symbol) + (elf-symbol-section-index symbol) + (elf-symbol-table-section symbol))) + '(2 #t "func" global func 4096 5 1 ".symtab")) + (test "elf file reader" (let ([path ".jasm-test-sample.o"]) (write-test-bytevector-file path (sample-elf64le)) --- a/tests/write-sample-elf.ss +++ b/tests/write-sample-elf.ss @@ -3,7 +3,7 @@ (import (chezscheme)) (define (sample-elf64le) - (let ([bv (make-bytevector #x200 0)]) + (let ([bv (make-bytevector #x300 0)]) (define (put8! offset value) (bytevector-u8-set! bv offset (modulo value #x100))) (define (put16le! offset value) @@ -34,6 +34,13 @@ (put32le! (+ offset 44) info) (put64le! (+ offset 48) align) (put64le! (+ offset 56) entry-size)) + (define (put-sym64! offset name info other section-index value size) + (put32le! offset name) + (put8! (+ offset 4) info) + (put8! (+ offset 5) other) + (put16le! (+ offset 6) section-index) + (put64le! (+ offset 8) value) + (put64le! (+ offset 16) size)) (put8! 0 #x7f) (put-ascii! 1 "ELF") (put8! 4 2) @@ -50,13 +57,17 @@ (put16le! 54 0) (put16le! 56 0) (put16le! 58 64) - (put16le! 60 3) + (put16le! 60 5) (put16le! 62 2) (put-ascii! #x80 (string #\x55 #\xc3 #\x90 #\x90 #\xc3)) - (put-ascii! #x90 (string #\nul #\. #\t #\e #\x #\t #\nul #\. #\s #\h #\s #\t #\r #\t #\a #\b #\nul)) + (put-ascii! #x90 (string #\nul #\. #\t #\e #\x #\t #\nul #\. #\s #\h #\s #\t #\r #\t #\a #\b #\nul #\. #\s #\t #\r #\t #\a #\b #\nul #\. #\s #\y #\m #\t #\a #\b #\nul)) + (put-ascii! #xb0 (string #\nul #\f #\u #\n #\c #\nul)) + (put-sym64! #x238 1 #x12 0 1 #x1000 5) (put-shdr64! #xc0 0 0 0 0 0 0 0 0 0 0) (put-shdr64! #x100 1 1 #x6 #x1000 #x80 5 0 0 16 0) - (put-shdr64! #x140 7 3 0 0 #x90 17 0 0 1 0) + (put-shdr64! #x140 7 3 0 0 #x90 33 0 0 1 0) + (put-shdr64! #x180 17 3 0 0 #xb0 6 0 0 1 0) + (put-shdr64! #x1c0 25 2 0 0 #x220 48 3 1 8 24) bv)) (define (write-bytevector-file path bv)