Add Mach-O symbol table support
ober
3cf76c22754444a47fece151fa7df05bbb7cf9f4
--- a/docs/plan.md +++ b/docs/plan.md @@ -49,8 +49,9 @@ reference material and parity targets. - Raw CLI mode accepts byte input from `--hex` or `--file`. The reusable bytevector API supports `max-bytes` and `stop-address` limits. - `(jasm object macho)` can parse 64-bit Mach-O headers and `LC_SEGMENT_64` - section tables. The CLI detects ELF vs. Mach-O for `headers`, `sections`, and - executable-section disassembly. + section tables, plus `LC_SYMTAB` symbol tables. The CLI detects ELF vs. + Mach-O for `headers`, `sections`, `symbols`, and executable-section + disassembly. ## Core Architecture @@ -238,8 +239,8 @@ Required: - Universal/fat binaries. - Load commands. Initial `LC_SEGMENT_64` support is implemented. - Segments and sections. Initial parser is implemented. -- Symbol table. -- String table. +- Symbol table. Initial `LC_SYMTAB` parsing is implemented. +- String table. Initial `LC_SYMTAB` string table parsing is implemented. - Dysymtab. - Relocations. - ARM64 and x86-64 architecture mapping. Binary files a/jasm and b/jasm differ --- a/lib/jasm/object/macho.ss +++ b/lib/jasm/object/macho.ss @@ -10,6 +10,7 @@ macho-object-architecture macho-object-file-type macho-object-sections + macho-object-symbols macho-section? macho-section-index macho-section-segment @@ -23,24 +24,32 @@ macho-section-executable? macho-section-by-name macho-executable-sections - macho-section-bytes) + macho-section-bytes + macho-symbol? + macho-symbol-index + macho-symbol-name + macho-symbol-type + macho-symbol-section-index + macho-symbol-description + macho-symbol-value) (import (chezscheme)) - (define (make-macho-object endian cpu-type file-type sections path bytes) - (vector 'macho-object endian cpu-type file-type sections path bytes)) + (define (make-macho-object endian cpu-type file-type sections symbols path bytes) + (vector 'macho-object endian cpu-type file-type sections symbols path bytes)) (define (macho-object? x) (and (vector? x) - (= (vector-length x) 7) + (= (vector-length x) 8) (eq? (vector-ref x 0) 'macho-object))) (define (macho-object-endian obj) (vector-ref obj 1)) (define (macho-object-cpu-type obj) (vector-ref obj 2)) (define (macho-object-file-type obj) (vector-ref obj 3)) (define (macho-object-sections obj) (vector-ref obj 4)) - (define (macho-object-path obj) (vector-ref obj 5)) - (define (macho-object-bytes obj) (vector-ref obj 6)) + (define (macho-object-symbols obj) (vector-ref obj 5)) + (define (macho-object-path obj) (vector-ref obj 6)) + (define (macho-object-bytes obj) (vector-ref obj 7)) (define (make-macho-section index segment name flags address offset size align) (vector 'macho-section index segment name flags address offset size align)) @@ -59,6 +68,21 @@ (define (macho-section-size section) (vector-ref section 7)) (define (macho-section-align section) (vector-ref section 8)) + (define (make-macho-symbol index name type section-index description value) + (vector 'macho-symbol index name type section-index description value)) + + (define (macho-symbol? x) + (and (vector? x) + (= (vector-length x) 7) + (eq? (vector-ref x 0) 'macho-symbol))) + + (define (macho-symbol-index symbol) (vector-ref symbol 1)) + (define (macho-symbol-name symbol) (vector-ref symbol 2)) + (define (macho-symbol-type symbol) (vector-ref symbol 3)) + (define (macho-symbol-section-index symbol) (vector-ref symbol 4)) + (define (macho-symbol-description symbol) (vector-ref symbol 5)) + (define (macho-symbol-value symbol) (vector-ref symbol 6)) + (define (macho-section-full-name section) (string-append (macho-section-segment section) "," (macho-section-name section))) @@ -74,6 +98,14 @@ (require-range 'read-macho-bytevector bv offset 1) (bytevector-u8-ref bv offset)) + (define (u16 bv offset endian) + (require-range 'read-macho-bytevector bv offset 2) + (let ([b0 (u8 bv offset)] + [b1 (u8 bv (+ offset 1))]) + (if (eq? endian 'little) + (+ b0 (* b1 #x100)) + (+ (* b0 #x100) b1)))) + (define (u32 bv offset endian) (require-range 'read-macho-bytevector bv offset 4) (let ([b0 (u8 bv offset)] @@ -148,24 +180,61 @@ sections))))))) (define (parse-load-commands endian bv ncmds) - (let loop ([i 0] [offset 32] [section-index 0] [sections '()]) + (let loop ([i 0] [offset 32] [section-index 0] [sections '()] [symtab #f]) (if (= i ncmds) - (reverse sections) + (vector (reverse sections) symtab) (let* ([cmd (u32 bv offset endian)] [cmdsize (u32 bv (+ offset 4) endian)]) (require-range 'read-macho-bytevector bv offset cmdsize) - (if (= cmd #x19) - (let* ([result (parse-segment-64 endian bv offset section-index)] - [next-index (car result)] - [new-sections (cdr result)]) - (loop (+ i 1) - (+ offset cmdsize) - next-index - (append (reverse new-sections) sections))) - (loop (+ i 1) - (+ offset cmdsize) - section-index - sections)))))) + (cond + [(= cmd #x19) + (let* ([result (parse-segment-64 endian bv offset section-index)] + [next-index (car result)] + [new-sections (cdr result)]) + (loop (+ i 1) + (+ offset cmdsize) + next-index + (append (reverse new-sections) sections) + symtab))] + [(= cmd #x2) + (loop (+ i 1) + (+ offset cmdsize) + section-index + sections + (list (u32 bv (+ offset 8) endian) + (u32 bv (+ offset 12) endian) + (u32 bv (+ offset 16) endian) + (u32 bv (+ offset 20) endian)))] + [else + (loop (+ i 1) + (+ offset cmdsize) + section-index + sections + symtab)]))))) + + (define (parse-symbols endian bv symtab) + (if (not symtab) + '() + (let* ([symoff (list-ref symtab 0)] + [nsyms (list-ref symtab 1)] + [stroff (list-ref symtab 2)] + [strsize (list-ref symtab 3)] + [str-limit (+ stroff strsize)]) + (let loop ([index 0] [symbols '()]) + (if (= index nsyms) + (reverse symbols) + (let* ([offset (+ symoff (* index 16))] + [strx (u32 bv offset endian)] + [type (u8 bv (+ offset 4))] + [section-index (u8 bv (+ offset 5))] + [description (u16 bv (+ offset 6) endian)] + [value (u64 bv (+ offset 8) endian)] + [name (if (zero? strx) + "" + (read-fixed-string bv (+ stroff strx) (- str-limit (+ stroff strx))))]) + (loop (+ index 1) + (cons (make-macho-symbol index name type section-index description value) + symbols)))))))) (define (read-macho-bytevector bv . maybe-path) (let ([path (if (null? maybe-path) #f (car maybe-path))]) @@ -179,12 +248,15 @@ [cpu-type (u32 bv 4 endian)] [file-type (u32 bv 12 endian)] [ncmds (u32 bv 16 endian)] - [sizeofcmds (u32 bv 20 endian)]) + [sizeofcmds (u32 bv 20 endian)] + [command-info #f]) (require-range 'read-macho-bytevector bv 32 sizeofcmds) + (set! command-info (parse-load-commands endian bv ncmds)) (make-macho-object endian cpu-type file-type - (parse-load-commands endian bv ncmds) + (vector-ref command-info 0) + (parse-symbols endian bv (vector-ref command-info 1)) path bv)))) --- a/main.ss +++ b/main.ss @@ -202,6 +202,34 @@ (for-each (lambda (symbol) (print-symbol-row obj symbol)) (elf-object-symbols obj))) +(define (macho-symbol-section-name obj index) + (if (and (> index 0) (<= index (length (macho-object-sections obj)))) + (macho-section-full-name (list-ref (macho-object-sections obj) (- index 1))) + (number->string index))) + +(define (print-macho-symbol-row obj symbol) + (display "[") + (display (macho-symbol-index symbol)) + (display "] ") + (display (hex-number (macho-symbol-value symbol))) + (display "\ttype=") + (display (hex-number (macho-symbol-type symbol))) + (display "\tdesc=") + (display (macho-symbol-description symbol)) + (display "\t") + (display (macho-symbol-section-name obj (macho-symbol-section-index symbol))) + (display "\t") + (display (macho-symbol-name symbol)) + (newline)) + +(define (print-object-symbols obj) + (cond + [(elf-object? obj) (print-elf-symbols obj)] + [(macho-object? obj) + (for-each (lambda (symbol) (print-macho-symbol-row obj symbol)) + (macho-object-symbols obj))] + [else (error 'jasm "unsupported object record" obj)])) + (define (symbol-name-by-index obj index) (if (and (>= index 0) (< index (length (elf-object-symbols obj)))) (elf-symbol-name (list-ref (elf-object-symbols obj) index)) @@ -367,9 +395,7 @@ (define (parse-symbols args) (let ([obj (read-object-file (one-file args "symbols"))]) - (unless (elf-object? obj) - (error 'jasm "symbols are currently implemented for ELF objects")) - (print-elf-symbols obj))) + (print-object-symbols obj))) (define (parse-relocs args) (let ([obj (read-object-file (one-file args "relocs"))]) --- a/tests/test-jasm.ss +++ b/tests/test-jasm.ss @@ -113,6 +113,9 @@ (let ([bv (make-bytevector #x200 0)]) (define (put8! offset value) (bytevector-u8-set! bv offset (modulo value #x100))) + (define (put16le! offset value) + (put8! offset value) + (put8! (+ offset 1) (quotient value #x100))) (define (put32le! offset value) (put8! offset value) (put8! (+ offset 1) (quotient value #x100)) @@ -131,8 +134,8 @@ (put32le! 4 #x0100000c) (put32le! 8 0) (put32le! 12 1) - (put32le! 16 1) - (put32le! 20 152) + (put32le! 16 2) + (put32le! 20 176) (put32le! 24 0) (put32le! 28 0) (put32le! #x20 #x19) @@ -155,7 +158,19 @@ (put32le! #xa0 0) (put32le! #xa4 0) (put32le! #xa8 #x80000400) + (put32le! #xb8 #x2) + (put32le! #xbc 24) + (put32le! #xc0 #x120) + (put32le! #xc4 1) + (put32le! #xc8 #x140) + (put32le! #xcc 7) (put-ascii! #x100 (string #\xc0 #\x03 #\x5f #\xd6)) + (put32le! #x120 1) + (put8! #x124 #xf) + (put8! #x125 1) + (put16le! #x126 0) + (put64le! #x128 #x1000) + (put-ascii! #x140 (string #\nul #\_ #\f #\u #\n #\c #\nul)) bv)) (printf "--- jasm tests ---~%") @@ -259,6 +274,17 @@ (bytevector->u8-list (macho-section-bytes obj text))) '(192 3 95 214)) +(test "mach-o symbols" + (let* ([obj (read-macho-bytevector (sample-macho64le))] + [symbol (car (macho-object-symbols obj))]) + (list (length (macho-object-symbols obj)) + (macho-symbol? symbol) + (macho-symbol-name symbol) + (macho-symbol-type symbol) + (macho-symbol-section-index symbol) + (macho-symbol-value symbol))) + '(1 #t "_func" 15 1 4096)) + (test "instruction records are vector-backed" (let ([insn (car (disassemble-bytevector 'x86-64 (hex-string->bytevector "55")))]) (list (instruction? insn)