zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

Elf.zig (13050B) - Raw


      1 const std = @import("std");
      2 const Allocator = std.mem.Allocator;
      3 const Target = std.Target;
      4 const Object = @import("../Object.zig");
      5 
      6 const Section = struct {
      7     data: std.array_list.Managed(u8),
      8     relocations: std.ArrayListUnmanaged(Relocation) = .empty,
      9     flags: u64,
     10     type: u32,
     11     index: u16 = undefined,
     12 };
     13 
     14 const Symbol = struct {
     15     section: ?*Section,
     16     size: u64,
     17     offset: u64,
     18     index: u16 = undefined,
     19     info: u8,
     20 };
     21 
     22 const Relocation = struct {
     23     symbol: *Symbol,
     24     addend: i64,
     25     offset: u48,
     26     type: u8,
     27 };
     28 
     29 const additional_sections = 3; // null section, strtab, symtab
     30 const strtab_index = 1;
     31 const symtab_index = 2;
     32 const strtab_default = "\x00.strtab\x00.symtab\x00";
     33 const strtab_name = 1;
     34 const symtab_name = "\x00.strtab\x00".len;
     35 
     36 const Elf = @This();
     37 
     38 obj: Object,
     39 /// The keys are owned by the Codegen.tree
     40 sections: std.StringHashMapUnmanaged(*Section) = .empty,
     41 local_symbols: std.StringHashMapUnmanaged(*Symbol) = .empty,
     42 global_symbols: std.StringHashMapUnmanaged(*Symbol) = .empty,
     43 unnamed_symbol_mangle: u32 = 0,
     44 strtab_len: u64 = strtab_default.len,
     45 arena: std.heap.ArenaAllocator,
     46 
     47 pub 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 
     56 pub 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();
     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 
     72 fn 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 
     83 pub fn getSection(elf: *Elf, section_kind: Object.Section) !*std.array_list.Managed(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.array_list.Managed(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 
    105 pub 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 
    153 pub 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
    173 pub fn finish(elf: *Elf, file: std.fs.File) !void {
    174     var file_buffer: [1024]u8 = undefined;
    175     var file_writer = file.writer(&file_buffer);
    176     const w = &file_writer.interface;
    177 
    178     var num_sections: std.elf.Elf64_Half = additional_sections;
    179     var relocations_len: std.elf.Elf64_Off = 0;
    180     var sections_len: std.elf.Elf64_Off = 0;
    181     {
    182         var it = elf.sections.valueIterator();
    183         while (it.next()) |sect| {
    184             sections_len += sect.*.data.items.len;
    185             relocations_len += sect.*.relocations.items.len * @sizeOf(std.elf.Elf64_Rela);
    186             sect.*.index = num_sections;
    187             num_sections += 1;
    188             num_sections += @intFromBool(sect.*.relocations.items.len != 0);
    189         }
    190     }
    191     const symtab_len = (elf.local_symbols.count() + elf.global_symbols.count() + 1) * @sizeOf(std.elf.Elf64_Sym);
    192 
    193     const symtab_offset = @sizeOf(std.elf.Elf64_Ehdr) + sections_len;
    194     const symtab_offset_aligned = std.mem.alignForward(u64, symtab_offset, 8);
    195     const rela_offset = symtab_offset_aligned + symtab_len;
    196     const strtab_offset = rela_offset + relocations_len;
    197     const sh_offset = strtab_offset + elf.strtab_len;
    198     const sh_offset_aligned = std.mem.alignForward(u64, sh_offset, 16);
    199 
    200     const elf_header = std.elf.Elf64_Ehdr{
    201         .e_ident = .{ 0x7F, 'E', 'L', 'F', 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    202         .e_type = std.elf.ET.REL, // we only produce relocatables
    203         .e_machine = elf.obj.target.toElfMachine(),
    204         .e_version = 1,
    205         .e_entry = 0, // linker will handle this
    206         .e_phoff = 0, // no program header
    207         .e_shoff = sh_offset_aligned, // section headers offset
    208         .e_flags = 0, // no flags
    209         .e_ehsize = @sizeOf(std.elf.Elf64_Ehdr),
    210         .e_phentsize = 0, // no program header
    211         .e_phnum = 0, // no program header
    212         .e_shentsize = @sizeOf(std.elf.Elf64_Shdr),
    213         .e_shnum = num_sections,
    214         .e_shstrndx = strtab_index,
    215     };
    216     try w.writeStruct(elf_header);
    217 
    218     // write contents of sections
    219     {
    220         var it = elf.sections.valueIterator();
    221         while (it.next()) |sect| try w.writeAll(sect.*.data.items);
    222     }
    223 
    224     // pad to 8 bytes
    225     try w.writeByteNTimes(0, @intCast(symtab_offset_aligned - symtab_offset));
    226 
    227     var name_offset: u32 = strtab_default.len;
    228     // write symbols
    229     {
    230         // first symbol must be null
    231         try w.writeStruct(std.mem.zeroes(std.elf.Elf64_Sym));
    232 
    233         var sym_index: u16 = 1;
    234         var it = elf.local_symbols.iterator();
    235         while (it.next()) |entry| {
    236             const sym = entry.value_ptr.*;
    237             try w.writeStruct(std.elf.Elf64_Sym{
    238                 .st_name = name_offset,
    239                 .st_info = sym.info,
    240                 .st_other = 0,
    241                 .st_shndx = if (sym.section) |some| some.index else 0,
    242                 .st_value = sym.offset,
    243                 .st_size = sym.size,
    244             });
    245             sym.index = sym_index;
    246             sym_index += 1;
    247             name_offset += @intCast(entry.key_ptr.len + 1); // +1 for null byte
    248         }
    249         it = elf.global_symbols.iterator();
    250         while (it.next()) |entry| {
    251             const sym = entry.value_ptr.*;
    252             try w.writeStruct(std.elf.Elf64_Sym{
    253                 .st_name = name_offset,
    254                 .st_info = sym.info,
    255                 .st_other = 0,
    256                 .st_shndx = if (sym.section) |some| some.index else 0,
    257                 .st_value = sym.offset,
    258                 .st_size = sym.size,
    259             });
    260             sym.index = sym_index;
    261             sym_index += 1;
    262             name_offset += @intCast(entry.key_ptr.len + 1); // +1 for null byte
    263         }
    264     }
    265 
    266     // write relocations
    267     {
    268         var it = elf.sections.valueIterator();
    269         while (it.next()) |sect| {
    270             for (sect.*.relocations.items) |rela| {
    271                 try w.writeStruct(std.elf.Elf64_Rela{
    272                     .r_offset = rela.offset,
    273                     .r_addend = rela.addend,
    274                     .r_info = (@as(u64, rela.symbol.index) << 32) | rela.type,
    275                 });
    276             }
    277         }
    278     }
    279 
    280     // write strtab
    281     try w.writeAll(strtab_default);
    282     {
    283         var it = elf.local_symbols.keyIterator();
    284         while (it.next()) |key| try w.print("{s}\x00", .{key.*});
    285         it = elf.global_symbols.keyIterator();
    286         while (it.next()) |key| try w.print("{s}\x00", .{key.*});
    287     }
    288     {
    289         var it = elf.sections.iterator();
    290         while (it.next()) |entry| {
    291             if (entry.value_ptr.*.relocations.items.len != 0) try w.writeAll(".rela");
    292             try w.print(".{s}\x00", .{entry.key_ptr.*});
    293         }
    294     }
    295 
    296     // pad to 16 bytes
    297     try w.writeByteNTimes(0, @intCast(sh_offset_aligned - sh_offset));
    298     // mandatory null header
    299     try w.writeStruct(std.mem.zeroes(std.elf.Elf64_Shdr));
    300 
    301     // write strtab section header
    302     {
    303         const sect_header = std.elf.Elf64_Shdr{
    304             .sh_name = strtab_name,
    305             .sh_type = std.elf.SHT_STRTAB,
    306             .sh_flags = 0,
    307             .sh_addr = 0,
    308             .sh_offset = strtab_offset,
    309             .sh_size = elf.strtab_len,
    310             .sh_link = 0,
    311             .sh_info = 0,
    312             .sh_addralign = 1,
    313             .sh_entsize = 0,
    314         };
    315         try w.writeStruct(sect_header);
    316     }
    317 
    318     // write symtab section header
    319     {
    320         const sect_header = std.elf.Elf64_Shdr{
    321             .sh_name = symtab_name,
    322             .sh_type = std.elf.SHT_SYMTAB,
    323             .sh_flags = 0,
    324             .sh_addr = 0,
    325             .sh_offset = symtab_offset_aligned,
    326             .sh_size = symtab_len,
    327             .sh_link = strtab_index,
    328             .sh_info = elf.local_symbols.size + 1,
    329             .sh_addralign = 8,
    330             .sh_entsize = @sizeOf(std.elf.Elf64_Sym),
    331         };
    332         try w.writeStruct(sect_header);
    333     }
    334 
    335     // remaining section headers
    336     {
    337         var sect_offset: u64 = @sizeOf(std.elf.Elf64_Ehdr);
    338         var rela_sect_offset: u64 = rela_offset;
    339         var it = elf.sections.iterator();
    340         while (it.next()) |entry| {
    341             const sect = entry.value_ptr.*;
    342             const rela_count = sect.relocations.items.len;
    343             const rela_name_offset: u32 = if (rela_count != 0) @truncate(".rela".len) else 0;
    344             try w.writeStruct(std.elf.Elf64_Shdr{
    345                 .sh_name = rela_name_offset + name_offset,
    346                 .sh_type = sect.type,
    347                 .sh_flags = sect.flags,
    348                 .sh_addr = 0,
    349                 .sh_offset = sect_offset,
    350                 .sh_size = sect.data.items.len,
    351                 .sh_link = 0,
    352                 .sh_info = 0,
    353                 .sh_addralign = if (sect.flags & std.elf.SHF_EXECINSTR != 0) 16 else 1,
    354                 .sh_entsize = 0,
    355             });
    356 
    357             if (rela_count != 0) {
    358                 const size = rela_count * @sizeOf(std.elf.Elf64_Rela);
    359                 try w.writeStruct(std.elf.Elf64_Shdr{
    360                     .sh_name = name_offset,
    361                     .sh_type = std.elf.SHT_RELA,
    362                     .sh_flags = 0,
    363                     .sh_addr = 0,
    364                     .sh_offset = rela_sect_offset,
    365                     .sh_size = rela_count * @sizeOf(std.elf.Elf64_Rela),
    366                     .sh_link = symtab_index,
    367                     .sh_info = sect.index,
    368                     .sh_addralign = 8,
    369                     .sh_entsize = @sizeOf(std.elf.Elf64_Rela),
    370                 });
    371                 rela_sect_offset += size;
    372             }
    373 
    374             sect_offset += sect.data.items.len;
    375             name_offset += @as(u32, @intCast(entry.key_ptr.len + ".\x00".len)) + rela_name_offset;
    376         }
    377     }
    378     try w.flush();
    379 }