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