2021-12-06 21:33:17 +03:00
//nolint:revive
2020-06-08 03:29:51 +03:00
package elf
2021-12-06 21:33:17 +03:00
// https://refspecs.linuxbase.org/elf/gabi4+/contents.html
2020-06-08 03:29:51 +03:00
// https://man7.org/linux/man-pages/man5/elf.5.html
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/elf.h
2021-12-06 21:33:17 +03:00
// TODO: dwarf
2020-06-08 03:29:51 +03:00
import (
"strings"
2021-08-17 13:06:32 +03:00
"github.com/wader/fq/format"
"github.com/wader/fq/pkg/decode"
2022-07-16 19:39:57 +03:00
"github.com/wader/fq/pkg/interp"
2021-12-02 00:48:25 +03:00
"github.com/wader/fq/pkg/scalar"
2020-06-08 03:29:51 +03:00
)
func init ( ) {
2022-07-16 19:39:57 +03:00
interp . RegisterFormat ( decode . Format {
2020-06-08 03:29:51 +03:00
Name : format . ELF ,
Description : "Executable and Linkable Format" ,
Groups : [ ] string { format . PROBE } ,
DecodeFn : elfDecode ,
} )
}
2021-11-05 17:04:26 +03:00
const (
LITTLE_ENDIAN = 1
BIG_ENDIAN = 2
)
2021-12-02 00:48:25 +03:00
var endianNames = scalar . UToSymStr {
2021-12-06 21:33:17 +03:00
LITTLE_ENDIAN : "little_endian" ,
BIG_ENDIAN : "big_endian" ,
2021-11-05 17:04:26 +03:00
}
2021-12-02 00:48:25 +03:00
var classBits = scalar . UToSymU {
2021-11-05 17:04:26 +03:00
1 : 32 ,
2 : 64 ,
}
2021-11-21 15:08:18 +03:00
const (
CLASS_32 = 1
CLASS_64 = 2
)
2021-12-02 00:48:25 +03:00
var osABINames = scalar . UToSymStr {
2021-12-06 21:33:17 +03:00
0 : "sysv" ,
1 : "hpux" ,
2 : "netbsd" ,
3 : "linux" ,
4 : "hurd" ,
2021-11-05 17:04:26 +03:00
5 : "86open" ,
2021-12-06 21:33:17 +03:00
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" } } ,
2021-11-05 17:04:26 +03:00
}
2020-06-08 03:29:51 +03:00
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
2021-12-06 21:33:17 +03:00
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"
2020-06-08 03:29:51 +03:00
)
2021-12-06 21:33:17 +03:00
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" ,
2020-06-08 03:29:51 +03:00
}
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 ]
}
2021-12-02 00:48:25 +03:00
type strTable string
func ( m strTable ) MapScalar ( s scalar . S ) ( scalar . S , error ) {
uv , ok := s . Actual . ( uint64 )
if ! ok {
2021-11-05 17:04:26 +03:00
return s , nil
}
2021-12-02 00:48:25 +03:00
s . Sym = strIndexNull ( int ( uv ) , string ( m ) )
return s , nil
2020-06-08 03:29:51 +03:00
}
2021-12-06 21:33:17 +03:00
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 ) {
2022-01-13 20:34:59 +03:00
d . FramedFn ( size , func ( d * decode . D ) {
2021-12-06 21:33:17 +03:00
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 {
2022-07-01 00:30:56 +03:00
entries int
strTabPtr int64
strSzVal int64
strTab string
symEnt int64
2021-12-06 21:33:17 +03:00
}
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
}
}
2020-06-08 03:29:51 +03:00
2021-12-06 21:33:17 +03:00
return dynamicContext {
2022-07-01 00:30:56 +03:00
entries : entries ,
strTabPtr : strTabPtr ,
strSzVal : strSzVal ,
symEnt : symEnt ,
2021-12-06 21:33:17 +03:00
}
}
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
}
2022-07-22 23:22:33 +03:00
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 ) ) )
}
2021-12-06 21:33:17 +03:00
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 )
}
2022-07-01 00:30:56 +03:00
// 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 ]
2022-07-22 23:22:33 +03:00
sh . dc . strTab = readStrTab ( d , strTabSh . offset , sh . dc . strSzVal / 8 )
2022-07-01 00:30:56 +03:00
}
}
2021-12-06 21:33:17 +03:00
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 ]
2022-07-22 23:22:33 +03:00
shStrTab = readStrTab ( d , sh . offset , sh . size / 8 )
2021-12-06 21:33:17 +03:00
for _ , sh := range ec . sections {
if sh . typ != SHT_STRTAB {
continue
}
2022-07-22 23:22:33 +03:00
ec . strTabMap [ strIndexNull ( sh . name , shStrTab ) ] = readStrTab ( d , sh . offset , sh . size / 8 )
2021-12-06 21:33:17 +03:00
}
}
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 ) {
2021-11-21 15:08:18 +03:00
var class uint64
2020-06-08 03:29:51 +03:00
var archBits int
2021-11-05 17:04:26 +03:00
var endian uint64
2020-06-08 03:29:51 +03:00
2021-11-05 17:04:26 +03:00
d . FieldStruct ( "ident" , func ( d * decode . D ) {
2021-11-16 15:03:45 +03:00
d . FieldRawLen ( "magic" , 4 * 8 , d . AssertBitBuf ( [ ] byte ( "\x7fELF" ) ) )
2021-12-02 00:48:25 +03:00
class = d . FieldU8 ( "class" , classBits )
endian = d . FieldU8 ( "data" , endianNames )
2020-06-08 03:29:51 +03:00
d . FieldU8 ( "version" )
2021-12-02 00:48:25 +03:00
d . FieldU8 ( "os_abi" , osABINames )
2020-06-08 03:29:51 +03:00
d . FieldU8 ( "abi_version" )
2021-12-02 00:48:25 +03:00
d . FieldRawLen ( "pad" , 7 * 8 , d . BitBufIsZero ( ) )
2020-06-08 03:29:51 +03:00
} )
2021-11-21 15:08:18 +03:00
switch class {
case CLASS_32 :
archBits = 32
case CLASS_64 :
archBits = 64
default :
d . Fatalf ( "unknown class %d" , class )
}
2021-11-05 17:04:26 +03:00
switch endian {
case LITTLE_ENDIAN :
d . Endian = decode . LittleEndian
case BIG_ENDIAN :
d . Endian = decode . BigEndian
default :
2021-11-21 15:08:18 +03:00
d . Fatalf ( "unknown endian %d" , endian )
2021-11-05 17:04:26 +03:00
}
2022-05-07 13:46:34 +03:00
d . FieldU16 ( "type" , typeNames , scalar . ActualHex )
machine := d . FieldU16 ( "machine" , machineNames , scalar . ActualHex )
2020-06-08 03:29:51 +03:00
d . FieldU32 ( "version" )
d . FieldU ( "entry" , archBits )
2021-12-06 21:33:17 +03:00
phOff := d . FieldU ( "phoff" , archBits )
shOff := d . FieldU ( "shoff" , archBits )
2020-06-08 03:29:51 +03:00
d . FieldU32 ( "flags" )
d . FieldU16 ( "ehsize" )
2021-12-06 21:33:17 +03:00
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 )
}
2020-06-08 03:29:51 +03:00
2021-12-06 21:33:17 +03:00
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" )
}
} )
2020-06-08 03:29:51 +03:00
}
2022-07-29 14:41:21 +03:00
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" )
}
2020-06-08 03:29:51 +03:00
2022-07-29 14:41:21 +03:00
d . RangeFn ( int64 ( offset * 8 ) , int64 ( size * 8 ) , func ( d * decode . D ) {
d . FieldRawLen ( "data" , d . BitsLeft ( ) )
2021-12-06 21:33:17 +03:00
} )
}
2020-06-08 03:29:51 +03:00
2021-12-06 21:33:17 +03:00
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 )
} )
}
}
2020-06-08 03:29:51 +03:00
2021-12-06 21:33:17 +03:00
func elfDecodeDynamicTag ( d * decode . D , ec elfContext , dc dynamicContext ) {
dtTag := d . FieldU ( "tag" , ec . archBits , dynamicTableMap )
name := "unspecified"
2022-05-07 13:46:34 +03:00
dfMapper := scalar . ActualHex
2021-12-06 21:33:17 +03:00
if de , ok := dynamicTableMap . lookup ( dtTag ) ; ok {
switch de . dUn {
case dUnIgnored :
name = "ignored"
case dUnVal :
name = "val"
2022-05-07 13:46:34 +03:00
dfMapper = scalar . ActualDec
2021-12-06 21:33:17 +03:00
case dUnPtr :
name = "ptr"
2020-06-08 03:29:51 +03:00
}
2021-12-06 21:33:17 +03:00
}
2020-06-08 03:29:51 +03:00
2021-12-06 21:33:17 +03:00
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 )
}
}
2020-06-08 03:29:51 +03:00
2021-12-06 21:33:17 +03:00
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 )
} )
}
}
2020-06-08 03:29:51 +03:00
2021-12-06 21:33:17 +03:00
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" )
2020-06-08 03:29:51 +03:00
}
2021-12-06 21:33:17 +03:00
} 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 ] ) )
2022-05-07 13:46:34 +03:00
typ = d . FieldU32 ( "type" , sectionHeaderTypeMap , scalar . ActualHex )
2021-12-06 21:33:17 +03:00
shFlags ( d , ec . archBits )
2022-05-07 13:46:34 +03:00
d . FieldU ( "addr" , ec . archBits , scalar . ActualHex )
2021-12-06 21:33:17 +03:00
offset = int64 ( d . FieldU ( "offset" , ec . archBits ) ) * 8
2022-05-07 13:46:34 +03:00
size = int64 ( d . FieldU32 ( "size" , scalar . ActualHex ) * 8 )
2021-12-06 21:33:17 +03:00
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 ] ) )
2022-05-07 13:46:34 +03:00
typ = d . FieldU32 ( "type" , sectionHeaderTypeMap , scalar . ActualHex )
2021-12-06 21:33:17 +03:00
shFlags ( d , ec . archBits )
2022-05-07 13:46:34 +03:00
d . FieldU ( "addr" , ec . archBits , scalar . ActualHex )
offset = int64 ( d . FieldU ( "offset" , ec . archBits , scalar . ActualHex ) * 8 )
2021-12-06 21:33:17 +03:00
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 ] )
} )
}
}
2022-07-19 19:33:50 +03:00
func elfDecode ( d * decode . D , _ any ) any {
2021-12-06 21:33:17 +03:00
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 )
2022-05-03 16:42:40 +03:00
d . FieldArray ( "program_headers" , func ( d * decode . D ) {
elfDecodeProgramHeaders ( d , ec )
} )
d . FieldArray ( "section_headers" , func ( d * decode . D ) {
elfDecodeSectionHeaders ( d , ec )
} )
2020-06-08 03:29:51 +03:00
return nil
}