master
  1const std = @import("std");
  2const Allocator = std.mem.Allocator;
  3const Target = std.Target;
  4const Object = @import("../Object.zig");
  5
  6const Section = struct {
  7    data: std.ArrayList(u8) = .empty,
  8    relocations: std.ArrayList(Relocation) = .empty,
  9    flags: u64,
 10    type: u32,
 11    index: u16 = undefined,
 12};
 13
 14const Symbol = struct {
 15    section: ?*Section,
 16    size: u64,
 17    offset: u64,
 18    index: u16 = undefined,
 19    info: u8,
 20};
 21
 22const Relocation = struct {
 23    symbol: *Symbol,
 24    addend: i64,
 25    offset: u48,
 26    type: u8,
 27};
 28
 29const additional_sections = 3; // null section, strtab, symtab
 30const strtab_index = 1;
 31const symtab_index = 2;
 32const strtab_default = "\x00.strtab\x00.symtab\x00";
 33const strtab_name = 1;
 34const symtab_name = "\x00.strtab\x00".len;
 35
 36const Elf = @This();
 37
 38obj: Object,
 39/// The keys are owned by the Codegen.tree
 40sections: std.StringHashMapUnmanaged(*Section) = .empty,
 41local_symbols: std.StringHashMapUnmanaged(*Symbol) = .empty,
 42global_symbols: std.StringHashMapUnmanaged(*Symbol) = .empty,
 43unnamed_symbol_mangle: u32 = 0,
 44strtab_len: u64 = strtab_default.len,
 45arena: std.heap.ArenaAllocator,
 46
 47pub fn create(gpa: Allocator, target: Target) !*Object {
 48    const elf = try gpa.create(Elf);
 49    elf.* = .{
 50        .obj = .{ .format = .elf, .target = target },
 51        .arena = std.heap.ArenaAllocator.init(gpa),
 52    };
 53    return &elf.obj;
 54}
 55
 56pub fn deinit(elf: *Elf) void {
 57    const gpa = elf.arena.child_allocator;
 58    {
 59        var it = elf.sections.valueIterator();
 60        while (it.next()) |sect| {
 61            sect.*.data.deinit(gpa);
 62            sect.*.relocations.deinit(gpa);
 63        }
 64    }
 65    elf.sections.deinit(gpa);
 66    elf.local_symbols.deinit(gpa);
 67    elf.global_symbols.deinit(gpa);
 68    elf.arena.deinit();
 69    gpa.destroy(elf);
 70}
 71
 72fn sectionString(sec: Object.Section) []const u8 {
 73    return switch (sec) {
 74        .undefined => unreachable,
 75        .data => "data",
 76        .read_only_data => "rodata",
 77        .func => "text",
 78        .strings => "rodata.str",
 79        .custom => |name| name,
 80    };
 81}
 82
 83pub fn getSection(elf: *Elf, section_kind: Object.Section) !*std.ArrayList(u8) {
 84    const section_name = sectionString(section_kind);
 85    const section = elf.sections.get(section_name) orelse blk: {
 86        const section = try elf.arena.allocator().create(Section);
 87        section.* = .{
 88            .data = std.ArrayList(u8).init(elf.arena.child_allocator),
 89            .type = std.elf.SHT_PROGBITS,
 90            .flags = switch (section_kind) {
 91                .func, .custom => std.elf.SHF_ALLOC + std.elf.SHF_EXECINSTR,
 92                .strings => std.elf.SHF_ALLOC + std.elf.SHF_MERGE + std.elf.SHF_STRINGS,
 93                .read_only_data => std.elf.SHF_ALLOC,
 94                .data => std.elf.SHF_ALLOC + std.elf.SHF_WRITE,
 95                .undefined => unreachable,
 96            },
 97        };
 98        try elf.sections.putNoClobber(elf.arena.child_allocator, section_name, section);
 99        elf.strtab_len += section_name.len + ".\x00".len;
100        break :blk section;
101    };
102    return &section.data;
103}
104
105pub fn declareSymbol(
106    elf: *Elf,
107    section_kind: Object.Section,
108    maybe_name: ?[]const u8,
109    linkage: std.builtin.GlobalLinkage,
110    @"type": Object.SymbolType,
111    offset: u64,
112    size: u64,
113) ![]const u8 {
114    const section = blk: {
115        if (section_kind == .undefined) break :blk null;
116        const section_name = sectionString(section_kind);
117        break :blk elf.sections.get(section_name);
118    };
119    const binding: u8 = switch (linkage) {
120        .Internal => std.elf.STB_LOCAL,
121        .Strong => std.elf.STB_GLOBAL,
122        .Weak => std.elf.STB_WEAK,
123        .LinkOnce => unreachable,
124    };
125    const sym_type: u8 = switch (@"type") {
126        .func => std.elf.STT_FUNC,
127        .variable => std.elf.STT_OBJECT,
128        .external => std.elf.STT_NOTYPE,
129    };
130    const name = if (maybe_name) |some| some else blk: {
131        defer elf.unnamed_symbol_mangle += 1;
132        break :blk try std.fmt.allocPrint(elf.arena.allocator(), ".L.{d}", .{elf.unnamed_symbol_mangle});
133    };
134
135    const gop = if (linkage == .Internal)
136        try elf.local_symbols.getOrPut(elf.arena.child_allocator, name)
137    else
138        try elf.global_symbols.getOrPut(elf.arena.child_allocator, name);
139
140    if (!gop.found_existing) {
141        gop.value_ptr.* = try elf.arena.allocator().create(Symbol);
142        elf.strtab_len += name.len + 1; // +1 for null byte
143    }
144    gop.value_ptr.*.* = .{
145        .section = section,
146        .size = size,
147        .offset = offset,
148        .info = (binding << 4) + sym_type,
149    };
150    return name;
151}
152
153pub fn addRelocation(elf: *Elf, name: []const u8, section_kind: Object.Section, address: u64, addend: i64) !void {
154    const section_name = sectionString(section_kind);
155    const symbol = elf.local_symbols.get(name) orelse elf.global_symbols.get(name).?; // reference to undeclared symbol
156    const section = elf.sections.get(section_name).?;
157    if (section.relocations.items.len == 0) elf.strtab_len += ".rela".len;
158
159    try section.relocations.append(elf.arena.child_allocator, .{
160        .symbol = symbol,
161        .offset = @intCast(address),
162        .addend = addend,
163        .type = if (symbol.section == null) 4 else 2, // TODO
164    });
165}
166
167/// elf header
168/// sections contents
169/// symbols
170/// relocations
171/// strtab
172/// section headers
173pub fn finish(elf: *Elf, w: *std.Io.Writer) !void {
174    var num_sections: std.elf.Half = additional_sections;
175    var relocations_len: std.elf.Elf64_Off = 0;
176    var sections_len: std.elf.Elf64_Off = 0;
177    {
178        var it = elf.sections.valueIterator();
179        while (it.next()) |sect| {
180            sections_len += sect.*.data.items.len;
181            relocations_len += sect.*.relocations.items.len * @sizeOf(std.elf.Elf64_Rela);
182            sect.*.index = num_sections;
183            num_sections += 1;
184            num_sections += @intFromBool(sect.*.relocations.items.len != 0);
185        }
186    }
187    const symtab_len = (elf.local_symbols.count() + elf.global_symbols.count() + 1) * @sizeOf(std.elf.Elf64_Sym);
188
189    const symtab_offset = @sizeOf(std.elf.Elf64_Ehdr) + sections_len;
190    const symtab_offset_aligned = std.mem.alignForward(u64, symtab_offset, 8);
191    const rela_offset = symtab_offset_aligned + symtab_len;
192    const strtab_offset = rela_offset + relocations_len;
193    const sh_offset = strtab_offset + elf.strtab_len;
194    const sh_offset_aligned = std.mem.alignForward(u64, sh_offset, 16);
195    const endian = elf.obj.target.cpu.arch.endian();
196
197    const elf_header: std.elf.Elf64_Ehdr = .{
198        .e_ident = .{ 0x7F, 'E', 'L', 'F', 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
199        .e_type = std.elf.ET.REL, // we only produce relocatables
200        .e_machine = elf.obj.target.toElfMachine(),
201        .e_version = 1,
202        .e_entry = 0, // linker will handle this
203        .e_phoff = 0, // no program header
204        .e_shoff = sh_offset_aligned, // section headers offset
205        .e_flags = 0, // no flags
206        .e_ehsize = @sizeOf(std.elf.Elf64_Ehdr),
207        .e_phentsize = 0, // no program header
208        .e_phnum = 0, // no program header
209        .e_shentsize = @sizeOf(std.elf.Elf64_Shdr),
210        .e_shnum = num_sections,
211        .e_shstrndx = strtab_index,
212    };
213    try w.writeStruct(elf_header, endian);
214
215    // write contents of sections
216    {
217        var it = elf.sections.valueIterator();
218        while (it.next()) |sect| try w.writeAll(sect.*.data.items);
219    }
220
221    // pad to 8 bytes
222    try w.splatByteAll(0, @intCast(symtab_offset_aligned - symtab_offset));
223
224    var name_offset: u32 = strtab_default.len;
225    // write symbols
226    {
227        // first symbol must be null
228        try w.writeStruct(std.mem.zeroes(std.elf.Elf64_Sym), endian);
229
230        var sym_index: u16 = 1;
231        var it = elf.local_symbols.iterator();
232        while (it.next()) |entry| {
233            const sym = entry.value_ptr.*;
234            try w.writeStruct(std.elf.Elf64_Sym{
235                .st_name = name_offset,
236                .st_info = sym.info,
237                .st_other = 0,
238                .st_shndx = if (sym.section) |some| some.index else 0,
239                .st_value = sym.offset,
240                .st_size = sym.size,
241            }, endian);
242            sym.index = sym_index;
243            sym_index += 1;
244            name_offset += @intCast(entry.key_ptr.len + 1); // +1 for null byte
245        }
246        it = elf.global_symbols.iterator();
247        while (it.next()) |entry| {
248            const sym = entry.value_ptr.*;
249            try w.writeStruct(std.elf.Elf64_Sym{
250                .st_name = name_offset,
251                .st_info = sym.info,
252                .st_other = 0,
253                .st_shndx = if (sym.section) |some| some.index else 0,
254                .st_value = sym.offset,
255                .st_size = sym.size,
256            }, endian);
257            sym.index = sym_index;
258            sym_index += 1;
259            name_offset += @intCast(entry.key_ptr.len + 1); // +1 for null byte
260        }
261    }
262
263    // write relocations
264    {
265        var it = elf.sections.valueIterator();
266        while (it.next()) |sect| {
267            for (sect.*.relocations.items) |rela| {
268                try w.writeStruct(std.elf.Elf64_Rela{
269                    .r_offset = rela.offset,
270                    .r_addend = rela.addend,
271                    .r_info = (@as(u64, rela.symbol.index) << 32) | rela.type,
272                }, endian);
273            }
274        }
275    }
276
277    // write strtab
278    try w.writeAll(strtab_default);
279    {
280        var it = elf.local_symbols.keyIterator();
281        while (it.next()) |key| try w.print("{s}\x00", .{key.*});
282        it = elf.global_symbols.keyIterator();
283        while (it.next()) |key| try w.print("{s}\x00", .{key.*});
284    }
285    {
286        var it = elf.sections.iterator();
287        while (it.next()) |entry| {
288            if (entry.value_ptr.*.relocations.items.len != 0) try w.writeAll(".rela");
289            try w.print(".{s}\x00", .{entry.key_ptr.*});
290        }
291    }
292
293    // pad to 16 bytes
294    try w.splatByteAll(0, @intCast(sh_offset_aligned - sh_offset));
295    // mandatory null header
296    try w.writeStruct(std.mem.zeroes(std.elf.Elf64_Shdr), endian);
297
298    // write strtab section header
299    {
300        const sect_header: std.elf.Elf64_Shdr = .{
301            .sh_name = strtab_name,
302            .sh_type = std.elf.SHT_STRTAB,
303            .sh_flags = 0,
304            .sh_addr = 0,
305            .sh_offset = strtab_offset,
306            .sh_size = elf.strtab_len,
307            .sh_link = 0,
308            .sh_info = 0,
309            .sh_addralign = 1,
310            .sh_entsize = 0,
311        };
312        try w.writeStruct(sect_header, endian);
313    }
314
315    // write symtab section header
316    {
317        const sect_header: std.elf.Elf64_Shdr = .{
318            .sh_name = symtab_name,
319            .sh_type = std.elf.SHT_SYMTAB,
320            .sh_flags = 0,
321            .sh_addr = 0,
322            .sh_offset = symtab_offset_aligned,
323            .sh_size = symtab_len,
324            .sh_link = strtab_index,
325            .sh_info = elf.local_symbols.size + 1,
326            .sh_addralign = 8,
327            .sh_entsize = @sizeOf(std.elf.Elf64_Sym),
328        };
329        try w.writeStruct(sect_header, endian);
330    }
331
332    // remaining section headers
333    {
334        var sect_offset: u64 = @sizeOf(std.elf.Elf64_Ehdr);
335        var rela_sect_offset: u64 = rela_offset;
336        var it = elf.sections.iterator();
337        while (it.next()) |entry| {
338            const sect = entry.value_ptr.*;
339            const rela_count = sect.relocations.items.len;
340            const rela_name_offset: u32 = if (rela_count != 0) @truncate(".rela".len) else 0;
341            try w.writeStruct(std.elf.Elf64_Shdr{
342                .sh_name = rela_name_offset + name_offset,
343                .sh_type = sect.type,
344                .sh_flags = sect.flags,
345                .sh_addr = 0,
346                .sh_offset = sect_offset,
347                .sh_size = sect.data.items.len,
348                .sh_link = 0,
349                .sh_info = 0,
350                .sh_addralign = if (sect.flags & std.elf.SHF_EXECINSTR != 0) 16 else 1,
351                .sh_entsize = 0,
352            }, endian);
353
354            if (rela_count != 0) {
355                const size = rela_count * @sizeOf(std.elf.Elf64_Rela);
356                try w.writeStruct(std.elf.Elf64_Shdr{
357                    .sh_name = name_offset,
358                    .sh_type = std.elf.SHT_RELA,
359                    .sh_flags = 0,
360                    .sh_addr = 0,
361                    .sh_offset = rela_sect_offset,
362                    .sh_size = rela_count * @sizeOf(std.elf.Elf64_Rela),
363                    .sh_link = symtab_index,
364                    .sh_info = sect.index,
365                    .sh_addralign = 8,
366                    .sh_entsize = @sizeOf(std.elf.Elf64_Rela),
367                }, endian);
368                rela_sect_offset += size;
369            }
370
371            sect_offset += sect.data.items.len;
372            name_offset += @as(u32, @intCast(entry.key_ptr.len + ".\x00".len)) + rela_name_offset;
373        }
374    }
375    try w.flush();
376}