elf.lua: decode program headers

This commit is contained in:
Přemysl Eric Janouch 2017-01-18 00:25:03 +01:00
parent 233a69e859
commit 52411d0a2e
Signed by: p
GPG Key ID: B715679E3A361BE6
1 changed files with 130 additions and 65 deletions

View File

@ -20,6 +20,82 @@ local detect = function (c)
return c:read (4) == "\x7FELF" return c:read (4) == "\x7FELF"
end 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 & 4 ~= 0 then table.insert (info, "read") end
if u32 & 2 ~= 0 then table.insert (info, "write") end
if u32 & 1 ~= 0 then table.insert (info, "execute") end
local junk = u32 & ~7
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")
end
local decode_sh = function (elf, c)
-- TODO
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 = { local type_table = {
[0] = "no file type", [0] = "no file type",
[1] = "relocatable file", [1] = "relocatable file",
@ -210,74 +286,20 @@ local machine_table = {
[247] = "Linux kernel bpf virtual machine" [247] = "Linux kernel bpf virtual machine"
} }
local decode32 = function (c)
-- TODO: like the 64-bit version, maybe try to merge the code somehow
end
local decode64 = function (c)
local type = c:u16 ("type of file: %s", function (u16)
name = type_table[u16]
if name then return name end
return "unknown: %d", u16
end)
local machine = c:u16 ("required architecture: %s", function (u16)
name = machine_table[u16]
if name then return name end
return "unknown: %d", u16
end)
local version = c:u32 ("version: %d")
local entry = c:u64 ("program entry address: %#x")
local ph_offset = c:u64 ("program header table offset: %#x")
local sh_offset = c:u64 ("section header table offset: %#x")
local flags = c:u32 ("processor-specific flags: %#x")
local eh_size = c:u16 ("ELF header size: %d")
local ph_entry_size = c:u16 ("program header size: %d")
local ph_number = c:u16 ("program header count: %d")
local sh_entry_size = c:u16 ("section header size: %d")
local sh_number = c:u16 ("section header count: %d")
local sh_string_index = c:u16 ("section header index for strings: %d")
-- TODO: decode all sections as well, see man 5 elf,
-- /usr/include/elf.h and /usr/include/llvm/Support/ELF.h
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 decode = function (c) local decode = function (c)
assert (c.position == 1)
if not detect (c ()) then error ("not an ELF file") end if not detect (c ()) then error ("not an ELF file") end
local p = c.position, c:read (4) local p = c.position, c:read (4)
c (p, p + 3):mark ("ELF magic") c (p, p + 3):mark ("ELF magic")
local class = c:u8 ("ELF class: %s", function (u8) local elf = {}
elf.class = c:u8 ("ELF class: %s", function (u8)
if u8 == 1 then return "32-bit" end if u8 == 1 then return "32-bit" end
if u8 == 2 then return "64-bit" end if u8 == 2 then return "64-bit" end
return "invalid: %d", u8 return "invalid: %d", u8
end) end)
local data = c:u8 ("ELF data: %s", function (u8) elf.data = c:u8 ("ELF data: %s", function (u8)
if u8 == 1 then if u8 == 1 then
c.endianity = "le" c.endianity = "le"
return "little-endian" return "little-endian"
@ -288,22 +310,65 @@ local decode = function (c)
end end
return "invalid: %d", u8 return "invalid: %d", u8
end) end)
local version = c:u8 ("ELF version: %d") elf.version = c:u8 ("ELF version: %d")
local abi = c:u8 ("OS ABI: %s", function (u8) elf.abi = c:u8 ("OS ABI: %s", function (u8)
name = abi_table[u8] name = abi_table[u8]
if name then return name end if name then return name end
return "unknown: %d", u8 return "unknown: %d", u8
end) end)
local abi_version = c:u8 ("OS ABI version: %d") elf.abi_version = c:u8 ("OS ABI version: %d")
-- The padding is reserved, no big sense in marking it -- The padding is reserved, no big sense in marking it
local padding = c:read (7) local padding = c:read (7)
-- We cannot decode anything further as we don't know how -- We cannot decode anything further if we don't know endianity
if data ~= 1 and data ~= 2 then return end if elf.data ~= 1 and elf.data ~= 2 then return end
if class == 1 then decode32 (c) end -- And the same applies to the class
if class == 2 then decode64 (c) end 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")
-- TODO: decode all headers as well, see man 5 elf,
-- /usr/include/elf.h and /usr/include/llvm/Support/ELF.h
for i = 1, elf.ph_number do
local start = elf.ph_offset + (i - 1) * elf.ph_entry_size
local ph = c (1 + start, start + elf.ph_entry_size)
ph:mark ("ELF program header %d", i - 1)
decode_ph (elf, ph)
end
-- TODO: we will need to decode "sh_string_index" first to get names
for i = 1, elf.sh_number do
local start = elf.sh_offset + (i - 1) * elf.sh_entry_size
local sh = c (1 + start, start + elf.sh_entry_size)
sh:mark ("ELF section header %d", i - 1)
decode_sh (elf, sh)
end
end end
hex.register { type="elf", detect=detect, decode=decode } hex.register { type="elf", detect=detect, decode=decode }