local ELFVERSION = 1 ---@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" 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) 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 phdrs ProgramHeader[] ---@param endian Endian ---@param class Class ---@nodiscard local function ElfPHeader_pack(phdrs, endian, class) local p = {} for i, phdr in ipairs(phdrs) do if class == Class.bit32 then p[i] = string.pack(efmt(endian) .. string.rep(szfmt(class), 8), phdr.type, phdr.offset, phdr.vaddr, phdr.paddr, phdr.filesz, phdr.memsz, phdr.flags, phdr.align) elseif class == Class.bit64 then p[i] = string.pack( efmt(endian) .. "I4I4" .. string.rep(szfmt(class), 6), phdr.type, phdr.flags, phdr.offset, phdr.vaddr, phdr.paddr, phdr.filesz, phdr.memsz, phdr.align) end end return table.concat(p) 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 = 0x34, shoff = 0xff, flags = 0, ehsize = 52, phentsize = 0, -- filled in by pack later phnum = 0, -- filled in by pack later shentsize = 0x28, 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") end output = arg[i + 1] ::continue:: end local f = assert(io.open(output, "w")) 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)) f:close() end main()