mirror of
https://github.com/wader/fq.git
synced 2024-12-26 15:02:28 +03:00
840292ba6e
Doing it thru a propery in the decode fn feels a bit hidden and will also not get set on failed decoding. Now array is not range sorted, logic is you care about index number and ordering. Struct is range sorted as you will prefer to fields by name.
930 lines
29 KiB
Go
930 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) {
|
|
uv, ok := s.Actual.(uint64)
|
|
if !ok {
|
|
return s, nil
|
|
}
|
|
s.Sym = strIndexNull(int(uv), 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
|
|
}
|