mirror of
https://github.com/wader/fq.git
synced 2024-12-24 22:05:31 +03:00
926 lines
29 KiB
Go
926 lines
29 KiB
Go
//nolint:revive
|
|
package elf
|
|
|
|
// https://refspecs.linuxbase.org/elf/gabi4+/contents.html
|
|
// https://man7.org/linux/man-pages/man5/elf.5.html
|
|
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/elf.h
|
|
|
|
// TODO: dwarf
|
|
|
|
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"
|
|
)
|
|
|
|
func init() {
|
|
interp.RegisterFormat(decode.Format{
|
|
Name: format.ELF,
|
|
Description: "Executable and Linkable Format",
|
|
Groups: []string{format.PROBE},
|
|
DecodeFn: elfDecode,
|
|
})
|
|
}
|
|
|
|
const (
|
|
LITTLE_ENDIAN = 1
|
|
BIG_ENDIAN = 2
|
|
)
|
|
|
|
var endianNames = scalar.UToSymStr{
|
|
LITTLE_ENDIAN: "little_endian",
|
|
BIG_ENDIAN: "big_endian",
|
|
}
|
|
|
|
var classBits = scalar.UToSymU{
|
|
1: 32,
|
|
2: 64,
|
|
}
|
|
|
|
const (
|
|
CLASS_32 = 1
|
|
CLASS_64 = 2
|
|
)
|
|
|
|
var osABINames = scalar.UToSymStr{
|
|
0: "sysv",
|
|
1: "hpux",
|
|
2: "netbsd",
|
|
3: "linux",
|
|
4: "hurd",
|
|
5: "86open",
|
|
6: "solaris",
|
|
7: "monterey",
|
|
8: "irix",
|
|
9: "freebsd",
|
|
10: "tru64",
|
|
11: "modesto",
|
|
12: "openbsd",
|
|
97: "arm",
|
|
255: "standalone",
|
|
}
|
|
|
|
var typeNames = scalar.URangeToScalar{
|
|
{Range: [2]uint64{0x00, 0x00}, S: scalar.S{Sym: "none"}},
|
|
{Range: [2]uint64{0x01, 0x01}, S: scalar.S{Sym: "rel"}},
|
|
{Range: [2]uint64{0x02, 0x02}, S: scalar.S{Sym: "exec"}},
|
|
{Range: [2]uint64{0x03, 0x03}, S: scalar.S{Sym: "dyn"}},
|
|
{Range: [2]uint64{0x04, 0x04}, S: scalar.S{Sym: "core"}},
|
|
{Range: [2]uint64{0xfe00, 0xfeff}, S: scalar.S{Sym: "os"}},
|
|
{Range: [2]uint64{0xff00, 0xffff}, S: scalar.S{Sym: "proc"}},
|
|
}
|
|
|
|
const (
|
|
EM_X86_64 = 0x3e
|
|
EM_ARM64 = 0xb7
|
|
)
|
|
|
|
var machineNames = scalar.UToScalar{
|
|
0x00: {Description: "No specific instruction set"},
|
|
0x01: {Sym: "we_32100", Description: "AT&T WE 32100"},
|
|
0x02: {Sym: "sparc", Description: "SPARC"},
|
|
0x03: {Sym: "x86", Description: "x86"},
|
|
0x04: {Sym: "m68k", Description: "Motorola 68000 (M68k)"},
|
|
0x05: {Sym: "m88k", Description: "Motorola 88000 (M88k)"},
|
|
0x06: {Sym: "intel_mcu", Description: "Intel MCU"},
|
|
0x07: {Sym: "intel_80860", Description: "Intel 80860"},
|
|
0x08: {Sym: "mips", Description: "MIPS"},
|
|
0x09: {Sym: "s370", Description: "IBM_System/370"},
|
|
0x0a: {Sym: "mips_rs3000le", Description: "MIPS RS3000 Little-endian"},
|
|
0x0e: {Sym: "pa_risc", Description: "Hewlett-Packard PA-RISC"},
|
|
0x0f: {Description: "Reserved for future use"},
|
|
0x13: {Sym: "80960", Description: "Intel 80960"},
|
|
0x14: {Sym: "powerpc", Description: "PowerPC"},
|
|
0x15: {Sym: "powerpc64", Description: "PowerPC (64-bit)"},
|
|
0x16: {Sym: "s390", Description: "S390, including S390x"},
|
|
0x17: {Sym: "ibm_spu_spc", Description: "IBM SPU/SPC"},
|
|
0x24: {Sym: "nec_v800", Description: "NEC V800"},
|
|
0x25: {Sym: "fr20", Description: "Fujitsu FR20"},
|
|
0x26: {Sym: "trw_rh_32", Description: "TRW RH-32"},
|
|
0x27: {Sym: "motorola_rce", Description: "Motorola RCE"},
|
|
0x28: {Sym: "arm", Description: "ARM (up to ARMv7/Aarch32)"},
|
|
0x29: {Sym: "alpha", Description: "Digital Alpha"},
|
|
0x2a: {Sym: "superh", Description: "SuperH"},
|
|
0x2b: {Sym: "sparc_v9", Description: "SPARC Version 9"},
|
|
0x2c: {Sym: "siemens_tricore", Description: "Siemens TriCore embedded processor"},
|
|
0x2d: {Sym: "argonaut_risc", Description: "Argonaut RISC Core"},
|
|
0x2e: {Sym: "h8_300", Description: "Hitachi H8/300"},
|
|
0x2f: {Sym: "h8_300h", Description: "Hitachi H8/300H"},
|
|
0x30: {Sym: "h8s", Description: "Hitachi H8S"},
|
|
0x31: {Sym: "h8/500", Description: "Hitachi H8/500"},
|
|
0x32: {Sym: "ia_64", Description: "IA-64"},
|
|
0x33: {Sym: "mips_x", Description: "Stanford MIPS-X"},
|
|
0x34: {Sym: "coldfire", Description: "Motorola ColdFire"},
|
|
0x35: {Sym: "m68hc12", Description: "Motorola M68HC12"},
|
|
0x36: {Sym: "fujitsu_mma", Description: "Fujitsu MMA Multimedia Accelerator"},
|
|
0x37: {Sym: "siemens_pcp", Description: "Siemens PCP"},
|
|
0x38: {Sym: "sony_ncpu_risc", Description: "Sony nCPU embedded RISC processor"},
|
|
0x39: {Sym: "denso_ndr1", Description: "Denso NDR1 microprocessor"},
|
|
0x3a: {Sym: "motorola_star", Description: "Motorola Star*Core processor"},
|
|
0x3b: {Sym: "toyota_me16", Description: "Toyota ME16 processor"},
|
|
0x3c: {Sym: "st100", Description: "STMicroelectronics ST100 processor"},
|
|
0x3d: {Sym: "tinyj", Description: "Advanced Logic Corp. TinyJ embedded processor family"},
|
|
EM_X86_64: {Sym: "x86_64", Description: "AMD x86-64"},
|
|
0x8c: {Sym: "tms320C6000", Description: "TMS320C6000 Family"},
|
|
EM_ARM64: {Sym: "arm64", Description: "ARM 64-bits (ARMv8/Aarch64)"},
|
|
0xf3: {Sym: "risc_v", Description: "RISC-V"},
|
|
0xf7: {Sym: "bpf", Description: "Berkeley Packet Filter"},
|
|
0x101: {Sym: "wdc_65C816", Description: "WDC 65C816"},
|
|
}
|
|
|
|
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"}},
|
|
{Range: [2]uint64{0x00000002, 0x00000002}, S: scalar.S{Sym: "dynamic", Description: "Dynamic linking information"}},
|
|
{Range: [2]uint64{0x00000003, 0x00000003}, S: scalar.S{Sym: "interp", Description: "Interpreter to invoke"}},
|
|
{Range: [2]uint64{0x00000004, 0x00000004}, S: scalar.S{Sym: "note", Description: "Auxiliary information"}},
|
|
{Range: [2]uint64{0x00000005, 0x00000005}, S: scalar.S{Sym: "shlib", Description: "Reserved but has unspecified"}},
|
|
{Range: [2]uint64{0x00000006, 0x00000006}, S: scalar.S{Sym: "phdr", Description: "Program header location and size"}},
|
|
{Range: [2]uint64{0x00000007, 0x00000007}, S: scalar.S{Sym: "tls", Description: "Thread-Local Storage template"}},
|
|
{Range: [2]uint64{0x6474e550, 0x6474e550}, S: scalar.S{Sym: "gnu_eh_frame", Description: "GNU frame unwind information"}},
|
|
{Range: [2]uint64{0x6474e551, 0x6474e551}, S: scalar.S{Sym: "gnu_stack", Description: "GNU stack permission"}},
|
|
{Range: [2]uint64{0x6474e552, 0x6474e552}, S: scalar.S{Sym: "gnu_relro", Description: "GNU read-only after relocation"}},
|
|
{Range: [2]uint64{0x60000000, 0x6fffffff}, S: scalar.S{Sym: "os", Description: "Operating system-specific"}},
|
|
{Range: [2]uint64{0x70000000, 0x7fffffff}, S: scalar.S{Sym: "proc", Description: "Processor-specific"}},
|
|
}
|
|
|
|
const (
|
|
SHT_NULL = 0x0
|
|
SHT_PROGBITS = 0x1
|
|
SHT_SYMTAB = 0x2
|
|
SHT_STRTAB = 0x3
|
|
SHT_RELA = 0x4
|
|
SHT_HASH = 0x5
|
|
SHT_DYNAMIC = 0x6
|
|
SHT_NOTE = 0x7
|
|
SHT_NOBITS = 0x8
|
|
SHT_REL = 0x9
|
|
SHT_SHLIB = 0x0a
|
|
SHT_DYNSYM = 0x0b
|
|
SHT_INIT_ARRAY = 0x0e
|
|
SHT_FINI_ARRAY = 0x0f
|
|
SHT_PREINIT_ARRAY = 0x10
|
|
SHT_GROUP = 0x11
|
|
SHT_SYMTAB_SHNDX = 0x12
|
|
SHT_GNU_HASH = 0x6ffffff6
|
|
)
|
|
|
|
var sectionHeaderTypeMap = scalar.UToScalar{
|
|
SHT_NULL: {Sym: "null", Description: "Header inactive"},
|
|
SHT_PROGBITS: {Sym: "progbits", Description: "Information defined by the program"},
|
|
SHT_SYMTAB: {Sym: "symtab", Description: "Symbol table"},
|
|
SHT_STRTAB: {Sym: "strtab", Description: "String table"},
|
|
SHT_RELA: {Sym: "rela", Description: "Relocation entries with explicit addends"},
|
|
SHT_HASH: {Sym: "hash", Description: "Symbol hash table"},
|
|
SHT_DYNAMIC: {Sym: "dynamic", Description: "Information for dynamic linking"},
|
|
SHT_NOTE: {Sym: "note", Description: "Information that marks the file in some way"},
|
|
SHT_NOBITS: {Sym: "nobits", Description: "No space in the file"},
|
|
SHT_REL: {Sym: "rel", Description: "Relocation entries without explicit addends"},
|
|
SHT_SHLIB: {Sym: "shlib", Description: "Reserved but has unspecified semantics"},
|
|
SHT_DYNSYM: {Sym: "dynsym", Description: "Dynamic linking symbol table"},
|
|
SHT_INIT_ARRAY: {Sym: "init_array", Description: "Initialization functions"},
|
|
SHT_FINI_ARRAY: {Sym: "fini_array", Description: "Termination functions"},
|
|
SHT_PREINIT_ARRAY: {Sym: "preinit_array", Description: "Pre initialization functions"},
|
|
SHT_GROUP: {Sym: "group", Description: "Section group"},
|
|
SHT_SYMTAB_SHNDX: {Sym: "symtab_shndx", Description: ""},
|
|
SHT_GNU_HASH: {Sym: "gnu_hash", Description: "GNU symbol hash table"},
|
|
}
|
|
|
|
const (
|
|
STRTAB_DYNSTR = ".dynstr"
|
|
STRTAB_SHSTRTAB = ".shstrtab"
|
|
STRTAB_STRTAB = ".strtab"
|
|
)
|
|
|
|
const (
|
|
DT_NULL = 0
|
|
DT_NEEDED = 1
|
|
DT_PLTRELSZ = 2
|
|
DT_PLTGOT = 3
|
|
DT_HASH = 4
|
|
DT_STRTAB = 5
|
|
DT_SYMTAB = 6
|
|
DT_RELA = 7
|
|
DT_RELASZ = 8
|
|
DT_RELAENT = 9
|
|
DT_STRSZ = 10
|
|
DT_SYMENT = 11
|
|
DT_INIT = 12
|
|
DT_FINI = 13
|
|
DT_SONAME = 14
|
|
DT_RPATH = 15
|
|
DT_SYMBOLIC = 16
|
|
DT_REL = 17
|
|
DT_RELSZ = 18
|
|
DT_RELENT = 19
|
|
DT_PLTREL = 20
|
|
DT_DEBUG = 21
|
|
DT_TEXTREL = 22
|
|
DT_JMPREL = 23
|
|
DT_BIND_NOW = 24
|
|
DT_INIT_ARRAY = 25
|
|
DT_FINI_ARRAY = 26
|
|
DT_INIT_ARRAYSZ = 27
|
|
DT_FINI_ARRAYSZ = 28
|
|
DT_RUNPATH = 29
|
|
DT_FLAGS = 30 // TODO: flag map
|
|
DT_ENCODING = 32 // or DT_PREINIT_ARRAY ?
|
|
DT_PREINIT_ARRAYSZ = 33
|
|
DT_LOOS = 0x6000000D
|
|
DT_HIOS = 0x6ffff000
|
|
DT_LOPROC = 0x70000000
|
|
DT_HIPROC = 0x7fffffff
|
|
)
|
|
|
|
const (
|
|
dUnIgnored = iota
|
|
dUnVal
|
|
dUnPtr
|
|
dUnUnspecified
|
|
)
|
|
|
|
type dtEntry struct {
|
|
r [2]uint64
|
|
dUn int
|
|
s scalar.S
|
|
}
|
|
|
|
type dynamicTableEntries []dtEntry
|
|
|
|
func (d dynamicTableEntries) lookup(u uint64) (dtEntry, bool) {
|
|
for _, de := range d {
|
|
if de.r[0] >= u && de.r[1] <= u {
|
|
return de, true
|
|
}
|
|
}
|
|
return dtEntry{}, false
|
|
}
|
|
|
|
func (d dynamicTableEntries) MapScalar(s scalar.S) (scalar.S, error) {
|
|
u := s.ActualU()
|
|
if de, ok := d.lookup(u); ok {
|
|
s = de.s
|
|
s.Actual = u
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
var dynamicTableMap = dynamicTableEntries{
|
|
{r: [2]uint64{DT_NULL, DT_NULL}, dUn: dUnIgnored, s: scalar.S{Sym: "null", Description: "Marks end of dynamic section"}},
|
|
{r: [2]uint64{DT_NEEDED, DT_NEEDED}, dUn: dUnVal, s: scalar.S{Sym: "needed", Description: "String table offset to name of a needed library"}},
|
|
{r: [2]uint64{DT_PLTRELSZ, DT_PLTRELSZ}, dUn: dUnVal, s: scalar.S{Sym: "pltrelsz", Description: "Size in bytes of PLT relocation entries"}},
|
|
{r: [2]uint64{DT_PLTGOT, DT_PLTGOT}, dUn: dUnPtr, s: scalar.S{Sym: "pltgot", Description: "Address of PLT and/or GOT"}},
|
|
{r: [2]uint64{DT_HASH, DT_HASH}, dUn: dUnPtr, s: scalar.S{Sym: "hash", Description: "Address of symbol hash table"}},
|
|
{r: [2]uint64{DT_STRTAB, DT_STRTAB}, dUn: dUnPtr, s: scalar.S{Sym: "strtab", Description: "Address of string table"}},
|
|
{r: [2]uint64{DT_SYMTAB, DT_SYMTAB}, dUn: dUnPtr, s: scalar.S{Sym: "symtab", Description: "Address of symbol table"}},
|
|
{r: [2]uint64{DT_RELA, DT_RELA}, dUn: dUnPtr, s: scalar.S{Sym: "rela", Description: "Address of Rela relocation table"}},
|
|
{r: [2]uint64{DT_RELASZ, DT_RELASZ}, dUn: dUnVal, s: scalar.S{Sym: "relasz", Description: "Size in bytes of the Rela relocation table"}},
|
|
{r: [2]uint64{DT_RELAENT, DT_RELAENT}, dUn: dUnVal, s: scalar.S{Sym: "relaent", Description: "Size in bytes of a Rela relocation table entry"}},
|
|
{r: [2]uint64{DT_STRSZ, DT_STRSZ}, dUn: dUnVal, s: scalar.S{Sym: "strsz", Description: "Size in bytes of string table"}},
|
|
{r: [2]uint64{DT_SYMENT, DT_SYMENT}, dUn: dUnVal, s: scalar.S{Sym: "syment", Description: "Size in bytes of a symbol table entry"}},
|
|
{r: [2]uint64{DT_INIT, DT_INIT}, dUn: dUnPtr, s: scalar.S{Sym: "init", Description: "Address of the initialization function"}},
|
|
{r: [2]uint64{DT_FINI, DT_FINI}, dUn: dUnPtr, s: scalar.S{Sym: "fini", Description: "Address of the termination function"}},
|
|
{r: [2]uint64{DT_SONAME, DT_SONAME}, dUn: dUnVal, s: scalar.S{Sym: "soname", Description: "String table offset to name of shared object"}},
|
|
{r: [2]uint64{DT_RPATH, DT_RPATH}, dUn: dUnVal, s: scalar.S{Sym: "rpath", Description: "String table offset to library search path (deprecated)"}},
|
|
{r: [2]uint64{DT_SYMBOLIC, DT_SYMBOLIC}, dUn: dUnIgnored, s: scalar.S{Sym: "symbolic", Description: "Alert linker to search this shared object before the executable for symbols DT_REL Address of Rel relocation table"}},
|
|
{r: [2]uint64{DT_REL, DT_REL}, dUn: dUnPtr, s: scalar.S{Sym: "rel", Description: ""}},
|
|
{r: [2]uint64{DT_RELSZ, DT_RELSZ}, dUn: dUnVal, s: scalar.S{Sym: "relsz", Description: "Size in bytes of Rel relocation table"}},
|
|
{r: [2]uint64{DT_RELENT, DT_RELENT}, dUn: dUnVal, s: scalar.S{Sym: "relent", Description: "Size in bytes of a Rel table entry"}},
|
|
{r: [2]uint64{DT_PLTREL, DT_PLTREL}, dUn: dUnVal, s: scalar.S{Sym: "pltrel", Description: "Type of relocation entry to which the PLT refers (Rela or Rel)"}},
|
|
{r: [2]uint64{DT_DEBUG, DT_DEBUG}, dUn: dUnPtr, s: scalar.S{Sym: "debug", Description: "Undefined use for debugging"}},
|
|
{r: [2]uint64{DT_TEXTREL, DT_TEXTREL}, dUn: dUnIgnored, s: scalar.S{Sym: "textrel", Description: "Absence of this entry indicates that no relocation entries should apply to a nonwritable segment"}},
|
|
{r: [2]uint64{DT_JMPREL, DT_JMPREL}, dUn: dUnPtr, s: scalar.S{Sym: "jmprel", Description: "Address of relocation entries associated solely with the PLT"}},
|
|
{r: [2]uint64{DT_BIND_NOW, DT_BIND_NOW}, dUn: dUnIgnored, s: scalar.S{Sym: "bind_now", Description: "Instruct dynamic linker to process all relocations before transferring control to the executable"}},
|
|
{r: [2]uint64{DT_INIT_ARRAY, DT_INIT_ARRAY}, dUn: dUnPtr, s: scalar.S{Sym: "init_array", Description: "Address of the array of pointers to initialization functions"}},
|
|
{r: [2]uint64{DT_FINI_ARRAY, DT_FINI_ARRAY}, dUn: dUnPtr, s: scalar.S{Sym: "fini_array", Description: "Address of the array of pointers to termination functions"}},
|
|
{r: [2]uint64{DT_INIT_ARRAYSZ, DT_INIT_ARRAYSZ}, dUn: dUnVal, s: scalar.S{Sym: "init_arraysz", Description: "Size in bytes of the array of initialization functions"}},
|
|
{r: [2]uint64{DT_FINI_ARRAYSZ, DT_FINI_ARRAYSZ}, dUn: dUnVal, s: scalar.S{Sym: "fini_arraysz", Description: "Size in bytes of the array of termination functions "}},
|
|
{r: [2]uint64{DT_RUNPATH, DT_RUNPATH}, dUn: dUnVal, s: scalar.S{Sym: "runpath", Description: "String table offset to library search path"}},
|
|
{r: [2]uint64{DT_FLAGS, DT_FLAGS}, dUn: dUnVal, s: scalar.S{Sym: "flags", Description: "Flag values specific to the object being loaded"}}, // TODO: flag ma}},
|
|
{r: [2]uint64{DT_ENCODING, DT_ENCODING}, dUn: dUnUnspecified, s: scalar.S{Sym: "encoding", Description: ""}}, // or DT_PREINIT_ARRAY }},
|
|
{r: [2]uint64{DT_PREINIT_ARRAYSZ, DT_PREINIT_ARRAYSZ}, dUn: dUnVal, s: scalar.S{Sym: "preinit_arraysz", Description: "Address of the array of pointers to pre-initialization functions"}},
|
|
{r: [2]uint64{DT_LOOS, DT_HIOS}, dUn: dUnUnspecified, s: scalar.S{Sym: "lo", Description: "Operating system-specific semantics"}},
|
|
{r: [2]uint64{DT_LOPROC, DT_HIPROC}, dUn: dUnUnspecified, s: scalar.S{Sym: "proc", Description: "Processor-specific semantics"}},
|
|
}
|
|
|
|
var symbolTableBindingMap = scalar.UToSymStr{
|
|
0: "local",
|
|
1: "global",
|
|
2: "weak",
|
|
10: "loos",
|
|
12: "hios",
|
|
13: "proc",
|
|
14: "proc",
|
|
15: "proc",
|
|
}
|
|
|
|
var symbolTableTypeMap = scalar.UToSymStr{
|
|
0: "notype",
|
|
1: "object",
|
|
2: "func",
|
|
3: "section",
|
|
4: "file",
|
|
5: "common",
|
|
6: "tls",
|
|
10: "loos",
|
|
12: "hios",
|
|
13: "proc",
|
|
14: "proc",
|
|
15: "proc",
|
|
}
|
|
|
|
var symbolTableVisibilityMap = scalar.UToSymStr{
|
|
0: "default",
|
|
1: "internal",
|
|
2: "hidden",
|
|
3: "protected",
|
|
}
|
|
|
|
func strIndexNull(idx int, s string) string {
|
|
if idx > len(s) {
|
|
return ""
|
|
}
|
|
i := strings.IndexByte(s[idx:], 0)
|
|
if i == -1 {
|
|
return s
|
|
}
|
|
return s[idx : idx+i]
|
|
}
|
|
|
|
type strTable string
|
|
|
|
func (m strTable) MapScalar(s scalar.S) (scalar.S, error) {
|
|
s.Sym = strIndexNull(int(s.ActualU()), string(m))
|
|
return s, nil
|
|
}
|
|
|
|
func elfDecodeSymbolHashTable(d *decode.D) {
|
|
nBucket := d.FieldU32("nbucket")
|
|
nChain := d.FieldU32("nchain")
|
|
|
|
repeatFn := func(r int, fn func(d *decode.D)) func(d *decode.D) {
|
|
return func(d *decode.D) {
|
|
for i := 0; i < r; i++ {
|
|
fn(d)
|
|
}
|
|
}
|
|
}
|
|
|
|
d.FieldArray("buckets", repeatFn(int(nBucket), func(d *decode.D) { d.FieldU32("bucket") }))
|
|
d.FieldArray("chains", repeatFn(int(nChain), func(d *decode.D) { d.FieldU32("chain") }))
|
|
}
|
|
|
|
func elfDecodeSymbolTable(d *decode.D, ec elfContext, nEntries int, strTab string) {
|
|
for i := 0; i < nEntries; i++ {
|
|
d.FieldStruct("symbol", func(d *decode.D) {
|
|
switch ec.archBits {
|
|
case 32:
|
|
d.FieldU32("name", strTable(strTab))
|
|
d.FieldU32("value")
|
|
d.FieldU32("size")
|
|
d.FieldU4("bind", symbolTableBindingMap)
|
|
d.FieldU4("type", symbolTableTypeMap)
|
|
d.FieldU6("other_unused")
|
|
d.FieldU2("visibility", symbolTableVisibilityMap)
|
|
d.FieldU16("shndx")
|
|
case 64:
|
|
d.FieldU32("name", strTable(strTab))
|
|
d.FieldU4("bind", symbolTableBindingMap)
|
|
d.FieldU4("type", symbolTableTypeMap)
|
|
d.FieldU6("other_unused")
|
|
d.FieldU2("visibility", symbolTableVisibilityMap)
|
|
d.FieldU16("shndx")
|
|
d.FieldU64("value")
|
|
d.FieldU64("size")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func elfDecodeGNUHash(d *decode.D, ec elfContext, size int64, strTab string) {
|
|
d.FramedFn(size, func(d *decode.D) {
|
|
nBuckets := d.FieldU32("nbuckets")
|
|
d.FieldU32("symndx")
|
|
maskwords := d.FieldU32("maskwords")
|
|
d.FieldU32("shift2")
|
|
|
|
repeatFn := func(r int, fn func(d *decode.D)) func(d *decode.D) {
|
|
return func(d *decode.D) {
|
|
for i := 0; i < r; i++ {
|
|
fn(d)
|
|
}
|
|
}
|
|
}
|
|
// TODO: possible to map to symbols?
|
|
_ = strTab
|
|
d.FieldArray("bloom_filter", repeatFn(int(maskwords), func(d *decode.D) { d.FieldU("maskword", ec.archBits) }))
|
|
d.FieldArray("buckets", repeatFn(int(nBuckets), func(d *decode.D) { d.FieldU32("bucket") }))
|
|
d.FieldArray("values", func(d *decode.D) {
|
|
for !d.End() {
|
|
d.FieldU32("value")
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
type dynamicContext struct {
|
|
entries int
|
|
strTabPtr int64
|
|
strSzVal int64
|
|
strTab string
|
|
symEnt int64
|
|
}
|
|
|
|
func elfReadDynamicTags(d *decode.D, ec *elfContext) dynamicContext {
|
|
var strTabPtr int64
|
|
var strSzVal int64
|
|
var symEnt int64
|
|
var entries int
|
|
|
|
seenNull := false
|
|
for !seenNull {
|
|
entries++
|
|
tag := d.U(ec.archBits)
|
|
valPtr := d.U(ec.archBits)
|
|
|
|
switch tag {
|
|
case DT_STRTAB:
|
|
strTabPtr = int64(valPtr) * 8
|
|
case DT_STRSZ:
|
|
strSzVal = int64(valPtr) * 8
|
|
case DT_SYMENT:
|
|
symEnt = int64(valPtr) * 8
|
|
case DT_NULL:
|
|
seenNull = true
|
|
}
|
|
}
|
|
|
|
return dynamicContext{
|
|
entries: entries,
|
|
strTabPtr: strTabPtr,
|
|
strSzVal: strSzVal,
|
|
symEnt: symEnt,
|
|
}
|
|
}
|
|
|
|
type symbol struct {
|
|
name uint64
|
|
value uint64
|
|
}
|
|
|
|
func elfReadSymbolTable(d *decode.D, ec *elfContext, sh sectionHeader) []symbol {
|
|
var ss []symbol
|
|
|
|
for i := 0; i < int(sh.size/sh.entSize); i++ {
|
|
var name uint64
|
|
var value uint64
|
|
switch ec.archBits {
|
|
case 32:
|
|
name = d.U32() // name
|
|
value = d.U32() // value
|
|
d.U32() // size
|
|
d.U4() // bind
|
|
d.U4() // type
|
|
d.U6() // other_unused
|
|
d.U2() // visibility
|
|
d.U16() // shndx
|
|
case 64:
|
|
name = d.U32() // name
|
|
d.U4() // bind
|
|
d.U4() // type
|
|
d.U6() // other_unused
|
|
d.U2() // visibility
|
|
d.U16() // shndx
|
|
value = d.U64() // value
|
|
d.U64() // size
|
|
}
|
|
ss = append(ss, symbol{name: name, value: value})
|
|
}
|
|
|
|
return ss
|
|
}
|
|
|
|
type sectionHeader struct {
|
|
addr int64
|
|
offset int64
|
|
size int64
|
|
entSize int64
|
|
name int
|
|
typ int
|
|
dc dynamicContext // if SHT_DYNAMIC
|
|
symbols []symbol
|
|
}
|
|
|
|
const maxStrTabSize = 100_000_000
|
|
|
|
func readStrTab(d *decode.D, firstBit int64, nBytes int64) string {
|
|
if nBytes > maxStrTabSize {
|
|
d.Errorf("string table too large %d > %d", nBytes, maxStrTabSize)
|
|
}
|
|
return string(d.BytesRange(firstBit, int(nBytes)))
|
|
}
|
|
|
|
func elfReadSectionHeaders(d *decode.D, ec *elfContext) {
|
|
for i := 0; i < ec.shNum; i++ {
|
|
d.SeekAbs(ec.shOff + int64(i)*ec.shEntSize)
|
|
var sh sectionHeader
|
|
|
|
switch ec.archBits {
|
|
case 32:
|
|
sh.name = int(d.U32())
|
|
sh.typ = int(d.U32())
|
|
d.U32() // flags
|
|
sh.addr = int64(d.U32() * 8) // addr
|
|
sh.offset = int64(d.U32()) * 8
|
|
sh.size = int64(d.U32()) * 8
|
|
d.U32() // link
|
|
d.U32() // info
|
|
d.U32() // addralign
|
|
sh.entSize = int64(d.U32()) * 8
|
|
case 64:
|
|
sh.name = int(d.U32())
|
|
sh.typ = int(d.U32())
|
|
d.U64() // addr
|
|
sh.addr = int64(d.U64() * 8) // flags
|
|
sh.offset = int64(d.U64()) * 8
|
|
sh.size = int64(d.U64()) * 8
|
|
d.U32() // link
|
|
d.U32() // info
|
|
d.U64() // addralign
|
|
sh.entSize = int64(d.U64()) * 8
|
|
default:
|
|
panic("unreachable")
|
|
}
|
|
|
|
switch sh.typ {
|
|
case SHT_DYNAMIC:
|
|
d.SeekAbs(sh.offset)
|
|
sh.dc = elfReadDynamicTags(d, ec)
|
|
case SHT_SYMTAB:
|
|
d.SeekAbs(sh.offset)
|
|
sh.symbols = elfReadSymbolTable(d, ec, sh)
|
|
}
|
|
|
|
ec.sections = append(ec.sections, sh)
|
|
}
|
|
|
|
// for dynamic linking sections find offset to string table by looking up
|
|
// section by address using string stable address
|
|
for i := range ec.sections {
|
|
sh := &ec.sections[i]
|
|
if sh.typ != SHT_DYNAMIC {
|
|
continue
|
|
}
|
|
if i, ok := ec.sectionIndexByAddr(sh.dc.strTabPtr); ok {
|
|
strTabSh := ec.sections[i]
|
|
sh.dc.strTab = readStrTab(d, strTabSh.offset, sh.dc.strSzVal/8)
|
|
}
|
|
}
|
|
|
|
ec.strTabMap = map[string]string{}
|
|
var shStrTab string
|
|
if ec.shStrNdx >= len(ec.sections) {
|
|
d.Fatalf("can't find shStrNdx %d", ec.shStrNdx)
|
|
}
|
|
sh := ec.sections[ec.shStrNdx]
|
|
|
|
shStrTab = readStrTab(d, sh.offset, sh.size/8)
|
|
for _, sh := range ec.sections {
|
|
if sh.typ != SHT_STRTAB {
|
|
continue
|
|
}
|
|
ec.strTabMap[strIndexNull(sh.name, shStrTab)] = readStrTab(d, sh.offset, sh.size/8)
|
|
}
|
|
}
|
|
|
|
type elfContext struct {
|
|
archBits int
|
|
machine int
|
|
endian decode.Endian
|
|
|
|
phOff int64
|
|
phNum int
|
|
phSize int64
|
|
|
|
shOff int64
|
|
shNum int
|
|
shEntSize int64
|
|
|
|
shStrNdx int
|
|
|
|
sections []sectionHeader
|
|
strTabMap map[string]string
|
|
}
|
|
|
|
func (ec *elfContext) sectionIndexByAddr(addr int64) (int, bool) {
|
|
for i, s := range ec.sections {
|
|
if s.addr == addr {
|
|
return i, true
|
|
}
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
func elfDecodeHeader(d *decode.D, ec *elfContext) {
|
|
var class uint64
|
|
var archBits int
|
|
var endian uint64
|
|
|
|
d.FieldStruct("ident", func(d *decode.D) {
|
|
d.FieldRawLen("magic", 4*8, d.AssertBitBuf([]byte("\x7fELF")))
|
|
class = d.FieldU8("class", classBits)
|
|
endian = d.FieldU8("data", endianNames)
|
|
d.FieldU8("version")
|
|
d.FieldU8("os_abi", osABINames)
|
|
d.FieldU8("abi_version")
|
|
d.FieldRawLen("pad", 7*8, d.BitBufIsZero())
|
|
})
|
|
|
|
switch class {
|
|
case CLASS_32:
|
|
archBits = 32
|
|
case CLASS_64:
|
|
archBits = 64
|
|
default:
|
|
d.Fatalf("unknown class %d", class)
|
|
}
|
|
|
|
switch endian {
|
|
case LITTLE_ENDIAN:
|
|
d.Endian = decode.LittleEndian
|
|
case BIG_ENDIAN:
|
|
d.Endian = decode.BigEndian
|
|
default:
|
|
d.Fatalf("unknown endian %d", endian)
|
|
}
|
|
|
|
d.FieldU16("type", typeNames, scalar.ActualHex)
|
|
machine := d.FieldU16("machine", machineNames, scalar.ActualHex)
|
|
d.FieldU32("version")
|
|
d.FieldU("entry", archBits)
|
|
phOff := d.FieldU("phoff", archBits)
|
|
shOff := d.FieldU("shoff", archBits)
|
|
d.FieldU32("flags")
|
|
d.FieldU16("ehsize")
|
|
phSize := d.FieldU16("phentsize")
|
|
phNum := d.FieldU16("phnum")
|
|
shEntSize := d.FieldU16("shentsize")
|
|
shNum := d.FieldU16("shnum")
|
|
shStrNdx := d.FieldU16("shstrndx")
|
|
|
|
ec.archBits = archBits
|
|
ec.endian = d.Endian
|
|
ec.machine = int(machine)
|
|
ec.phOff = int64(phOff) * 8
|
|
ec.phNum = int(phNum)
|
|
ec.phSize = int64(phSize) * 8
|
|
ec.shOff = int64(shOff) * 8
|
|
ec.shNum = int(shNum)
|
|
ec.shEntSize = int64(shEntSize) * 8
|
|
ec.shStrNdx = int(shStrNdx)
|
|
}
|
|
|
|
func elfDecodeProgramHeader(d *decode.D, ec elfContext) {
|
|
pFlags := func(d *decode.D) {
|
|
d.FieldStruct("flags", func(d *decode.D) {
|
|
if d.Endian == decode.LittleEndian {
|
|
d.FieldU5("unused0")
|
|
d.FieldBool("r")
|
|
d.FieldBool("w")
|
|
d.FieldBool("x")
|
|
d.FieldU24("unused1")
|
|
} else {
|
|
d.FieldU29("unused0")
|
|
d.FieldBool("r")
|
|
d.FieldBool("w")
|
|
d.FieldBool("x")
|
|
}
|
|
})
|
|
}
|
|
|
|
var offset uint64
|
|
var size uint64
|
|
|
|
switch ec.archBits {
|
|
case 32:
|
|
d.FieldU32("type", phTypeNames)
|
|
offset = d.FieldU("offset", ec.archBits, scalar.ActualHex)
|
|
d.FieldU("vaddr", ec.archBits, scalar.ActualHex)
|
|
d.FieldU("paddr", ec.archBits, scalar.ActualHex)
|
|
size = d.FieldU32("filesz")
|
|
d.FieldU32("memsz")
|
|
pFlags(d)
|
|
d.FieldU32("align")
|
|
case 64:
|
|
d.FieldU32("type", phTypeNames)
|
|
pFlags(d)
|
|
offset = d.FieldU("offset", ec.archBits, scalar.ActualHex)
|
|
d.FieldU("vaddr", ec.archBits, scalar.ActualHex)
|
|
d.FieldU("paddr", ec.archBits, scalar.ActualHex)
|
|
size = d.FieldU64("filesz")
|
|
d.FieldU64("memsz")
|
|
d.FieldU64("align")
|
|
}
|
|
|
|
d.RangeFn(int64(offset*8), int64(size*8), func(d *decode.D) {
|
|
d.FieldRawLen("data", d.BitsLeft())
|
|
})
|
|
}
|
|
|
|
func elfDecodeProgramHeaders(d *decode.D, ec elfContext) {
|
|
for i := 0; i < ec.phNum; i++ {
|
|
d.FieldStruct("program_header", func(d *decode.D) {
|
|
d.SeekAbs(ec.phOff + int64(i)*ec.phSize)
|
|
elfDecodeProgramHeader(d, ec)
|
|
})
|
|
}
|
|
}
|
|
|
|
func elfDecodeDynamicTag(d *decode.D, ec elfContext, dc dynamicContext) {
|
|
dtTag := d.FieldU("tag", ec.archBits, dynamicTableMap)
|
|
name := "unspecified"
|
|
dfMapper := scalar.ActualHex
|
|
if de, ok := dynamicTableMap.lookup(dtTag); ok {
|
|
switch de.dUn {
|
|
case dUnIgnored:
|
|
name = "ignored"
|
|
case dUnVal:
|
|
name = "val"
|
|
dfMapper = scalar.ActualDec
|
|
case dUnPtr:
|
|
name = "ptr"
|
|
}
|
|
}
|
|
|
|
switch dtTag {
|
|
case DT_NEEDED:
|
|
d.FieldU(name, ec.archBits, dfMapper, strTable(dc.strTab))
|
|
case DT_HASH:
|
|
v := d.FieldU(name, ec.archBits, dfMapper)
|
|
if i, ok := ec.sectionIndexByAddr(int64(v) * 8); ok {
|
|
d.FieldValueU("section_index", uint64(i))
|
|
}
|
|
case DT_SYMTAB,
|
|
DT_STRTAB,
|
|
DT_PLTGOT,
|
|
DT_JMPREL,
|
|
DT_INIT,
|
|
DT_FINI:
|
|
v := d.FieldU(name, ec.archBits, dfMapper)
|
|
if i, ok := ec.sectionIndexByAddr(int64(v) * 8); ok {
|
|
d.FieldValueU("section_index", uint64(i))
|
|
}
|
|
default:
|
|
d.FieldU(name, ec.archBits, dfMapper)
|
|
}
|
|
}
|
|
|
|
func elfDecodeDynamicTags(d *decode.D, ec elfContext, dc dynamicContext) {
|
|
for i := 0; i < dc.entries; i++ {
|
|
d.FieldStruct("dynamic_tags", func(d *decode.D) {
|
|
elfDecodeDynamicTag(d, ec, dc)
|
|
})
|
|
}
|
|
}
|
|
|
|
func elfDecodeSectionHeader(d *decode.D, ec elfContext, sh sectionHeader) {
|
|
shFlags := func(d *decode.D, archBits int) {
|
|
d.FieldStruct("flags", func(d *decode.D) {
|
|
if d.Endian == decode.LittleEndian {
|
|
d.FieldBool("link_order")
|
|
d.FieldBool("info_link")
|
|
d.FieldBool("strings")
|
|
d.FieldBool("merge")
|
|
d.FieldU1("unused0")
|
|
d.FieldBool("execinstr")
|
|
d.FieldBool("alloc")
|
|
d.FieldBool("write")
|
|
d.FieldBool("tls")
|
|
d.FieldBool("group")
|
|
d.FieldBool("os_nonconforming")
|
|
|
|
d.FieldU9("unused1")
|
|
|
|
d.FieldU8("os_specific")
|
|
d.FieldU4("processor_specific")
|
|
if archBits == 64 {
|
|
d.FieldU32("unused2")
|
|
}
|
|
} else {
|
|
// TODO: add.FieldUnused that is per decoder?
|
|
if archBits == 64 {
|
|
d.FieldU32("unused0")
|
|
}
|
|
d.FieldU4("processor_specific")
|
|
d.FieldU8("os_specific")
|
|
d.FieldU9("unused1")
|
|
d.FieldBool("tls")
|
|
d.FieldBool("group")
|
|
d.FieldBool("os_nonconforming")
|
|
d.FieldBool("link_order")
|
|
d.FieldBool("info_link")
|
|
d.FieldBool("strings")
|
|
d.FieldBool("merge")
|
|
d.FieldU1("unused2")
|
|
d.FieldBool("execinstr")
|
|
d.FieldBool("alloc")
|
|
d.FieldBool("write")
|
|
}
|
|
})
|
|
}
|
|
|
|
var offset int64
|
|
var size int64
|
|
var entSize int64
|
|
var typ uint64
|
|
|
|
switch ec.archBits {
|
|
case 32:
|
|
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)
|
|
offset = int64(d.FieldU("offset", ec.archBits)) * 8
|
|
size = int64(d.FieldU32("size", scalar.ActualHex) * 8)
|
|
d.FieldU32("link")
|
|
d.FieldU32("info")
|
|
d.FieldU32("addralign")
|
|
entSize = int64(d.FieldU32("entsize") * 8)
|
|
case 64:
|
|
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)
|
|
offset = int64(d.FieldU("offset", ec.archBits, scalar.ActualHex) * 8)
|
|
size = int64(d.FieldU64("size") * 8)
|
|
d.FieldU32("link")
|
|
d.FieldU32("info")
|
|
d.FieldU64("addralign")
|
|
entSize = int64(d.FieldU64("entsize") * 8)
|
|
}
|
|
|
|
if typ == SHT_NOBITS {
|
|
// section occupies no space in file
|
|
return
|
|
}
|
|
|
|
d.SeekAbs(offset)
|
|
switch typ {
|
|
case SHT_STRTAB:
|
|
d.FieldUTF8("string", int(size/8))
|
|
case SHT_DYNAMIC:
|
|
d.FieldArray("dynamic_tags", func(d *decode.D) {
|
|
elfDecodeDynamicTags(d, ec, sh.dc)
|
|
})
|
|
case SHT_HASH:
|
|
d.FieldStruct("symbol_hash_table", elfDecodeSymbolHashTable)
|
|
case SHT_SYMTAB:
|
|
d.FieldArray("symbol_table", func(d *decode.D) {
|
|
elfDecodeSymbolTable(d, ec, int(size/entSize), ec.strTabMap[STRTAB_STRTAB])
|
|
})
|
|
case SHT_DYNSYM:
|
|
d.FieldArray("symbol_table", func(d *decode.D) {
|
|
elfDecodeSymbolTable(d, ec, int(size/entSize), ec.strTabMap[STRTAB_DYNSTR])
|
|
})
|
|
case SHT_PROGBITS:
|
|
// TODO: name progbits?
|
|
// TODO: decode opcodes
|
|
d.FieldRawLen("data", size)
|
|
case SHT_GNU_HASH:
|
|
d.FieldStruct("gnu_hash", func(d *decode.D) {
|
|
elfDecodeGNUHash(d, ec, size, ec.strTabMap[STRTAB_DYNSTR])
|
|
})
|
|
default:
|
|
d.FieldRawLen("data", size)
|
|
}
|
|
}
|
|
|
|
func elfDecodeSectionHeaders(d *decode.D, ec elfContext) {
|
|
for i := 0; i < ec.shNum; i++ {
|
|
d.SeekAbs(ec.shOff + int64(i)*ec.shEntSize)
|
|
d.FieldStruct("section_header", func(d *decode.D) {
|
|
elfDecodeSectionHeader(d, ec, ec.sections[i])
|
|
})
|
|
}
|
|
}
|
|
|
|
func elfDecode(d *decode.D, _ any) any {
|
|
var ec elfContext
|
|
|
|
d.FieldStruct("header", func(d *decode.D) { elfDecodeHeader(d, &ec) })
|
|
d.Endian = ec.endian
|
|
// a first pass to find all sections and string table information etc
|
|
elfReadSectionHeaders(d, &ec)
|
|
d.FieldArray("program_headers", func(d *decode.D) {
|
|
elfDecodeProgramHeaders(d, ec)
|
|
})
|
|
d.FieldArray("section_headers", func(d *decode.D) {
|
|
elfDecodeSectionHeaders(d, ec)
|
|
})
|
|
|
|
return nil
|
|
}
|