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 §ion.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 }