authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-09-11 10:52:30+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-09-11 10:52:30+02:00
log7a9eba2f8597a94e4a6def62253e9bf5220a46af
tree67c8a855e014cae8595a05048cb18b610b66a579
parentd07edfabd6e45a486d1611e357887d01b38db32e

elf: emit relocation to an extern function


7 files changed, 252 insertions(+), 44 deletions(-)

src/arch/x86_64/CodeGen.zig+19-3
......@@ -125,7 +125,9 @@ const Owner = union(enum) {
125125 .func_index => |func_index| {
126126 const mod = ctx.bin_file.options.module.?;
127127 const decl_index = mod.funcOwnerDeclIndex(func_index);
128 if (ctx.bin_file.cast(link.File.MachO)) |macho_file| {
128 if (ctx.bin_file.cast(link.File.Elf)) |elf_file| {
129 return elf_file.getOrCreateMetadataForDecl(decl_index);
130 } else if (ctx.bin_file.cast(link.File.MachO)) |macho_file| {
129131 const atom = try macho_file.getOrCreateAtomForDecl(decl_index);
130132 return macho_file.getAtom(atom).getSymbolIndex().?;
131133 } else if (ctx.bin_file.cast(link.File.Coff)) |coff_file| {
......@@ -136,7 +138,10 @@ const Owner = union(enum) {
136138 } else unreachable;
137139 },
138140 .lazy_sym => |lazy_sym| {
139 if (ctx.bin_file.cast(link.File.MachO)) |macho_file| {
141 if (ctx.bin_file.cast(link.File.Elf)) |elf_file| {
142 return elf_file.getOrCreateMetadataForLazySymbol(lazy_sym) catch |err|
143 ctx.fail("{s} creating lazy symbol", .{@errorName(err)});
144 } else if (ctx.bin_file.cast(link.File.MachO)) |macho_file| {
140145 const atom = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err|
141146 return ctx.fail("{s} creating lazy symbol", .{@errorName(err)});
142147 return macho_file.getAtom(atom).getSymbolIndex().?;
......@@ -8178,7 +8183,18 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
81788183 } else if (func_value.getExternFunc(mod)) |extern_func| {
81798184 const decl_name = mod.intern_pool.stringToSlice(mod.declPtr(extern_func.decl).name);
81808185 const lib_name = mod.intern_pool.stringToSliceUnwrap(extern_func.lib_name);
8181 if (self.bin_file.cast(link.File.Coff)) |coff_file| {
8186 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
8187 const atom_index = try self.owner.getSymbolIndex(self);
8188 const sym_index = try elf_file.getGlobalSymbol(decl_name, lib_name);
8189 _ = try self.addInst(.{
8190 .tag = .call,
8191 .ops = .extern_fn_reloc,
8192 .data = .{ .reloc = .{
8193 .atom_index = atom_index,
8194 .sym_index = sym_index,
8195 } },
8196 });
8197 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
81828198 const atom_index = try self.owner.getSymbolIndex(self);
81838199 const sym_index = try coff_file.getGlobalSymbol(decl_name, lib_name);
81848200 _ = try self.addInst(.{
src/arch/x86_64/Emit.zig+9-1
......@@ -41,7 +41,15 @@ pub fn emitMir(emit: *Emit) Error!void {
4141 .offset = end_offset - 4,
4242 .length = @as(u5, @intCast(end_offset - start_offset)),
4343 }),
44 .linker_extern_fn => |symbol| if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
44 .linker_extern_fn => |symbol| if (emit.bin_file.cast(link.File.Elf)) |elf_file| {
45 // Add relocation to the decl.
46 const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
47 try atom_ptr.addReloc(elf_file, .{
48 .r_offset = end_offset,
49 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | std.elf.R_X86_64_PLT32,
50 .r_addend = -4,
51 });
52 } else if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
4553 // Add relocation to the decl.
4654 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
4755 const target = macho_file.getGlobalByIndex(symbol.sym_index);
src/link.zig+1-1
......@@ -549,7 +549,7 @@ pub const File = struct {
549549 switch (base.tag) {
550550 // zig fmt: off
551551 .coff => return @fieldParentPtr(Coff, "base", base).getGlobalSymbol(name, lib_name),
552 .elf => unreachable,
552 .elf => return @fieldParentPtr(Elf, "base", base).getGlobalSymbol(name, lib_name),
553553 .macho => return @fieldParentPtr(MachO, "base", base).getGlobalSymbol(name, lib_name),
554554 .plan9 => unreachable,
555555 .spirv => unreachable,
src/link/Elf.zig+28-5
......@@ -302,9 +302,9 @@ pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.
302302 const vaddr = this_sym.value;
303303 const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(self).?;
304304 try parent_atom.addReloc(self, .{
305 .target = this_sym_index,
306 .offset = reloc_info.offset,
307 .addend = reloc_info.addend,
305 .r_offset = reloc_info.offset,
306 .r_info = (@as(u64, @intCast(this_sym_index)) << 32) | elf.R_X86_64_64,
307 .r_addend = reloc_info.addend,
308308 });
309309
310310 return vaddr;
......@@ -1020,8 +1020,17 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
10201020 // Beyond this point, everything has been allocated a virtual address and we can resolve
10211021 // the relocations.
10221022 if (self.zig_module_index) |index| {
1023 for (self.file(index).?.zig_module.atoms.items) |atom_index| {
1024 try self.atom(atom_index).?.resolveRelocs(self);
1023 for (self.file(index).?.zig_module.atoms.keys()) |atom_index| {
1024 const atom_ptr = self.atom(atom_index).?;
1025 if (!atom_ptr.alive) continue;
1026 const shdr = &self.shdrs.items[atom_ptr.output_section_index];
1027 const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;
1028 const code = try gpa.alloc(u8, atom_ptr.size);
1029 defer gpa.free(code);
1030 const amt = try self.base.file.?.preadAll(code, file_offset);
1031 if (amt != code.len) return error.InputOutput;
1032 try atom_ptr.resolveRelocs(self, code);
1033 try self.base.file.?.pwriteAll(code, file_offset);
10251034 }
10261035 }
10271036
......@@ -2185,6 +2194,7 @@ fn updateDeclCode(
21852194 const shdr_index = sym.output_section_index;
21862195
21872196 sym.name_offset = try self.strtab.insert(gpa, decl_name);
2197 atom_ptr.alive = true;
21882198 atom_ptr.name_offset = sym.name_offset;
21892199 esym.st_name = sym.name_offset;
21902200 esym.st_info |= stt_bits;
......@@ -2440,6 +2450,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.
24402450 local_esym.st_info |= elf.STT_OBJECT;
24412451 local_esym.st_size = code.len;
24422452 const atom_ptr = local_sym.atom(self).?;
2453 atom_ptr.alive = true;
24432454 atom_ptr.name_offset = name_str_index;
24442455 atom_ptr.alignment = math.log2_int(u64, required_alignment);
24452456 atom_ptr.size = code.len;
......@@ -2515,6 +2526,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
25152526 local_esym.st_info |= elf.STT_OBJECT;
25162527 local_esym.st_size = code.len;
25172528 const atom_ptr = local_sym.atom(self).?;
2529 atom_ptr.alive = true;
25182530 atom_ptr.name_offset = name_str_index;
25192531 atom_ptr.alignment = math.log2_int(u64, required_alignment);
25202532 atom_ptr.size = code.len;
......@@ -3374,6 +3386,17 @@ pub fn globalByName(self: *Elf, name: []const u8) ?Symbol.Index {
33743386 return self.resolver.get(name_off);
33753387}
33763388
3389pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 {
3390 _ = lib_name;
3391 const gpa = self.base.allocator;
3392 const name_off = try self.strtab.insert(gpa, name);
3393 const gop = try self.getOrPutGlobal(name_off);
3394 if (!gop.found_existing) {
3395 try self.unresolved.putNoClobber(gpa, name_off, {});
3396 }
3397 return gop.index;
3398}
3399
33773400fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) {
33783401 return .{ .data = self };
33793402}
src/link/Elf/Atom.zig+170-22
......@@ -26,7 +26,7 @@ relocs_section_index: Index = 0,
2626atom_index: Index = 0,
2727
2828/// Specifies whether this atom is alive or has been garbage collected.
29alive: bool = true,
29alive: bool = false,
3030
3131/// Specifies if the atom has been visited during garbage collection.
3232visited: bool = false,
......@@ -192,6 +192,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void {
192192 log.debug("freeAtom {d} ({s})", .{ self.atom_index, self.name(elf_file) });
193193
194194 const gpa = elf_file.base.allocator;
195 const zig_module = elf_file.file(self.file_index).?.zig_module;
195196 const shndx = self.output_section_index;
196197 const meta = elf_file.last_atom_and_free_list_table.getPtr(shndx).?;
197198 const free_list = &meta.free_list;
......@@ -242,17 +243,18 @@ pub fn free(self: *Atom, elf_file: *Elf) void {
242243
243244 // TODO create relocs free list
244245 self.freeRelocs(elf_file);
246 assert(zig_module.atoms.swapRemove(self.atom_index));
245247 self.* = .{};
246248}
247249
248pub fn relocs(self: Atom, elf_file: *Elf) []const Relocation {
250pub fn relocs(self: Atom, elf_file: *Elf) []const elf.Elf64_Rela {
249251 const file_ptr = elf_file.file(self.file_index).?;
250252 if (file_ptr != .zig_module) @panic("TODO");
251253 const zig_module = file_ptr.zig_module;
252254 return zig_module.relocs.items[self.relocs_section_index].items;
253255}
254256
255pub fn addReloc(self: Atom, elf_file: *Elf, reloc: Relocation) !void {
257pub fn addReloc(self: Atom, elf_file: *Elf, reloc: elf.Elf64_Rela) !void {
256258 const gpa = elf_file.base.allocator;
257259 const file_ptr = elf_file.file(self.file_index).?;
258260 assert(file_ptr == .zig_module);
......@@ -269,31 +271,178 @@ pub fn freeRelocs(self: Atom, elf_file: *Elf) void {
269271}
270272
271273/// TODO mark relocs dirty
272pub fn resolveRelocs(self: Atom, elf_file: *Elf) !void {
274pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {
273275 relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) });
274 const shdr = &elf_file.shdrs.items[self.output_section_index];
275 for (self.relocs(elf_file)) |reloc| {
276 const target_sym = elf_file.symbol(reloc.target);
277 const target_vaddr = target_sym.value + reloc.addend;
278 const section_offset = (self.value + reloc.offset) - shdr.sh_addr;
279 const file_offset = shdr.sh_offset + section_offset;
280
281 relocs_log.debug(" ({x}: [() => 0x{x}] ({s}))", .{
282 reloc.offset,
283 target_vaddr,
284 target_sym.name(elf_file),
276
277 var stream = std.io.fixedBufferStream(code);
278 const cwriter = stream.writer();
279
280 for (self.relocs(elf_file)) |rel| {
281 const r_type = rel.r_type();
282 if (r_type == elf.R_X86_64_NONE) continue;
283
284 const target = elf_file.symbol(rel.r_sym());
285
286 // We will use equation format to resolve relocations:
287 // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/
288 //
289 // Address of the source atom.
290 const P = @as(i64, @intCast(self.value + rel.r_offset));
291 // Addend from the relocation.
292 const A = rel.r_addend;
293 // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub.
294 const S = @as(i64, @intCast(target.address(.{}, elf_file)));
295 // Address of the global offset table.
296 const GOT = blk: {
297 const shndx = if (elf_file.got_plt_section_index) |shndx|
298 shndx
299 else if (elf_file.got_section_index) |shndx|
300 shndx
301 else
302 null;
303 break :blk if (shndx) |index| @as(i64, @intCast(elf_file.shdrs.items[index].sh_addr)) else 0;
304 };
305 // Relative offset to the start of the global offset table.
306 const G = @as(i64, @intCast(target.gotAddress(elf_file))) - GOT;
307 // // Address of the thread pointer.
308 // const TP = @as(i64, @intCast(elf_file.getTpAddress()));
309 // // Address of the dynamic thread pointer.
310 // const DTP = @as(i64, @intCast(elf_file.getDtpAddress()));
311
312 relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ({s})", .{
313 fmtRelocType(r_type),
314 rel.r_offset,
315 P,
316 S + A,
317 G + GOT + A,
318 target.name(elf_file),
285319 });
286320
287 switch (elf_file.ptr_width) {
288 .p32 => try elf_file.base.file.?.pwriteAll(
289 std.mem.asBytes(&@as(u32, @intCast(target_vaddr))),
290 file_offset,
291 ),
292 .p64 => try elf_file.base.file.?.pwriteAll(std.mem.asBytes(&target_vaddr), file_offset),
321 try stream.seekTo(rel.r_offset);
322
323 switch (rel.r_type()) {
324 elf.R_X86_64_NONE => unreachable,
325 elf.R_X86_64_64 => try cwriter.writeIntLittle(i64, S + A),
326 elf.R_X86_64_PLT32 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - P))),
327 else => @panic("TODO"),
293328 }
294329 }
295330}
296331
332pub fn fmtRelocType(r_type: u32) std.fmt.Formatter(formatRelocType) {
333 return .{ .data = r_type };
334}
335
336fn formatRelocType(
337 r_type: u32,
338 comptime unused_fmt_string: []const u8,
339 options: std.fmt.FormatOptions,
340 writer: anytype,
341) !void {
342 _ = unused_fmt_string;
343 _ = options;
344 const str = switch (r_type) {
345 elf.R_X86_64_NONE => "R_X86_64_NONE",
346 elf.R_X86_64_64 => "R_X86_64_64",
347 elf.R_X86_64_PC32 => "R_X86_64_PC32",
348 elf.R_X86_64_GOT32 => "R_X86_64_GOT32",
349 elf.R_X86_64_PLT32 => "R_X86_64_PLT32",
350 elf.R_X86_64_COPY => "R_X86_64_COPY",
351 elf.R_X86_64_GLOB_DAT => "R_X86_64_GLOB_DAT",
352 elf.R_X86_64_JUMP_SLOT => "R_X86_64_JUMP_SLOT",
353 elf.R_X86_64_RELATIVE => "R_X86_64_RELATIVE",
354 elf.R_X86_64_GOTPCREL => "R_X86_64_GOTPCREL",
355 elf.R_X86_64_32 => "R_X86_64_32",
356 elf.R_X86_64_32S => "R_X86_64_32S",
357 elf.R_X86_64_16 => "R_X86_64_16",
358 elf.R_X86_64_PC16 => "R_X86_64_PC16",
359 elf.R_X86_64_8 => "R_X86_64_8",
360 elf.R_X86_64_PC8 => "R_X86_64_PC8",
361 elf.R_X86_64_DTPMOD64 => "R_X86_64_DTPMOD64",
362 elf.R_X86_64_DTPOFF64 => "R_X86_64_DTPOFF64",
363 elf.R_X86_64_TPOFF64 => "R_X86_64_TPOFF64",
364 elf.R_X86_64_TLSGD => "R_X86_64_TLSGD",
365 elf.R_X86_64_TLSLD => "R_X86_64_TLSLD",
366 elf.R_X86_64_DTPOFF32 => "R_X86_64_DTPOFF32",
367 elf.R_X86_64_GOTTPOFF => "R_X86_64_GOTTPOFF",
368 elf.R_X86_64_TPOFF32 => "R_X86_64_TPOFF32",
369 elf.R_X86_64_PC64 => "R_X86_64_PC64",
370 elf.R_X86_64_GOTOFF64 => "R_X86_64_GOTOFF64",
371 elf.R_X86_64_GOTPC32 => "R_X86_64_GOTPC32",
372 elf.R_X86_64_GOT64 => "R_X86_64_GOT64",
373 elf.R_X86_64_GOTPCREL64 => "R_X86_64_GOTPCREL64",
374 elf.R_X86_64_GOTPC64 => "R_X86_64_GOTPC64",
375 elf.R_X86_64_GOTPLT64 => "R_X86_64_GOTPLT64",
376 elf.R_X86_64_PLTOFF64 => "R_X86_64_PLTOFF64",
377 elf.R_X86_64_SIZE32 => "R_X86_64_SIZE32",
378 elf.R_X86_64_SIZE64 => "R_X86_64_SIZE64",
379 elf.R_X86_64_GOTPC32_TLSDESC => "R_X86_64_GOTPC32_TLSDESC",
380 elf.R_X86_64_TLSDESC_CALL => "R_X86_64_TLSDESC_CALL",
381 elf.R_X86_64_TLSDESC => "R_X86_64_TLSDESC",
382 elf.R_X86_64_IRELATIVE => "R_X86_64_IRELATIVE",
383 elf.R_X86_64_RELATIVE64 => "R_X86_64_RELATIVE64",
384 elf.R_X86_64_GOTPCRELX => "R_X86_64_GOTPCRELX",
385 elf.R_X86_64_REX_GOTPCRELX => "R_X86_64_REX_GOTPCRELX",
386 elf.R_X86_64_NUM => "R_X86_64_NUM",
387 else => "R_X86_64_UNKNOWN",
388 };
389 try writer.print("{s}", .{str});
390}
391
392pub fn format(
393 atom: Atom,
394 comptime unused_fmt_string: []const u8,
395 options: std.fmt.FormatOptions,
396 writer: anytype,
397) !void {
398 _ = atom;
399 _ = unused_fmt_string;
400 _ = options;
401 _ = writer;
402 @compileError("do not format symbols directly");
403}
404
405pub fn fmt(atom: Atom, elf_file: *Elf) std.fmt.Formatter(format2) {
406 return .{ .data = .{
407 .atom = atom,
408 .elf_file = elf_file,
409 } };
410}
411
412const FormatContext = struct {
413 atom: Atom,
414 elf_file: *Elf,
415};
416
417fn format2(
418 ctx: FormatContext,
419 comptime unused_fmt_string: []const u8,
420 options: std.fmt.FormatOptions,
421 writer: anytype,
422) !void {
423 _ = options;
424 _ = unused_fmt_string;
425 const atom = ctx.atom;
426 const elf_file = ctx.elf_file;
427 try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x})", .{
428 atom.atom_index, atom.name(elf_file), atom.value,
429 atom.output_section_index, atom.alignment, atom.size,
430 });
431 // if (atom.fde_start != atom.fde_end) {
432 // try writer.writeAll(" : fdes{ ");
433 // for (atom.getFdes(elf_file), atom.fde_start..) |fde, i| {
434 // try writer.print("{d}", .{i});
435 // if (!fde.alive) try writer.writeAll("([*])");
436 // if (i < atom.fde_end - 1) try writer.writeAll(", ");
437 // }
438 // try writer.writeAll(" }");
439 // }
440 const gc_sections = if (elf_file.base.options.gc_sections) |gc_sections| gc_sections else false;
441 if (gc_sections and !atom.alive) {
442 try writer.writeAll(" : [*]");
443 }
444}
445
297446pub const Index = u32;
298447
299448const std = @import("std");
......@@ -306,4 +455,3 @@ const Allocator = std.mem.Allocator;
306455const Atom = @This();
307456const Elf = @import("../Elf.zig");
308457const File = @import("file.zig").File;
309const Relocation = @import("Relocation.zig");
src/link/Elf/Relocation.zig deleted-8
......@@ -1,8 +0,0 @@
1target: Symbol.Index,
2offset: u64,
3addend: u32,
4
5const std = @import("std");
6
7const Symbol = @import("Symbol.zig");
8const Relocation = @This();
src/link/Elf/ZigModule.zig+25-4
......@@ -8,8 +8,8 @@ local_symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{},
88elf_global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
99global_symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{},
1010
11atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
12relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(Relocation)) = .{},
11atoms: std.AutoArrayHashMapUnmanaged(Atom.Index, void) = .{},
12relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{},
1313
1414alive: bool = true,
1515
......@@ -43,7 +43,7 @@ pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !
4343 const relocs = try self.relocs.addOne(gpa);
4444 relocs.* = .{};
4545 atom_ptr.relocs_section_index = relocs_index;
46 try self.atoms.append(gpa, atom_index);
46 try self.atoms.putNoClobber(gpa, atom_index, {});
4747 return symbol_index;
4848}
4949
......@@ -184,6 +184,28 @@ fn formatSymtab(
184184 }
185185}
186186
187pub fn fmtAtoms(self: *ZigModule, elf_file: *Elf) std.fmt.Formatter(formatAtoms) {
188 return .{ .data = .{
189 .self = self,
190 .elf_file = elf_file,
191 } };
192}
193
194fn formatAtoms(
195 ctx: FormatContext,
196 comptime unused_fmt_string: []const u8,
197 options: std.fmt.FormatOptions,
198 writer: anytype,
199) !void {
200 _ = unused_fmt_string;
201 _ = options;
202 try writer.writeAll(" atoms\n");
203 for (ctx.self.atoms.keys()) |atom_index| {
204 const atom = ctx.elf_file.atom(atom_index) orelse continue;
205 try writer.print(" {}\n", .{atom.fmt(ctx.elf_file)});
206 }
207}
208
187209const assert = std.debug.assert;
188210const std = @import("std");
189211const elf = std.elf;
......@@ -193,6 +215,5 @@ const Atom = @import("Atom.zig");
193215const Elf = @import("../Elf.zig");
194216const File = @import("file.zig").File;
195217const Module = @import("../../Module.zig");
196const Relocation = @import("Relocation.zig");
197218const Symbol = @import("Symbol.zig");
198219const ZigModule = @This();