summaryrefslogtreecommitdiff
path: root/mkelf.lua
blob: 1140be2d5af31830008d44dd64142278e93e48f9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
local elf = require("elf")
local ec = require("elf.constants")

local function die(fmt, ...)
    io.stderr:write("mkelf: ", string.format(fmt, ...), "\n")
    os.exit(1)
end

local function usage()
    die("[-o output] [input]")
end

---@param fn string|nil
---@return Header, Segments, Sections
local function loadconfig(fn)
    local header, segments, sections = {}, {}, {}
    local header_mt = { __index = elf.header }
    local segments_mt = { __index = elf.segment }
    local sections_mt = { __index = elf.section }
    local env = setmetatable({}, {
        __index = {
            -- 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) header = setmetatable(t, header_mt) end,
            segments = function(t) segments = setmetatable(t, segments_mt) end,
            sections = function(t) sections = setmetatable(t, sections_mt) end,
        },
        __newindex = function(_, name)
            error(("may not define global %q"):format(name), 2)
        end,
        __metatable = false,
    })
    -- TODO error check for multiple headers
    local chunk, err = loadfile(fn, "t", env)
    if not chunk then die("%s", err) end
    -- TODO sandbox against malformed configs
    local ok, perr = pcall(chunk)
    if not ok then die("%s", perr) end
    return header, segments, sections
end

local function main()
    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
        i = i + 1
    end
    local header, segments, sections = loadconfig(infn)
    header.phnum = #segments
    header.phentsize = (header.class == ec.Class.bit64) and 0x38 or 0x20
    -- TODO write to a temp file and atomic rename to outfn.
    local out <close> = assert(io.open(outfn, "wb"))
    out:write(header:pack())
    out:write(segments:pack(header.data, header.class))
    -- TODO finish section.pack
    out:write(sections:pack(header.data))
end

main()