448 lines
15 KiB
Lua
448 lines
15 KiB
Lua
--
|
|
-- elf.lua: Executable and Linkable Format
|
|
--
|
|
-- Copyright (c) 2017, Přemysl Janouch <p@janouch.name>
|
|
--
|
|
-- Permission to use, copy, modify, and/or distribute this software for any
|
|
-- purpose with or without fee is hereby granted.
|
|
--
|
|
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
-- SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
-- OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
-- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
--
|
|
|
|
-- See man 5 elf, /usr/include/elf.h and /usr/include/llvm/Support/ELF.h
|
|
|
|
local detect = function (c)
|
|
return c:read (4) == "\x7FELF"
|
|
end
|
|
|
|
local ph_type_table = {
|
|
[0] = "unused segment",
|
|
[1] = "loadable segment",
|
|
[2] = "dynamic linking information",
|
|
[3] = "interpreter pathname",
|
|
[4] = "auxiliary information",
|
|
[5] = "reserved",
|
|
[6] = "the program header table itself",
|
|
[7] = "the thread-local storage template"
|
|
}
|
|
|
|
local xform_ph_flags = function (u32)
|
|
local info = {}
|
|
if u32 & 0x4 ~= 0 then table.insert (info, "read") end
|
|
if u32 & 0x2 ~= 0 then table.insert (info, "write") end
|
|
if u32 & 0x1 ~= 0 then table.insert (info, "execute") end
|
|
|
|
local junk = u32 & ~0x7
|
|
if junk ~= 0 then
|
|
table.insert (info, ("unknown: %#x"):format (junk))
|
|
end
|
|
|
|
if #info == 0 then return 0 end
|
|
|
|
local result = info[1]
|
|
for i = 2, #info do result = result .. ", " .. info[i] end
|
|
return result
|
|
end
|
|
|
|
local decode_ph = function (elf, c)
|
|
local ph = {}
|
|
ph.type = c:u32 ("type: %s", function (u32)
|
|
-- TODO: there are more known weird values and ranges
|
|
name = ph_type_table[u32]
|
|
if name then return name end
|
|
return "unknown: %#x", u32
|
|
end)
|
|
if elf.class == 2 then ph.flags = c:u32 ("flags: %s", xform_ph_flags) end
|
|
ph.offset = elf.uwide (c, "offset in file: %#x")
|
|
ph.vaddr = elf.uwide (c, "virtual address: %#x")
|
|
ph.paddr = elf.uwide (c, "physical address: %#x")
|
|
ph.filesz = elf.uwide (c, "size in file: %d")
|
|
ph.memsz = elf.uwide (c, "sise in memory: %d")
|
|
if elf.class == 1 then ph.flags = c:u32 ("flags: %s", xform_ph_flags) end
|
|
ph.align = elf.uwide (c, "alignment: %d")
|
|
return ph
|
|
end
|
|
|
|
local sh_type_table = {
|
|
[0] = "null entry",
|
|
[1] = "program-defined contents",
|
|
[2] = "symbol table",
|
|
[3] = "string table",
|
|
[4] = "relocation entries",
|
|
[5] = "symbol hash table",
|
|
[6] = "dynamic linking information",
|
|
[7] = "information about the file",
|
|
[8] = "data occupies no space in file",
|
|
[9] = "relocation entries",
|
|
[10] = "reserved",
|
|
[11] = "symbol table",
|
|
[14] = "pointers to initialization functions",
|
|
[15] = "pointers to terminnation functions",
|
|
[16] = "pointers to pre-init functions",
|
|
[17] = "section group",
|
|
[18] = "indices for SHN_XINDEX entries"
|
|
}
|
|
|
|
local xform_sh_flags = function (u)
|
|
-- TODO: there are more known weird values and ranges
|
|
local info = {}
|
|
if u & 0x1 ~= 0 then table.insert (info, "write") end
|
|
if u & 0x2 ~= 0 then table.insert (info, "alloc") end
|
|
if u & 0x4 ~= 0 then table.insert (info, "execinstr") end
|
|
|
|
local junk = u & ~0x7
|
|
if junk ~= 0 then
|
|
table.insert (info, ("unknown: %#x"):format (junk))
|
|
end
|
|
|
|
if #info == 0 then return 0 end
|
|
|
|
local result = info[1]
|
|
for i = 2, #info do result = result .. ", " .. info[i] end
|
|
return result
|
|
end
|
|
|
|
local decode_sh = function (elf, c)
|
|
local sh = {}
|
|
-- TODO: decode the values, give the fields meaning
|
|
sh.name = c:u32 ("name index: %d")
|
|
sh.type = c:u32 ("type: %s", function (u32)
|
|
-- TODO: there are more known weird values and ranges
|
|
name = sh_type_table[u32]
|
|
if name then return name end
|
|
return "unknown: %#x", u32
|
|
end)
|
|
sh.flags = elf.uwide (c, "flags: %s", xform_sh_flags)
|
|
sh.addr = elf.uwide (c, "load address: %#x")
|
|
sh.offset = elf.uwide (c, "offset in file: %#x")
|
|
sh.size = elf.uwide (c, "size: %d")
|
|
sh.link = c:u32 ("header table index link: %d")
|
|
sh.info = c:u32 ("extra information: %d")
|
|
sh.addralign = elf.uwide (c, "address alignment: %d")
|
|
sh.entsize = elf.uwide (c, "size of records: %d")
|
|
return sh
|
|
end
|
|
|
|
local abi_table = {
|
|
[0] = "UNIX System V ABI",
|
|
[1] = "HP-UX operating system",
|
|
[2] = "NetBSD",
|
|
[3] = "GNU/Linux",
|
|
[4] = "GNU/Hurd",
|
|
[6] = "Solaris",
|
|
[7] = "AIX",
|
|
[8] = "IRIX",
|
|
[9] = "FreeBSD",
|
|
[10] = "TRU64 UNIX",
|
|
[11] = "Novell Modesto",
|
|
[12] = "OpenBSD",
|
|
[13] = "OpenVMS",
|
|
[14] = "Hewlett-Packard Non-Stop Kernel",
|
|
[15] = "AROS",
|
|
[16] = "FenixOS",
|
|
[17] = "Nuxi CloudABI",
|
|
[64] = "Bare-metal TMS320C6000",
|
|
[64] = "AMD HSA runtime",
|
|
[65] = "Linux TMS320C6000",
|
|
[97] = "ARM",
|
|
[255] = "Standalone (embedded) application"
|
|
}
|
|
|
|
local type_table = {
|
|
[0] = "no file type",
|
|
[1] = "relocatable file",
|
|
[2] = "executable file",
|
|
[3] = "shared object file",
|
|
[4] = "core file"
|
|
}
|
|
|
|
local machine_table = {
|
|
[0] = "no machine",
|
|
[1] = "AT&T WE 32100",
|
|
[2] = "SPARC",
|
|
[3] = "Intel 386",
|
|
[4] = "Motorola 68000",
|
|
[5] = "Motorola 88000",
|
|
[6] = "Intel MCU",
|
|
[7] = "Intel 80860",
|
|
[8] = "MIPS R3000",
|
|
[9] = "IBM System/370",
|
|
[10] = "MIPS RS3000 Little-endian",
|
|
[15] = "Hewlett-Packard PA-RISC",
|
|
[17] = "Fujitsu VPP500",
|
|
[18] = "Enhanced instruction set SPARC",
|
|
[19] = "Intel 80960",
|
|
[20] = "PowerPC",
|
|
[21] = "PowerPC64",
|
|
[22] = "IBM System/390",
|
|
[23] = "IBM SPU/SPC",
|
|
[36] = "NEC V800",
|
|
[37] = "Fujitsu FR20",
|
|
[38] = "TRW RH-32",
|
|
[39] = "Motorola RCE",
|
|
[40] = "ARM",
|
|
[41] = "DEC Alpha",
|
|
[42] = "Hitachi SH",
|
|
[43] = "SPARC V9",
|
|
[44] = "Siemens TriCore",
|
|
[45] = "Argonaut RISC Core",
|
|
[46] = "Hitachi H8/300",
|
|
[47] = "Hitachi H8/300H",
|
|
[48] = "Hitachi H8S",
|
|
[49] = "Hitachi H8/500",
|
|
[50] = "Intel IA-64 processor architecture",
|
|
[51] = "Stanford MIPS-X",
|
|
[52] = "Motorola ColdFire",
|
|
[53] = "Motorola M68HC12",
|
|
[54] = "Fujitsu MMA Multimedia Accelerator",
|
|
[55] = "Siemens PCP",
|
|
[56] = "Sony nCPU embedded RISC processor",
|
|
[57] = "Denso NDR1 microprocessor",
|
|
[58] = "Motorola Star*Core processor",
|
|
[59] = "Toyota ME16 processor",
|
|
[60] = "STMicroelectronics ST100 processor",
|
|
[61] = "Advanced Logic Corp. TinyJ embedded processor family",
|
|
[62] = "AMD x86-64 architecture",
|
|
[63] = "Sony DSP Processor",
|
|
[64] = "Digital Equipment Corp. PDP-10",
|
|
[65] = "Digital Equipment Corp. PDP-11",
|
|
[66] = "Siemens FX66 microcontroller",
|
|
[67] = "STMicroelectronics ST9+ 8/16 bit microcontroller",
|
|
[68] = "STMicroelectronics ST7 8-bit microcontroller",
|
|
[69] = "Motorola MC68HC16 Microcontroller",
|
|
[70] = "Motorola MC68HC11 Microcontroller",
|
|
[71] = "Motorola MC68HC08 Microcontroller",
|
|
[72] = "Motorola MC68HC05 Microcontroller",
|
|
[73] = "Silicon Graphics SVx",
|
|
[74] = "STMicroelectronics ST19 8-bit microcontroller",
|
|
[75] = "Digital VAX",
|
|
[76] = "Axis Communications 32-bit embedded processor",
|
|
[77] = "Infineon Technologies 32-bit embedded processor",
|
|
[78] = "Element 14 64-bit DSP Processor",
|
|
[79] = "LSI Logic 16-bit DSP Processor",
|
|
[80] = "Donald Knuth's educational 64-bit processor",
|
|
[81] = "Harvard University machine-independent object files",
|
|
[82] = "SiTera Prism",
|
|
[83] = "Atmel AVR 8-bit microcontroller",
|
|
[84] = "Fujitsu FR30",
|
|
[85] = "Mitsubishi D10V",
|
|
[86] = "Mitsubishi D30V",
|
|
[87] = "NEC v850",
|
|
[88] = "Mitsubishi M32R",
|
|
[89] = "Matsushita MN10300",
|
|
[90] = "Matsushita MN10200",
|
|
[91] = "picoJava",
|
|
[92] = "OpenRISC 32-bit embedded processor",
|
|
[93] = "ARC International ARCompact processor",
|
|
[94] = "Tensilica Xtensa Architecture",
|
|
[95] = "Alphamosaic VideoCore processor",
|
|
[96] = "Thompson Multimedia General Purpose Processor",
|
|
[97] = "National Semiconductor 32000 series",
|
|
[98] = "Tenor Network TPC processor",
|
|
[99] = "Trebia SNP 1000 processor",
|
|
[100] = "STMicroelectronics (www.st.com) ST200",
|
|
[101] = "Ubicom IP2xxx microcontroller family",
|
|
[102] = "MAX Processor",
|
|
[103] = "National Semiconductor CompactRISC microprocessor",
|
|
[104] = "Fujitsu F2MC16",
|
|
[105] = "Texas Instruments embedded microcontroller msp430",
|
|
[106] = "Analog Devices Blackfin (DSP) processor",
|
|
[107] = "S1C33 Family of Seiko Epson processors",
|
|
[108] = "Sharp embedded microprocessor",
|
|
[109] = "Arca RISC Microprocessor",
|
|
[110] = "microprocessor series from PKU-Unity Ltd." ..
|
|
" and MPRC of Peking University",
|
|
[111] = "eXcess: 16/32/64-bit configurable embedded CPU",
|
|
[112] = "Icera Semiconductor Inc. Deep Execution Processor",
|
|
[113] = "Altera Nios II soft-core processor",
|
|
[114] = "National Semiconductor CompactRISC CRX",
|
|
[115] = "Motorola XGATE embedded processor",
|
|
[116] = "Infineon C16x/XC16x processor",
|
|
[117] = "Renesas M16C series microprocessors",
|
|
[118] = "Microchip Technology dsPIC30F Digital Signal Controller",
|
|
[119] = "Freescale Communication Engine RISC core",
|
|
[120] = "Renesas M32C series microprocessors",
|
|
[131] = "Altium TSK3000 core",
|
|
[132] = "Freescale RS08 embedded processor",
|
|
[133] = "Analog Devices SHARC family of 32-bit DSP processors",
|
|
[134] = "Cyan Technology eCOG2 microprocessor",
|
|
[135] = "Sunplus S+core7 RISC processor",
|
|
[136] = "New Japan Radio (NJR) 24-bit DSP Processor",
|
|
[137] = "Broadcom VideoCore III processor",
|
|
[138] = "RISC processor for Lattice FPGA architecture",
|
|
[139] = "Seiko Epson C17 family",
|
|
[140] = "The Texas Instruments TMS320C6000 DSP family",
|
|
[141] = "The Texas Instruments TMS320C2000 DSP family",
|
|
[142] = "The Texas Instruments TMS320C55x DSP family",
|
|
[160] = "STMicroelectronics 64bit VLIW Data Signal Processor",
|
|
[161] = "Cypress M8C microprocessor",
|
|
[162] = "Renesas R32C series microprocessors",
|
|
[163] = "NXP Semiconductors TriMedia architecture family",
|
|
[164] = "Qualcomm Hexagon processor",
|
|
[165] = "Intel 8051 and variants",
|
|
[166] = "STMicroelectronics STxP7x family of configurable" ..
|
|
" and extensible RISC processors",
|
|
[167] = "Andes Technology compact code size embedded RISC processor family",
|
|
[168] = "Cyan Technology eCOG1X family",
|
|
[168] = "Cyan Technology eCOG1X family",
|
|
[169] = "Dallas Semiconductor MAXQ30 Core Micro-controllers",
|
|
[170] = "New Japan Radio (NJR) 16-bit DSP Processor",
|
|
[171] = "M2000 Reconfigurable RISC Microprocessor",
|
|
[172] = "Cray Inc. NV2 vector architecture",
|
|
[173] = "Renesas RX family",
|
|
[174] = "Imagination Technologies META processor architecture",
|
|
[175] = "MCST Elbrus general purpose hardware architecture",
|
|
[176] = "Cyan Technology eCOG16 family",
|
|
[177] = "National Semiconductor CompactRISC CR16 16-bit microprocessor",
|
|
[178] = "Freescale Extended Time Processing Unit",
|
|
[179] = "Infineon Technologies SLE9X core",
|
|
[180] = "Intel L10M",
|
|
[181] = "Intel K10M",
|
|
[183] = "ARM AArch64",
|
|
[185] = "Atmel Corporation 32-bit microprocessor family",
|
|
[186] = "STMicroeletronics STM8 8-bit microcontroller",
|
|
[187] = "Tilera TILE64 multicore architecture family",
|
|
[188] = "Tilera TILEPro multicore architecture family",
|
|
[190] = "NVIDIA CUDA architecture",
|
|
[191] = "Tilera TILE-Gx multicore architecture family",
|
|
[192] = "CloudShield architecture family",
|
|
[193] = "KIPO-KAIST Core-A 1st generation processor family",
|
|
[194] = "KIPO-KAIST Core-A 2nd generation processor family",
|
|
[195] = "Synopsys ARCompact V2",
|
|
[196] = "Open8 8-bit RISC soft processor core",
|
|
[197] = "Renesas RL78 family",
|
|
[198] = "Broadcom VideoCore V processor",
|
|
[199] = "Renesas 78KOR family",
|
|
[200] = "Freescale 56800EX Digital Signal Controller (DSC)",
|
|
[201] = "Beyond BA1 CPU architecture",
|
|
[202] = "Beyond BA2 CPU architecture",
|
|
[203] = "XMOS xCORE processor family",
|
|
[204] = "Microchip 8-bit PIC(r) family",
|
|
[205] = "reserved by Intel",
|
|
[206] = "reserved by Intel",
|
|
[207] = "reserved by Intel",
|
|
[208] = "reserved by Intel",
|
|
[209] = "reserved by Intel",
|
|
[210] = "KM211 KM32 32-bit processor",
|
|
[211] = "KM211 KMX32 32-bit processor",
|
|
[212] = "KM211 KMX16 16-bit processor",
|
|
[213] = "KM211 KMX8 8-bit processor",
|
|
[214] = "KM211 KVARC processor",
|
|
[215] = "Paneve CDP architecture family",
|
|
[216] = "Cognitive Smart Memory Processor",
|
|
[217] = "iCelero CoolEngine",
|
|
[218] = "Nanoradio Optimized RISC",
|
|
[219] = "CSR Kalimba architecture family",
|
|
[224] = "AMD GPU architecture",
|
|
[244] = "Lanai 32-bit processor",
|
|
[247] = "Linux kernel bpf virtual machine"
|
|
}
|
|
|
|
local decode = function (c)
|
|
assert (c.position == 1)
|
|
if not detect (c ()) then error ("not an ELF file") end
|
|
|
|
local p = c.position, c:read (4)
|
|
c (p, p + 3):mark ("ELF magic")
|
|
|
|
local elf = {}
|
|
elf.class = c:u8 ("ELF class: %s", function (u8)
|
|
if u8 == 1 then return "32-bit" end
|
|
if u8 == 2 then return "64-bit" end
|
|
return "invalid: %d", u8
|
|
end)
|
|
elf.data = c:u8 ("ELF data: %s", function (u8)
|
|
if u8 == 1 then
|
|
c.endianity = "le"
|
|
return "little-endian"
|
|
end
|
|
if u8 == 2 then
|
|
c.endianity = "be"
|
|
return "big-endian"
|
|
end
|
|
return "invalid: %d", u8
|
|
end)
|
|
elf.version = c:u8 ("ELF version: %d")
|
|
elf.abi = c:u8 ("OS ABI: %s", function (u8)
|
|
name = abi_table[u8]
|
|
if name then return name end
|
|
return "unknown: %d", u8
|
|
end)
|
|
elf.abi_version = c:u8 ("OS ABI version: %d")
|
|
|
|
-- The padding is reserved, no big sense in marking it
|
|
local padding = c:read (7)
|
|
|
|
-- We cannot decode anything further if we don't know endianity
|
|
if elf.data ~= 1 and elf.data ~= 2 then return end
|
|
|
|
-- And the same applies to the class
|
|
if elf.class == 1 then elf.uwide = c.u32
|
|
elseif elf.class == 2 then elf.uwide = c.u64
|
|
else return end
|
|
|
|
elf.type = c:u16 ("type of file: %s", function (u16)
|
|
name = type_table[u16]
|
|
if name then return name end
|
|
return "unknown: %d", u16
|
|
end)
|
|
elf.machine = c:u16 ("required architecture: %s", function (u16)
|
|
name = machine_table[u16]
|
|
if name then return name end
|
|
return "unknown: %d", u16
|
|
end)
|
|
elf.version = c:u32 ("version: %d")
|
|
elf.entry = elf.uwide (c, "program entry address: %#x")
|
|
elf.ph_offset = elf.uwide (c, "program header table offset: %#x")
|
|
elf.sh_offset = elf.uwide (c, "section header table offset: %#x")
|
|
elf.flags = c:u32 ("processor-specific flags: %#x")
|
|
elf.eh_size = c:u16 ("ELF header size: %d")
|
|
c (1, elf.eh_size):mark ("ELF header")
|
|
|
|
elf.ph_entry_size = c:u16 ("program header size: %d")
|
|
elf.ph_number = c:u16 ("program header count: %d")
|
|
elf.sh_entry_size = c:u16 ("section header size: %d")
|
|
elf.sh_number = c:u16 ("section header count: %d")
|
|
elf.sh_string_index = c:u16 ("section header index for strings: %d")
|
|
|
|
for i = 1, elf.ph_number do
|
|
local start = elf.ph_offset + (i - 1) * elf.ph_entry_size
|
|
local pchunk = c (1 + start, start + elf.ph_entry_size)
|
|
pchunk:mark ("ELF program header %d", i - 1)
|
|
decode_ph (elf, pchunk)
|
|
end
|
|
|
|
-- Only mark section headers after we've decoded them all,
|
|
-- so that we can name them using the section containing section names
|
|
local shs = {}
|
|
for i = 1, elf.sh_number do
|
|
local start = elf.sh_offset + (i - 1) * elf.sh_entry_size
|
|
local schunk = c (1 + start, start + elf.sh_entry_size)
|
|
sh = decode_sh (elf, schunk)
|
|
shs[i], shs[sh] = sh, schunk
|
|
end
|
|
|
|
local strings
|
|
if elf.sh_string_index ~= 0 and elf.sh_string_index < elf.sh_number then
|
|
local sh = shs[elf.sh_string_index + 1]
|
|
strings = c (sh.offset + 1, sh.offset + sh.size)
|
|
end
|
|
for i, sh in ipairs (shs) do
|
|
local schunk = shs[sh]
|
|
if strings and sh.name < #strings then
|
|
sh.name_string = strings (sh.name + 1):cstring ()
|
|
schunk:mark ("ELF section header %d (%s)", i - 1, sh.name_string)
|
|
else
|
|
schunk:mark ("ELF section header %d", i - 1)
|
|
end
|
|
end
|
|
end
|
|
|
|
hex.register { type="elf", detect=detect, decode=decode }
|