authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-12-21 12:28:01-05:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-05-17 15:58:27-04:00
logb8227089a0ea688f53c754006d89b9c981c8d84d
treeab1dc2e7af706f22e9437c9773127162bf05dca6
parentbdfbf432dd626b495cee0a8aa53e77b4ef718ff2

Elf2: improve dynamic linking support


1 files changed, 109 insertions(+), 23 deletions(-)

src/link/Elf2.zig+109-23
......@@ -743,7 +743,7 @@ pub const Reloc = extern struct {
743743 }
744744 };
745745
746 pub fn apply(reloc: *const Reloc, elf: *Elf) void {
746 pub fn apply(reloc: *Reloc, elf: *Elf) void {
747747 assert(elf.ehdrField(.type) != .REL);
748748 const loc_ni = reloc.loc.get(elf).ni;
749749 switch (loc_ni) {
......@@ -766,7 +766,7 @@ pub const Reloc = extern struct {
766766 elf.targetLoad(&target_sym.value) +% @as(u64, @bitCast(reloc.addend));
767767 switch (elf.ehdrField(.machine)) {
768768 else => |machine| @panic(@tagName(machine)),
769 .X86_64 => switch (reloc.type.X86_64) {
769 .X86_64 => type: switch (reloc.type.X86_64) {
770770 else => |kind| @panic(@tagName(kind)),
771771 .@"64" => std.mem.writeInt(
772772 u64,
......@@ -852,6 +852,75 @@ pub const Reloc = extern struct {
852852 elf.targetLoad(&target_sym.size) +% @as(u64, @bitCast(reloc.addend)),
853853 target_endian,
854854 ),
855 .GOTPCRELX => {
856 relax: switch (Symbol.Index.Shndx.fromSection(
857 elf.targetLoad(&target_sym.shndx),
858 )) {
859 .UNDEF => {},
860 else => {
861 const inst = (loc_slice.ptr - 2)[0..2];
862 if (inst[0] != 0xff) break :relax;
863 const mod_rm: packed struct {
864 rm: u3,
865 opcode: u3,
866 mod: u2,
867 } = @bitCast(inst[1]);
868 if (mod_rm.mod != 0b00) break :relax;
869 if (mod_rm.rm != 0b101) break :relax;
870 switch (mod_rm.opcode) {
871 0, // incl 0x0(%rip)
872 1, // decl 0x0(%rip)
873 3, // lcall *0x0(%rip)
874 5, // ljmp *0x0(%rip)
875 6, // push 0x0(%rip)
876 7, // ud 0x0(%rip)
877 => break :relax,
878 2 => { // call *0x0(%rip)
879 inst[1] = 0xe8; // call 0
880 },
881 4 => { // jmp *0x0(%rip)
882 inst[1] = 0xe9; // jmp 0
883 },
884 }
885 inst[0] = 0x48; // rex.W
886 reloc.type.X86_64 = .PC32;
887 continue :type .PC32;
888 },
889 }
890 @panic("relax failure");
891 },
892 .REX_GOTPCRELX => {
893 relax: switch (Symbol.Index.Shndx.fromSection(
894 elf.targetLoad(&target_sym.shndx),
895 )) {
896 .UNDEF => {},
897 else => {
898 const inst = (loc_slice.ptr - 3)[0..3];
899 const rex: packed struct {
900 b: bool,
901 x: bool,
902 r: bool,
903 w: bool,
904 encoded4: u4,
905 } = @bitCast(inst[0]);
906 if (rex.encoded4 != 0b0100) break :relax;
907 if (!rex.w) break :relax;
908 if (rex.x) break :relax;
909 if (inst[1] != 0x8b) break :relax; // mov
910 const mod_rm: packed struct {
911 rm: u3,
912 r: u3,
913 mod: u2,
914 } = @bitCast(inst[2]);
915 if (mod_rm.mod != 0b00) break :relax;
916 if (mod_rm.rm != 0b101) break :relax;
917 inst[1] = 0x8d; // lea
918 reloc.type.X86_64 = .PC32;
919 continue :type .PC32;
920 },
921 }
922 @panic("relax failure");
923 },
855924 },
856925 }
857926 },
......@@ -1176,7 +1245,7 @@ fn initHeaders(
11761245 }
11771246
11781247 assert(elf.ni.shdr == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
1179 .size = elf.ehdrField(.shentsize) * elf.ehdrField(.shnum),
1248 .size = @as(u64, elf.ehdrField(.shentsize)) * elf.ehdrField(.shnum),
11801249 .alignment = elf.mf.flags.block_size,
11811250 .moved = true,
11821251 .resized = true,
......@@ -1193,7 +1262,7 @@ fn initHeaders(
11931262 elf.phdrs.items[rodata_phndx] = elf.ni.rodata;
11941263
11951264 assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{
1196 .size = elf.ehdrField(.phentsize) * elf.ehdrField(.phnum),
1265 .size = @as(u64, elf.ehdrField(.phentsize)) * elf.ehdrField(.phnum),
11971266 .alignment = addr_align,
11981267 .moved = true,
11991268 .resized = true,
......@@ -2108,7 +2177,7 @@ fn loadObject(
21082177 if (ehdr.machine != elf.ehdrField(.machine))
21092178 return diags.failParse(path, "bad machine", .{});
21102179 if (ehdr.shoff == 0 or ehdr.shnum <= 1) return;
2111 if (ehdr.shoff + ehdr.shentsize * ehdr.shnum > fl.size)
2180 if (ehdr.shoff + @as(ElfN.Off, ehdr.shentsize) * ehdr.shnum > fl.size)
21122181 return diags.failParse(path, "bad section header location", .{});
21132182 if (ehdr.shentsize < @sizeOf(ElfN.Shdr))
21142183 return diags.failParse(path, "unsupported shentsize", .{});
......@@ -2213,11 +2282,11 @@ fn loadObject(
22132282 si.* = .null;
22142283 const input_sym = try r.peekStruct(ElfN.Sym, target_endian);
22152284 try r.discardAll64(symtab.shdr.entsize);
2216 if (input_sym.name >= strtab.len or input_sym.shndx == std.elf.SHN_UNDEF or
2217 input_sym.shndx >= ehdr.shnum) continue;
2285 if (input_sym.name >= strtab.len or input_sym.shndx >= ehdr.shnum) continue;
22182286 switch (input_sym.info.type) {
22192287 .NOTYPE, .OBJECT, .FUNC => {},
22202288 .SECTION => {
2289 if (input_sym.shndx == std.elf.SHN_UNDEF) continue;
22212290 const section = &sections[input_sym.shndx];
22222291 if (input_sym.value == section.shdr.addr) si.* = section.si;
22232292 continue;
......@@ -2239,21 +2308,30 @@ fn loadObject(
22392308 switch (input_sym.info.bind) {
22402309 else => {},
22412310 .GLOBAL => {
2242 const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad(
2243 &@field(elf.symPtr(si.*), @tagName(class)).name,
2244 ));
2245 if (gop.found_existing) switch (elf.targetLoad(
2246 switch (elf.symPtr(gop.value_ptr.*)) {
2247 inline else => |sym| &sym.info,
2311 const sym = @field(elf.symPtr(si.*), @tagName(class));
2312 const gop =
2313 elf.globals.getOrPutAssumeCapacity(elf.targetLoad(&sym.name));
2314 if (gop.found_existing) switch (input_sym.shndx) {
2315 std.elf.SHN_UNDEF => {},
2316 else => {
2317 const existing_sym =
2318 @field(elf.symPtr(gop.value_ptr.*), @tagName(class));
2319 switch (elf.targetLoad(&existing_sym.info).bind) {
2320 else => unreachable,
2321 .GLOBAL => switch (elf.targetLoad(&existing_sym.shndx)) {
2322 std.elf.SHN_UNDEF => {},
2323 else => return diags.failParse(
2324 path,
2325 "multiple definitions of '{s}'",
2326 .{name},
2327 ),
2328 },
2329 .WEAK => {},
2330 }
2331 existing_sym.size = sym.size;
2332 existing_sym.shndx = sym.shndx;
2333 gop.value_ptr.flushMoved(elf, input_sym.value);
22482334 },
2249 ).bind) {
2250 else => unreachable,
2251 .GLOBAL => return diags.failParse(
2252 path,
2253 "multiple definitions of '{s}'",
2254 .{name},
2255 ),
2256 .WEAK => {},
22572335 };
22582336 gop.value_ptr.* = si.*;
22592337 },
......@@ -2322,7 +2400,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
23222400 const r = &fr.interface;
23232401
23242402 log.debug("loadDso({f})", .{path.fmtEscapeString()});
2325 const ident = try r.peek(std.elf.EI.NIDENT);
2403 const ident = try r.peek(std.elf.EI.OSABI);
23262404 if (!std.mem.eql(u8, ident[0..std.elf.MAGIC.len], std.elf.MAGIC)) return error.BadMagic;
23272405 if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.memory_map.memory[std.elf.MAGIC.len..ident.len]))
23282406 return diags.failParse(path, "bad ident", .{});
......@@ -2606,7 +2684,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
26062684 },
26072685 };
26082686 assert(shndx < @intFromEnum(Symbol.Index.Shndx.LORESERVE));
2609 break :shndx .{ @enumFromInt(shndx), elf.targetLoad(&ehdr.shentsize) * shnum };
2687 break :shndx .{ @enumFromInt(shndx), @as(u64, elf.targetLoad(&ehdr.shentsize)) * shnum };
26102688 },
26112689 };
26122690 _, const shdr_node_size = elf.ni.shdr.location(&elf.mf).resolve(&elf.mf);
......@@ -3706,6 +3784,14 @@ fn updateExportsInner(
37063784 },
37073785 }
37083786 export_si.flushMoved(elf, value);
3787 if (elf.dynsym.getIndex(export_si)) |export_dsi| switch (elf.dynsymSlice()) {
3788 inline else => |dynsyms| {
3789 const dynsym = &dynsyms[export_dsi];
3790 elf.targetStore(&dynsym.value, @intCast(value));
3791 dynsym.size = @intCast(size);
3792 dynsym.shndx = shndx;
3793 },
3794 };
37093795 }
37103796}
37113797