From 79f6b161f892f621a909ce1df99b3c680f3682a2 Mon Sep 17 00:00:00 2001 From: Sunil Nimmagadda Date: Fri, 23 Oct 2026 22:43:44 +0530 Subject: Refactor: Move to an elf module. While here, define a elf.d.lua to help author type check configuration file. --- mkelf.lua | 321 ++++++++++---------------------------------------------------- 1 file changed, 52 insertions(+), 269 deletions(-) (limited to 'mkelf.lua') diff --git a/mkelf.lua b/mkelf.lua index 6a90504..3775956 100644 --- a/mkelf.lua +++ b/mkelf.lua @@ -1,282 +1,65 @@ -local ELFVERSION = 1 +local elf = require("elf") +local ec = require("elf.constants") ----@enum Osabi -local Osabi = { - sysv = 0x00, - hpux = 0x01, - netbsd = 0x02, - linux = 0x03, - hurd = 0x04, - solaris = 0x06, - aix = 0x07, - irix = 0x08, - freebsd = 0x09, - tru64 = 0x0a, - modesto = 0x0b, - openbsd = 0x0c, - openvms = 0x0d, - nsk = 0x0e, - aros = 0x0f, - fenixos = 0x10, - cloudabi = 0x11, - openvos = 0x12, -} - ----@enum Class -local Class = { - bit32 = 0x1, - bit64 = 0x2, -} - ----@enum Endian -local Endian = { - little = 0x1, - big = 0x2, -} - ----@enum Type -local Type = { - none = 0x00, - rel = 0x01, - exec = 0x02, - dyn = 0x03, - core = 0x04, - loos = 0xfe00, - hios = 0xfeff, - loproc = 0xff00, - hiproc = 0xffff, -} - ----@enum Machine -local Machine = { - none = 0x00, - m32 = 0x01, -- AT&T WE 32100 - sparc = 0x02, -- SPARC - i386 = 0x03, -- Intel 80386 - m68k = 0x04, -- Motorola 68000 (M68k) - m88k = 0x05, -- Motorola 88000 (M88k) - mcu = 0x06, -- Intel MCU - i860 = 0x07, -- Intel 80860 - mips = 0x08, -- MIPS - s370 = 0x09, -- IBM System/370 - mipsrs3le = 0x0a, -- MIPS RS3000 Little Endian - parisc = 0x0f, -- Hewlett-Packard PA-RISC - vpp500 = 0x11, -- Fujitsu VPP500 - sparc32 = 0x12, -- Enhanced instruction set SPARC - i960 = 0x13, -- Intel 80960 - ppc = 0x14, -- PowerPC - ppc64 = 0x15, -- PowerPC (64-bit) - s390 = 0x16, -- IBM System/390 - spu = 0x17, -- IBM SPC/SPU - v800 = 0x24, -- NEC V800 - fr20 = 0x25, -- Fujitsu FR20 - -- todo -} - ----@alias ElfMagic [integer, integer, integer, integer] ----@class ElfHeader ----@field magic ElfMagic ----@field class Class ----@field data Endian ----@field ident_version integer ----@field osabi Osabi ----@field abiversion integer ----@field type Type ----@field machine Machine ----@field version integer ----@field entry integer ----@field phoff integer ----@field shoff integer ----@field flags integer ----@field ehsize integer ----@field phentsize integer ----@field phnum integer ----@field shentsize integer ----@field shnum integer ----@field shstrndx integer - ----@param endian Endian ----@nodiscard -local function efmt(endian) - return (endian == Endian.big) and ">" or "<" -end - ----@param class Class ----@nodiscard -local function szfmt(class) - return (class == Class.bit64) and "I8" or "I4" +local function die(fmt, ...) + io.stderr:write("mkelf: ", string.format(fmt, ...), "\n") + os.exit(1, true) end ----@param hdr ElfHeader ----@nodiscard -local function ElfHeader_pack(hdr) - return string.pack("BBBBBBBBBxxxxxxx" .. - efmt(hdr.data) .. - "I2I2I4" .. - string.rep(szfmt(hdr.class), 3) .. - "I4I2I2I2I2I2I2", - hdr.magic[1], hdr.magic[2], hdr.magic[3], hdr.magic[4], - hdr.class, - hdr.data, - hdr.ident_version, - hdr.osabi, - hdr.abiversion, - hdr.type, - hdr.machine, - hdr.version, - hdr.entry, - hdr.phoff, - hdr.shoff, - hdr.flags, - hdr.ehsize, - hdr.phentsize, - hdr.phnum, - hdr.shentsize, - hdr.shnum, - hdr.shstrndx) +local function usage() + die("[-o output] input") end ----@class ProgramHeader ----@field type integer ----@field flags integer ----@field offset integer ----@field vaddr integer ----@field paddr integer ----@field filesz integer ----@field memsz integer ----@field align integer - ----@enum SegmentType -local SegmentType = { - null = 0x00000000, - load = 0x00000001, - dynamic = 0x00000002, - interp = 0x00000003, - note = 0x00000004, - shlib = 0x00000005, - phdr = 0x00000006, - tls = 0x00000007, - loos = 0x60000000, - hios = 0x6FFFFFFF, - loproc = 0x70000000, - hiproc = 0x7FFFFFFF, -} - ----@enum SegmentFlags -local SegmentFlags = { - x = 0x1, - w = 0x2, - r = 0x4, -} - ----@param phdr ProgramHeader ----@param endian Endian ----@return string ----@nodiscard -local function ElfPHeader_pack32(phdr, endian) - return string.pack(efmt(endian) .. string.rep(szfmt(Class.bit32), 8), - phdr.type, - phdr.offset, - phdr.vaddr, - phdr.paddr, - phdr.filesz, - phdr.memsz, - phdr.flags, - phdr.align) -end - ----@param phdr ProgramHeader ----@param endian Endian ----@return string ----@nodiscard -local function ElfPHeader_pack64(phdr, endian) - return string.pack( - efmt(endian) .. "I4I4" .. string.rep(szfmt(Class.bit64), 6), - phdr.type, - phdr.flags, - phdr.offset, - phdr.vaddr, - phdr.paddr, - phdr.filesz, - phdr.memsz, - phdr.align) -end - ----@param phdrs ProgramHeader[] ----@param endian Endian ----@param class Class ----@return string ----@nodiscard -local function ElfPHeader_pack(phdrs, endian, class) - local p = {} - for i, phdr in ipairs(phdrs) do - p[i] = (class == Class.bit32) and - ElfPHeader_pack32(phdr, endian) or - ElfPHeader_pack64(phdr, endian) - end - return table.concat(p) +---@param fn string|nil +---@return {header: Header, sections: Section[], segments: Segment[]} +local function loadconfig(fn) + local spec = {} + local env = { + -- Keep in sync with types/elf.d.lua + Class = elf.Class, + Endian = elf.Endian, + Osabi = elf.Osabi, + Type = elf.Type, + Machine = elf.Machine, + SegmentType = elf.SegmentType, + SegmentFlags = elf.SegmentFlags, + ELFVERSION = elf.VERSION, + + header = function(t) spec.header = t end, + sections = function(t) spec.sections = t end, + segments = function(t) spec.segments = t end, + } + -- TODO error check for multiple headers + local chunk, err = loadfile(fn, "t", env) + if not chunk then die("%s", err) end + local ok, perr = pcall(chunk) + if not ok then die("%s", perr) end + return spec end local function main() - ---@type ElfHeader - local header = { - magic = { 0x7f, 0x45, 0x4c, 0x46 }, - class = Class.bit64, - data = Endian.big, - ident_version = ELFVERSION, - osabi = Osabi.netbsd, - abiversion = 0, - type = Type.exec, - machine = Machine.i386, - version = ELFVERSION, - entry = 0, - phoff = 0x40, - shoff = 0xff, - flags = 0, - ehsize = 64, - phentsize = 0, -- filled in by pack later - phnum = 0, -- filled in by pack later - shentsize = 0x40, - shnum = 0, - shstrndx = 0, - } - ---@type ProgramHeader[] - local phdrs = { - { - type = SegmentType.dynamic, - flags = SegmentFlags.x, - offset = 0xf, - vaddr = 0xff, - paddr = 0xfff, - filesz = 0xffff, - memsz = 0xfffff, - align = 1, - }, - { - type = SegmentType.dynamic, - flags = SegmentFlags.r, - offset = 0xa, - vaddr = 0xaa, - paddr = 0xaaa, - filesz = 0xaaaa, - memsz = 0xaaaaa, - align = 1, - } - } - local output = "a.out" - for i in ipairs(arg) do - if arg[i] ~= "-o" then goto continue end - if not arg[i + 1] then - error("mkelf: missing filename after -o") + local outfn, infn = "a.out", nil + local i = 1 + while arg[i] do + if arg[i] == "-o" then + i = i + 1 + outfn = arg[i] or die("missing filename after -o") + elseif infn then + usage() + else + infn = arg[i] end - output = arg[i + 1] - ::continue:: + i = i + 1 end - local f = assert(io.open(output, "wb")) - header.phnum = #phdrs - header.phentsize = (header.class == Class.bit64) and 0x38 or 0x20 - f:write(ElfHeader_pack(header)) - f:write(ElfPHeader_pack(phdrs, header.data, header.class)) + local c = loadconfig(infn) + c.header.phnum = #c.segments + c.header.phentsize = (c.header.class == ec.Class.bit64) and 0x38 or 0x20 + -- TODO write to a temp file and atomic rename to outfn. + local out = assert(io.open(outfn, "wb")) + out:write(elf.header.pack(c.header)) + out:write(elf.segment.pack(c.segments, c.header.data, c.header.class)) + -- TODO finish section.pack + -- out:write(elf.section.pack(c.sections, c.header.data)) end main() -- cgit v1.3