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 {
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 },
......@@ -1174,7 +1243,7 @@ fn initHeaders(
11741243 }
11751244
11761245 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),
11781247 .alignment = elf.mf.flags.block_size,
11791248 .moved = true,
11801249 .resized = true,
......@@ -1191,7 +1260,7 @@ fn initHeaders(
11911260 elf.phdrs.items[rodata_phndx] = elf.ni.rodata;
11921261
11931262 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),
11951264 .alignment = addr_align,
11961265 .moved = true,
11971266 .resized = true,
......@@ -2123,7 +2192,7 @@ fn loadObject(
21232192 if (ehdr.machine != elf.ehdrField(.machine))
21242193 return diags.failParse(path, "bad machine", .{});
21252194 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)
21272196 return diags.failParse(path, "bad section header location", .{});
21282197 if (ehdr.shentsize < @sizeOf(ElfN.Shdr))
21292198 return diags.failParse(path, "unsupported shentsize", .{});
......@@ -2228,11 +2297,11 @@ fn loadObject(
22282297 si.* = .null;
22292298 const input_sym = try r.peekStruct(ElfN.Sym, target_endian);
22302299 try r.discardAll64(symtab.shdr.entsize);
2231 if (input_sym.name >= strtab.len or input_sym.shndx == std.elf.SHN_UNDEF or
2232 input_sym.shndx >= ehdr.shnum) continue;
2300 if (input_sym.name >= strtab.len or input_sym.shndx >= ehdr.shnum) continue;
22332301 switch (input_sym.info.type) {
22342302 .NOTYPE, .OBJECT, .FUNC => {},
22352303 .SECTION => {
2304 if (input_sym.shndx == std.elf.SHN_UNDEF) continue;
22362305 const section = &sections[input_sym.shndx];
22372306 if (input_sym.value == section.shdr.addr) si.* = section.si;
22382307 continue;
......@@ -2254,21 +2323,30 @@ fn loadObject(
22542323 switch (input_sym.info.bind) {
22552324 else => {},
22562325 .GLOBAL => {
2257 const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad(
2258 &@field(elf.symPtr(si.*), @tagName(class)).name,
2259 ));
2260 if (gop.found_existing) switch (elf.targetLoad(
2261 switch (elf.symPtr(gop.value_ptr.*)) {
2262 inline else => |sym| &sym.info,
2326 const sym = @field(elf.symPtr(si.*), @tagName(class));
2327 const gop =
2328 elf.globals.getOrPutAssumeCapacity(elf.targetLoad(&sym.name));
2329 if (gop.found_existing) switch (input_sym.shndx) {
2330 std.elf.SHN_UNDEF => {},
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);
22632349 },
2264 ).bind) {
2265 else => unreachable,
2266 .GLOBAL => return diags.failParse(
2267 path,
2268 "multiple definitions of '{s}'",
2269 .{name},
2270 ),
2271 .WEAK => {},
22722350 };
22732351 gop.value_ptr.* = si.*;
22742352 },
......@@ -2337,7 +2415,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
23372415 const r = &fr.interface;
23382416
23392417 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);
23412419 if (!std.mem.eql(u8, ident[0..std.elf.MAGIC.len], std.elf.MAGIC)) return error.BadMagic;
23422420 if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.contents[std.elf.MAGIC.len..ident.len]))
23432421 return diags.failParse(path, "bad ident", .{});
......@@ -2621,7 +2699,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
26212699 },
26222700 };
26232701 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 };
26252703 },
26262704 };
26272705 _, const shdr_node_size = elf.ni.shdr.location(&elf.mf).resolve(&elf.mf);
......@@ -3720,6 +3798,14 @@ fn updateExportsInner(
37203798 },
37213799 }
37223800 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 };
37233809 }
37243810}
37253811