This is mostly for fun - I wanted to see how hard it would be to parse a binary with Go. Right now we use the same logic as objdump to load it, and then print Symbols (and I found an entry to where the Dwarf is).
🚧️ under development 🚧️
To build the gosmeagle binary, you can do:
$ make
You can also interact as follows:
$ go run main.go
Parsing means outputting a corpus to JSON.
$ go run main.go parse libtest.so
{"library":"libtest.so","functions":[{"name":"__printf_chk","type":"Function"},{"parameters":[{"type":"long int","size":8},{"type":"long int","size":8},{"type":"long int","size":8},{"type":"long int","size":8},{"type":"long int","size":8},{"type":"__int128","size":16}],"name":"bigcall","type":"Function"}]}
or print pretty:
$ go run main.go parse libtest.so --pretty
{
"library": "libtest.so",
"functions": [
{
"name": "__printf_chk",
"type": "Function"
},
{
"parameters": [
{
"type": "long int",
"size": 8
},
{
"type": "long int",
"size": 8
},
{
"type": "long int",
"size": 8
},
{
"type": "long int",
"size": 8
},
{
"type": "long int",
"size": 8
},
{
"type": "__int128",
"size": 16
}
],
"name": "bigcall",
"type": "Function"
}
]
}
Disassembling means printing Assembly.
$ go run main.go disasm libtest.so
TEXT register_tm_clones(SB)
TEXT __do_global_dtors_aux(SB)
TEXT frame_dummy(SB)
TEXT bigcall(SB)
0x1120 f3 ?
0x1121 0f ?
0x1122 1e ?
0x1123 fa CLI // cli
0x1124 4883ec10 SUBQ $0x10, SP // sub $0x10,%rsp
0x1128 4989c9 MOVQ CX, R9 // mov %rcx,%r9
0x112b 31c0 XORL AX, AX // xor %eax,%eax
0x112d 4889f1 MOVQ SI, CX // mov %rsi,%rcx
0x1130 ff742418 PUSHQ 0x18(SP) // pushq 0x18(%rsp)
0x1134 488d35c50e0000 LEAQ 0xec5(IP), SI // lea 0xec5(%rip),%rsi
0x113b ff742428 PUSHQ 0x28(SP) // pushq 0x28(%rsp)
0x113f 4150 PUSHL R8 // push %r8
0x1141 4989d0 MOVQ DX, R8 // mov %rdx,%r8
0x1144 4889fa MOVQ DI, DX // mov %rdi,%rdx
0x1147 bf01000000 MOVL $0x1, DI // mov $0x1,%edi
0x114c e8fffeffff CALL 0x1050 // callq 0x1050
0x1151 4883c428 ADDQ $0x28, SP // add $0x28,%rsp
0x1155 c3 RET // retq
Note that this library is under development, so stay tuned!
To test loading a json corpus, we have the load command:
$ go run main.go load example/smeagle-output.json
I started this library after discussion (see this thread) and wanting to extend Dwarf a bit and also reproduce Smeagle in Go.
Since I needed some functionality from debug/dwarf that was not public, the library is included here (with proper license/credit headers) in pkg/debug along with ELF that needs to return a matching type. The changes I made include
- renaming readType to ReadType so it's public.
- also renaming sigToType to SigToType so it's public
- made typeCache public (TypeCache)
- Added an "Original" (interface) to a CommonType, and then changed ReadType in dwarf/debug/type.go so that each case sets
t.Original = t
so we can return the original type to further parse (t.Common().Original
). - Added a StructCache to the dwarf.Data in pkg/debug/dwarf/open.go that is populated in pkg/debug/dwarf/type.go as follows:
// ADDED: save the struct to the struct cache for later lookup
d.StructCache[t.StructName] = t
And then used in parsers/x86_64/parse.go to match a typedef (which only has name and type string) to a fully parsed struct (a struct, union, or class).
You can also use docker:
$ docker build -t ghcr.io/vsoch/gosmeagle .
$ docker run -it -v $PWD:/data ghcr.io/vsoch/gosmeagle parse /data/libtest.so