1
1
mirror of https://github.com/wader/fq.git synced 2024-12-24 13:52:02 +03:00
fq/format/elf/elf.go
Mattias Wadman 9b81d4d3ab decode: More type safe API and split scalar into multiple types
Preparation to make decoder use less memory and API more type safe.
Now each scalar type has it's own struct type so it can store different
things and enables to have a scalar interface.
Also own types will enable experimenting with decode DLS designs like
using chained methods that are type aware.
2022-12-14 16:23:58 +01:00

1119 lines
39 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
// https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=include/elf/external.h;hb=HEAD
// 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.UintMapSymStr{
LITTLE_ENDIAN: "little_endian",
BIG_ENDIAN: "big_endian",
}
var classBits = scalar.UintMapSymUint{
1: 32,
2: 64,
}
const (
CLASS_32 = 1
CLASS_64 = 2
)
var osABINames = scalar.UintMapSymStr{
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",
}
const (
ET_NONE = 0
ET_REL = 1
ET_EXEC = 2
ET_DYN = 3
ET_CORE = 4
)
var typeNames = scalar.UintRangeToScalar{
{Range: [2]uint64{ET_NONE, ET_NONE}, S: scalar.Uint{Sym: "none"}},
{Range: [2]uint64{ET_REL, ET_REL}, S: scalar.Uint{Sym: "rel"}},
{Range: [2]uint64{ET_EXEC, ET_EXEC}, S: scalar.Uint{Sym: "exec"}},
{Range: [2]uint64{ET_DYN, ET_DYN}, S: scalar.Uint{Sym: "dyn"}},
{Range: [2]uint64{ET_CORE, ET_CORE}, S: scalar.Uint{Sym: "core"}},
{Range: [2]uint64{0xfe00, 0xfeff}, S: scalar.Uint{Sym: "os"}},
{Range: [2]uint64{0xff00, 0xffff}, S: scalar.Uint{Sym: "proc"}},
}
const (
EM_X86_64 = 0x3e
EM_ARM64 = 0xb7
)
var machineNames = scalar.UintMap{
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"},
}
const (
PT_NULL = 0
PT_LOAD = 1
PT_DYNAMIC = 2
PT_INTERP = 3
PT_NOTE = 4
PT_SHLIB = 5
PT_PHDR = 6
PT_TLS = 7
)
var phTypeNames = scalar.UintRangeToScalar{
{Range: [2]uint64{PT_NULL, PT_NULL}, S: scalar.Uint{Sym: "null", Description: "Unused element"}},
{Range: [2]uint64{PT_LOAD, PT_LOAD}, S: scalar.Uint{Sym: "load", Description: "Loadable segment"}},
{Range: [2]uint64{PT_DYNAMIC, PT_DYNAMIC}, S: scalar.Uint{Sym: "dynamic", Description: "Dynamic linking information"}},
{Range: [2]uint64{PT_INTERP, PT_INTERP}, S: scalar.Uint{Sym: "interp", Description: "Interpreter to invoke"}},
{Range: [2]uint64{PT_NOTE, PT_NOTE}, S: scalar.Uint{Sym: "note", Description: "Auxiliary information"}},
{Range: [2]uint64{PT_SHLIB, PT_SHLIB}, S: scalar.Uint{Sym: "shlib", Description: "Reserved but has unspecified"}},
{Range: [2]uint64{PT_PHDR, PT_PHDR}, S: scalar.Uint{Sym: "phdr", Description: "Program header location and size"}},
{Range: [2]uint64{PT_TLS, PT_TLS}, S: scalar.Uint{Sym: "tls", Description: "Thread-Local Storage template"}},
{Range: [2]uint64{0x6474e550, 0x6474e550}, S: scalar.Uint{Sym: "gnu_eh_frame", Description: "GNU frame unwind information"}},
{Range: [2]uint64{0x6474e551, 0x6474e551}, S: scalar.Uint{Sym: "gnu_stack", Description: "GNU stack permission"}},
{Range: [2]uint64{0x6474e552, 0x6474e552}, S: scalar.Uint{Sym: "gnu_relro", Description: "GNU read-only after relocation"}},
{Range: [2]uint64{0x60000000, 0x6fffffff}, S: scalar.Uint{Sym: "os", Description: "Operating system-specific"}},
{Range: [2]uint64{0x70000000, 0x7fffffff}, S: scalar.Uint{Sym: "proc", Description: "Processor-specific"}},
}
const (
NT_PRSTATUS = 1
NT_PRFPREG = 2
NT_PRPSINFO = 3
NT_TASKSTRUCT = 4
NT_AUXV = 6
NT_SIGINFO = 0x53494749 // "SIGI"
NT_FILE = 0x46494c45 // "FILE"
NT_PRXFPREG = 0x46e62b7f
NT_PPC_VMX = 0x100
NT_PPC_SPE = 0x101
NT_PPC_VSX = 0x102
NT_PPC_TAR = 0x103
NT_PPC_PPR = 0x104
NT_PPC_DSCR = 0x105
NT_PPC_EBB = 0x106
NT_PPC_PMU = 0x107
NT_PPC_TM_CGPR = 0x108
NT_PPC_TM_CFPR = 0x109
NT_PPC_TM_CVMX = 0x10a
NT_PPC_TM_CVSX = 0x10b
NT_PPC_TM_SPR = 0x10c
NT_PPC_TM_CTAR = 0x10d
NT_PPC_TM_CPPR = 0x10e
NT_PPC_TM_CDSCR = 0x10f
NT_PPC_PKEY = 0x110
NT_386_TLS = 0x200
NT_386_IOPERM = 0x201
NT_X86_XSTATE = 0x202
NT_S390_HIGH_GPRS = 0x300
NT_S390_TIMER = 0x301
NT_S390_TODCMP = 0x302
NT_S390_TODPREG = 0x303
NT_S390_CTRS = 0x304
NT_S390_PREFIX = 0x305
NT_S390_LAST_BREAK = 0x306
NT_S390_SYSTEM_CALL = 0x307
NT_S390_TDB = 0x308
NT_S390_VXRS_LOW = 0x309
NT_S390_VXRS_HIGH = 0x30a
NT_S390_GS_CB = 0x30b
NT_S390_GS_BC = 0x30c
NT_S390_RI_CB = 0x30d
NT_S390_PV_CPU_DATA = 0x30e
NT_ARM_VFP = 0x400
NT_ARM_TLS = 0x401
NT_ARM_HW_BREAK = 0x402
NT_ARM_HW_WATCH = 0x403
NT_ARM_SYSTEM_CALL = 0x404
NT_ARM_SVE = 0x405
NT_ARM_PAC_MASK = 0x406
NT_ARM_PACA_KEYS = 0x407
NT_ARM_PACG_KEYS = 0x408
NT_ARM_TAGGED_ADDR_CTRL = 0x409
NT_ARM_PAC_ENABLED_KEYS = 0x40a
NT_ARM_SSVE = 0x40b
NT_ARM_ZA = 0x40c
NT_ARC_V2 = 0x600
NT_VMCOREDD = 0x700
NT_MIPS_DSP = 0x800
NT_MIPS_FP_MODE = 0x801
NT_MIPS_MSA = 0x802
NT_LOONGARCH_CPUCFG = 0xa00
NT_LOONGARCH_CSR = 0xa01
NT_LOONGARCH_LSX = 0xa02
NT_LOONGARCH_LASX = 0xa03
NT_LOONGARCH_LBT = 0xa04
)
var coreNoteNames = scalar.UintMap{
NT_PRSTATUS: {Sym: "prstatus"},
NT_PRFPREG: {Sym: "prfpreg"},
NT_PRPSINFO: {Sym: "prpsinfo"},
NT_TASKSTRUCT: {Sym: "taskstruct"},
NT_AUXV: {Sym: "auxv"},
NT_SIGINFO: {Sym: "siginfo", Description: "Signal info"},
NT_FILE: {Sym: "file", Description: "File info"},
NT_PRXFPREG: {Sym: "prxfpreg"},
NT_PPC_SPE: {Sym: "ppc_spe", Description: "PowerPC SPE/EVR registers"},
NT_PPC_VSX: {Sym: "ppc_vsx", Description: "PowerPC VSX registers"},
NT_PPC_TAR: {Sym: "ppc_tar", Description: "Target Address Register"},
NT_PPC_PPR: {Sym: "ppc_ppr", Description: "Program Priority Register"},
NT_PPC_DSCR: {Sym: "ppc_dscr", Description: "Data Stream Control Register"},
NT_PPC_EBB: {Sym: "ppc_ebb", Description: "Event Based Branch Registers"},
NT_PPC_PMU: {Sym: "ppc_pmu", Description: "Performance Monitor Registers"},
NT_PPC_TM_CGPR: {Sym: "ppc_tm_cgpr", Description: "TM checkpointed GPR Registers"},
NT_PPC_TM_CFPR: {Sym: "ppc_tm_cfpr", Description: "TM checkpointed FPR Registers"},
NT_PPC_TM_CVMX: {Sym: "ppc_tm_cvmx", Description: "TM checkpointed VMX Registers"},
NT_PPC_TM_CVSX: {Sym: "ppc_tm_cvsx", Description: "TM checkpointed VSX Registers"},
NT_PPC_TM_SPR: {Sym: "ppc_tm_spr", Description: "TM Special Purpose Registers"},
NT_PPC_TM_CTAR: {Sym: "ppc_tm_ctar", Description: "TM checkpointed Target Address Register"},
NT_PPC_TM_CPPR: {Sym: "ppc_tm_cppr", Description: "TM checkpointed Program Priority Register"},
NT_PPC_TM_CDSCR: {Sym: "ppc_tm_cdscr", Description: "TM checkpointed Data Stream Control Register"},
NT_PPC_PKEY: {Sym: "ppc_pkey", Description: "Memory Protection Keys registers"},
NT_386_TLS: {Sym: "386_tls", Description: "i386 TLS slots (struct user_desc)"},
NT_386_IOPERM: {Sym: "386_ioperm", Description: "x86 io permission bitmap (1=deny)"},
NT_X86_XSTATE: {Sym: "x86_xstate", Description: "x86 extended state using xsave"},
NT_S390_HIGH_GPRS: {Sym: "s390_high_gprs", Description: "s390 upper register halves"},
NT_S390_TIMER: {Sym: "s390_timer", Description: "s390 timer register"},
NT_S390_TODCMP: {Sym: "s390_todcmp", Description: "s390 TOD clock comparator register"},
NT_S390_TODPREG: {Sym: "s390_todpreg", Description: "s390 TOD programmable register"},
NT_S390_CTRS: {Sym: "s390_ctrs", Description: "s390 control registers"},
NT_S390_PREFIX: {Sym: "s390_prefix", Description: "s390 prefix register"},
NT_S390_LAST_BREAK: {Sym: "s390_last_break", Description: "s390 breaking event address"},
NT_S390_SYSTEM_CALL: {Sym: "s390_system_call", Description: "s390 system call restart data"},
NT_S390_TDB: {Sym: "s390_tdb", Description: "s390 transaction diagnostic block"},
NT_S390_VXRS_LOW: {Sym: "s390_vxrs_low", Description: "s390 vector registers 0-15 upper half"},
NT_S390_VXRS_HIGH: {Sym: "s390_vxrs_high", Description: "s390 vector registers 16-31"},
NT_S390_GS_CB: {Sym: "s390_gs_cb", Description: "s390 guarded storage registers"},
NT_S390_GS_BC: {Sym: "s390_gs_bc", Description: "s390 guarded storage broadcast control block"},
NT_S390_RI_CB: {Sym: "s390_ri_cb", Description: "s390 runtime instrumentation"},
NT_S390_PV_CPU_DATA: {Sym: "s390_pv_cpu_data", Description: "s390 protvirt cpu dump data"},
NT_ARM_VFP: {Sym: "arm_vfp", Description: "ARM VFP/NEON registers"},
NT_ARM_TLS: {Sym: "arm_tls", Description: "ARM TLS register"},
NT_ARM_HW_BREAK: {Sym: "arm_hw_break", Description: "ARM hardware breakpoint registers"},
NT_ARM_HW_WATCH: {Sym: "arm_hw_watch", Description: "ARM hardware watchpoint registers"},
NT_ARM_SYSTEM_CALL: {Sym: "arm_system_call", Description: "ARM system call number"},
NT_ARM_SVE: {Sym: "arm_sve", Description: "ARM Scalable Vector Extension registers"},
NT_ARM_PAC_MASK: {Sym: "arm_pac_mask", Description: "ARM pointer authentication code masks"},
NT_ARM_PACA_KEYS: {Sym: "arm_paca_keys", Description: "ARM pointer authentication address keys"},
NT_ARM_PACG_KEYS: {Sym: "arm_pacg_keys", Description: "ARM pointer authentication generic key"},
NT_ARM_TAGGED_ADDR_CTRL: {Sym: "arm_tagged_addr_ctrl", Description: "arm64 tagged address control (prctl())"},
NT_ARM_PAC_ENABLED_KEYS: {Sym: "arm_pac_enabled_keys", Description: "arm64 ptr auth enabled keys (prctl())"},
NT_ARM_SSVE: {Sym: "arm_ssve", Description: "ARM Streaming SVE registers"},
NT_ARM_ZA: {Sym: "arm_za", Description: "ARM SME ZA registers"},
NT_ARC_V2: {Sym: "arc_v2", Description: "ARCv2 accumulator/extra registers"},
NT_VMCOREDD: {Sym: "vmcoredd", Description: "Vmcore Device Dump Note"},
NT_MIPS_DSP: {Sym: "mips_dsp", Description: "MIPS DSP ASE registers"},
NT_MIPS_FP_MODE: {Sym: "mips_fp_mode", Description: "MIPS floating-point mode"},
NT_MIPS_MSA: {Sym: "mips_msa", Description: "MIPS SIMD registers"},
NT_LOONGARCH_CPUCFG: {Sym: "loongarch_cpucfg", Description: "LoongArch CPU config registers"},
NT_LOONGARCH_CSR: {Sym: "loongarch_csr", Description: "LoongArch control and status registers"},
NT_LOONGARCH_LSX: {Sym: "loongarch_lsx", Description: "LoongArch Loongson SIMD Extension registers"},
NT_LOONGARCH_LASX: {Sym: "loongarch_lasx", Description: "LoongArch Loongson Advanced SIMD Extension registers"},
NT_LOONGARCH_LBT: {Sym: "loongarch_lbt", Description: "LoongArch Loongson Binary Translation registers"},
}
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.UintMap{
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.Uint
}
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) MapUint(s scalar.Uint) (scalar.Uint, error) {
u := s.Actual
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.Uint{Sym: "null", Description: "Marks end of dynamic section"}},
{r: [2]uint64{DT_NEEDED, DT_NEEDED}, dUn: dUnVal, s: scalar.Uint{Sym: "needed", Description: "String table offset to name of a needed library"}},
{r: [2]uint64{DT_PLTRELSZ, DT_PLTRELSZ}, dUn: dUnVal, s: scalar.Uint{Sym: "pltrelsz", Description: "Size in bytes of PLT relocation entries"}},
{r: [2]uint64{DT_PLTGOT, DT_PLTGOT}, dUn: dUnPtr, s: scalar.Uint{Sym: "pltgot", Description: "Address of PLT and/or GOT"}},
{r: [2]uint64{DT_HASH, DT_HASH}, dUn: dUnPtr, s: scalar.Uint{Sym: "hash", Description: "Address of symbol hash table"}},
{r: [2]uint64{DT_STRTAB, DT_STRTAB}, dUn: dUnPtr, s: scalar.Uint{Sym: "strtab", Description: "Address of string table"}},
{r: [2]uint64{DT_SYMTAB, DT_SYMTAB}, dUn: dUnPtr, s: scalar.Uint{Sym: "symtab", Description: "Address of symbol table"}},
{r: [2]uint64{DT_RELA, DT_RELA}, dUn: dUnPtr, s: scalar.Uint{Sym: "rela", Description: "Address of Rela relocation table"}},
{r: [2]uint64{DT_RELASZ, DT_RELASZ}, dUn: dUnVal, s: scalar.Uint{Sym: "relasz", Description: "Size in bytes of the Rela relocation table"}},
{r: [2]uint64{DT_RELAENT, DT_RELAENT}, dUn: dUnVal, s: scalar.Uint{Sym: "relaent", Description: "Size in bytes of a Rela relocation table entry"}},
{r: [2]uint64{DT_STRSZ, DT_STRSZ}, dUn: dUnVal, s: scalar.Uint{Sym: "strsz", Description: "Size in bytes of string table"}},
{r: [2]uint64{DT_SYMENT, DT_SYMENT}, dUn: dUnVal, s: scalar.Uint{Sym: "syment", Description: "Size in bytes of a symbol table entry"}},
{r: [2]uint64{DT_INIT, DT_INIT}, dUn: dUnPtr, s: scalar.Uint{Sym: "init", Description: "Address of the initialization function"}},
{r: [2]uint64{DT_FINI, DT_FINI}, dUn: dUnPtr, s: scalar.Uint{Sym: "fini", Description: "Address of the termination function"}},
{r: [2]uint64{DT_SONAME, DT_SONAME}, dUn: dUnVal, s: scalar.Uint{Sym: "soname", Description: "String table offset to name of shared object"}},
{r: [2]uint64{DT_RPATH, DT_RPATH}, dUn: dUnVal, s: scalar.Uint{Sym: "rpath", Description: "String table offset to library search path (deprecated)"}},
{r: [2]uint64{DT_SYMBOLIC, DT_SYMBOLIC}, dUn: dUnIgnored, s: scalar.Uint{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.Uint{Sym: "rel", Description: ""}},
{r: [2]uint64{DT_RELSZ, DT_RELSZ}, dUn: dUnVal, s: scalar.Uint{Sym: "relsz", Description: "Size in bytes of Rel relocation table"}},
{r: [2]uint64{DT_RELENT, DT_RELENT}, dUn: dUnVal, s: scalar.Uint{Sym: "relent", Description: "Size in bytes of a Rel table entry"}},
{r: [2]uint64{DT_PLTREL, DT_PLTREL}, dUn: dUnVal, s: scalar.Uint{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.Uint{Sym: "debug", Description: "Undefined use for debugging"}},
{r: [2]uint64{DT_TEXTREL, DT_TEXTREL}, dUn: dUnIgnored, s: scalar.Uint{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.Uint{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.Uint{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.Uint{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.Uint{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.Uint{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.Uint{Sym: "fini_arraysz", Description: "Size in bytes of the array of termination functions "}},
{r: [2]uint64{DT_RUNPATH, DT_RUNPATH}, dUn: dUnVal, s: scalar.Uint{Sym: "runpath", Description: "String table offset to library search path"}},
{r: [2]uint64{DT_FLAGS, DT_FLAGS}, dUn: dUnVal, s: scalar.Uint{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.Uint{Sym: "encoding", Description: ""}}, // or DT_PREINIT_ARRAY }},
{r: [2]uint64{DT_PREINIT_ARRAYSZ, DT_PREINIT_ARRAYSZ}, dUn: dUnVal, s: scalar.Uint{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.Uint{Sym: "lo", Description: "Operating system-specific semantics"}},
{r: [2]uint64{DT_LOPROC, DT_HIPROC}, dUn: dUnUnspecified, s: scalar.Uint{Sym: "proc", Description: "Processor-specific semantics"}},
}
var symbolTableBindingMap = scalar.UintMapSymStr{
0: "local",
1: "global",
2: "weak",
10: "loos",
12: "hios",
13: "proc",
14: "proc",
15: "proc",
}
var symbolTableTypeMap = scalar.UintMapSymStr{
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.UintMapSymStr{
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) MapUint(s scalar.Uint) (scalar.Uint, error) {
s.Sym = strIndexNull(int(s.Actual), 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)
}
}
// provide default empty string tables to be more robust
ec.strTabMap = map[string]string{
STRTAB_DYNSTR: "",
STRTAB_SHSTRTAB: "",
STRTAB_STRTAB: "",
}
var shStrTab string
if ec.shStrNdx < len(ec.sections) {
shStr := ec.sections[ec.shStrNdx]
shStrTab = readStrTab(d, shStr.offset, shStr.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
typ 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)
}
typ := d.FieldU16("type", typeNames, scalar.UintHex)
machine := d.FieldU16("machine", machineNames, scalar.UintHex)
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.typ = int(typ)
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 typ uint64
var offset uint64
var size uint64
switch ec.archBits {
case 32:
typ = d.FieldU32("type", phTypeNames)
offset = d.FieldU("offset", ec.archBits, scalar.UintHex)
d.FieldU("vaddr", ec.archBits, scalar.UintHex)
d.FieldU("paddr", ec.archBits, scalar.UintHex)
size = d.FieldU32("filesz")
d.FieldU32("memsz")
pFlags(d)
d.FieldU32("align")
case 64:
typ = d.FieldU32("type", phTypeNames)
pFlags(d)
offset = d.FieldU("offset", ec.archBits, scalar.UintHex)
d.FieldU("vaddr", ec.archBits, scalar.UintHex)
d.FieldU("paddr", ec.archBits, scalar.UintHex)
size = d.FieldU64("filesz")
d.FieldU64("memsz")
d.FieldU64("align")
}
d.RangeFn(int64(offset*8), int64(size*8), func(d *decode.D) {
switch {
case typ == PT_NOTE:
d.FieldArray("notes", func(d *decode.D) {
for !d.End() {
d.FieldStruct("note", func(d *decode.D) {
// elf manpage says this is 32 or 64 bit but it seems it is always 32
// and that is also what readelf external.h says
nameSz := d.FieldU32("n_namesz")
descSz := d.FieldU32("n_descsz")
if ec.typ == ET_CORE {
d.FieldU32("n_type", coreNoteNames, scalar.UintHex)
} else {
d.FieldU32("n_type", scalar.UintHex)
}
d.FieldUTF8NullFixedLen("name", int(nameSz))
nameAlign := d.AlignBits(4 * 8)
if nameAlign != 0 {
d.FieldRawLen("name_align", int64(nameAlign))
}
d.FieldRawLen("desc", int64(descSz)*8)
descAlign := d.AlignBits(4 * 8)
if descAlign != 0 {
d.FieldRawLen("decs_align", int64(descAlign))
}
})
}
})
default:
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.UintHex
if de, ok := dynamicTableMap.lookup(dtTag); ok {
switch de.dUn {
case dUnIgnored:
name = "ignored"
case dUnVal:
name = "val"
dfMapper = scalar.UintDec
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.FieldValueUint("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.FieldValueUint("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.UintHex)
shFlags(d, ec.archBits)
d.FieldU("addr", ec.archBits, scalar.UintHex)
offset = int64(d.FieldU("offset", ec.archBits)) * 8
size = int64(d.FieldU32("size", scalar.UintHex) * 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.UintHex)
shFlags(d, ec.archBits)
d.FieldU("addr", ec.archBits, scalar.UintHex)
offset = int64(d.FieldU("offset", ec.archBits, scalar.UintHex) * 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
}