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-01-02 23:47:59-05:00
log4f7a3c23e731a4a2e0694b6fa2e4751eb4c7f82d
treeae29c8504847cd012a8fc62b104ada644c44286b
parentc9fa8e46df21cdf66b291fb3dfdcef1b6a1d3cab

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 },
...@@ -1174,7 +1243,7 @@ fn initHeaders(...@@ -1174,7 +1243,7 @@ fn initHeaders(
1174 }1243 }
11751244
1176 assert(elf.ni.shdr == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{1245 assert(elf.ni.shdr == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
1177 .size = elf.ehdrField(.shentsize) * elf.ehdrField(.shnum),1246 .size = @as(u64, elf.ehdrField(.shentsize)) * elf.ehdrField(.shnum),
1178 .alignment = elf.mf.flags.block_size,1247 .alignment = elf.mf.flags.block_size,
1179 .moved = true,1248 .moved = true,
1180 .resized = true,1249 .resized = true,
...@@ -1191,7 +1260,7 @@ fn initHeaders(...@@ -1191,7 +1260,7 @@ fn initHeaders(
1191 elf.phdrs.items[rodata_phndx] = elf.ni.rodata;1260 elf.phdrs.items[rodata_phndx] = elf.ni.rodata;
11921261
1193 assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{1262 assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{
1194 .size = elf.ehdrField(.phentsize) * elf.ehdrField(.phnum),1263 .size = @as(u64, elf.ehdrField(.phentsize)) * elf.ehdrField(.phnum),
1195 .alignment = addr_align,1264 .alignment = addr_align,
1196 .moved = true,1265 .moved = true,
1197 .resized = true,1266 .resized = true,
...@@ -2123,7 +2192,7 @@ fn loadObject(...@@ -2123,7 +2192,7 @@ fn loadObject(
2123 if (ehdr.machine != elf.ehdrField(.machine))2192 if (ehdr.machine != elf.ehdrField(.machine))
2124 return diags.failParse(path, "bad machine", .{});2193 return diags.failParse(path, "bad machine", .{});
2125 if (ehdr.shoff == 0 or ehdr.shnum <= 1) return;2194 if (ehdr.shoff == 0 or ehdr.shnum <= 1) return;
2126 if (ehdr.shoff + ehdr.shentsize * ehdr.shnum > fl.size)2195 if (ehdr.shoff + @as(ElfN.Off, ehdr.shentsize) * ehdr.shnum > fl.size)
2127 return diags.failParse(path, "bad section header location", .{});2196 return diags.failParse(path, "bad section header location", .{});
2128 if (ehdr.shentsize < @sizeOf(ElfN.Shdr))2197 if (ehdr.shentsize < @sizeOf(ElfN.Shdr))
2129 return diags.failParse(path, "unsupported shentsize", .{});2198 return diags.failParse(path, "unsupported shentsize", .{});
...@@ -2228,11 +2297,11 @@ fn loadObject(...@@ -2228,11 +2297,11 @@ fn loadObject(
2228 si.* = .null;2297 si.* = .null;
2229 const input_sym = try r.peekStruct(ElfN.Sym, target_endian);2298 const input_sym = try r.peekStruct(ElfN.Sym, target_endian);
2230 try r.discardAll64(symtab.shdr.entsize);2299 try r.discardAll64(symtab.shdr.entsize);
2231 if (input_sym.name >= strtab.len or input_sym.shndx == std.elf.SHN_UNDEF or2300 if (input_sym.name >= strtab.len or input_sym.shndx >= ehdr.shnum) continue;
2232 input_sym.shndx >= ehdr.shnum) continue;
2233 switch (input_sym.info.type) {2301 switch (input_sym.info.type) {
2234 .NOTYPE, .OBJECT, .FUNC => {},2302 .NOTYPE, .OBJECT, .FUNC => {},
2235 .SECTION => {2303 .SECTION => {
2304 if (input_sym.shndx == std.elf.SHN_UNDEF) continue;
2236 const section = &sections[input_sym.shndx];2305 const section = &sections[input_sym.shndx];
2237 if (input_sym.value == section.shdr.addr) si.* = section.si;2306 if (input_sym.value == section.shdr.addr) si.* = section.si;
2238 continue;2307 continue;
...@@ -2254,21 +2323,30 @@ fn loadObject(...@@ -2254,21 +2323,30 @@ fn loadObject(
2254 switch (input_sym.info.bind) {2323 switch (input_sym.info.bind) {
2255 else => {},2324 else => {},
2256 .GLOBAL => {2325 .GLOBAL => {
2257 const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad(2326 const sym = @field(elf.symPtr(si.*), @tagName(class));
2258 &@field(elf.symPtr(si.*), @tagName(class)).name,2327 const gop =
2259 ));2328 elf.globals.getOrPutAssumeCapacity(elf.targetLoad(&sym.name));
2260 if (gop.found_existing) switch (elf.targetLoad(2329 if (gop.found_existing) switch (input_sym.shndx) {
2261 switch (elf.symPtr(gop.value_ptr.*)) {2330 std.elf.SHN_UNDEF => {},
2262 inline else => |sym| &sym.info,2331 else => {
2332 const existing_sym =
2333 @field(elf.symPtr(gop.value_ptr.*), @tagName(class));
2334 switch (elf.targetLoad(&existing_sym.info).bind) {
2335 else => unreachable,
2336 .GLOBAL => switch (elf.targetLoad(&existing_sym.shndx)) {
2337 std.elf.SHN_UNDEF => {},
2338 else => return diags.failParse(
2339 path,
2340 "multiple definitions of '{s}'",
2341 .{name},
2342 ),
2343 },
2344 .WEAK => {},
2345 }
2346 existing_sym.size = sym.size;
2347 existing_sym.shndx = sym.shndx;
2348 gop.value_ptr.flushMoved(elf, input_sym.value);
2263 },2349 },
2264 ).bind) {
2265 else => unreachable,
2266 .GLOBAL => return diags.failParse(
2267 path,
2268 "multiple definitions of '{s}'",
2269 .{name},
2270 ),
2271 .WEAK => {},
2272 };2350 };
2273 gop.value_ptr.* = si.*;2351 gop.value_ptr.* = si.*;
2274 },2352 },
...@@ -2337,7 +2415,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {...@@ -2337,7 +2415,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
2337 const r = &fr.interface;2415 const r = &fr.interface;
23382416
2339 log.debug("loadDso({f})", .{path.fmtEscapeString()});2417 log.debug("loadDso({f})", .{path.fmtEscapeString()});
2340 const ident = try r.peek(std.elf.EI.NIDENT);2418 const ident = try r.peek(std.elf.EI.OSABI);
2341 if (!std.mem.eql(u8, ident[0..std.elf.MAGIC.len], std.elf.MAGIC)) return error.BadMagic;2419 if (!std.mem.eql(u8, ident[0..std.elf.MAGIC.len], std.elf.MAGIC)) return error.BadMagic;
2342 if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.contents[std.elf.MAGIC.len..ident.len]))2420 if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.contents[std.elf.MAGIC.len..ident.len]))
2343 return diags.failParse(path, "bad ident", .{});2421 return diags.failParse(path, "bad ident", .{});
...@@ -2621,7 +2699,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -2621,7 +2699,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
2621 },2699 },
2622 };2700 };
2623 assert(shndx < @intFromEnum(Symbol.Index.Shndx.LORESERVE));2701 assert(shndx < @intFromEnum(Symbol.Index.Shndx.LORESERVE));
2624 break :shndx .{ @enumFromInt(shndx), elf.targetLoad(&ehdr.shentsize) * shnum };2702 break :shndx .{ @enumFromInt(shndx), @as(u64, elf.targetLoad(&ehdr.shentsize)) * shnum };
2625 },2703 },
2626 };2704 };
2627 _, const shdr_node_size = elf.ni.shdr.location(&elf.mf).resolve(&elf.mf);2705 _, const shdr_node_size = elf.ni.shdr.location(&elf.mf).resolve(&elf.mf);
...@@ -3720,6 +3798,14 @@ fn updateExportsInner(...@@ -3720,6 +3798,14 @@ fn updateExportsInner(
3720 },3798 },
3721 }3799 }
3722 export_si.flushMoved(elf, value);3800 export_si.flushMoved(elf, value);
3801 if (elf.dynsym.getIndex(export_si)) |export_dsi| switch (elf.dynsymSlice()) {
3802 inline else => |dynsyms| {
3803 const dynsym = &dynsyms[export_dsi];
3804 elf.targetStore(&dynsym.value, @intCast(value));
3805 dynsym.size = @intCast(size);
3806 dynsym.shndx = shndx;
3807 },
3808 };
3723 }3809 }
3724}3810}
37253811