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 {...@@ -743,7 +743,7 @@ pub const Reloc = extern struct {
743 }743 }
744 };744 };
745745
746 pub fn apply(reloc: *const Reloc, elf: *Elf) void {746 pub fn apply(reloc: *Reloc, elf: *Elf) void {
747 assert(elf.ehdrField(.type) != .REL);747 assert(elf.ehdrField(.type) != .REL);
748 const loc_ni = reloc.loc.get(elf).ni;748 const loc_ni = reloc.loc.get(elf).ni;
749 switch (loc_ni) {749 switch (loc_ni) {
...@@ -766,7 +766,7 @@ pub const Reloc = extern struct {...@@ -766,7 +766,7 @@ pub const Reloc = extern struct {
766 elf.targetLoad(&target_sym.value) +% @as(u64, @bitCast(reloc.addend));766 elf.targetLoad(&target_sym.value) +% @as(u64, @bitCast(reloc.addend));
767 switch (elf.ehdrField(.machine)) {767 switch (elf.ehdrField(.machine)) {
768 else => |machine| @panic(@tagName(machine)),768 else => |machine| @panic(@tagName(machine)),
769 .X86_64 => switch (reloc.type.X86_64) {769 .X86_64 => type: switch (reloc.type.X86_64) {
770 else => |kind| @panic(@tagName(kind)),770 else => |kind| @panic(@tagName(kind)),
771 .@"64" => std.mem.writeInt(771 .@"64" => std.mem.writeInt(
772 u64,772 u64,
...@@ -852,6 +852,75 @@ pub const Reloc = extern struct {...@@ -852,6 +852,75 @@ pub const Reloc = extern struct {
852 elf.targetLoad(&target_sym.size) +% @as(u64, @bitCast(reloc.addend)),852 elf.targetLoad(&target_sym.size) +% @as(u64, @bitCast(reloc.addend)),
853 target_endian,853 target_endian,
854 ),854 ),
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 },
855 },924 },
856 }925 }
857 },926 },
...@@ -1176,7 +1245,7 @@ fn initHeaders(...@@ -1176,7 +1245,7 @@ fn initHeaders(
1176 }1245 }
11771246
1178 assert(elf.ni.shdr == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{1247 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),
1180 .alignment = elf.mf.flags.block_size,1249 .alignment = elf.mf.flags.block_size,
1181 .moved = true,1250 .moved = true,
1182 .resized = true,1251 .resized = true,
...@@ -1193,7 +1262,7 @@ fn initHeaders(...@@ -1193,7 +1262,7 @@ fn initHeaders(
1193 elf.phdrs.items[rodata_phndx] = elf.ni.rodata;1262 elf.phdrs.items[rodata_phndx] = elf.ni.rodata;
11941263
1195 assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{1264 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),
1197 .alignment = addr_align,1266 .alignment = addr_align,
1198 .moved = true,1267 .moved = true,
1199 .resized = true,1268 .resized = true,
...@@ -2108,7 +2177,7 @@ fn loadObject(...@@ -2108,7 +2177,7 @@ fn loadObject(
2108 if (ehdr.machine != elf.ehdrField(.machine))2177 if (ehdr.machine != elf.ehdrField(.machine))
2109 return diags.failParse(path, "bad machine", .{});2178 return diags.failParse(path, "bad machine", .{});
2110 if (ehdr.shoff == 0 or ehdr.shnum <= 1) return;2179 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)
2112 return diags.failParse(path, "bad section header location", .{});2181 return diags.failParse(path, "bad section header location", .{});
2113 if (ehdr.shentsize < @sizeOf(ElfN.Shdr))2182 if (ehdr.shentsize < @sizeOf(ElfN.Shdr))
2114 return diags.failParse(path, "unsupported shentsize", .{});2183 return diags.failParse(path, "unsupported shentsize", .{});
...@@ -2213,11 +2282,11 @@ fn loadObject(...@@ -2213,11 +2282,11 @@ fn loadObject(
2213 si.* = .null;2282 si.* = .null;
2214 const input_sym = try r.peekStruct(ElfN.Sym, target_endian);2283 const input_sym = try r.peekStruct(ElfN.Sym, target_endian);
2215 try r.discardAll64(symtab.shdr.entsize);2284 try r.discardAll64(symtab.shdr.entsize);
2216 if (input_sym.name >= strtab.len or input_sym.shndx == std.elf.SHN_UNDEF or2285 if (input_sym.name >= strtab.len or input_sym.shndx >= ehdr.shnum) continue;
2217 input_sym.shndx >= ehdr.shnum) continue;
2218 switch (input_sym.info.type) {2286 switch (input_sym.info.type) {
2219 .NOTYPE, .OBJECT, .FUNC => {},2287 .NOTYPE, .OBJECT, .FUNC => {},
2220 .SECTION => {2288 .SECTION => {
2289 if (input_sym.shndx == std.elf.SHN_UNDEF) continue;
2221 const section = &sections[input_sym.shndx];2290 const section = &sections[input_sym.shndx];
2222 if (input_sym.value == section.shdr.addr) si.* = section.si;2291 if (input_sym.value == section.shdr.addr) si.* = section.si;
2223 continue;2292 continue;
...@@ -2239,21 +2308,30 @@ fn loadObject(...@@ -2239,21 +2308,30 @@ fn loadObject(
2239 switch (input_sym.info.bind) {2308 switch (input_sym.info.bind) {
2240 else => {},2309 else => {},
2241 .GLOBAL => {2310 .GLOBAL => {
2242 const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad(2311 const sym = @field(elf.symPtr(si.*), @tagName(class));
2243 &@field(elf.symPtr(si.*), @tagName(class)).name,2312 const gop =
2244 ));2313 elf.globals.getOrPutAssumeCapacity(elf.targetLoad(&sym.name));
2245 if (gop.found_existing) switch (elf.targetLoad(2314 if (gop.found_existing) switch (input_sym.shndx) {
2246 switch (elf.symPtr(gop.value_ptr.*)) {2315 std.elf.SHN_UNDEF => {},
2247 inline else => |sym| &sym.info,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);
2248 },2334 },
2249 ).bind) {
2250 else => unreachable,
2251 .GLOBAL => return diags.failParse(
2252 path,
2253 "multiple definitions of '{s}'",
2254 .{name},
2255 ),
2256 .WEAK => {},
2257 };2335 };
2258 gop.value_ptr.* = si.*;2336 gop.value_ptr.* = si.*;
2259 },2337 },
...@@ -2322,7 +2400,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {...@@ -2322,7 +2400,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
2322 const r = &fr.interface;2400 const r = &fr.interface;
23232401
2324 log.debug("loadDso({f})", .{path.fmtEscapeString()});2402 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);
2326 if (!std.mem.eql(u8, ident[0..std.elf.MAGIC.len], std.elf.MAGIC)) return error.BadMagic;2404 if (!std.mem.eql(u8, ident[0..std.elf.MAGIC.len], std.elf.MAGIC)) return error.BadMagic;
2327 if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.memory_map.memory[std.elf.MAGIC.len..ident.len]))2405 if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.memory_map.memory[std.elf.MAGIC.len..ident.len]))
2328 return diags.failParse(path, "bad ident", .{});2406 return diags.failParse(path, "bad ident", .{});
...@@ -2606,7 +2684,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -2606,7 +2684,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
2606 },2684 },
2607 };2685 };
2608 assert(shndx < @intFromEnum(Symbol.Index.Shndx.LORESERVE));2686 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 };
2610 },2688 },
2611 };2689 };
2612 _, const shdr_node_size = elf.ni.shdr.location(&elf.mf).resolve(&elf.mf);2690 _, const shdr_node_size = elf.ni.shdr.location(&elf.mf).resolve(&elf.mf);
...@@ -3706,6 +3784,14 @@ fn updateExportsInner(...@@ -3706,6 +3784,14 @@ fn updateExportsInner(
3706 },3784 },
3707 }3785 }
3708 export_si.flushMoved(elf, value);3786 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 };
3709 }3795 }
3710}3796}
37113797