diff --git a/format/all/all.go b/format/all/all.go index d791b36c3..ecd6dea94 100644 --- a/format/all/all.go +++ b/format/all/all.go @@ -24,6 +24,7 @@ import ( _ "github.com/wader/fq/format/icc" _ "github.com/wader/fq/format/id3" _ "github.com/wader/fq/format/inet" + _ "github.com/wader/fq/format/isa" _ "github.com/wader/fq/format/jpeg" _ "github.com/wader/fq/format/json" _ "github.com/wader/fq/format/macho" diff --git a/format/elf/elf.go b/format/elf/elf.go index eeb9e5e7d..caf3bb118 100644 --- a/format/elf/elf.go +++ b/format/elf/elf.go @@ -16,12 +16,20 @@ import ( "github.com/wader/fq/pkg/scalar" ) +var x86_64Format decode.Group +var arm64Format decode.Group + func init() { interp.RegisterFormat(decode.Format{ Name: format.ELF, Description: "Executable and Linkable Format", Groups: []string{format.PROBE}, DecodeFn: elfDecode, + Dependencies: []decode.Dependency{ + // TODO: x86_32? + {Names: []string{format.X86_64}, Group: &x86_64Format}, + {Names: []string{format.ARM64}, Group: &arm64Format}, + }, }) } @@ -131,6 +139,15 @@ var machineNames = scalar.UToScalar{ 0x101: {Sym: "wdc_65C816", Description: "WDC 65C816"}, } +var machineToFormatFn = map[int]func(d *decode.D, base uint64, symLookup func(uint64) (string, uint64)){ + EM_X86_64: func(d *decode.D, base uint64, symLookup func(uint64) (string, uint64)) { + d.Format(x86_64Format, format.X86_64In{Base: int64(base), SymLookup: symLookup}) + }, + EM_ARM64: func(d *decode.D, base uint64, symLookup func(uint64) (string, uint64)) { + d.Format(arm64Format, format.ARM64In{Base: int64(base), SymLookup: symLookup}) + }, +} + var phTypeNames = scalar.URangeToScalar{ {Range: [2]uint64{0x00000000, 0x00000000}, S: scalar.S{Sym: "null", Description: "Unused element"}}, {Range: [2]uint64{0x00000001, 0x00000001}, S: scalar.S{Sym: "load", Description: "Loadable segment"}}, @@ -793,6 +810,8 @@ func elfDecodeDynamicTags(d *decode.D, ec elfContext, dc dynamicContext) { } func elfDecodeSectionHeader(d *decode.D, ec elfContext, sh sectionHeader) { + var execInstr bool + shFlags := func(d *decode.D, archBits int) { d.FieldStruct("flags", func(d *decode.D) { if d.Endian == decode.LittleEndian { @@ -801,7 +820,7 @@ func elfDecodeSectionHeader(d *decode.D, ec elfContext, sh sectionHeader) { d.FieldBool("strings") d.FieldBool("merge") d.FieldU1("unused0") - d.FieldBool("execinstr") + execInstr = d.FieldBool("execinstr") d.FieldBool("alloc") d.FieldBool("write") d.FieldBool("tls") @@ -831,13 +850,14 @@ func elfDecodeSectionHeader(d *decode.D, ec elfContext, sh sectionHeader) { d.FieldBool("strings") d.FieldBool("merge") d.FieldU1("unused2") - d.FieldBool("execinstr") + execInstr = d.FieldBool("execinstr") d.FieldBool("alloc") d.FieldBool("write") } }) } + var addr uint64 var offset int64 var size int64 var entSize int64 @@ -848,7 +868,7 @@ func elfDecodeSectionHeader(d *decode.D, ec elfContext, sh sectionHeader) { d.FieldU32("name", strTable(ec.strTabMap[STRTAB_SHSTRTAB])) typ = d.FieldU32("type", sectionHeaderTypeMap, scalar.ActualHex) shFlags(d, ec.archBits) - d.FieldU("addr", ec.archBits, scalar.ActualHex) + addr = d.FieldU("addr", ec.archBits, scalar.ActualHex) offset = int64(d.FieldU("offset", ec.archBits)) * 8 size = int64(d.FieldU32("size", scalar.ActualHex) * 8) d.FieldU32("link") @@ -859,7 +879,7 @@ func elfDecodeSectionHeader(d *decode.D, ec elfContext, sh sectionHeader) { d.FieldU32("name", strTable(ec.strTabMap[STRTAB_SHSTRTAB])) typ = d.FieldU32("type", sectionHeaderTypeMap, scalar.ActualHex) shFlags(d, ec.archBits) - d.FieldU("addr", ec.archBits, scalar.ActualHex) + addr = d.FieldU("addr", ec.archBits, scalar.ActualHex) offset = int64(d.FieldU("offset", ec.archBits, scalar.ActualHex) * 8) size = int64(d.FieldU64("size") * 8) d.FieldU32("link") @@ -892,9 +912,34 @@ func elfDecodeSectionHeader(d *decode.D, ec elfContext, sh sectionHeader) { elfDecodeSymbolTable(d, ec, int(size/entSize), ec.strTabMap[STRTAB_DYNSTR]) }) case SHT_PROGBITS: - // TODO: name progbits? - // TODO: decode opcodes - d.FieldRawLen("data", size) + // TODO: verify this, seems to result in strange relative addresses + symLookup := func(symAddr uint64) (string, uint64) { + var best *symbol + + for _, sh := range ec.sections { + for i, s := range sh.symbols { + if symAddr >= s.value && (best == nil || symAddr-s.value < best.value-s.value) { + best = &sh.symbols[i] + } + } + } + if best == nil { + return "", 0 + } + return strIndexNull(int(best.name), ec.strTabMap[STRTAB_STRTAB]), best.value + } + + // TODO: name progbits? instructions? + if fn, ok := machineToFormatFn[ec.machine]; execInstr && ok { + d.FieldArray("code", func(d *decode.D) { + d.FramedFn(size, func(d *decode.D) { + fn(d, addr, symLookup) + }) + }) + } else { + d.FieldRawLen("data", size) + } + case SHT_GNU_HASH: d.FieldStruct("gnu_hash", func(d *decode.D) { elfDecodeGNUHash(d, ec, size, ec.strTabMap[STRTAB_DYNSTR]) diff --git a/format/format.go b/format/format.go index 46b95adee..8196ae871 100644 --- a/format/format.go +++ b/format/format.go @@ -26,6 +26,7 @@ const ( AMF0 = "amf0" APEV2 = "apev2" AR = "ar" + ARM64 = "arm64" ASN1_BER = "asn1_ber" AV1_CCR = "av1_ccr" AV1_FRAME = "av1_frame" @@ -119,6 +120,9 @@ const ( VPX_CCR = "vpx_ccr" WAV = "wav" WEBP = "webp" + X86_16 = "x86_16" + X86_32 = "x86_32" + X86_64 = "x86_64" XING = "xing" XML = "xml" YAML = "yaml" @@ -302,3 +306,13 @@ type CSVLIn struct { Comma string `doc:"Separator character"` Comment string `doc:"Comment line character"` } + +type X86_64In struct { + SymLookup func(symAddr uint64) (string, uint64) + Base int64 +} + +type ARM64In struct { + SymLookup func(symAddr uint64) (string, uint64) + Base int64 +} diff --git a/format/isa/arm64.go b/format/isa/arm64.go new file mode 100644 index 000000000..955acba8b --- /dev/null +++ b/format/isa/arm64.go @@ -0,0 +1,55 @@ +package isa + +import ( + "strings" + + "github.com/wader/fq/format" + "github.com/wader/fq/pkg/decode" + "github.com/wader/fq/pkg/interp" + "github.com/wader/fq/pkg/scalar" + "golang.org/x/arch/arm64/arm64asm" +) + +func init() { + interp.RegisterFormat(decode.Format{ + Name: format.ARM64, + Description: "ARM64 instructions", + DecodeFn: decodeARM64, + RootArray: true, + RootName: "instructions", + }) +} + +func decodeARM64(d *decode.D, in interface{}) interface{} { + var symLookup func(uint64) (string, uint64) + var base int64 + if xi, ok := in.(format.ARM64In); ok { + symLookup = xi.SymLookup + base = xi.Base + } + + bb := d.BytesRange(0, int(d.BitsLeft()/8)) + // TODO: uint64? + pc := base + + for !d.End() { + d.FieldStruct("instruction", func(d *decode.D) { + i, err := arm64asm.Decode(bb) + if err != nil { + d.Fatalf("failed to decode arm64 instruction: %s", err) + } + + // TODO: other syntax + d.FieldRawLen("opcode", int64(4)*8, scalar.Sym(arm64asm.GoSyntax(i, uint64(pc), symLookup, nil)), scalar.ActualHex) + + // TODO: Enc? + d.FieldValueU("op", uint64(i.Enc), scalar.Sym(strings.ToLower(i.Op.String())), scalar.ActualHex) + + bb = bb[4:] + pc += int64(4) + }) + + } + + return nil +} diff --git a/format/isa/x86_64.go b/format/isa/x86_64.go new file mode 100644 index 000000000..654f7bad6 --- /dev/null +++ b/format/isa/x86_64.go @@ -0,0 +1,73 @@ +package isa + +import ( + "strings" + + "github.com/wader/fq/format" + "github.com/wader/fq/pkg/decode" + "github.com/wader/fq/pkg/interp" + "github.com/wader/fq/pkg/scalar" + "golang.org/x/arch/x86/x86asm" +) + +func init() { + // amd64? + interp.RegisterFormat(decode.Format{ + Name: format.X86_64, + Description: "x86-64 instructions", + DecodeFn: func(d *decode.D, in interface{}) interface{} { return decodeX86(d, in, 64) }, + RootArray: true, + RootName: "instructions", + }) + interp.RegisterFormat(decode.Format{ + Name: format.X86_32, + Description: "x86-32 instructions", + DecodeFn: func(d *decode.D, in interface{}) interface{} { return decodeX86(d, in, 32) }, + RootArray: true, + RootName: "instructions", + }) + interp.RegisterFormat(decode.Format{ + Name: format.X86_16, + Description: "x86-16 instructions", + DecodeFn: func(d *decode.D, in interface{}) interface{} { return decodeX86(d, in, 16) }, + RootArray: true, + RootName: "instructions", + }) +} + +func decodeX86(d *decode.D, in interface{}, mode int) interface{} { + var symLookup func(uint64) (string, uint64) + var base int64 + if xi, ok := in.(format.X86_64In); ok { + symLookup = xi.SymLookup + base = xi.Base + } + + bb := d.BytesRange(0, int(d.BitsLeft()/8)) + // TODO: uint64? + pc := base + + for !d.End() { + d.FieldStruct("instruction", func(d *decode.D) { + i, err := x86asm.Decode(bb, mode) + if err != nil { + d.Fatalf("failed to decode x86 instruction: %s", err) + } + + d.FieldRawLen("opcode", int64(i.Len)*8, scalar.Sym(x86asm.IntelSyntax(i, uint64(pc), symLookup)), scalar.ActualHex) + + // log.Printf("i.Len: %#+v\n", i.Len) + // log.Printf("i.Opcode: %x\n", i.Opcode) + // log.Printf("i: %#+v\n", i) + + // TODO: rebuild op lower? + d.FieldValueU("op", uint64(i.Opcode), scalar.Sym(strings.ToLower(i.Op.String())), scalar.ActualHex) + + bb = bb[i.Len:] + pc += int64(i.Len) + }) + + } + + return nil +} diff --git a/go.mod b/go.mod index 5a340f562..eb7267917 100644 --- a/go.mod +++ b/go.mod @@ -54,6 +54,8 @@ require ( gopkg.in/yaml.v3 v3.0.1 ) +require golang.org/x/arch v0.0.0-20220401014709-5424468ecbac + require ( github.com/itchyny/timefmt-go v0.1.3 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect diff --git a/go.sum b/go.sum index bfaf1a71d..759e4f83e 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,8 @@ github.com/wader/gojq v0.12.1-0.20220703094036-0eed2734a1d7 h1:3IQ6iYU/tkMcEpYu6 github.com/wader/gojq v0.12.1-0.20220703094036-0eed2734a1d7/go.mod h1:HM2cB+ANeJ4kBhxQp/4cNKewKjvYHACuCpBneQBgHkg= github.com/wader/readline v0.0.0-20220704090837-31be50517a56 h1:MEvdJFQfJD9D5nH2C5aXW+jWGcU1YmL8fJWIDsXvrJw= github.com/wader/readline v0.0.0-20220704090837-31be50517a56/go.mod h1:Zgz8IJWvJoe7NK23CCPpC109XMCqJCpUhpHcnnA4XaM= +golang.org/x/arch v0.0.0-20220401014709-5424468ecbac h1:05z6X/pDgf2qll8x7kbRRVdr33GjdV/GOCGiQfnaJS8= +golang.org/x/arch v0.0.0-20220401014709-5424468ecbac/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=