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, opevos = 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 = 0xfe00, hiproc = 0xfeff, } ---@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 = 0x3, } ---@param hdr ProgramHeader ---@param endian Endian ---@param class Class ---@nodiscard local function ElfPHeader_pack(hdr, endian, class) if class == Class.bit32 then return string.pack(efmt(endian) .. string.rep(szfmt(class), 8), hdr.type, hdr.offset, hdr.vaddr, hdr.paddr, hdr.filesz, hdr.memsz, hdr.flags, hdr.align) elseif class == Class.bit64 then return string.pack(efmt(endian) .. "I4I4" .. string.rep(szfmt(class), 6), hdr.type, hdr.flags, hdr.offset, hdr.vaddr, hdr.paddr, hdr.filesz, hdr.memsz, hdr.align) end end local function main() ---@type ElfHeader local header = { magic = { 0x7f, 0x45, 0x4c, 0x46 }, class = Class.bit32, 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 = 0x20, phnum = 0, shentsize = 0x28, shnum = 0, shstrndx = 0, } ---@type ProgramHeader local pheader = { type = SegmentType.dynamic, flags = SegmentFlags.x, offset = 0xf, vaddr = 0xff, paddr = 0xfff, filesz = 0xffff, memsz = 0xfffff, 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")) f:write(ElfHeader_pack(header)) f:write(ElfPHeader_pack(pheader, header.data, header.class)) f:close() end main()