authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-17 17:36:40+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-10-17 17:36:40+02:00
logeb5276c94eaab238551fdae9a2e77b0133e31cfb
treea8040cc914bb1db73b2484087cf81d5f20bab28c
parent5039a5db8365413794b0522a51137d3e97d8ba5d
parent742a130ce55ae776372f99b0724c32a462040caf
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17556 from ziglang/elf-link-zig-proper

elf: port 99% of zld ELF linker to Zig proper

22 files changed, 8739 insertions(+), 1778 deletions(-)

CMakeLists.txt+2
...@@ -591,10 +591,12 @@ set(ZIG_STAGE2_SOURCES...@@ -591,10 +591,12 @@ set(ZIG_STAGE2_SOURCES
591 "${CMAKE_SOURCE_DIR}/src/link/Elf/Atom.zig"591 "${CMAKE_SOURCE_DIR}/src/link/Elf/Atom.zig"
592 "${CMAKE_SOURCE_DIR}/src/link/Elf/LinkerDefined.zig"592 "${CMAKE_SOURCE_DIR}/src/link/Elf/LinkerDefined.zig"
593 "${CMAKE_SOURCE_DIR}/src/link/Elf/Object.zig"593 "${CMAKE_SOURCE_DIR}/src/link/Elf/Object.zig"
594 "${CMAKE_SOURCE_DIR}/src/link/Elf/SharedObject.zig"
594 "${CMAKE_SOURCE_DIR}/src/link/Elf/Symbol.zig"595 "${CMAKE_SOURCE_DIR}/src/link/Elf/Symbol.zig"
595 "${CMAKE_SOURCE_DIR}/src/link/Elf/ZigModule.zig"596 "${CMAKE_SOURCE_DIR}/src/link/Elf/ZigModule.zig"
596 "${CMAKE_SOURCE_DIR}/src/link/Elf/eh_frame.zig"597 "${CMAKE_SOURCE_DIR}/src/link/Elf/eh_frame.zig"
597 "${CMAKE_SOURCE_DIR}/src/link/Elf/file.zig"598 "${CMAKE_SOURCE_DIR}/src/link/Elf/file.zig"
599 "${CMAKE_SOURCE_DIR}/src/link/Elf/gc.zig"
598 "${CMAKE_SOURCE_DIR}/src/link/Elf/synthetic_sections.zig"600 "${CMAKE_SOURCE_DIR}/src/link/Elf/synthetic_sections.zig"
599 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"601 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
600 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"602 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
src/arch/aarch64/CodeGen.zig+7-3
...@@ -4012,6 +4012,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -4012,6 +4012,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
4012 .got => .load_memory_ptr_got,4012 .got => .load_memory_ptr_got,
4013 .direct => .load_memory_ptr_direct,4013 .direct => .load_memory_ptr_direct,
4014 .import => unreachable,4014 .import => unreachable,
4015 .extern_got => unreachable,
4015 };4016 };
4016 const atom_index = switch (self.bin_file.tag) {4017 const atom_index = switch (self.bin_file.tag) {
4017 .macho => blk: {4018 .macho => blk: {
...@@ -4318,8 +4319,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4318,8 +4319,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4318 if (self.bin_file.cast(link.File.Elf)) |elf_file| {4319 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4319 const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);4320 const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);
4320 const sym = elf_file.symbol(sym_index);4321 const sym = elf_file.symbol(sym_index);
4321 _ = try sym.getOrCreateGotEntry(sym_index, elf_file);4322 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
4322 const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file)));4323 const got_addr = @as(u32, @intCast(sym.zigGotAddress(elf_file)));
4323 try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr });4324 try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr });
4324 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {4325 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4325 const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl);4326 const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl);
...@@ -5531,6 +5532,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -5531,6 +5532,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
5531 .got => .load_memory_ptr_got,5532 .got => .load_memory_ptr_got,
5532 .direct => .load_memory_ptr_direct,5533 .direct => .load_memory_ptr_direct,
5533 .import => unreachable,5534 .import => unreachable,
5535 .extern_got => unreachable,
5534 };5536 };
5535 const atom_index = switch (self.bin_file.tag) {5537 const atom_index = switch (self.bin_file.tag) {
5536 .macho => blk: {5538 .macho => blk: {
...@@ -5652,6 +5654,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5652,6 +5654,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5652 .got => .load_memory_got,5654 .got => .load_memory_got,
5653 .direct => .load_memory_direct,5655 .direct => .load_memory_direct,
5654 .import => .load_memory_import,5656 .import => .load_memory_import,
5657 .extern_got => unreachable,
5655 };5658 };
5656 const atom_index = switch (self.bin_file.tag) {5659 const atom_index = switch (self.bin_file.tag) {
5657 .macho => blk: {5660 .macho => blk: {
...@@ -5849,6 +5852,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I...@@ -5849,6 +5852,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
5849 .got => .load_memory_ptr_got,5852 .got => .load_memory_ptr_got,
5850 .direct => .load_memory_ptr_direct,5853 .direct => .load_memory_ptr_direct,
5851 .import => unreachable,5854 .import => unreachable,
5855 .extern_got => unreachable,
5852 };5856 };
5853 const atom_index = switch (self.bin_file.tag) {5857 const atom_index = switch (self.bin_file.tag) {
5854 .macho => blk: {5858 .macho => blk: {
...@@ -6176,7 +6180,7 @@ fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {...@@ -6176,7 +6180,7 @@ fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
6176 .memory => |addr| .{ .memory = addr },6180 .memory => |addr| .{ .memory = addr },
6177 .load_got => |sym_index| .{ .linker_load = .{ .type = .got, .sym_index = sym_index } },6181 .load_got => |sym_index| .{ .linker_load = .{ .type = .got, .sym_index = sym_index } },
6178 .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } },6182 .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } },
6179 .load_tlv => unreachable, // TODO6183 .load_extern_got, .load_tlv => unreachable, // TODO
6180 },6184 },
6181 .fail => |msg| {6185 .fail => |msg| {
6182 self.err_msg = msg;6186 self.err_msg = msg;
src/arch/arm/CodeGen.zig+3-3
...@@ -4304,8 +4304,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4304,8 +4304,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4304 if (self.bin_file.cast(link.File.Elf)) |elf_file| {4304 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4305 const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);4305 const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);
4306 const sym = elf_file.symbol(sym_index);4306 const sym = elf_file.symbol(sym_index);
4307 _ = try sym.getOrCreateGotEntry(sym_index, elf_file);4307 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
4308 const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file)));4308 const got_addr = @as(u32, @intCast(sym.zigGotAddress(elf_file)));
4309 try self.genSetReg(Type.usize, .lr, .{ .memory = got_addr });4309 try self.genSetReg(Type.usize, .lr, .{ .memory = got_addr });
4310 } else if (self.bin_file.cast(link.File.MachO)) |_| {4310 } else if (self.bin_file.cast(link.File.MachO)) |_| {
4311 unreachable; // unsupported architecture for MachO4311 unreachable; // unsupported architecture for MachO
...@@ -6135,7 +6135,7 @@ fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {...@@ -6135,7 +6135,7 @@ fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
6135 .mcv => |mcv| switch (mcv) {6135 .mcv => |mcv| switch (mcv) {
6136 .none => .none,6136 .none => .none,
6137 .undef => .undef,6137 .undef => .undef,
6138 .load_got, .load_direct, .load_tlv => unreachable, // TODO6138 .load_got, .load_extern_got, .load_direct, .load_tlv => unreachable, // TODO
6139 .immediate => |imm| .{ .immediate = @as(u32, @truncate(imm)) },6139 .immediate => |imm| .{ .immediate = @as(u32, @truncate(imm)) },
6140 .memory => |addr| .{ .memory = addr },6140 .memory => |addr| .{ .memory = addr },
6141 },6141 },
src/arch/riscv64/CodeGen.zig+3-3
...@@ -1754,8 +1754,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -1754,8 +1754,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
1754 .func => |func| {1754 .func => |func| {
1755 const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);1755 const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);
1756 const sym = elf_file.symbol(sym_index);1756 const sym = elf_file.symbol(sym_index);
1757 _ = try sym.getOrCreateGotEntry(sym_index, elf_file);1757 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
1758 const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file)));1758 const got_addr = @as(u32, @intCast(sym.zigGotAddress(elf_file)));
1759 try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr });1759 try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr });
1760 _ = try self.addInst(.{1760 _ = try self.addInst(.{
1761 .tag = .jalr,1761 .tag = .jalr,
...@@ -2591,7 +2591,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {...@@ -2591,7 +2591,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
2591 .mcv => |mcv| switch (mcv) {2591 .mcv => |mcv| switch (mcv) {
2592 .none => .none,2592 .none => .none,
2593 .undef => .undef,2593 .undef => .undef,
2594 .load_got, .load_direct, .load_tlv => unreachable, // TODO2594 .load_got, .load_extern_got, .load_direct, .load_tlv => unreachable, // TODO
2595 .immediate => |imm| .{ .immediate = imm },2595 .immediate => |imm| .{ .immediate = imm },
2596 .memory => |addr| .{ .memory = addr },2596 .memory => |addr| .{ .memory = addr },
2597 },2597 },
src/arch/sparc64/CodeGen.zig+3-3
...@@ -1349,8 +1349,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -1349,8 +1349,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
1349 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {1349 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1350 const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);1350 const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);
1351 const sym = elf_file.symbol(sym_index);1351 const sym = elf_file.symbol(sym_index);
1352 _ = try sym.getOrCreateGotEntry(sym_index, elf_file);1352 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
1353 break :blk @as(u32, @intCast(sym.gotAddress(elf_file)));1353 break :blk @as(u32, @intCast(sym.zigGotAddress(elf_file)));
1354 } else unreachable;1354 } else unreachable;
13551355
1356 try self.genSetReg(Type.usize, .o7, .{ .memory = got_addr });1356 try self.genSetReg(Type.usize, .o7, .{ .memory = got_addr });
...@@ -4137,7 +4137,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {...@@ -4137,7 +4137,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
4137 .mcv => |mcv| switch (mcv) {4137 .mcv => |mcv| switch (mcv) {
4138 .none => .none,4138 .none => .none,
4139 .undef => .undef,4139 .undef => .undef,
4140 .load_got, .load_direct, .load_tlv => unreachable, // TODO4140 .load_got, .load_extern_got, .load_direct, .load_tlv => unreachable, // TODO
4141 .immediate => |imm| .{ .immediate = imm },4141 .immediate => |imm| .{ .immediate = imm },
4142 .memory => |addr| .{ .memory = addr },4142 .memory => |addr| .{ .memory = addr },
4143 },4143 },
src/arch/x86_64/CodeGen.zig+129-57
...@@ -207,6 +207,12 @@ pub const MCValue = union(enum) {...@@ -207,6 +207,12 @@ pub const MCValue = union(enum) {
207 /// The value is a pointer to a value referenced indirectly via GOT.207 /// The value is a pointer to a value referenced indirectly via GOT.
208 /// Payload is a symbol index.208 /// Payload is a symbol index.
209 lea_got: u32,209 lea_got: u32,
210 /// The value is an extern variable referenced via GOT.
211 /// Payload is a symbol index.
212 load_extern_got: u32,
213 /// The value is a pointer to an extern variable referenced via GOT.
214 /// Payload is a symbol index.
215 lea_extern_got: u32,
210 /// The value is a threadlocal variable.216 /// The value is a threadlocal variable.
211 /// Payload is a symbol index.217 /// Payload is a symbol index.
212 load_tlv: u32,218 load_tlv: u32,
...@@ -295,6 +301,7 @@ pub const MCValue = union(enum) {...@@ -295,6 +301,7 @@ pub const MCValue = union(enum) {
295 .register_overflow,301 .register_overflow,
296 .lea_direct,302 .lea_direct,
297 .lea_got,303 .lea_got,
304 .lea_extern_got,
298 .lea_tlv,305 .lea_tlv,
299 .lea_frame,306 .lea_frame,
300 .reserved_frame,307 .reserved_frame,
...@@ -308,6 +315,7 @@ pub const MCValue = union(enum) {...@@ -308,6 +315,7 @@ pub const MCValue = union(enum) {
308 .load_direct => |sym_index| .{ .lea_direct = sym_index },315 .load_direct => |sym_index| .{ .lea_direct = sym_index },
309 .load_got => |sym_index| .{ .lea_got = sym_index },316 .load_got => |sym_index| .{ .lea_got = sym_index },
310 .load_tlv => |sym_index| .{ .lea_tlv = sym_index },317 .load_tlv => |sym_index| .{ .lea_tlv = sym_index },
318 .load_extern_got => |sym_index| .{ .lea_extern_got = sym_index },
311 .load_frame => |frame_addr| .{ .lea_frame = frame_addr },319 .load_frame => |frame_addr| .{ .lea_frame = frame_addr },
312 };320 };
313 }321 }
...@@ -325,6 +333,7 @@ pub const MCValue = union(enum) {...@@ -325,6 +333,7 @@ pub const MCValue = union(enum) {
325 .indirect,333 .indirect,
326 .load_direct,334 .load_direct,
327 .load_got,335 .load_got,
336 .load_extern_got,
328 .load_tlv,337 .load_tlv,
329 .load_frame,338 .load_frame,
330 .reserved_frame,339 .reserved_frame,
...@@ -335,6 +344,7 @@ pub const MCValue = union(enum) {...@@ -335,6 +344,7 @@ pub const MCValue = union(enum) {
335 .register_offset => |reg_off| .{ .indirect = reg_off },344 .register_offset => |reg_off| .{ .indirect = reg_off },
336 .lea_direct => |sym_index| .{ .load_direct = sym_index },345 .lea_direct => |sym_index| .{ .load_direct = sym_index },
337 .lea_got => |sym_index| .{ .load_got = sym_index },346 .lea_got => |sym_index| .{ .load_got = sym_index },
347 .lea_extern_got => |sym_index| .{ .load_extern_got = sym_index },
338 .lea_tlv => |sym_index| .{ .load_tlv = sym_index },348 .lea_tlv => |sym_index| .{ .load_tlv = sym_index },
339 .lea_frame => |frame_addr| .{ .load_frame = frame_addr },349 .lea_frame => |frame_addr| .{ .load_frame = frame_addr },
340 };350 };
...@@ -358,6 +368,8 @@ pub const MCValue = union(enum) {...@@ -358,6 +368,8 @@ pub const MCValue = union(enum) {
358 .lea_direct,368 .lea_direct,
359 .load_got,369 .load_got,
360 .lea_got,370 .lea_got,
371 .load_extern_got,
372 .lea_extern_got,
361 .load_tlv,373 .load_tlv,
362 .lea_tlv,374 .lea_tlv,
363 .load_frame,375 .load_frame,
...@@ -392,6 +404,8 @@ pub const MCValue = union(enum) {...@@ -392,6 +404,8 @@ pub const MCValue = union(enum) {
392 .lea_direct,404 .lea_direct,
393 .load_got,405 .load_got,
394 .lea_got,406 .lea_got,
407 .load_extern_got,
408 .lea_extern_got,
395 .load_tlv,409 .load_tlv,
396 .lea_tlv,410 .lea_tlv,
397 .lea_frame,411 .lea_frame,
...@@ -434,6 +448,8 @@ pub const MCValue = union(enum) {...@@ -434,6 +448,8 @@ pub const MCValue = union(enum) {
434 .lea_direct => |pl| try writer.print("direct:{d}", .{pl}),448 .lea_direct => |pl| try writer.print("direct:{d}", .{pl}),
435 .load_got => |pl| try writer.print("[got:{d}]", .{pl}),449 .load_got => |pl| try writer.print("[got:{d}]", .{pl}),
436 .lea_got => |pl| try writer.print("got:{d}", .{pl}),450 .lea_got => |pl| try writer.print("got:{d}", .{pl}),
451 .load_extern_got => |pl| try writer.print("[extern_got:{d}]", .{pl}),
452 .lea_extern_got => |pl| try writer.print("extern_got:{d}", .{pl}),
437 .load_tlv => |pl| try writer.print("[tlv:{d}]", .{pl}),453 .load_tlv => |pl| try writer.print("[tlv:{d}]", .{pl}),
438 .lea_tlv => |pl| try writer.print("tlv:{d}", .{pl}),454 .lea_tlv => |pl| try writer.print("tlv:{d}", .{pl}),
439 .load_frame => |pl| try writer.print("[{} + 0x{x}]", .{ pl.index, pl.off }),455 .load_frame => |pl| try writer.print("[{} + 0x{x}]", .{ pl.index, pl.off }),
...@@ -461,6 +477,8 @@ const InstTracking = struct {...@@ -461,6 +477,8 @@ const InstTracking = struct {
461 .lea_direct,477 .lea_direct,
462 .load_got,478 .load_got,
463 .lea_got,479 .lea_got,
480 .load_extern_got,
481 .lea_extern_got,
464 .load_tlv,482 .load_tlv,
465 .lea_tlv,483 .lea_tlv,
466 .load_frame,484 .load_frame,
...@@ -520,6 +538,8 @@ const InstTracking = struct {...@@ -520,6 +538,8 @@ const InstTracking = struct {
520 .lea_direct,538 .lea_direct,
521 .load_got,539 .load_got,
522 .lea_got,540 .lea_got,
541 .load_extern_got,
542 .lea_extern_got,
523 .load_tlv,543 .load_tlv,
524 .lea_tlv,544 .lea_tlv,
525 .load_frame,545 .load_frame,
...@@ -555,6 +575,8 @@ const InstTracking = struct {...@@ -555,6 +575,8 @@ const InstTracking = struct {
555 .lea_direct,575 .lea_direct,
556 .load_got,576 .load_got,
557 .lea_got,577 .lea_got,
578 .load_extern_got,
579 .lea_extern_got,
558 .load_tlv,580 .load_tlv,
559 .lea_tlv,581 .lea_tlv,
560 .lea_frame,582 .lea_frame,
...@@ -4371,6 +4393,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -4371,6 +4393,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
4371 .memory,4393 .memory,
4372 .load_direct,4394 .load_direct,
4373 .load_got,4395 .load_got,
4396 .load_extern_got,
4374 .load_tlv,4397 .load_tlv,
4375 => try self.genSetReg(addr_reg, Type.usize, array.address()),4398 => try self.genSetReg(addr_reg, Type.usize, array.address()),
4376 .lea_direct, .lea_tlv => unreachable,4399 .lea_direct, .lea_tlv => unreachable,
...@@ -5851,6 +5874,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro...@@ -5851,6 +5874,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro
5851 .register_offset,5874 .register_offset,
5852 .lea_direct,5875 .lea_direct,
5853 .lea_got,5876 .lea_got,
5877 .lea_extern_got,
5854 .lea_tlv,5878 .lea_tlv,
5855 .lea_frame,5879 .lea_frame,
5856 => try self.genCopy(dst_ty, dst_mcv, ptr_mcv.deref()),5880 => try self.genCopy(dst_ty, dst_mcv, ptr_mcv.deref()),
...@@ -5858,6 +5882,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro...@@ -5858,6 +5882,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro
5858 .indirect,5882 .indirect,
5859 .load_direct,5883 .load_direct,
5860 .load_got,5884 .load_got,
5885 .load_extern_got,
5861 .load_tlv,5886 .load_tlv,
5862 .load_frame,5887 .load_frame,
5863 => {5888 => {
...@@ -5996,6 +6021,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr...@@ -5996,6 +6021,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr
5996 .register_offset,6021 .register_offset,
5997 .lea_direct,6022 .lea_direct,
5998 .lea_got,6023 .lea_got,
6024 .lea_extern_got,
5999 .lea_tlv,6025 .lea_tlv,
6000 .lea_frame,6026 .lea_frame,
6001 => try self.genCopy(src_ty, ptr_mcv.deref(), src_mcv),6027 => try self.genCopy(src_ty, ptr_mcv.deref(), src_mcv),
...@@ -6003,6 +6029,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr...@@ -6003,6 +6029,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr
6003 .indirect,6029 .indirect,
6004 .load_direct,6030 .load_direct,
6005 .load_got,6031 .load_got,
6032 .load_extern_got,
6006 .load_tlv,6033 .load_tlv,
6007 .load_frame,6034 .load_frame,
6008 => {6035 => {
...@@ -6424,6 +6451,7 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MC...@@ -6424,6 +6451,7 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MC
6424 .register_overflow,6451 .register_overflow,
6425 .lea_direct,6452 .lea_direct,
6426 .lea_got,6453 .lea_got,
6454 .lea_extern_got,
6427 .lea_tlv,6455 .lea_tlv,
6428 .lea_frame,6456 .lea_frame,
6429 .reserved_frame,6457 .reserved_frame,
...@@ -6431,7 +6459,7 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MC...@@ -6431,7 +6459,7 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MC
6431 => unreachable, // unmodifiable destination6459 => unreachable, // unmodifiable destination
6432 .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)),6460 .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)),
6433 .register_pair => unreachable, // unimplemented6461 .register_pair => unreachable, // unimplemented
6434 .memory, .load_got, .load_direct, .load_tlv => {6462 .memory, .load_got, .load_extern_got, .load_direct, .load_tlv => {
6435 const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);6463 const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
6436 const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);6464 const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
6437 defer self.register_manager.unlockReg(addr_reg_lock);6465 defer self.register_manager.unlockReg(addr_reg_lock);
...@@ -7389,6 +7417,8 @@ fn genBinOp(...@@ -7389,6 +7417,8 @@ fn genBinOp(
7389 .lea_direct,7417 .lea_direct,
7390 .load_got,7418 .load_got,
7391 .lea_got,7419 .lea_got,
7420 .load_extern_got,
7421 .lea_extern_got,
7392 .load_tlv,7422 .load_tlv,
7393 .lea_tlv,7423 .lea_tlv,
7394 .lea_frame,7424 .lea_frame,
...@@ -7445,6 +7475,8 @@ fn genBinOp(...@@ -7445,6 +7475,8 @@ fn genBinOp(
7445 .lea_direct,7475 .lea_direct,
7446 .load_got,7476 .load_got,
7447 .lea_got,7477 .lea_got,
7478 .load_extern_got,
7479 .lea_extern_got,
7448 .load_tlv,7480 .load_tlv,
7449 .lea_tlv,7481 .lea_tlv,
7450 .lea_frame,7482 .lea_frame,
...@@ -8397,6 +8429,7 @@ fn genBinOpMir(...@@ -8397,6 +8429,7 @@ fn genBinOpMir(
8397 .register_overflow,8429 .register_overflow,
8398 .lea_direct,8430 .lea_direct,
8399 .lea_got,8431 .lea_got,
8432 .lea_extern_got,
8400 .lea_tlv,8433 .lea_tlv,
8401 .lea_frame,8434 .lea_frame,
8402 .reserved_frame,8435 .reserved_frame,
...@@ -8485,6 +8518,8 @@ fn genBinOpMir(...@@ -8485,6 +8518,8 @@ fn genBinOpMir(
8485 .lea_direct,8518 .lea_direct,
8486 .load_got,8519 .load_got,
8487 .lea_got,8520 .lea_got,
8521 .load_extern_got,
8522 .lea_extern_got,
8488 .load_tlv,8523 .load_tlv,
8489 .lea_tlv,8524 .lea_tlv,
8490 .load_frame,8525 .load_frame,
...@@ -8517,6 +8552,7 @@ fn genBinOpMir(...@@ -8517,6 +8552,7 @@ fn genBinOpMir(
8517 .register_offset,8552 .register_offset,
8518 .lea_direct,8553 .lea_direct,
8519 .lea_got,8554 .lea_got,
8555 .lea_extern_got,
8520 .lea_tlv,8556 .lea_tlv,
8521 .lea_frame,8557 .lea_frame,
8522 => {8558 => {
...@@ -8532,6 +8568,7 @@ fn genBinOpMir(...@@ -8532,6 +8568,7 @@ fn genBinOpMir(
8532 .memory,8568 .memory,
8533 .load_direct,8569 .load_direct,
8534 .load_got,8570 .load_got,
8571 .load_extern_got,
8535 .load_tlv,8572 .load_tlv,
8536 => {8573 => {
8537 const ptr_ty = try mod.singleConstPtrType(ty);8574 const ptr_ty = try mod.singleConstPtrType(ty);
...@@ -8552,13 +8589,13 @@ fn genBinOpMir(...@@ -8552,13 +8589,13 @@ fn genBinOpMir(
8552 }8589 }
8553 }8590 }
8554 },8591 },
8555 .memory, .indirect, .load_got, .load_direct, .load_tlv, .load_frame => {8592 .memory, .indirect, .load_got, .load_extern_got, .load_direct, .load_tlv, .load_frame => {
8556 const OpInfo = ?struct { addr_reg: Register, addr_lock: RegisterLock };8593 const OpInfo = ?struct { addr_reg: Register, addr_lock: RegisterLock };
8557 const limb_abi_size: u32 = @min(abi_size, 8);8594 const limb_abi_size: u32 = @min(abi_size, 8);
85588595
8559 const dst_info: OpInfo = switch (dst_mcv) {8596 const dst_info: OpInfo = switch (dst_mcv) {
8560 else => unreachable,8597 else => unreachable,
8561 .memory, .load_got, .load_direct, .load_tlv => dst: {8598 .memory, .load_got, .load_extern_got, .load_direct, .load_tlv => dst: {
8562 const dst_addr_reg =8599 const dst_addr_reg =
8563 (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64();8600 (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64();
8564 const dst_addr_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg);8601 const dst_addr_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg);
...@@ -8592,16 +8629,17 @@ fn genBinOpMir(...@@ -8592,16 +8629,17 @@ fn genBinOpMir(
8592 .indirect,8629 .indirect,
8593 .lea_direct,8630 .lea_direct,
8594 .lea_got,8631 .lea_got,
8632 .lea_extern_got,
8595 .lea_tlv,8633 .lea_tlv,
8596 .load_frame,8634 .load_frame,
8597 .lea_frame,8635 .lea_frame,
8598 => null,8636 => null,
8599 .memory, .load_got, .load_direct, .load_tlv => src: {8637 .memory, .load_got, .load_extern_got, .load_direct, .load_tlv => src: {
8600 switch (resolved_src_mcv) {8638 switch (resolved_src_mcv) {
8601 .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr))) != null and8639 .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr))) != null and
8602 math.cast(i32, @as(i64, @bitCast(addr)) + abi_size - limb_abi_size) != null)8640 math.cast(i32, @as(i64, @bitCast(addr)) + abi_size - limb_abi_size) != null)
8603 break :src null,8641 break :src null,
8604 .load_got, .load_direct, .load_tlv => {},8642 .load_got, .load_extern_got, .load_direct, .load_tlv => {},
8605 else => unreachable,8643 else => unreachable,
8606 }8644 }
86078645
...@@ -8644,6 +8682,7 @@ fn genBinOpMir(...@@ -8644,6 +8682,7 @@ fn genBinOpMir(
8644 switch (dst_mcv) {8682 switch (dst_mcv) {
8645 .memory,8683 .memory,
8646 .load_got,8684 .load_got,
8685 .load_extern_got,
8647 .load_direct,8686 .load_direct,
8648 .load_tlv,8687 .load_tlv,
8649 => .{ .base = .{ .reg = dst_info.?.addr_reg }, .disp = off },8688 => .{ .base = .{ .reg = dst_info.?.addr_reg }, .disp = off },
...@@ -8728,6 +8767,8 @@ fn genBinOpMir(...@@ -8728,6 +8767,8 @@ fn genBinOpMir(
8728 .lea_direct,8767 .lea_direct,
8729 .load_got,8768 .load_got,
8730 .lea_got,8769 .lea_got,
8770 .load_extern_got,
8771 .lea_extern_got,
8731 .load_tlv,8772 .load_tlv,
8732 .lea_tlv,8773 .lea_tlv,
8733 .load_frame,8774 .load_frame,
...@@ -8743,6 +8784,7 @@ fn genBinOpMir(...@@ -8743,6 +8784,7 @@ fn genBinOpMir(
8743 .register_offset,8784 .register_offset,
8744 .lea_direct,8785 .lea_direct,
8745 .lea_got,8786 .lea_got,
8787 .lea_extern_got,
8746 .lea_tlv,8788 .lea_tlv,
8747 .lea_frame,8789 .lea_frame,
8748 => switch (limb_i) {8790 => switch (limb_i) {
...@@ -8792,6 +8834,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M...@@ -8792,6 +8834,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
8792 .register_overflow,8834 .register_overflow,
8793 .lea_direct,8835 .lea_direct,
8794 .lea_got,8836 .lea_got,
8837 .lea_extern_got,
8795 .lea_tlv,8838 .lea_tlv,
8796 .lea_frame,8839 .lea_frame,
8797 .reserved_frame,8840 .reserved_frame,
...@@ -8840,6 +8883,8 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M...@@ -8840,6 +8883,8 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
8840 .lea_direct,8883 .lea_direct,
8841 .load_got,8884 .load_got,
8842 .lea_got,8885 .lea_got,
8886 .load_extern_got,
8887 .lea_extern_got,
8843 .load_tlv,8888 .load_tlv,
8844 .lea_tlv,8889 .lea_tlv,
8845 .lea_frame,8890 .lea_frame,
...@@ -8878,7 +8923,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M...@@ -8878,7 +8923,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
8878 }8923 }
8879 },8924 },
8880 .register_pair => unreachable, // unimplemented8925 .register_pair => unreachable, // unimplemented
8881 .memory, .indirect, .load_direct, .load_got, .load_tlv, .load_frame => {8926 .memory, .indirect, .load_direct, .load_got, .load_extern_got, .load_tlv, .load_frame => {
8882 const tmp_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);8927 const tmp_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);
8883 const tmp_mcv = MCValue{ .register = tmp_reg };8928 const tmp_mcv = MCValue{ .register = tmp_reg };
8884 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);8929 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
...@@ -8971,6 +9016,7 @@ fn genVarDbgInfo(...@@ -8971,6 +9016,7 @@ fn genVarDbgInfo(
8971 //} },9016 //} },
8972 .memory => |address| .{ .memory = address },9017 .memory => |address| .{ .memory = address },
8973 .load_got => |sym_index| .{ .linker_load = .{ .type = .got, .sym_index = sym_index } },9018 .load_got => |sym_index| .{ .linker_load = .{ .type = .got, .sym_index = sym_index } },
9019 .load_extern_got => |sym_index| .{ .linker_load = .{ .type = .extern_got, .sym_index = sym_index } },
8974 .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } },9020 .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } },
8975 .immediate => |x| .{ .immediate = x },9021 .immediate => |x| .{ .immediate = x },
8976 .undef => .undef,9022 .undef => .undef,
...@@ -9189,16 +9235,20 @@ fn genCall(self: *Self, info: union(enum) {...@@ -9189,16 +9235,20 @@ fn genCall(self: *Self, info: union(enum) {
9189 if (self.bin_file.cast(link.File.Elf)) |elf_file| {9235 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
9190 const sym_index = try elf_file.getOrCreateMetadataForDecl(owner_decl);9236 const sym_index = try elf_file.getOrCreateMetadataForDecl(owner_decl);
9191 const sym = elf_file.symbol(sym_index);9237 const sym = elf_file.symbol(sym_index);
9192 sym.flags.needs_got = true;9238 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
9193 _ = try sym.getOrCreateGotEntry(sym_index, elf_file);9239 if (self.bin_file.options.pic) {
9194 _ = try self.addInst(.{9240 try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym.esym_index });
9195 .tag = .call,9241 try self.asmRegister(.{ ._, .call }, .rax);
9196 .ops = .direct_got_reloc,9242 } else {
9197 .data = .{ .reloc = .{9243 _ = try self.addInst(.{
9198 .atom_index = try self.owner.getSymbolIndex(self),9244 .tag = .call,
9199 .sym_index = sym.esym_index,9245 .ops = .direct_got_reloc,
9200 } },9246 .data = .{ .reloc = .{
9201 });9247 .atom_index = try self.owner.getSymbolIndex(self),
9248 .sym_index = sym.esym_index,
9249 } },
9250 });
9251 }
9202 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {9252 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
9203 const atom = try coff_file.getOrCreateAtomForDecl(owner_decl);9253 const atom = try coff_file.getOrCreateAtomForDecl(owner_decl);
9204 const sym_index = coff_file.getAtom(atom).getSymbolIndex().?;9254 const sym_index = coff_file.getAtom(atom).getSymbolIndex().?;
...@@ -9406,13 +9456,14 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {...@@ -9406,13 +9456,14 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
9406 .indirect,9456 .indirect,
9407 .lea_direct,9457 .lea_direct,
9408 .lea_got,9458 .lea_got,
9459 .lea_extern_got,
9409 .lea_tlv,9460 .lea_tlv,
9410 .lea_frame,9461 .lea_frame,
9411 .reserved_frame,9462 .reserved_frame,
9412 .air_ref,9463 .air_ref,
9413 => unreachable,9464 => unreachable,
9414 .register_pair, .load_frame => null,9465 .register_pair, .load_frame => null,
9415 .memory, .load_got, .load_direct, .load_tlv => dst: {9466 .memory, .load_got, .load_extern_got, .load_direct, .load_tlv => dst: {
9416 switch (resolved_dst_mcv) {9467 switch (resolved_dst_mcv) {
9417 .memory => |addr| if (math.cast(9468 .memory => |addr| if (math.cast(
9418 i32,9469 i32,
...@@ -9421,7 +9472,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {...@@ -9421,7 +9472,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
9421 i32,9472 i32,
9422 @as(i64, @bitCast(addr)) + abi_size - 8,9473 @as(i64, @bitCast(addr)) + abi_size - 8,
9423 ) != null) break :dst null,9474 ) != null) break :dst null,
9424 .load_got, .load_direct, .load_tlv => {},9475 .load_got, .load_extern_got, .load_direct, .load_tlv => {},
9425 else => unreachable,9476 else => unreachable,
9426 }9477 }
94279478
...@@ -9464,13 +9515,14 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {...@@ -9464,13 +9515,14 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
9464 .indirect,9515 .indirect,
9465 .lea_direct,9516 .lea_direct,
9466 .lea_got,9517 .lea_got,
9518 .lea_extern_got,
9467 .lea_tlv,9519 .lea_tlv,
9468 .lea_frame,9520 .lea_frame,
9469 .reserved_frame,9521 .reserved_frame,
9470 .air_ref,9522 .air_ref,
9471 => unreachable,9523 => unreachable,
9472 .register_pair, .load_frame => null,9524 .register_pair, .load_frame => null,
9473 .memory, .load_got, .load_direct, .load_tlv => src: {9525 .memory, .load_got, .load_extern_got, .load_direct, .load_tlv => src: {
9474 switch (resolved_src_mcv) {9526 switch (resolved_src_mcv) {
9475 .memory => |addr| if (math.cast(9527 .memory => |addr| if (math.cast(
9476 i32,9528 i32,
...@@ -9479,7 +9531,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {...@@ -9479,7 +9531,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
9479 i32,9531 i32,
9480 @as(i64, @bitCast(addr)) + abi_size - 8,9532 @as(i64, @bitCast(addr)) + abi_size - 8,
9481 ) != null) break :src null,9533 ) != null) break :src null,
9482 .load_got, .load_direct, .load_tlv => {},9534 .load_got, .load_extern_got, .load_direct, .load_tlv => {},
9483 else => unreachable,9535 else => unreachable,
9484 }9536 }
94859537
...@@ -9898,6 +9950,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC...@@ -9898,6 +9950,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC
9898 .register_overflow,9950 .register_overflow,
9899 .lea_direct,9951 .lea_direct,
9900 .lea_got,9952 .lea_got,
9953 .lea_extern_got,
9901 .lea_tlv,9954 .lea_tlv,
9902 .lea_frame,9955 .lea_frame,
9903 .reserved_frame,9956 .reserved_frame,
...@@ -9924,6 +9977,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC...@@ -9924,6 +9977,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC
99249977
9925 .memory,9978 .memory,
9926 .load_got,9979 .load_got,
9980 .load_extern_got,
9927 .load_direct,9981 .load_direct,
9928 .load_tlv,9982 .load_tlv,
9929 => {9983 => {
...@@ -10481,7 +10535,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -10481,7 +10535,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
10481 .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |_|10535 .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |_|
10482 break :arg input_mcv,10536 break :arg input_mcv,
10483 .indirect, .load_frame => break :arg input_mcv,10537 .indirect, .load_frame => break :arg input_mcv,
10484 .load_direct, .load_got, .load_tlv => {},10538 .load_direct, .load_got, .load_extern_got, .load_tlv => {},
10485 else => {10539 else => {
10486 const temp_mcv = try self.allocTempRegOrMem(ty, false);10540 const temp_mcv = try self.allocTempRegOrMem(ty, false);
10487 try self.genCopy(ty, temp_mcv, input_mcv);10541 try self.genCopy(ty, temp_mcv, input_mcv);
...@@ -11142,6 +11196,7 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError...@@ -11142,6 +11196,7 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError
11142 .register_overflow,11196 .register_overflow,
11143 .lea_direct,11197 .lea_direct,
11144 .lea_got,11198 .lea_got,
11199 .lea_extern_got,
11145 .lea_tlv,11200 .lea_tlv,
11146 .lea_frame,11201 .lea_frame,
11147 .reserved_frame,11202 .reserved_frame,
...@@ -11193,11 +11248,11 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError...@@ -11193,11 +11248,11 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError
11193 }11248 }
11194 },11249 },
11195 .indirect => |reg_off| try self.genSetMem(.{ .reg = reg_off.reg }, reg_off.off, ty, src_mcv),11250 .indirect => |reg_off| try self.genSetMem(.{ .reg = reg_off.reg }, reg_off.off, ty, src_mcv),
11196 .memory, .load_direct, .load_got, .load_tlv => {11251 .memory, .load_direct, .load_got, .load_extern_got, .load_tlv => {
11197 switch (dst_mcv) {11252 switch (dst_mcv) {
11198 .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr|11253 .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr|
11199 return self.genSetMem(.{ .reg = .ds }, small_addr, ty, src_mcv),11254 return self.genSetMem(.{ .reg = .ds }, small_addr, ty, src_mcv),
11200 .load_direct, .load_got, .load_tlv => {},11255 .load_direct, .load_got, .load_extern_got, .load_tlv => {},
11201 else => unreachable,11256 else => unreachable,
11202 }11257 }
1120311258
...@@ -11359,7 +11414,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr...@@ -11359,7 +11414,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
11359 else => unreachable,11414 else => unreachable,
11360 },11415 },
11361 )),11416 )),
11362 .memory, .load_direct, .load_got, .load_tlv => {11417 .memory, .load_direct, .load_got, .load_extern_got, .load_tlv => {
11363 switch (src_mcv) {11418 switch (src_mcv) {
11364 .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr|11419 .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr|
11365 return (try self.moveStrategy(11420 return (try self.moveStrategy(
...@@ -11387,7 +11442,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr...@@ -11387,7 +11442,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
11387 },11442 },
11388 .Float, .Vector => {},11443 .Float, .Vector => {},
11389 },11444 },
11390 .load_got, .load_tlv => {},11445 .load_got, .load_extern_got, .load_tlv => {},
11391 else => unreachable,11446 else => unreachable,
11392 }11447 }
1139311448
...@@ -11401,17 +11456,18 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr...@@ -11401,17 +11456,18 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
11401 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .{ .reg = addr_reg } }),11456 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .{ .reg = addr_reg } }),
11402 );11457 );
11403 },11458 },
11404 .lea_direct, .lea_got => |sym_index| {11459 .lea_direct, .lea_got, .lea_extern_got => |sym_index| {
11405 const atom_index = try self.owner.getSymbolIndex(self);11460 const atom_index = try self.owner.getSymbolIndex(self);
11406 _ = try self.addInst(.{11461 _ = try self.addInst(.{
11407 .tag = switch (src_mcv) {11462 .tag = switch (src_mcv) {
11408 .lea_direct => .lea,11463 .lea_direct => .lea,
11409 .lea_got => .mov,11464 .lea_got, .lea_extern_got => .mov,
11410 else => unreachable,11465 else => unreachable,
11411 },11466 },
11412 .ops = switch (src_mcv) {11467 .ops = switch (src_mcv) {
11413 .lea_direct => .direct_reloc,11468 .lea_direct => .direct_reloc,
11414 .lea_got => .got_reloc,11469 .lea_got => .got_reloc,
11470 .lea_extern_got => .extern_got_reloc,
11415 else => unreachable,11471 else => unreachable,
11416 },11472 },
11417 .data = .{ .rx = .{11473 .data = .{ .rx = .{
...@@ -11547,6 +11603,8 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal...@@ -11547,6 +11603,8 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal
11547 .lea_direct,11603 .lea_direct,
11548 .load_got,11604 .load_got,
11549 .lea_got,11605 .lea_got,
11606 .load_extern_got,
11607 .lea_extern_got,
11550 .load_tlv,11608 .load_tlv,
11551 .lea_tlv,11609 .lea_tlv,
11552 .load_frame,11610 .load_frame,
...@@ -11637,36 +11695,49 @@ fn genLazySymbolRef(...@@ -11637,36 +11695,49 @@ fn genLazySymbolRef(
11637 const sym_index = elf_file.getOrCreateMetadataForLazySymbol(lazy_sym) catch |err|11695 const sym_index = elf_file.getOrCreateMetadataForLazySymbol(lazy_sym) catch |err|
11638 return self.fail("{s} creating lazy symbol", .{@errorName(err)});11696 return self.fail("{s} creating lazy symbol", .{@errorName(err)});
11639 const sym = elf_file.symbol(sym_index);11697 const sym = elf_file.symbol(sym_index);
11640 sym.flags.needs_got = true;11698 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
11641 _ = try sym.getOrCreateGotEntry(sym_index, elf_file);11699
11642 const reloc = Mir.Reloc{11700 if (self.bin_file.options.pic) {
11643 .atom_index = try self.owner.getSymbolIndex(self),11701 switch (tag) {
11644 .sym_index = sym.esym_index,11702 .lea, .call => try self.genSetReg(reg, Type.usize, .{ .lea_got = sym.esym_index }),
11645 };11703 .mov => try self.genSetReg(reg, Type.usize, .{ .load_got = sym.esym_index }),
11646 switch (tag) {11704 else => unreachable,
11647 .lea, .mov => _ = try self.addInst(.{11705 }
11648 .tag = .mov,11706 switch (tag) {
11649 .ops = .direct_got_reloc,11707 .lea, .mov => {},
11650 .data = .{ .rx = .{11708 .call => try self.asmRegister(.{ ._, .call }, reg),
11651 .r1 = reg.to64(),11709 else => unreachable,
11652 .payload = try self.addExtra(reloc),11710 }
11653 } },11711 } else {
11654 }),11712 const reloc = Mir.Reloc{
11655 .call => _ = try self.addInst(.{11713 .atom_index = try self.owner.getSymbolIndex(self),
11656 .tag = .call,11714 .sym_index = sym.esym_index,
11657 .ops = .direct_got_reloc,11715 };
11658 .data = .{ .reloc = reloc },11716 switch (tag) {
11659 }),11717 .lea, .mov => _ = try self.addInst(.{
11660 else => unreachable,11718 .tag = .mov,
11661 }11719 .ops = .direct_got_reloc,
11662 switch (tag) {11720 .data = .{ .rx = .{
11663 .lea, .call => {},11721 .r1 = reg.to64(),
11664 .mov => try self.asmRegisterMemory(11722 .payload = try self.addExtra(reloc),
11665 .{ ._, tag },11723 } },
11666 reg.to64(),11724 }),
11667 Memory.sib(.qword, .{ .base = .{ .reg = reg.to64() } }),11725 .call => _ = try self.addInst(.{
11668 ),11726 .tag = .call,
11669 else => unreachable,11727 .ops = .direct_got_reloc,
11728 .data = .{ .reloc = reloc },
11729 }),
11730 else => unreachable,
11731 }
11732 switch (tag) {
11733 .lea, .call => {},
11734 .mov => try self.asmRegisterMemory(
11735 .{ ._, tag },
11736 reg.to64(),
11737 Memory.sib(.qword, .{ .base = .{ .reg = reg.to64() } }),
11738 ),
11739 else => unreachable,
11740 }
11670 }11741 }
11671 } else if (self.bin_file.cast(link.File.Plan9)) |p9_file| {11742 } else if (self.bin_file.cast(link.File.Plan9)) |p9_file| {
11672 const atom_index = p9_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err|11743 const atom_index = p9_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err|
...@@ -13539,6 +13610,7 @@ fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {...@@ -13539,6 +13610,7 @@ fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
13539 .memory => |addr| .{ .memory = addr },13610 .memory => |addr| .{ .memory = addr },
13540 .load_direct => |sym_index| .{ .load_direct = sym_index },13611 .load_direct => |sym_index| .{ .load_direct = sym_index },
13541 .load_got => |sym_index| .{ .lea_got = sym_index },13612 .load_got => |sym_index| .{ .lea_got = sym_index },
13613 .load_extern_got => |sym_index| .{ .lea_extern_got = sym_index },
13542 .load_tlv => |sym_index| .{ .lea_tlv = sym_index },13614 .load_tlv => |sym_index| .{ .lea_tlv = sym_index },
13543 },13615 },
13544 .fail => |msg| {13616 .fail => |msg| {
src/arch/x86_64/Emit.zig+11-2
...@@ -79,20 +79,29 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -79,20 +79,29 @@ pub fn emitMir(emit: *Emit) Error!void {
79 @tagName(emit.bin_file.tag),79 @tagName(emit.bin_file.tag),
80 }),80 }),
81 .linker_got,81 .linker_got,
82 .linker_extern_got,
82 .linker_direct,83 .linker_direct,
83 .linker_direct_got,84 .linker_direct_got,
84 .linker_import,85 .linker_import,
85 .linker_tlv,86 .linker_tlv,
86 => |symbol| if (emit.bin_file.cast(link.File.Elf)) |elf_file| {87 => |symbol| if (emit.bin_file.cast(link.File.Elf)) |elf_file| {
87 const r_type: u32 = switch (lowered_relocs[0].target) {88 const r_type: u32 = switch (lowered_relocs[0].target) {
88 .linker_direct_got => std.elf.R_X86_64_GOT32,89 .linker_direct_got => link.File.Elf.R_X86_64_ZIG_GOT32,
90 .linker_got => link.File.Elf.R_X86_64_ZIG_GOTPCREL,
91 .linker_extern_got => std.elf.R_X86_64_GOTPCREL,
92 .linker_direct => std.elf.R_X86_64_PC32,
93 else => unreachable,
94 };
95 const r_addend: i64 = switch (lowered_relocs[0].target) {
96 .linker_direct_got => 0,
97 .linker_got, .linker_extern_got, .linker_direct => -4,
89 else => unreachable,98 else => unreachable,
90 };99 };
91 const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;100 const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
92 try atom_ptr.addReloc(elf_file, .{101 try atom_ptr.addReloc(elf_file, .{
93 .r_offset = end_offset - 4,102 .r_offset = end_offset - 4,
94 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type,103 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type,
95 .r_addend = 0,104 .r_addend = r_addend,
96 });105 });
97 } else if (emit.bin_file.cast(link.File.MachO)) |macho_file| {106 } else if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
98 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;107 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
src/arch/x86_64/Lower.zig+4-2
...@@ -51,6 +51,7 @@ pub const Reloc = struct {...@@ -51,6 +51,7 @@ pub const Reloc = struct {
51 inst: Mir.Inst.Index,51 inst: Mir.Inst.Index,
52 linker_extern_fn: Mir.Reloc,52 linker_extern_fn: Mir.Reloc,
53 linker_got: Mir.Reloc,53 linker_got: Mir.Reloc,
54 linker_extern_got: Mir.Reloc,
54 linker_direct: Mir.Reloc,55 linker_direct: Mir.Reloc,
55 linker_direct_got: Mir.Reloc,56 linker_direct_got: Mir.Reloc,
56 linker_import: Mir.Reloc,57 linker_import: Mir.Reloc,
...@@ -388,7 +389,7 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {...@@ -388,7 +389,7 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
388 .rrmi_sib, .rrmi_rip => inst.data.rrix.fixes,389 .rrmi_sib, .rrmi_rip => inst.data.rrix.fixes,
389 .mi_sib_u, .mi_rip_u, .mi_sib_s, .mi_rip_s => inst.data.x.fixes,390 .mi_sib_u, .mi_rip_u, .mi_sib_s, .mi_rip_s => inst.data.x.fixes,
390 .m_sib, .m_rip, .rax_moffs, .moffs_rax => inst.data.x.fixes,391 .m_sib, .m_rip, .rax_moffs, .moffs_rax => inst.data.x.fixes,
391 .extern_fn_reloc, .got_reloc, .direct_reloc, .direct_got_reloc, .import_reloc, .tlv_reloc => ._,392 .extern_fn_reloc, .got_reloc, .extern_got_reloc, .direct_reloc, .direct_got_reloc, .import_reloc, .tlv_reloc => ._,
392 else => return lower.fail("TODO lower .{s}", .{@tagName(inst.ops)}),393 else => return lower.fail("TODO lower .{s}", .{@tagName(inst.ops)}),
393 };394 };
394 try lower.emit(switch (fixes) {395 try lower.emit(switch (fixes) {
...@@ -532,11 +533,12 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {...@@ -532,11 +533,12 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
532 else => unreachable,533 else => unreachable,
533 }534 }
534 },535 },
535 .got_reloc, .direct_reloc, .import_reloc, .tlv_reloc => ops: {536 .got_reloc, .extern_got_reloc, .direct_reloc, .import_reloc, .tlv_reloc => ops: {
536 const reg = inst.data.rx.r1;537 const reg = inst.data.rx.r1;
537 const extra = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data;538 const extra = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data;
538 _ = lower.reloc(switch (inst.ops) {539 _ = lower.reloc(switch (inst.ops) {
539 .got_reloc => .{ .linker_got = extra },540 .got_reloc => .{ .linker_got = extra },
541 .extern_got_reloc => .{ .linker_extern_got = extra },
540 .direct_reloc => .{ .linker_direct = extra },542 .direct_reloc => .{ .linker_direct = extra },
541 .import_reloc => .{ .linker_import = extra },543 .import_reloc => .{ .linker_import = extra },
542 .tlv_reloc => .{ .linker_tlv = extra },544 .tlv_reloc => .{ .linker_tlv = extra },
src/arch/x86_64/Mir.zig+3
...@@ -783,6 +783,9 @@ pub const Inst = struct {...@@ -783,6 +783,9 @@ pub const Inst = struct {
783 /// Linker relocation - GOT indirection.783 /// Linker relocation - GOT indirection.
784 /// Uses `rx` payload with extra data of type `Reloc`.784 /// Uses `rx` payload with extra data of type `Reloc`.
785 got_reloc,785 got_reloc,
786 /// Linker relocation - reference to an extern variable via GOT.
787 /// Uses `rx` payload with extra data of type `Reloc`.
788 extern_got_reloc,
786 /// Linker relocation - direct reference.789 /// Linker relocation - direct reference.
787 /// Uses `rx` payload with extra data of type `Reloc`.790 /// Uses `rx` payload with extra data of type `Reloc`.
788 direct_reloc,791 direct_reloc,
src/codegen.zig+26-4
...@@ -793,11 +793,13 @@ fn lowerDeclRef(...@@ -793,11 +793,13 @@ fn lowerDeclRef(
793793
794/// Helper struct to denote that the value is in memory but requires a linker relocation fixup:794/// Helper struct to denote that the value is in memory but requires a linker relocation fixup:
795/// * got - the value is referenced indirectly via GOT entry index (the linker emits a got-type reloc)795/// * got - the value is referenced indirectly via GOT entry index (the linker emits a got-type reloc)
796/// * extern_got - pointer to extern variable referenced via GOT
796/// * direct - the value is referenced directly via symbol index index (the linker emits a displacement reloc)797/// * direct - the value is referenced directly via symbol index index (the linker emits a displacement reloc)
797/// * import - the value is referenced indirectly via import entry index (the linker emits an import-type reloc)798/// * import - the value is referenced indirectly via import entry index (the linker emits an import-type reloc)
798pub const LinkerLoad = struct {799pub const LinkerLoad = struct {
799 type: enum {800 type: enum {
800 got,801 got,
802 extern_got,
801 direct,803 direct,
802 import,804 import,
803 },805 },
...@@ -827,6 +829,8 @@ pub const GenResult = union(enum) {...@@ -827,6 +829,8 @@ pub const GenResult = union(enum) {
827 load_got: u32,829 load_got: u32,
828 /// Direct by-address reference to memory location.830 /// Direct by-address reference to memory location.
829 memory: u64,831 memory: u64,
832 /// Pointer to extern variable via GOT.
833 load_extern_got: u32,
830 };834 };
831835
832 fn mcv(val: MCValue) GenResult {836 fn mcv(val: MCValue) GenResult {
...@@ -885,13 +889,26 @@ fn genDeclRef(...@@ -885,13 +889,26 @@ fn genDeclRef(
885 try mod.markDeclAlive(decl);889 try mod.markDeclAlive(decl);
886890
887 const is_threadlocal = tv.val.isPtrToThreadLocal(mod) and !bin_file.options.single_threaded;891 const is_threadlocal = tv.val.isPtrToThreadLocal(mod) and !bin_file.options.single_threaded;
892 const is_extern = decl.isExtern(mod);
888893
889 if (bin_file.cast(link.File.Elf)) |elf_file| {894 if (bin_file.cast(link.File.Elf)) |elf_file| {
895 if (is_extern) {
896 const name = mod.intern_pool.stringToSlice(decl.name);
897 // TODO audit this
898 const lib_name = if (decl.getOwnedVariable(mod)) |ov|
899 mod.intern_pool.stringToSliceUnwrap(ov.lib_name)
900 else
901 null;
902 return GenResult.mcv(.{ .load_extern_got = try elf_file.getGlobalSymbol(name, lib_name) });
903 }
890 const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index);904 const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index);
891 const sym = elf_file.symbol(sym_index);905 const sym = elf_file.symbol(sym_index);
892 sym.flags.needs_got = true;906 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
893 _ = try sym.getOrCreateGotEntry(sym_index, elf_file);907 if (bin_file.options.pic) {
894 return GenResult.mcv(.{ .memory = sym.gotAddress(elf_file) });908 return GenResult.mcv(.{ .load_got = sym.esym_index });
909 } else {
910 return GenResult.mcv(.{ .memory = sym.zigGotAddress(elf_file) });
911 }
895 } else if (bin_file.cast(link.File.MachO)) |macho_file| {912 } else if (bin_file.cast(link.File.MachO)) |macho_file| {
896 const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);913 const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
897 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;914 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
...@@ -926,7 +943,12 @@ fn genUnnamedConst(...@@ -926,7 +943,12 @@ fn genUnnamedConst(
926 return GenResult.fail(bin_file.allocator, src_loc, "lowering unnamed constant failed: {s}", .{@errorName(err)});943 return GenResult.fail(bin_file.allocator, src_loc, "lowering unnamed constant failed: {s}", .{@errorName(err)});
927 };944 };
928 if (bin_file.cast(link.File.Elf)) |elf_file| {945 if (bin_file.cast(link.File.Elf)) |elf_file| {
929 return GenResult.mcv(.{ .memory = elf_file.symbol(local_sym_index).value });946 const local = elf_file.symbol(local_sym_index);
947 if (bin_file.options.pic) {
948 return GenResult.mcv(.{ .load_direct = local.esym_index });
949 } else {
950 return GenResult.mcv(.{ .memory = local.value });
951 }
930 } else if (bin_file.cast(link.File.MachO)) |_| {952 } else if (bin_file.cast(link.File.MachO)) |_| {
931 return GenResult.mcv(.{ .load_direct = local_sym_index });953 return GenResult.mcv(.{ .load_direct = local_sym_index });
932 } else if (bin_file.cast(link.File.Coff)) |_| {954 } else if (bin_file.cast(link.File.Coff)) |_| {
src/link/Dwarf.zig+4-4
...@@ -1389,6 +1389,7 @@ pub fn commitDeclState(...@@ -1389,6 +1389,7 @@ pub fn commitDeclState(
1389 .prev_vaddr = 0,1389 .prev_vaddr = 0,
1390 });1390 });
1391 },1391 },
1392 .elf => {}, // TODO
1392 else => unreachable,1393 else => unreachable,
1393 }1394 }
1394 }1395 }
...@@ -1850,8 +1851,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u...@@ -1850,8 +1851,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u
1850 // not including the initial length itself.1851 // not including the initial length itself.
1851 // We have to come back and write it later after we know the size.1852 // We have to come back and write it later after we know the size.
1852 const after_init_len = di_buf.items.len + init_len_size;1853 const after_init_len = di_buf.items.len + init_len_size;
1853 // +1 for the final 0 that ends the compilation unit children.1854 const dbg_info_end = self.getDebugInfoEnd().?;
1854 const dbg_info_end = self.getDebugInfoEnd().? + 1;
1855 const init_len = dbg_info_end - after_init_len;1855 const init_len = dbg_info_end - after_init_len;
1856 if (self.bin_file.tag == .macho) {1856 if (self.bin_file.tag == .macho) {
1857 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @as(u32, @intCast(init_len)));1857 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @as(u32, @intCast(init_len)));
...@@ -2500,7 +2500,7 @@ fn getDebugInfoOff(self: Dwarf) ?u32 {...@@ -2500,7 +2500,7 @@ fn getDebugInfoOff(self: Dwarf) ?u32 {
2500fn getDebugInfoEnd(self: Dwarf) ?u32 {2500fn getDebugInfoEnd(self: Dwarf) ?u32 {
2501 const last_index = self.di_atom_last_index orelse return null;2501 const last_index = self.di_atom_last_index orelse return null;
2502 const last = self.getAtom(.di_atom, last_index);2502 const last = self.getAtom(.di_atom, last_index);
2503 return last.off + last.len;2503 return last.off + last.len + 1;
2504}2504}
25052505
2506fn getDebugLineProgramOff(self: Dwarf) ?u32 {2506fn getDebugLineProgramOff(self: Dwarf) ?u32 {
...@@ -2642,7 +2642,7 @@ fn addDIFile(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index) !u28 {...@@ -2642,7 +2642,7 @@ fn addDIFile(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index) !u28 {
2642 switch (self.bin_file.tag) {2642 switch (self.bin_file.tag) {
2643 .elf => {2643 .elf => {
2644 const elf_file = self.bin_file.cast(File.Elf).?;2644 const elf_file = self.bin_file.cast(File.Elf).?;
2645 elf_file.markDirty(elf_file.debug_line_section_index.?, null);2645 elf_file.markDirty(elf_file.debug_line_section_index.?);
2646 },2646 },
2647 .macho => {2647 .macho => {
2648 const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?;2648 const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?;
src/link/Elf.zig+2714-1002
...@@ -14,6 +14,7 @@ files: std.MultiArrayList(File.Entry) = .{},...@@ -14,6 +14,7 @@ files: std.MultiArrayList(File.Entry) = .{},
14zig_module_index: ?File.Index = null,14zig_module_index: ?File.Index = null,
15linker_defined_index: ?File.Index = null,15linker_defined_index: ?File.Index = null,
16objects: std.ArrayListUnmanaged(File.Index) = .{},16objects: std.ArrayListUnmanaged(File.Index) = .{},
17shared_objects: std.ArrayListUnmanaged(File.Index) = .{},
1718
18/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.19/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
19/// Same order as in the file.20/// Same order as in the file.
...@@ -22,35 +23,47 @@ shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{},...@@ -22,35 +23,47 @@ shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{},
22phdr_to_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{},23phdr_to_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{},
23/// File offset into the shdr table.24/// File offset into the shdr table.
24shdr_table_offset: ?u64 = null,25shdr_table_offset: ?u64 = null,
26/// Table of lists of atoms per output section.
27/// This table is not used to track incrementally generated atoms.
28output_sections: std.AutoArrayHashMapUnmanaged(u16, std.ArrayListUnmanaged(Atom.Index)) = .{},
2529
26/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.30/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
27/// Same order as in the file.31/// Same order as in the file.
28phdrs: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{},32phdrs: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{},
29/// The index into the program headers of the PT_PHDR program header33
30phdr_table_index: ?u16 = null,34/// Tracked loadable segments during incremental linking.
31/// The index into the program headers of the PT_LOAD program header containing the phdr
32/// Most linkers would merge this with phdr_load_ro_index,
33/// but incremental linking means we can't ensure they are consecutive.
34phdr_table_load_index: ?u16 = null,
35/// The index into the program headers of a PT_LOAD program header with Read and Execute flags35/// The index into the program headers of a PT_LOAD program header with Read and Execute flags
36phdr_load_re_index: ?u16 = null,36phdr_zig_load_re_index: ?u16 = null,
37/// The index into the program headers of the global offset table.37/// The index into the program headers of the global offset table.
38/// It needs PT_LOAD and Read flags.38/// It needs PT_LOAD and Read flags.
39phdr_got_index: ?u16 = null,39phdr_zig_got_index: ?u16 = null,
40/// The index into the program headers of a PT_LOAD program header with Read flag40/// The index into the program headers of a PT_LOAD program header with Read flag
41phdr_load_ro_index: ?u16 = null,41phdr_zig_load_ro_index: ?u16 = null,
42/// The index into the program headers of a PT_LOAD program header with Write flag42/// The index into the program headers of a PT_LOAD program header with Write flag
43phdr_load_rw_index: ?u16 = null,43phdr_zig_load_rw_index: ?u16 = null,
44/// The index into the program headers of a PT_LOAD program header with zerofill data.44/// The index into the program headers of a PT_LOAD program header with zerofill data.
45phdr_load_zerofill_index: ?u16 = null,45phdr_zig_load_zerofill_index: ?u16 = null,
46/// The index into the program headers of the PT_TLS program header.46
47/// Special program headers
48/// PT_PHDR
49phdr_table_index: ?u16 = null,
50/// PT_LOAD for PHDR table
51/// We add this special load segment to ensure the PHDR table is always
52/// loaded into memory.
53phdr_table_load_index: ?u16 = null,
54/// PT_INTERP
55phdr_interp_index: ?u16 = null,
56/// PT_DYNAMIC
57phdr_dynamic_index: ?u16 = null,
58/// PT_GNU_EH_FRAME
59phdr_gnu_eh_frame_index: ?u16 = null,
60/// PT_GNU_STACK
61phdr_gnu_stack_index: ?u16 = null,
62/// PT_TLS
63/// TODO I think ELF permits multiple TLS segments but for now, assume one per file.
47phdr_tls_index: ?u16 = null,64phdr_tls_index: ?u16 = null,
48/// The index into the program headers of a PT_LOAD program header with TLS data.
49phdr_load_tls_data_index: ?u16 = null,
50/// The index into the program headers of a PT_LOAD program header with TLS zerofill data.
51phdr_load_tls_zerofill_index: ?u16 = null,
5265
53entry_addr: ?u64 = null,66entry_index: ?Symbol.Index = null,
54page_size: u32,67page_size: u32,
55default_sym_version: elf.Elf64_Versym,68default_sym_version: elf.Elf64_Versym,
5669
...@@ -58,29 +71,76 @@ default_sym_version: elf.Elf64_Versym,...@@ -58,29 +71,76 @@ default_sym_version: elf.Elf64_Versym,
58shstrtab: StringTable(.strtab) = .{},71shstrtab: StringTable(.strtab) = .{},
59/// .strtab buffer72/// .strtab buffer
60strtab: StringTable(.strtab) = .{},73strtab: StringTable(.strtab) = .{},
6174/// Dynamic symbol table. Only populated and emitted when linking dynamically.
62/// Representation of the GOT table as committed to the file.75dynsym: DynsymSection = .{},
76/// .dynstrtab buffer
77dynstrtab: StringTable(.dynstrtab) = .{},
78/// Version symbol table. Only populated and emitted when linking dynamically.
79versym: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{},
80/// .verneed section
81verneed: VerneedSection = .{},
82/// .got section
63got: GotSection = .{},83got: GotSection = .{},
84/// .rela.dyn section
85rela_dyn: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{},
86/// .dynamic section
87dynamic: DynamicSection = .{},
88/// .hash section
89hash: HashSection = .{},
90/// .gnu.hash section
91gnu_hash: GnuHashSection = .{},
92/// .plt section
93plt: PltSection = .{},
94/// .got.plt section
95got_plt: GotPltSection = .{},
96/// .plt.got section
97plt_got: PltGotSection = .{},
98/// .copyrel section
99copy_rel: CopyRelSection = .{},
100/// .rela.plt section
101rela_plt: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{},
102/// .zig.got section
103zig_got: ZigGotSection = .{},
104
105/// Tracked section headers with incremental updates to Zig module
106zig_text_section_index: ?u16 = null,
107zig_rodata_section_index: ?u16 = null,
108zig_data_section_index: ?u16 = null,
109zig_bss_section_index: ?u16 = null,
110zig_got_section_index: ?u16 = null,
111
112debug_info_section_index: ?u16 = null,
113debug_abbrev_section_index: ?u16 = null,
114debug_str_section_index: ?u16 = null,
115debug_aranges_section_index: ?u16 = null,
116debug_line_section_index: ?u16 = null,
117
118/// Size contribution of Zig's metadata to each debug section.
119/// Used to track start of metadata from input object files.
120debug_info_section_zig_size: u64 = 0,
121debug_abbrev_section_zig_size: u64 = 0,
122debug_str_section_zig_size: u64 = 0,
123debug_aranges_section_zig_size: u64 = 0,
124debug_line_section_zig_size: u64 = 0,
64125
65/// Tracked section headers126copy_rel_section_index: ?u16 = null,
66text_section_index: ?u16 = null,127dynamic_section_index: ?u16 = null,
67rodata_section_index: ?u16 = null,128dynstrtab_section_index: ?u16 = null,
68data_section_index: ?u16 = null,129dynsymtab_section_index: ?u16 = null,
69bss_section_index: ?u16 = null,
70tdata_section_index: ?u16 = null,
71tbss_section_index: ?u16 = null,
72eh_frame_section_index: ?u16 = null,130eh_frame_section_index: ?u16 = null,
73eh_frame_hdr_section_index: ?u16 = null,131eh_frame_hdr_section_index: ?u16 = null,
74dynamic_section_index: ?u16 = null,132hash_section_index: ?u16 = null,
133gnu_hash_section_index: ?u16 = null,
75got_section_index: ?u16 = null,134got_section_index: ?u16 = null,
76got_plt_section_index: ?u16 = null,135got_plt_section_index: ?u16 = null,
136interp_section_index: ?u16 = null,
77plt_section_index: ?u16 = null,137plt_section_index: ?u16 = null,
138plt_got_section_index: ?u16 = null,
78rela_dyn_section_index: ?u16 = null,139rela_dyn_section_index: ?u16 = null,
79debug_info_section_index: ?u16 = null,140rela_plt_section_index: ?u16 = null,
80debug_abbrev_section_index: ?u16 = null,141versym_section_index: ?u16 = null,
81debug_str_section_index: ?u16 = null,142verneed_section_index: ?u16 = null,
82debug_aranges_section_index: ?u16 = null,143
83debug_line_section_index: ?u16 = null,
84shstrtab_section_index: ?u16 = null,144shstrtab_section_index: ?u16 = null,
85strtab_section_index: ?u16 = null,145strtab_section_index: ?u16 = null,
86symtab_section_index: ?u16 = null,146symtab_section_index: ?u16 = null,
...@@ -109,11 +169,8 @@ symbols_extra: std.ArrayListUnmanaged(u32) = .{},...@@ -109,11 +169,8 @@ symbols_extra: std.ArrayListUnmanaged(u32) = .{},
109resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},169resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},
110symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},170symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},
111171
112phdr_table_dirty: bool = false,172has_text_reloc: bool = false,
113shdr_table_dirty: bool = false,173num_ifunc_dynrelocs: usize = 0,
114shstrtab_dirty: bool = false,
115strtab_dirty: bool = false,
116got_dirty: bool = false,
117174
118debug_strtab_dirty: bool = false,175debug_strtab_dirty: bool = false,
119debug_abbrev_section_dirty: bool = false,176debug_abbrev_section_dirty: bool = false,
...@@ -161,6 +218,7 @@ comdat_groups: std.ArrayListUnmanaged(ComdatGroup) = .{},...@@ -161,6 +218,7 @@ comdat_groups: std.ArrayListUnmanaged(ComdatGroup) = .{},
161comdat_groups_owners: std.ArrayListUnmanaged(ComdatGroupOwner) = .{},218comdat_groups_owners: std.ArrayListUnmanaged(ComdatGroupOwner) = .{},
162comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{},219comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{},
163220
221const AtomList = std.ArrayListUnmanaged(Atom.Index);
164const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index));222const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index));
165const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, Symbol.Index);223const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, Symbol.Index);
166const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata);224const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata);
...@@ -183,6 +241,9 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -183,6 +241,9 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
183 const self = try createEmpty(allocator, options);241 const self = try createEmpty(allocator, options);
184 errdefer self.base.destroy();242 errdefer self.base.destroy();
185243
244 const is_obj = options.output_mode == .Obj;
245 const is_obj_or_ar = is_obj or (options.output_mode == .Lib and options.link_mode == .Static);
246
186 if (options.use_llvm) {247 if (options.use_llvm) {
187 const use_lld = build_options.have_llvm and self.base.options.use_lld;248 const use_lld = build_options.have_llvm and self.base.options.use_lld;
188 if (use_lld) return self;249 if (use_lld) return self;
...@@ -192,6 +253,10 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -192,6 +253,10 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
192 sub_path, options.target.ofmt.fileExt(options.target.cpu.arch),253 sub_path, options.target.ofmt.fileExt(options.target.cpu.arch),
193 });254 });
194 }255 }
256 if (is_obj) {
257 // TODO until we implement -r option, we don't want to open a file at this stage.
258 return self;
259 }
195 }260 }
196 errdefer if (self.base.intermediary_basename) |path| allocator.free(path);261 errdefer if (self.base.intermediary_basename) |path| allocator.free(path);
197262
...@@ -201,8 +266,6 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -201,8 +266,6 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
201 .mode = link.determineMode(options),266 .mode = link.determineMode(options),
202 });267 });
203268
204 self.shdr_table_dirty = true;
205
206 // Index 0 is always a null symbol.269 // Index 0 is always a null symbol.
207 try self.symbols.append(allocator, .{});270 try self.symbols.append(allocator, .{});
208 // Index 0 is always a null symbol.271 // Index 0 is always a null symbol.
...@@ -211,21 +274,71 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -211,21 +274,71 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
211 try self.atoms.append(allocator, .{});274 try self.atoms.append(allocator, .{});
212 // Append null file at index 0275 // Append null file at index 0
213 try self.files.append(allocator, .null);276 try self.files.append(allocator, .null);
277 // Append null byte to string tables
278 try self.shstrtab.buffer.append(allocator, 0);
279 try self.strtab.buffer.append(allocator, 0);
214 // There must always be a null shdr in index 0280 // There must always be a null shdr in index 0
215 try self.shdrs.append(allocator, .{281 _ = try self.addSection(.{ .name = "" });
216 .sh_name = 0,282
217 .sh_type = elf.SHT_NULL,283 if (!is_obj_or_ar) {
218 .sh_flags = 0,284 try self.dynstrtab.buffer.append(allocator, 0);
219 .sh_addr = 0,285
220 .sh_offset = 0,286 // Initialize PT_PHDR program header
221 .sh_size = 0,287 const p_align: u16 = switch (self.ptr_width) {
222 .sh_link = 0,288 .p32 => @alignOf(elf.Elf32_Phdr),
223 .sh_info = 0,289 .p64 => @alignOf(elf.Elf64_Phdr),
224 .sh_addralign = 0,290 };
225 .sh_entsize = 0,291 const image_base = self.calcImageBase();
226 });292 const offset: u64 = switch (self.ptr_width) {
293 .p32 => @sizeOf(elf.Elf32_Ehdr),
294 .p64 => @sizeOf(elf.Elf64_Ehdr),
295 };
296 self.phdr_table_index = try self.addPhdr(.{
297 .type = elf.PT_PHDR,
298 .flags = elf.PF_R,
299 .@"align" = p_align,
300 .addr = image_base + offset,
301 .offset = offset,
302 });
303 self.phdr_table_load_index = try self.addPhdr(.{
304 .type = elf.PT_LOAD,
305 .flags = elf.PF_R,
306 .@"align" = self.page_size,
307 .addr = image_base,
308 });
309 }
310
311 if (options.module != null and !options.use_llvm) {
312 if (!options.strip) {
313 self.dwarf = Dwarf.init(allocator, &self.base, options.target);
314 }
315
316 const index = @as(File.Index, @intCast(try self.files.addOne(allocator)));
317 self.files.set(index, .{ .zig_module = .{
318 .index = index,
319 .path = options.module.?.main_mod.root_src_path,
320 } });
321 self.zig_module_index = index;
322 const zig_module = self.file(index).?.zig_module;
323
324 try zig_module.atoms.append(allocator, 0); // null input section
325
326 const name_off = try self.strtab.insert(allocator, std.fs.path.stem(options.module.?.main_mod.root_src_path));
327 const symbol_index = try self.addSymbol();
328 try zig_module.local_symbols.append(allocator, symbol_index);
329 const symbol_ptr = self.symbol(symbol_index);
330 symbol_ptr.file_index = zig_module.index;
331 symbol_ptr.name_offset = name_off;
332
333 const esym_index = try zig_module.addLocalEsym(allocator);
334 const esym = &zig_module.local_esyms.items[esym_index];
335 esym.st_name = name_off;
336 esym.st_info |= elf.STT_FILE;
337 esym.st_shndx = elf.SHN_ABS;
338 symbol_ptr.esym_index = esym_index;
227339
228 try self.populateMissingMetadata();340 try self.initMetadata();
341 }
229342
230 return self;343 return self;
231}344}
...@@ -244,17 +357,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {...@@ -244,17 +357,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
244 .sparc64 => 0x2000,357 .sparc64 => 0x2000,
245 else => 0x1000,358 else => 0x1000,
246 };359 };
247 const default_sym_version: elf.Elf64_Versym = if (options.output_mode == .Lib and options.link_mode == .Dynamic)360 const is_dyn_lib = options.output_mode == .Lib and options.link_mode == .Dynamic;
361 const default_sym_version: elf.Elf64_Versym = if (is_dyn_lib or options.rdynamic)
248 elf.VER_NDX_GLOBAL362 elf.VER_NDX_GLOBAL
249 else363 else
250 elf.VER_NDX_LOCAL;364 elf.VER_NDX_LOCAL;
251365
252 const use_llvm = options.use_llvm;
253 var dwarf: ?Dwarf = if (!options.strip and options.module != null and !use_llvm)
254 Dwarf.init(gpa, &self.base, options.target)
255 else
256 null;
257
258 self.* = .{366 self.* = .{
259 .base = .{367 .base = .{
260 .tag = .elf,368 .tag = .elf,
...@@ -262,12 +370,11 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {...@@ -262,12 +370,11 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
262 .allocator = gpa,370 .allocator = gpa,
263 .file = null,371 .file = null,
264 },372 },
265 .dwarf = dwarf,
266 .ptr_width = ptr_width,373 .ptr_width = ptr_width,
267 .page_size = page_size,374 .page_size = page_size,
268 .default_sym_version = default_sym_version,375 .default_sym_version = default_sym_version,
269 };376 };
270 if (use_llvm and options.module != null) {377 if (options.use_llvm and options.module != null) {
271 self.llvm_object = try LlvmObject.create(gpa, options);378 self.llvm_object = try LlvmObject.create(gpa, options);
272 }379 }
273380
...@@ -284,20 +391,24 @@ pub fn deinit(self: *Elf) void {...@@ -284,20 +391,24 @@ pub fn deinit(self: *Elf) void {
284 .zig_module => data.zig_module.deinit(gpa),391 .zig_module => data.zig_module.deinit(gpa),
285 .linker_defined => data.linker_defined.deinit(gpa),392 .linker_defined => data.linker_defined.deinit(gpa),
286 .object => data.object.deinit(gpa),393 .object => data.object.deinit(gpa),
287 // .shared_object => data.shared_object.deinit(gpa),394 .shared_object => data.shared_object.deinit(gpa),
288 };395 };
289 self.files.deinit(gpa);396 self.files.deinit(gpa);
290 self.objects.deinit(gpa);397 self.objects.deinit(gpa);
398 self.shared_objects.deinit(gpa);
291399
292 self.shdrs.deinit(gpa);400 self.shdrs.deinit(gpa);
293 self.phdr_to_shdr_table.deinit(gpa);401 self.phdr_to_shdr_table.deinit(gpa);
294 self.phdrs.deinit(gpa);402 self.phdrs.deinit(gpa);
403 for (self.output_sections.values()) |*list| {
404 list.deinit(gpa);
405 }
406 self.output_sections.deinit(gpa);
295 self.shstrtab.deinit(gpa);407 self.shstrtab.deinit(gpa);
296 self.strtab.deinit(gpa);408 self.strtab.deinit(gpa);
297 self.symbols.deinit(gpa);409 self.symbols.deinit(gpa);
298 self.symbols_extra.deinit(gpa);410 self.symbols_extra.deinit(gpa);
299 self.symbols_free_list.deinit(gpa);411 self.symbols_free_list.deinit(gpa);
300 self.got.deinit(gpa);
301 self.resolver.deinit(gpa);412 self.resolver.deinit(gpa);
302 self.start_stop_indexes.deinit(gpa);413 self.start_stop_indexes.deinit(gpa);
303414
...@@ -333,6 +444,19 @@ pub fn deinit(self: *Elf) void {...@@ -333,6 +444,19 @@ pub fn deinit(self: *Elf) void {
333 self.comdat_groups.deinit(gpa);444 self.comdat_groups.deinit(gpa);
334 self.comdat_groups_owners.deinit(gpa);445 self.comdat_groups_owners.deinit(gpa);
335 self.comdat_groups_table.deinit(gpa);446 self.comdat_groups_table.deinit(gpa);
447
448 self.got.deinit(gpa);
449 self.plt.deinit(gpa);
450 self.plt_got.deinit(gpa);
451 self.dynsym.deinit(gpa);
452 self.dynstrtab.deinit(gpa);
453 self.dynamic.deinit(gpa);
454 self.hash.deinit(gpa);
455 self.versym.deinit(gpa);
456 self.verneed.deinit(gpa);
457 self.copy_rel.deinit(gpa);
458 self.rela_dyn.deinit(gpa);
459 self.rela_plt.deinit(gpa);
336}460}
337461
338pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 {462pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 {
...@@ -368,7 +492,7 @@ pub fn lowerAnonDecl(self: *Elf, decl_val: InternPool.Index, src_loc: Module.Src...@@ -368,7 +492,7 @@ pub fn lowerAnonDecl(self: *Elf, decl_val: InternPool.Index, src_loc: Module.Src
368 const tv = TypedValue{ .ty = ty, .val = val };492 const tv = TypedValue{ .ty = ty, .val = val };
369 const name = try std.fmt.allocPrint(gpa, "__anon_{d}", .{@intFromEnum(decl_val)});493 const name = try std.fmt.allocPrint(gpa, "__anon_{d}", .{@intFromEnum(decl_val)});
370 defer gpa.free(name);494 defer gpa.free(name);
371 const res = self.lowerConst(name, tv, self.rodata_section_index.?, src_loc) catch |err| switch (err) {495 const res = self.lowerConst(name, tv, self.zig_rodata_section_index.?, src_loc) catch |err| switch (err) {
372 else => {496 else => {
373 // TODO improve error message497 // TODO improve error message
374 const em = try Module.ErrorMsg.create(gpa, src_loc, "lowerAnonDecl failed with error: {s}", .{498 const em = try Module.ErrorMsg.create(gpa, src_loc, "lowerAnonDecl failed with error: {s}", .{
...@@ -420,21 +544,23 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {...@@ -420,21 +544,23 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
420 }544 }
421545
422 for (self.shdrs.items) |shdr| {546 for (self.shdrs.items) |shdr| {
423 // SHT_NOBITS takes no physical space in the output file so set its size to 0.547 if (shdr.sh_type == elf.SHT_NOBITS) continue;
424 const sh_size = if (shdr.sh_type == elf.SHT_NOBITS) 0 else shdr.sh_size;548 const increased_size = padToIdeal(shdr.sh_size);
425 const increased_size = padToIdeal(sh_size);
426 const test_end = shdr.sh_offset + increased_size;549 const test_end = shdr.sh_offset + increased_size;
427 if (end > shdr.sh_offset and start < test_end) {550 if (end > shdr.sh_offset and start < test_end) {
428 return test_end;551 return test_end;
429 }552 }
430 }553 }
554
431 for (self.phdrs.items) |phdr| {555 for (self.phdrs.items) |phdr| {
556 if (phdr.p_type != elf.PT_LOAD) continue;
432 const increased_size = padToIdeal(phdr.p_filesz);557 const increased_size = padToIdeal(phdr.p_filesz);
433 const test_end = phdr.p_offset + increased_size;558 const test_end = phdr.p_offset + increased_size;
434 if (end > phdr.p_offset and start < test_end) {559 if (end > phdr.p_offset and start < test_end) {
435 return test_end;560 return test_end;
436 }561 }
437 }562 }
563
438 return null;564 return null;
439}565}
440566
...@@ -474,55 +600,34 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 {...@@ -474,55 +600,34 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 {
474}600}
475601
476const AllocateSegmentOpts = struct {602const AllocateSegmentOpts = struct {
477 size: u64,603 addr: u64,
604 memsz: u64,
605 filesz: u64,
478 alignment: u64,606 alignment: u64,
479 addr: ?u64 = null,
480 flags: u32 = elf.PF_R,607 flags: u32 = elf.PF_R,
481};608};
482609
483pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}!u16 {610pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}!u16 {
484 const gpa = self.base.allocator;611 const off = self.findFreeSpace(opts.filesz, opts.alignment);
485 const index = @as(u16, @intCast(self.phdrs.items.len));612 const index = try self.addPhdr(.{
486 try self.phdrs.ensureUnusedCapacity(gpa, 1);613 .type = elf.PT_LOAD,
487 const off = self.findFreeSpace(opts.size, opts.alignment);614 .offset = off,
488 // Currently, we automatically allocate memory in sequence by finding the largest615 .filesz = opts.filesz,
489 // allocated virtual address and going from there.616 .addr = opts.addr,
490 // TODO we want to keep machine code segment in the furthest memory range among all617 .memsz = opts.memsz,
491 // segments as it is most likely to grow.618 .@"align" = opts.alignment,
492 const addr = opts.addr orelse blk: {619 .flags = opts.flags,
493 const reserved_capacity = self.calcImageBase() * 4;620 });
494 // Calculate largest VM address
495 var addresses = std.ArrayList(u64).init(gpa);
496 defer addresses.deinit();
497 try addresses.ensureTotalCapacityPrecise(self.phdrs.items.len);
498 for (self.phdrs.items) |phdr| {
499 if (phdr.p_type != elf.PT_LOAD) continue;
500 addresses.appendAssumeCapacity(phdr.p_vaddr + reserved_capacity);
501 }
502 mem.sort(u64, addresses.items, {}, std.sort.asc(u64));
503 break :blk mem.alignForward(u64, addresses.pop(), opts.alignment);
504 };
505 log.debug("allocating phdr({d})({c}{c}{c}) from 0x{x} to 0x{x} (0x{x} - 0x{x})", .{621 log.debug("allocating phdr({d})({c}{c}{c}) from 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
506 index,622 index,
507 if (opts.flags & elf.PF_R != 0) @as(u8, 'R') else '_',623 if (opts.flags & elf.PF_R != 0) @as(u8, 'R') else '_',
508 if (opts.flags & elf.PF_W != 0) @as(u8, 'W') else '_',624 if (opts.flags & elf.PF_W != 0) @as(u8, 'W') else '_',
509 if (opts.flags & elf.PF_X != 0) @as(u8, 'X') else '_',625 if (opts.flags & elf.PF_X != 0) @as(u8, 'X') else '_',
510 off,626 off,
511 off + opts.size,627 off + opts.filesz,
512 addr,628 opts.addr,
513 addr + opts.size,629 opts.addr + opts.memsz,
514 });630 });
515 self.phdrs.appendAssumeCapacity(.{
516 .p_type = elf.PT_LOAD,
517 .p_offset = off,
518 .p_filesz = opts.size,
519 .p_vaddr = addr,
520 .p_paddr = addr,
521 .p_memsz = opts.size,
522 .p_align = opts.alignment,
523 .p_flags = opts.flags,
524 });
525 self.phdr_table_dirty = true;
526 return index;631 return index;
527}632}
528633
...@@ -537,9 +642,13 @@ const AllocateAllocSectionOpts = struct {...@@ -537,9 +642,13 @@ const AllocateAllocSectionOpts = struct {
537pub fn allocateAllocSection(self: *Elf, opts: AllocateAllocSectionOpts) error{OutOfMemory}!u16 {642pub fn allocateAllocSection(self: *Elf, opts: AllocateAllocSectionOpts) error{OutOfMemory}!u16 {
538 const gpa = self.base.allocator;643 const gpa = self.base.allocator;
539 const phdr = &self.phdrs.items[opts.phdr_index];644 const phdr = &self.phdrs.items[opts.phdr_index];
540 const index = @as(u16, @intCast(self.shdrs.items.len));645 const index = try self.addSection(.{
541 try self.shdrs.ensureUnusedCapacity(gpa, 1);646 .name = opts.name,
542 const sh_name = try self.shstrtab.insert(gpa, opts.name);647 .type = opts.type,
648 .flags = opts.flags,
649 .addralign = opts.alignment,
650 });
651 const shdr = &self.shdrs.items[index];
543 try self.phdr_to_shdr_table.putNoClobber(gpa, index, opts.phdr_index);652 try self.phdr_to_shdr_table.putNoClobber(gpa, index, opts.phdr_index);
544 log.debug("allocating '{s}' in phdr({d}) from 0x{x} to 0x{x} (0x{x} - 0x{x})", .{653 log.debug("allocating '{s}' in phdr({d}) from 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
545 opts.name,654 opts.name,
...@@ -549,19 +658,9 @@ pub fn allocateAllocSection(self: *Elf, opts: AllocateAllocSectionOpts) error{Ou...@@ -549,19 +658,9 @@ pub fn allocateAllocSection(self: *Elf, opts: AllocateAllocSectionOpts) error{Ou
549 phdr.p_vaddr,658 phdr.p_vaddr,
550 phdr.p_vaddr + phdr.p_memsz,659 phdr.p_vaddr + phdr.p_memsz,
551 });660 });
552 self.shdrs.appendAssumeCapacity(.{661 shdr.sh_addr = phdr.p_vaddr;
553 .sh_name = sh_name,662 shdr.sh_offset = phdr.p_offset;
554 .sh_type = opts.type,663 shdr.sh_size = phdr.p_memsz;
555 .sh_flags = opts.flags,
556 .sh_addr = phdr.p_vaddr,
557 .sh_offset = phdr.p_offset,
558 .sh_size = phdr.p_memsz,
559 .sh_link = 0,
560 .sh_info = 0,
561 .sh_addralign = opts.alignment,
562 .sh_entsize = 0,
563 });
564 self.shdr_table_dirty = true;
565 return index;664 return index;
566}665}
567666
...@@ -577,268 +676,132 @@ const AllocateNonAllocSectionOpts = struct {...@@ -577,268 +676,132 @@ const AllocateNonAllocSectionOpts = struct {
577};676};
578677
579fn allocateNonAllocSection(self: *Elf, opts: AllocateNonAllocSectionOpts) error{OutOfMemory}!u16 {678fn allocateNonAllocSection(self: *Elf, opts: AllocateNonAllocSectionOpts) error{OutOfMemory}!u16 {
580 const index = @as(u16, @intCast(self.shdrs.items.len));679 const index = try self.addSection(.{
581 try self.shdrs.ensureUnusedCapacity(self.base.allocator, 1);680 .name = opts.name,
582 const sh_name = try self.shstrtab.insert(self.base.allocator, opts.name);681 .type = opts.type,
682 .flags = opts.flags,
683 .link = opts.link,
684 .info = opts.info,
685 .addralign = opts.alignment,
686 .entsize = opts.entsize,
687 });
688 const shdr = &self.shdrs.items[index];
583 const off = self.findFreeSpace(opts.size, opts.alignment);689 const off = self.findFreeSpace(opts.size, opts.alignment);
584 log.debug("allocating '{s}' from 0x{x} to 0x{x} ", .{ opts.name, off, off + opts.size });690 log.debug("allocating '{s}' from 0x{x} to 0x{x} ", .{ opts.name, off, off + opts.size });
585 self.shdrs.appendAssumeCapacity(.{691 shdr.sh_offset = off;
586 .sh_name = sh_name,692 shdr.sh_size = opts.size;
587 .sh_type = opts.type,
588 .sh_flags = opts.flags,
589 .sh_addr = 0,
590 .sh_offset = off,
591 .sh_size = opts.size,
592 .sh_link = opts.link,
593 .sh_info = opts.info,
594 .sh_addralign = opts.alignment,
595 .sh_entsize = opts.entsize,
596 });
597 self.shdr_table_dirty = true;
598 return index;693 return index;
599}694}
600695
601pub fn populateMissingMetadata(self: *Elf) !void {696/// TODO move to ZigModule
697pub fn initMetadata(self: *Elf) !void {
602 const gpa = self.base.allocator;698 const gpa = self.base.allocator;
603 const small_ptr = switch (self.ptr_width) {699 const ptr_size = self.ptrWidthBytes();
604 .p32 => true,700 const ptr_bit_width = self.base.options.target.ptrBitWidth();
605 .p64 => false,
606 };
607 const ptr_size: u8 = self.ptrWidthBytes();
608 const is_linux = self.base.options.target.os.tag == .linux;701 const is_linux = self.base.options.target.os.tag == .linux;
609 const image_base = self.calcImageBase();
610
611 if (self.phdr_table_index == null) {
612 self.phdr_table_index = @intCast(self.phdrs.items.len);
613 const p_align: u16 = switch (self.ptr_width) {
614 .p32 => @alignOf(elf.Elf32_Phdr),
615 .p64 => @alignOf(elf.Elf64_Phdr),
616 };
617 try self.phdrs.append(gpa, .{
618 .p_type = elf.PT_PHDR,
619 .p_offset = 0,
620 .p_filesz = 0,
621 .p_vaddr = image_base,
622 .p_paddr = image_base,
623 .p_memsz = 0,
624 .p_align = p_align,
625 .p_flags = elf.PF_R,
626 });
627 self.phdr_table_dirty = true;
628 }
629
630 if (self.phdr_table_load_index == null) {
631 self.phdr_table_load_index = try self.allocateSegment(.{
632 .addr = image_base,
633 .size = 0,
634 .alignment = self.page_size,
635 });
636 self.phdr_table_dirty = true;
637 }
638702
639 if (self.phdr_load_re_index == null) {703 if (self.phdr_zig_load_re_index == null) {
640 self.phdr_load_re_index = try self.allocateSegment(.{704 self.phdr_zig_load_re_index = try self.allocateSegment(.{
641 .size = self.base.options.program_code_size_hint,705 .addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000,
706 .memsz = self.base.options.program_code_size_hint,
707 .filesz = self.base.options.program_code_size_hint,
642 .alignment = self.page_size,708 .alignment = self.page_size,
643 .flags = elf.PF_X | elf.PF_R | elf.PF_W,709 .flags = elf.PF_X | elf.PF_R | elf.PF_W,
644 });710 });
645 self.entry_addr = null;
646 }711 }
647712
648 if (self.phdr_got_index == null) {713 if (self.phdr_zig_got_index == null) {
649 // We really only need ptr alignment but since we are using PROGBITS, linux requires714 // We really only need ptr alignment but since we are using PROGBITS, linux requires
650 // page align.715 // page align.
651 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);716 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
652 self.phdr_got_index = try self.allocateSegment(.{717 self.phdr_zig_got_index = try self.allocateSegment(.{
653 .size = @as(u64, ptr_size) * self.base.options.symbol_count_hint,718 .addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000,
719 .memsz = @as(u64, ptr_size) * self.base.options.symbol_count_hint,
720 .filesz = @as(u64, ptr_size) * self.base.options.symbol_count_hint,
654 .alignment = alignment,721 .alignment = alignment,
655 .flags = elf.PF_R | elf.PF_W,722 .flags = elf.PF_R | elf.PF_W,
656 });723 });
657 }724 }
658725
659 if (self.phdr_load_ro_index == null) {726 if (self.phdr_zig_load_ro_index == null) {
660 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);727 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
661 self.phdr_load_ro_index = try self.allocateSegment(.{728 self.phdr_zig_load_ro_index = try self.allocateSegment(.{
662 .size = 1024,729 .addr = if (ptr_bit_width >= 32) 0xc000000 else 0xa000,
730 .memsz = 1024,
731 .filesz = 1024,
663 .alignment = alignment,732 .alignment = alignment,
664 .flags = elf.PF_R | elf.PF_W,733 .flags = elf.PF_R | elf.PF_W,
665 });734 });
666 }735 }
667736
668 if (self.phdr_load_rw_index == null) {737 if (self.phdr_zig_load_rw_index == null) {
669 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);738 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
670 self.phdr_load_rw_index = try self.allocateSegment(.{739 self.phdr_zig_load_rw_index = try self.allocateSegment(.{
671 .size = 1024,740 .addr = if (ptr_bit_width >= 32) 0x10000000 else 0xc000,
741 .memsz = 1024,
742 .filesz = 1024,
672 .alignment = alignment,743 .alignment = alignment,
673 .flags = elf.PF_R | elf.PF_W,744 .flags = elf.PF_R | elf.PF_W,
674 });745 });
675 }746 }
676747
677 if (self.phdr_load_zerofill_index == null) {748 if (self.phdr_zig_load_zerofill_index == null) {
678 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);749 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
679 self.phdr_load_zerofill_index = try self.allocateSegment(.{750 self.phdr_zig_load_zerofill_index = try self.addPhdr(.{
680 .size = 0,751 .type = elf.PT_LOAD,
681 .alignment = alignment,752 .addr = if (ptr_bit_width >= 32) 0x14000000 else 0xf000,
753 .memsz = 1024,
754 .@"align" = alignment,
682 .flags = elf.PF_R | elf.PF_W,755 .flags = elf.PF_R | elf.PF_W,
683 });756 });
684 const phdr = &self.phdrs.items[self.phdr_load_zerofill_index.?];
685 phdr.p_offset = self.phdrs.items[self.phdr_load_rw_index.?].p_offset; // .bss overlaps .data
686 phdr.p_memsz = 1024;
687 }
688
689 if (!self.base.options.single_threaded) {
690 if (self.phdr_load_tls_data_index == null) {
691 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
692 self.phdr_load_tls_data_index = try self.allocateSegment(.{
693 .size = 1024,
694 .alignment = alignment,
695 .flags = elf.PF_R | elf.PF_W,
696 });
697 }
698
699 if (self.phdr_load_tls_zerofill_index == null) {
700 // TODO .tbss doesn't need any physical or memory representation (aka a loadable segment)
701 // since the loader only cares about the PT_TLS to work out TLS size. However, when
702 // relocating we need to have .tdata and .tbss contiguously laid out so that we can
703 // work out correct offsets to the start/end of the TLS segment. I am thinking that
704 // perhaps it's possible to completely spoof it by having an abstracted mechanism
705 // for this that wouldn't require us to explicitly track .tbss. Anyhow, for now,
706 // we go the savage route of treating .tbss like .bss.
707 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
708 self.phdr_load_tls_zerofill_index = try self.allocateSegment(.{
709 .size = 0,
710 .alignment = alignment,
711 .flags = elf.PF_R | elf.PF_W,
712 });
713 const phdr = &self.phdrs.items[self.phdr_load_tls_zerofill_index.?];
714 phdr.p_offset = self.phdrs.items[self.phdr_load_tls_data_index.?].p_offset; // .tbss overlaps .tdata
715 phdr.p_memsz = 1024;
716 }
717
718 if (self.phdr_tls_index == null) {
719 self.phdr_tls_index = @intCast(self.phdrs.items.len);
720 const phdr_tdata = &self.phdrs.items[self.phdr_load_tls_data_index.?];
721 const phdr_tbss = &self.phdrs.items[self.phdr_load_tls_zerofill_index.?];
722 try self.phdrs.append(gpa, .{
723 .p_type = elf.PT_TLS,
724 .p_offset = phdr_tdata.p_offset,
725 .p_vaddr = phdr_tdata.p_vaddr,
726 .p_paddr = phdr_tdata.p_paddr,
727 .p_filesz = phdr_tdata.p_filesz,
728 .p_memsz = phdr_tbss.p_vaddr + phdr_tbss.p_memsz - phdr_tdata.p_vaddr,
729 .p_align = ptr_size,
730 .p_flags = elf.PF_R,
731 });
732 self.phdr_table_dirty = true;
733 }
734 }
735
736 if (self.shstrtab_section_index == null) {
737 assert(self.shstrtab.buffer.items.len == 0);
738 try self.shstrtab.buffer.append(gpa, 0); // need a 0 at position 0
739 self.shstrtab_section_index = try self.allocateNonAllocSection(.{
740 .name = ".shstrtab",
741 .size = @intCast(self.shstrtab.buffer.items.len),
742 .type = elf.SHT_STRTAB,
743 });
744 self.shstrtab_dirty = true;
745 }
746
747 if (self.strtab_section_index == null) {
748 assert(self.strtab.buffer.items.len == 0);
749 try self.strtab.buffer.append(gpa, 0); // need a 0 at position 0
750 self.strtab_section_index = try self.allocateNonAllocSection(.{
751 .name = ".strtab",
752 .size = @intCast(self.strtab.buffer.items.len),
753 .type = elf.SHT_STRTAB,
754 });
755 self.strtab_dirty = true;
756 }757 }
757758
758 if (self.text_section_index == null) {759 if (self.zig_text_section_index == null) {
759 self.text_section_index = try self.allocateAllocSection(.{760 self.zig_text_section_index = try self.allocateAllocSection(.{
760 .name = ".text",761 .name = ".zig.text",
761 .phdr_index = self.phdr_load_re_index.?,762 .phdr_index = self.phdr_zig_load_re_index.?,
762 .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,763 .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
763 });764 });
764 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.text_section_index.?, .{});765 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_text_section_index.?, .{});
765 }766 }
766767
767 if (self.got_section_index == null) {768 if (self.zig_got_section_index == null) {
768 self.got_section_index = try self.allocateAllocSection(.{769 self.zig_got_section_index = try self.allocateAllocSection(.{
769 .name = ".got",770 .name = ".zig.got",
770 .phdr_index = self.phdr_got_index.?,771 .phdr_index = self.phdr_zig_got_index.?,
771 .alignment = ptr_size,772 .alignment = ptr_size,
773 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
772 });774 });
773 }775 }
774776
775 if (self.rodata_section_index == null) {777 if (self.zig_rodata_section_index == null) {
776 self.rodata_section_index = try self.allocateAllocSection(.{778 self.zig_rodata_section_index = try self.allocateAllocSection(.{
777 .name = ".rodata",779 .name = ".zig.rodata",
778 .phdr_index = self.phdr_load_ro_index.?,780 .phdr_index = self.phdr_zig_load_ro_index.?,
781 .flags = elf.SHF_ALLOC | elf.SHF_WRITE, // TODO rename this section to .data.rel.ro
779 });782 });
780 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.rodata_section_index.?, .{});783 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_rodata_section_index.?, .{});
781 }784 }
782785
783 if (self.data_section_index == null) {786 if (self.zig_data_section_index == null) {
784 self.data_section_index = try self.allocateAllocSection(.{787 self.zig_data_section_index = try self.allocateAllocSection(.{
785 .name = ".data",788 .name = ".zig.data",
786 .phdr_index = self.phdr_load_rw_index.?,789 .phdr_index = self.phdr_zig_load_rw_index.?,
787 .alignment = ptr_size,790 .alignment = ptr_size,
788 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,791 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
789 });792 });
790 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.data_section_index.?, .{});793 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_data_section_index.?, .{});
791 }794 }
792795
793 if (self.bss_section_index == null) {796 if (self.zig_bss_section_index == null) {
794 self.bss_section_index = try self.allocateAllocSection(.{797 self.zig_bss_section_index = try self.allocateAllocSection(.{
795 .name = ".bss",798 .name = ".zig.bss",
796 .phdr_index = self.phdr_load_zerofill_index.?,799 .phdr_index = self.phdr_zig_load_zerofill_index.?,
797 .alignment = ptr_size,800 .alignment = ptr_size,
798 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,801 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
799 .type = elf.SHT_NOBITS,802 .type = elf.SHT_NOBITS,
800 });803 });
801 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.bss_section_index.?, .{});804 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_bss_section_index.?, .{});
802 }
803
804 if (self.phdr_load_tls_data_index) |phdr_index| {
805 if (self.tdata_section_index == null) {
806 self.tdata_section_index = try self.allocateAllocSection(.{
807 .name = ".tdata",
808 .phdr_index = phdr_index,
809 .alignment = ptr_size,
810 .flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS,
811 });
812 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.tdata_section_index.?, .{});
813 }
814 }
815
816 if (self.phdr_load_tls_zerofill_index) |phdr_index| {
817 if (self.tbss_section_index == null) {
818 self.tbss_section_index = try self.allocateAllocSection(.{
819 .name = ".tbss",
820 .phdr_index = phdr_index,
821 .alignment = ptr_size,
822 .flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS,
823 .type = elf.SHT_NOBITS,
824 });
825 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.tbss_section_index.?, .{});
826 }
827 }
828
829 if (self.symtab_section_index == null) {
830 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
831 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
832 self.symtab_section_index = try self.allocateNonAllocSection(.{
833 .name = ".symtab",
834 .size = self.base.options.symbol_count_hint * each_size,
835 .alignment = min_align,
836 .type = elf.SHT_SYMTAB,
837 .link = self.strtab_section_index.?, // Index of associated string table
838 .info = @intCast(self.symbols.items.len),
839 .entsize = each_size,
840 });
841 self.shdr_table_dirty = true;
842 }805 }
843806
844 if (self.dwarf) |*dw| {807 if (self.dwarf) |*dw| {
...@@ -890,68 +853,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -890,68 +853,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {
890 self.debug_line_header_dirty = true;853 self.debug_line_header_dirty = true;
891 }854 }
892 }855 }
893
894 const shsize: u64 = switch (self.ptr_width) {
895 .p32 => @sizeOf(elf.Elf32_Shdr),
896 .p64 => @sizeOf(elf.Elf64_Shdr),
897 };
898 const shalign: u16 = switch (self.ptr_width) {
899 .p32 => @alignOf(elf.Elf32_Shdr),
900 .p64 => @alignOf(elf.Elf64_Shdr),
901 };
902 if (self.shdr_table_offset == null) {
903 self.shdr_table_offset = self.findFreeSpace(self.shdrs.items.len * shsize, shalign);
904 self.shdr_table_dirty = true;
905 }
906
907 {
908 // Iterate over symbols, populating free_list and last_text_block.
909 if (self.symbols.items.len != 1) {
910 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");
911 }
912 // We are starting with an empty file. The default values are correct, null and empty list.
913 }
914
915 if (self.shdr_table_dirty) {
916 // We need to find out what the max file offset is according to section headers.
917 // Otherwise, we may end up with an ELF binary with file size not matching the final section's
918 // offset + it's filesize.
919 var max_file_offset: u64 = 0;
920
921 for (self.shdrs.items) |shdr| {
922 if (shdr.sh_offset + shdr.sh_size > max_file_offset) {
923 max_file_offset = shdr.sh_offset + shdr.sh_size;
924 }
925 }
926
927 try self.base.file.?.pwriteAll(&[_]u8{0}, max_file_offset);
928 }
929
930 if (self.base.options.module) |module| {
931 if (self.zig_module_index == null and !self.base.options.use_llvm) {
932 const index: File.Index = @intCast(try self.files.addOne(gpa));
933 self.files.set(index, .{ .zig_module = .{
934 .index = index,
935 .path = module.main_mod.root_src_path,
936 } });
937 self.zig_module_index = index;
938 const zig_module = self.file(index).?.zig_module;
939
940 const name_off = try self.strtab.insert(gpa, std.fs.path.stem(module.main_mod.root_src_path));
941 const symbol_index = try self.addSymbol();
942 try zig_module.local_symbols.append(gpa, symbol_index);
943 const symbol_ptr = self.symbol(symbol_index);
944 symbol_ptr.file_index = zig_module.index;
945 symbol_ptr.name_offset = name_off;
946
947 const esym_index = try zig_module.addLocalEsym(gpa);
948 const esym = &zig_module.local_esyms.items[esym_index];
949 esym.st_name = name_off;
950 esym.st_info |= elf.STT_FILE;
951 esym.st_shndx = elf.SHN_ABS;
952 symbol_ptr.esym_index = esym_index;
953 }
954 }
955}856}
956857
957pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {858pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {
...@@ -987,89 +888,17 @@ pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {...@@ -987,89 +888,17 @@ pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {
987888
988 const mem_capacity = self.allocatedVirtualSize(phdr.p_vaddr);889 const mem_capacity = self.allocatedVirtualSize(phdr.p_vaddr);
989 if (needed_size > mem_capacity) {890 if (needed_size > mem_capacity) {
990 // We are exceeding our allocated VM capacity so we need to shift everything in memory891 var err = try self.addErrorWithNotes(2);
991 // and grow.892 try err.addMsg(self, "fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{
992 {893 phdr_index,
993 const dirty_addr = phdr.p_vaddr + phdr.p_memsz;894 });
994 self.got_dirty = for (self.got.entries.items) |entry| {895 try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});
995 if (self.symbol(entry.symbol_index).value >= dirty_addr) break true;896 try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
996 } else false;
997
998 // TODO mark relocs dirty
999 }
1000 try self.growSegment(shdr_index, needed_size);
1001
1002 if (self.zig_module_index != null) {
1003 // TODO self-hosted backends cannot yet handle this condition correctly as the linker
1004 // cannot update emitted virtual addresses of symbols already committed to the final file.
1005 var err = try self.addErrorWithNotes(2);
1006 try err.addMsg(self, "fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{
1007 phdr_index,
1008 });
1009 try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});
1010 try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
1011 }
1012 }897 }
1013898
1014 phdr.p_memsz = needed_size;899 phdr.p_memsz = needed_size;
1015900
1016 self.markDirty(shdr_index, phdr_index);901 self.markDirty(shdr_index);
1017}
1018
1019fn growSegment(self: *Elf, shndx: u16, needed_size: u64) !void {
1020 const phdr_index = self.phdr_to_shdr_table.get(shndx).?;
1021 const phdr = &self.phdrs.items[phdr_index];
1022 const increased_size = padToIdeal(needed_size);
1023 const end_addr = phdr.p_vaddr + phdr.p_memsz;
1024 const old_aligned_end = phdr.p_vaddr + mem.alignForward(u64, phdr.p_memsz, phdr.p_align);
1025 const new_aligned_end = phdr.p_vaddr + mem.alignForward(u64, increased_size, phdr.p_align);
1026 const diff = new_aligned_end - old_aligned_end;
1027 log.debug("growing phdr({d}) in memory by {x}", .{ phdr_index, diff });
1028
1029 // Update symbols and atoms.
1030 var files = std.ArrayList(File.Index).init(self.base.allocator);
1031 defer files.deinit();
1032 try files.ensureTotalCapacityPrecise(self.objects.items.len + 1);
1033
1034 if (self.zig_module_index) |index| files.appendAssumeCapacity(index);
1035 files.appendSliceAssumeCapacity(self.objects.items);
1036
1037 for (files.items) |index| {
1038 const file_ptr = self.file(index).?;
1039
1040 for (file_ptr.locals()) |sym_index| {
1041 const sym = self.symbol(sym_index);
1042 const atom_ptr = sym.atom(self) orelse continue;
1043 if (!atom_ptr.flags.alive or !atom_ptr.flags.allocated) continue;
1044 if (sym.value >= end_addr) sym.value += diff;
1045 }
1046
1047 for (file_ptr.globals()) |sym_index| {
1048 const sym = self.symbol(sym_index);
1049 if (sym.file_index != index) continue;
1050 const atom_ptr = sym.atom(self) orelse continue;
1051 if (!atom_ptr.flags.alive or !atom_ptr.flags.allocated) continue;
1052 if (sym.value >= end_addr) sym.value += diff;
1053 }
1054
1055 for (file_ptr.atoms()) |atom_index| {
1056 const atom_ptr = self.atom(atom_index) orelse continue;
1057 if (!atom_ptr.flags.alive or !atom_ptr.flags.allocated) continue;
1058 if (atom_ptr.value >= end_addr) atom_ptr.value += diff;
1059 }
1060 }
1061
1062 // Finally, update section headers.
1063 for (self.shdrs.items, 0..) |*other_shdr, other_shndx| {
1064 if (other_shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
1065 if (other_shndx == shndx) continue;
1066 const other_phdr_index = self.phdr_to_shdr_table.get(@intCast(other_shndx)) orelse continue;
1067 const other_phdr = &self.phdrs.items[other_phdr_index];
1068 if (other_phdr.p_vaddr < end_addr) continue;
1069 other_shdr.sh_addr += diff;
1070 other_phdr.p_vaddr += diff;
1071 other_phdr.p_paddr += diff;
1072 }
1073}902}
1074903
1075pub fn growNonAllocSection(904pub fn growNonAllocSection(
...@@ -1106,18 +935,12 @@ pub fn growNonAllocSection(...@@ -1106,18 +935,12 @@ pub fn growNonAllocSection(
1106 shdr.sh_offset = new_offset;935 shdr.sh_offset = new_offset;
1107 }936 }
1108937
1109 shdr.sh_size = needed_size; // anticipating adding the global symbols later938 shdr.sh_size = needed_size;
1110939
1111 self.markDirty(shdr_index, null);940 self.markDirty(shdr_index);
1112}941}
1113942
1114pub fn markDirty(self: *Elf, shdr_index: u16, phdr_index: ?u16) void {943pub fn markDirty(self: *Elf, shdr_index: u16) void {
1115 self.shdr_table_dirty = true; // TODO look into only writing one section
1116
1117 if (phdr_index) |_| {
1118 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
1119 }
1120
1121 if (self.dwarf) |_| {944 if (self.dwarf) |_| {
1122 if (self.debug_info_section_index.? == shdr_index) {945 if (self.debug_info_section_index.? == shdr_index) {
1123 self.debug_info_header_dirty = true;946 self.debug_info_header_dirty = true;
...@@ -1144,10 +967,11 @@ pub fn flush(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link...@@ -1144,10 +967,11 @@ pub fn flush(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link
1144 if (use_lld) {967 if (use_lld) {
1145 return self.linkWithLLD(comp, prog_node);968 return self.linkWithLLD(comp, prog_node);
1146 }969 }
1147 switch (self.base.options.output_mode) {970 if (self.base.options.output_mode == .Lib and self.isStatic()) {
1148 .Exe, .Obj => return self.flushModule(comp, prog_node),971 // TODO writing static library files
1149 .Lib => return error.TODOImplementWritingLibFiles,972 return error.TODOImplementWritingLibFiles;
1150 }973 }
974 try self.flushModule(comp, prog_node);
1151}975}
1152976
1153pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {977pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {
...@@ -1173,70 +997,411 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1173,70 +997,411 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1173 const target = self.base.options.target;997 const target = self.base.options.target;
1174 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.998 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
1175 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});999 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
1000 const module_obj_path: ?[]const u8 = if (self.base.intermediary_basename) |path| blk: {
1001 if (fs.path.dirname(full_out_path)) |dirname| {
1002 break :blk try fs.path.join(arena, &.{ dirname, path });
1003 } else {
1004 break :blk path;
1005 }
1006 } else null;
1007 const gc_sections = self.base.options.gc_sections orelse false;
11761008
1177 // Here we will parse input positional and library files (if referenced).1009 if (self.base.options.output_mode == .Obj and self.zig_module_index == null) {
1178 // This will roughly match in any linker backend we support.1010 // TODO this will become -r route I guess. For now, just copy the object file.
1179 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);1011 assert(self.base.file == null); // TODO uncomment once we implement -r
11801012 const the_object_path = blk: {
1181 if (self.base.intermediary_basename) |path| {1013 if (self.base.options.objects.len != 0) {
1182 const full_path = blk: {1014 break :blk self.base.options.objects[0].path;
1183 if (fs.path.dirname(full_out_path)) |dirname| {
1184 break :blk try fs.path.join(arena, &.{ dirname, path });
1185 } else {
1186 break :blk path;
1187 }1015 }
1188 };
1189 try positionals.append(.{ .path = full_path });
1190 }
11911016
1192 try positionals.ensureUnusedCapacity(self.base.options.objects.len);1017 if (comp.c_object_table.count() != 0)
1193 positionals.appendSliceAssumeCapacity(self.base.options.objects);1018 break :blk comp.c_object_table.keys()[0].status.success.object_path;
11941019
1195 // This is a set of object files emitted by clang in a single `build-exe` invocation.1020 if (module_obj_path) |p|
1196 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up1021 break :blk p;
1197 // in this set.1022
1198 for (comp.c_object_table.keys()) |key| {1023 // TODO I think this is unreachable. Audit this situation when solving the above TODO
1199 try positionals.append(.{ .path = key.status.success.object_path });1024 // regarding eliding redundant object -> object transformations.
1025 return error.NoObjectsToLink;
1026 };
1027 // This can happen when using --enable-cache and using the stage1 backend. In this case
1028 // we can skip the file copy.
1029 if (!mem.eql(u8, the_object_path, full_out_path)) {
1030 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
1031 }
1032 return;
1200 }1033 }
12011034
1202 // csu prelude
1203 var csu = try CsuObjects.init(arena, self.base.options, comp);1035 var csu = try CsuObjects.init(arena, self.base.options, comp);
1204 if (csu.crt0) |v| try positionals.append(.{ .path = v });1036 const compiler_rt_path: ?[]const u8 = blk: {
1205 if (csu.crti) |v| try positionals.append(.{ .path = v });1037 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
1206 if (csu.crtbegin) |v| try positionals.append(.{ .path = v });1038 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
1039 break :blk null;
1040 };
12071041
1208 for (positionals.items) |obj| {1042 // --verbose-link
1209 const in_file = try std.fs.cwd().openFile(obj.path, .{});1043 if (self.base.options.verbose_link) {
1210 defer in_file.close();1044 var argv = std.ArrayList([]const u8).init(arena);
1211 var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
1212 self.parsePositional(in_file, obj.path, obj.must_link, &parse_ctx) catch |err|
1213 try self.handleAndReportParseError(obj.path, err, &parse_ctx);
1214 }
12151045
1216 var system_libs = std.ArrayList(SystemLib).init(arena);1046 try argv.append("zig");
1047 try argv.append("ld");
12171048
1218 // libc dep1049 try argv.append("-o");
1219 self.error_flags.missing_libc = false;1050 try argv.append(full_out_path);
1220 if (self.base.options.link_libc) {1051
1221 if (self.base.options.libc_installation != null) {1052 if (self.base.options.entry) |entry| {
1222 @panic("TODO explicit libc_installation");1053 try argv.append("--entry");
1223 } else if (target.isGnuLibC()) {1054 try argv.append(entry);
1224 try system_libs.ensureUnusedCapacity(glibc.libs.len + 1);1055 }
1225 for (glibc.libs) |lib| {1056
1226 const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{1057 if (self.base.options.dynamic_linker) |path| {
1227 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,1058 try argv.append("-dynamic-linker");
1228 });1059 try argv.append(path);
1229 system_libs.appendAssumeCapacity(.{ .path = lib_path });1060 }
1230 }1061
1231 system_libs.appendAssumeCapacity(.{1062 if (self.base.options.soname) |name| {
1232 .path = try comp.get_libc_crt_file(arena, "libc_nonshared.a"),1063 try argv.append("-soname");
1233 });1064 try argv.append(name);
1234 } else if (target.isMusl()) {1065 }
1235 const path = try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) {1066
1236 .Static => "libc.a",1067 for (self.base.options.rpath_list) |rpath| {
1237 .Dynamic => "libc.so",1068 try argv.append("-rpath");
1238 });1069 try argv.append(rpath);
1239 try system_libs.append(.{ .path = path });1070 }
1071
1072 if (self.base.options.each_lib_rpath) {
1073 for (self.base.options.lib_dirs) |lib_dir_path| {
1074 try argv.append("-rpath");
1075 try argv.append(lib_dir_path);
1076 }
1077 for (self.base.options.objects) |obj| {
1078 if (Compilation.classifyFileExt(obj.path) == .shared_library) {
1079 const lib_dir_path = std.fs.path.dirname(obj.path) orelse continue;
1080 if (obj.loption) continue;
1081
1082 try argv.append("-rpath");
1083 try argv.append(lib_dir_path);
1084 }
1085 }
1086 }
1087
1088 if (self.base.options.stack_size_override) |ss| {
1089 try argv.append("-z");
1090 try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{ss}));
1091 }
1092
1093 if (self.base.options.image_base_override) |image_base| {
1094 try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base}));
1095 }
1096
1097 if (gc_sections) {
1098 try argv.append("--gc-sections");
1099 }
1100
1101 if (self.base.options.print_gc_sections) {
1102 try argv.append("--print-gc-sections");
1103 }
1104
1105 if (self.base.options.eh_frame_hdr) {
1106 try argv.append("--eh-frame-hdr");
1107 }
1108
1109 if (self.base.options.rdynamic) {
1110 try argv.append("--export-dynamic");
1111 }
1112
1113 if (self.base.options.strip) {
1114 try argv.append("-s");
1115 }
1116
1117 if (self.base.options.z_notext) {
1118 try argv.append("-z");
1119 try argv.append("notext");
1120 }
1121
1122 if (self.base.options.z_nocopyreloc) {
1123 try argv.append("-z");
1124 try argv.append("nocopyreloc");
1125 }
1126
1127 if (self.base.options.z_now) {
1128 try argv.append("-z");
1129 try argv.append("now");
1130 }
1131
1132 if (self.isStatic()) {
1133 try argv.append("-static");
1134 } else if (self.isDynLib()) {
1135 try argv.append("-shared");
1136 }
1137
1138 if (self.base.options.pie and self.isExe()) {
1139 try argv.append("-pie");
1140 }
1141
1142 // csu prelude
1143 if (csu.crt0) |v| try argv.append(v);
1144 if (csu.crti) |v| try argv.append(v);
1145 if (csu.crtbegin) |v| try argv.append(v);
1146
1147 for (self.base.options.lib_dirs) |lib_dir| {
1148 try argv.append("-L");
1149 try argv.append(lib_dir);
1150 }
1151
1152 if (self.base.options.link_libc) {
1153 if (self.base.options.libc_installation) |libc_installation| {
1154 try argv.append("-L");
1155 try argv.append(libc_installation.crt_dir.?);
1156 }
1157 }
1158
1159 var whole_archive = false;
1160 for (self.base.options.objects) |obj| {
1161 if (obj.must_link and !whole_archive) {
1162 try argv.append("-whole-archive");
1163 whole_archive = true;
1164 } else if (!obj.must_link and whole_archive) {
1165 try argv.append("-no-whole-archive");
1166 whole_archive = false;
1167 }
1168
1169 if (obj.loption) {
1170 assert(obj.path[0] == ':');
1171 try argv.append("-l");
1172 }
1173 try argv.append(obj.path);
1174 }
1175 if (whole_archive) {
1176 try argv.append("-no-whole-archive");
1177 whole_archive = false;
1178 }
1179
1180 for (comp.c_object_table.keys()) |key| {
1181 try argv.append(key.status.success.object_path);
1182 }
1183
1184 if (module_obj_path) |p| {
1185 try argv.append(p);
1186 }
1187
1188 // TSAN
1189 if (self.base.options.tsan) {
1190 try argv.append(comp.tsan_static_lib.?.full_object_path);
1191 }
1192
1193 // libc
1194 if (!self.base.options.skip_linker_dependencies and
1195 !self.base.options.link_libc)
1196 {
1197 if (comp.libc_static_lib) |lib| {
1198 try argv.append(lib.full_object_path);
1199 }
1200 }
1201
1202 // stack-protector.
1203 // Related: https://github.com/ziglang/zig/issues/7265
1204 if (comp.libssp_static_lib) |ssp| {
1205 try argv.append(ssp.full_object_path);
1206 }
1207
1208 // Shared libraries.
1209 // Worst-case, we need an --as-needed argument for every lib, as well
1210 // as one before and one after.
1211 try argv.ensureUnusedCapacity(self.base.options.system_libs.keys().len * 2 + 2);
1212 argv.appendAssumeCapacity("--as-needed");
1213 var as_needed = true;
1214
1215 for (self.base.options.system_libs.values()) |lib_info| {
1216 const lib_as_needed = !lib_info.needed;
1217 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
1218 0b00, 0b11 => {},
1219 0b01 => {
1220 argv.appendAssumeCapacity("--no-as-needed");
1221 as_needed = false;
1222 },
1223 0b10 => {
1224 argv.appendAssumeCapacity("--as-needed");
1225 as_needed = true;
1226 },
1227 }
1228 argv.appendAssumeCapacity(lib_info.path.?);
1229 }
1230
1231 if (!as_needed) {
1232 argv.appendAssumeCapacity("--as-needed");
1233 as_needed = true;
1234 }
1235
1236 // libc++ dep
1237 if (self.base.options.link_libcpp) {
1238 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
1239 try argv.append(comp.libcxx_static_lib.?.full_object_path);
1240 }
1241
1242 // libunwind dep
1243 if (self.base.options.link_libunwind) {
1244 try argv.append(comp.libunwind_static_lib.?.full_object_path);
1245 }
1246
1247 // libc dep
1248 if (self.base.options.link_libc) {
1249 if (self.base.options.libc_installation != null) {
1250 const needs_grouping = self.base.options.link_mode == .Static;
1251 if (needs_grouping) try argv.append("--start-group");
1252 try argv.appendSlice(target_util.libcFullLinkFlags(target));
1253 if (needs_grouping) try argv.append("--end-group");
1254 } else if (target.isGnuLibC()) {
1255 for (glibc.libs) |lib| {
1256 const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
1257 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1258 });
1259 try argv.append(lib_path);
1260 }
1261 try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a"));
1262 } else if (target.isMusl()) {
1263 try argv.append(try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) {
1264 .Static => "libc.a",
1265 .Dynamic => "libc.so",
1266 }));
1267 }
1268 }
1269
1270 // compiler-rt
1271 if (compiler_rt_path) |p| {
1272 try argv.append(p);
1273 }
1274
1275 // crt postlude
1276 if (csu.crtend) |v| try argv.append(v);
1277 if (csu.crtn) |v| try argv.append(v);
1278
1279 Compilation.dump_argv(argv.items);
1280 }
1281
1282 // Here we will parse input positional and library files (if referenced).
1283 // This will roughly match in any linker backend we support.
1284 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
1285
1286 // csu prelude
1287 if (csu.crt0) |v| try positionals.append(.{ .path = v });
1288 if (csu.crti) |v| try positionals.append(.{ .path = v });
1289 if (csu.crtbegin) |v| try positionals.append(.{ .path = v });
1290
1291 try positionals.ensureUnusedCapacity(self.base.options.objects.len);
1292 positionals.appendSliceAssumeCapacity(self.base.options.objects);
1293
1294 // This is a set of object files emitted by clang in a single `build-exe` invocation.
1295 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
1296 // in this set.
1297 for (comp.c_object_table.keys()) |key| {
1298 try positionals.append(.{ .path = key.status.success.object_path });
1299 }
1300
1301 if (module_obj_path) |path| try positionals.append(.{ .path = path });
1302
1303 // rpaths
1304 var rpath_table = std.StringArrayHashMap(void).init(self.base.allocator);
1305 defer rpath_table.deinit();
1306 for (self.base.options.rpath_list) |rpath| {
1307 _ = try rpath_table.put(rpath, {});
1308 }
1309
1310 if (self.base.options.each_lib_rpath) {
1311 var test_path = std.ArrayList(u8).init(self.base.allocator);
1312 defer test_path.deinit();
1313 for (self.base.options.lib_dirs) |lib_dir_path| {
1314 for (self.base.options.system_libs.keys()) |link_lib| {
1315 test_path.clearRetainingCapacity();
1316 const sep = fs.path.sep_str;
1317 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.so", .{
1318 lib_dir_path, link_lib,
1319 });
1320 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
1321 error.FileNotFound => continue,
1322 else => |e| return e,
1323 };
1324 _ = try rpath_table.put(lib_dir_path, {});
1325 }
1326 }
1327 for (self.base.options.objects) |obj| {
1328 if (Compilation.classifyFileExt(obj.path) == .shared_library) {
1329 const lib_dir_path = std.fs.path.dirname(obj.path) orelse continue;
1330 if (obj.loption) continue;
1331 _ = try rpath_table.put(lib_dir_path, {});
1332 }
1333 }
1334 }
1335
1336 // TSAN
1337 if (self.base.options.tsan) {
1338 try positionals.append(.{ .path = comp.tsan_static_lib.?.full_object_path });
1339 }
1340
1341 // libc
1342 if (!self.base.options.skip_linker_dependencies and
1343 !self.base.options.link_libc)
1344 {
1345 if (comp.libc_static_lib) |lib| {
1346 try positionals.append(.{ .path = lib.full_object_path });
1347 }
1348 }
1349
1350 // stack-protector.
1351 // Related: https://github.com/ziglang/zig/issues/7265
1352 if (comp.libssp_static_lib) |ssp| {
1353 try positionals.append(.{ .path = ssp.full_object_path });
1354 }
1355
1356 for (positionals.items) |obj| {
1357 const in_file = try std.fs.cwd().openFile(obj.path, .{});
1358 defer in_file.close();
1359 var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
1360 self.parsePositional(in_file, obj.path, obj.must_link, &parse_ctx) catch |err|
1361 try self.handleAndReportParseError(obj.path, err, &parse_ctx);
1362 }
1363
1364 var system_libs = std.ArrayList(SystemLib).init(arena);
1365
1366 try system_libs.ensureUnusedCapacity(self.base.options.system_libs.values().len);
1367 for (self.base.options.system_libs.values()) |lib_info| {
1368 system_libs.appendAssumeCapacity(.{ .needed = lib_info.needed, .path = lib_info.path.? });
1369 }
1370
1371 // libc++ dep
1372 if (self.base.options.link_libcpp) {
1373 try system_libs.ensureUnusedCapacity(2);
1374 system_libs.appendAssumeCapacity(.{ .path = comp.libcxxabi_static_lib.?.full_object_path });
1375 system_libs.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path });
1376 }
1377
1378 // libunwind dep
1379 if (self.base.options.link_libunwind) {
1380 try system_libs.append(.{ .path = comp.libunwind_static_lib.?.full_object_path });
1381 }
1382
1383 // libc dep
1384 self.error_flags.missing_libc = false;
1385 if (self.base.options.link_libc) {
1386 if (self.base.options.libc_installation != null) {
1387 @panic("TODO explicit libc_installation");
1388 } else if (target.isGnuLibC()) {
1389 try system_libs.ensureUnusedCapacity(glibc.libs.len + 1);
1390 for (glibc.libs) |lib| {
1391 const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
1392 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1393 });
1394 system_libs.appendAssumeCapacity(.{ .path = lib_path });
1395 }
1396 system_libs.appendAssumeCapacity(.{
1397 .path = try comp.get_libc_crt_file(arena, "libc_nonshared.a"),
1398 });
1399 } else if (target.isMusl()) {
1400 const path = try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) {
1401 .Static => "libc.a",
1402 .Dynamic => "libc.so",
1403 });
1404 try system_libs.append(.{ .path = path });
1240 } else {1405 } else {
1241 self.error_flags.missing_libc = true;1406 self.error_flags.missing_libc = true;
1242 }1407 }
...@@ -1256,11 +1421,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1256,11 +1421,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1256 // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs1421 // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs
1257 // to be after the shared libraries, so they are picked up from the shared1422 // to be after the shared libraries, so they are picked up from the shared
1258 // libraries, not libcompiler_rt.1423 // libraries, not libcompiler_rt.
1259 const compiler_rt_path: ?[]const u8 = blk: {
1260 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
1261 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
1262 break :blk null;
1263 };
1264 if (compiler_rt_path) |path| try positionals.append(.{ .path = path });1424 if (compiler_rt_path) |path| try positionals.append(.{ .path = path });
12651425
1266 // csu postlude1426 // csu postlude
...@@ -1301,127 +1461,80 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1301,127 +1461,80 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1301 if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed;1461 if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed;
1302 }1462 }
13031463
1304 const target_endian = self.base.options.target.cpu.arch.endian();
1305 const foreign_endian = target_endian != builtin.cpu.arch.endian();
1306
1307 if (self.dwarf) |*dw| {1464 if (self.dwarf) |*dw| {
1308 try dw.flushModule(self.base.options.module.?);1465 try dw.flushModule(self.base.options.module.?);
1309 }1466 }
13101467
1311 // If we haven't already, create a linker-generated input file comprising of1468 // Dedup shared objects
1312 // linker-defined synthetic symbols only such as `_DYNAMIC`, etc.1469 {
1470 var seen_dsos = std.StringHashMap(void).init(gpa);
1471 defer seen_dsos.deinit();
1472 try seen_dsos.ensureTotalCapacity(@as(u32, @intCast(self.shared_objects.items.len)));
1473
1474 var i: usize = 0;
1475 while (i < self.shared_objects.items.len) {
1476 const index = self.shared_objects.items[i];
1477 const shared_object = self.file(index).?.shared_object;
1478 const soname = shared_object.soname();
1479 const gop = seen_dsos.getOrPutAssumeCapacity(soname);
1480 if (gop.found_existing) {
1481 _ = self.shared_objects.orderedRemove(i);
1482 } else i += 1;
1483 }
1484 }
1485
1486 // If we haven't already, create a linker-generated input file comprising of
1487 // linker-defined synthetic symbols only such as `_DYNAMIC`, etc.
1313 if (self.linker_defined_index == null) {1488 if (self.linker_defined_index == null) {
1314 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));1489 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
1315 self.files.set(index, .{ .linker_defined = .{ .index = index } });1490 self.files.set(index, .{ .linker_defined = .{ .index = index } });
1316 self.linker_defined_index = index;1491 self.linker_defined_index = index;
1317 }1492 }
1318 try self.addLinkerDefinedSymbols();
13191493
1320 // Now, we are ready to resolve the symbols across all input files.1494 // Now, we are ready to resolve the symbols across all input files.
1321 // We will first resolve the files in the ZigModule, next in the parsed1495 // We will first resolve the files in the ZigModule, next in the parsed
1322 // input Object files.1496 // input Object files.
1323 // Any qualifing unresolved symbol will be upgraded to an absolute, weak1497 // Any qualifing unresolved symbol will be upgraded to an absolute, weak
1324 // symbol for potential resolution at load-time.1498 // symbol for potential resolution at load-time.
1325 try self.resolveSymbols();1499 self.resolveSymbols();
1500 self.markEhFrameAtomsDead();
1501 try self.convertCommonSymbols();
1326 self.markImportsExports();1502 self.markImportsExports();
1327 self.claimUnresolved();
1328
1329 // Scan and create missing synthetic entries such as GOT indirection.
1330 try self.scanRelocs();
1331
1332 // Allocate atoms parsed from input object files, followed by allocating
1333 // linker-defined synthetic symbols.
1334 try self.allocateObjects();
1335 self.allocateLinkerDefinedSymbols();
1336
1337 // .bss always overlaps .data in file offset, but is zero-sized in file so it doesn't
1338 // get mapped by the loader
1339 if (self.data_section_index) |data_shndx| blk: {
1340 const bss_shndx = self.bss_section_index orelse break :blk;
1341 const data_phndx = self.phdr_to_shdr_table.get(data_shndx).?;
1342 const bss_phndx = self.phdr_to_shdr_table.get(bss_shndx).?;
1343 self.shdrs.items[bss_shndx].sh_offset = self.shdrs.items[data_shndx].sh_offset;
1344 self.phdrs.items[bss_phndx].p_offset = self.phdrs.items[data_phndx].p_offset;
1345 }
1346
1347 // Same treatment for .tbss section.
1348 if (self.tdata_section_index) |tdata_shndx| blk: {
1349 const tbss_shndx = self.tbss_section_index orelse break :blk;
1350 const tdata_phndx = self.phdr_to_shdr_table.get(tdata_shndx).?;
1351 const tbss_phndx = self.phdr_to_shdr_table.get(tbss_shndx).?;
1352 self.shdrs.items[tbss_shndx].sh_offset = self.shdrs.items[tdata_shndx].sh_offset;
1353 self.phdrs.items[tbss_phndx].p_offset = self.phdrs.items[tdata_phndx].p_offset;
1354 }
1355
1356 if (self.phdr_tls_index) |tls_index| {
1357 const tdata_phdr = &self.phdrs.items[self.phdr_load_tls_data_index.?];
1358 const tbss_phdr = &self.phdrs.items[self.phdr_load_tls_zerofill_index.?];
1359 const phdr = &self.phdrs.items[tls_index];
1360 phdr.p_offset = tdata_phdr.p_offset;
1361 phdr.p_filesz = tdata_phdr.p_filesz;
1362 phdr.p_vaddr = tdata_phdr.p_vaddr;
1363 phdr.p_paddr = tdata_phdr.p_vaddr;
1364 phdr.p_memsz = tbss_phdr.p_vaddr + tbss_phdr.p_memsz - tdata_phdr.p_vaddr;
1365 }
1366
1367 // Beyond this point, everything has been allocated a virtual address and we can resolve
1368 // the relocations, and commit objects to file.
1369 if (self.zig_module_index) |index| {
1370 const zig_module = self.file(index).?.zig_module;
1371 for (zig_module.atoms.keys()) |atom_index| {
1372 const atom_ptr = self.atom(atom_index).?;
1373 if (!atom_ptr.flags.alive) continue;
1374 const shdr = &self.shdrs.items[atom_ptr.outputShndx().?];
1375 if (shdr.sh_type == elf.SHT_NOBITS) continue;
1376 const code = try zig_module.codeAlloc(self, atom_index);
1377 defer gpa.free(code);
1378 const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;
1379 try atom_ptr.resolveRelocs(self, code);
1380 try self.base.file.?.pwriteAll(code, file_offset);
1381 }
1382 }
1383 try self.writeObjects();
1384
1385 if (self.got_dirty) {
1386 const shdr = &self.shdrs.items[self.got_section_index.?];
1387 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got.size(self));
1388 defer buffer.deinit();
1389 try self.got.writeAllEntries(self, buffer.writer());
1390 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
1391 self.got_dirty = false;
1392 }
13931503
1394 // Look for entry address in objects if not set by the incremental compiler.1504 // Look for entry address in objects if not set by the incremental compiler.
1395 if (self.entry_addr == null) {1505 if (self.entry_index == null) {
1396 const entry: ?[]const u8 = entry: {1506 const entry: ?[]const u8 = entry: {
1397 if (self.base.options.entry) |entry| break :entry entry;1507 if (self.base.options.entry) |entry| break :entry entry;
1398 if (!self.isDynLib()) break :entry "_start";1508 if (!self.isDynLib()) break :entry "_start";
1399 break :entry null;1509 break :entry null;
1400 };1510 };
1401 self.entry_addr = if (entry) |name| entry_addr: {1511 self.entry_index = if (entry) |name| self.globalByName(name) else null;
1402 const global_index = self.globalByName(name) orelse break :entry_addr null;1512 }
1403 break :entry_addr self.symbol(global_index).value;1513
1404 } else null;1514 if (gc_sections) {
1515 try gc.gcAtoms(self);
1516
1517 if (self.base.options.print_gc_sections) {
1518 try gc.dumpPrunedAtoms(self);
1519 }
1405 }1520 }
14061521
1407 // Generate and emit the symbol table.1522 try self.addLinkerDefinedSymbols();
1408 try self.updateSymtabSize();1523 self.claimUnresolved();
1409 try self.writeSymtab();1524
1525 // Scan and create missing synthetic entries such as GOT indirection.
1526 try self.scanRelocs();
14101527
1528 // TODO I need to re-think how to handle ZigModule's debug sections AND debug sections
1529 // extracted from input object files correctly.
1411 if (self.dwarf) |*dw| {1530 if (self.dwarf) |*dw| {
1412 if (self.debug_abbrev_section_dirty) {1531 if (self.debug_abbrev_section_dirty) {
1413 try dw.writeDbgAbbrev();1532 try dw.writeDbgAbbrev();
1414 if (!self.shdr_table_dirty) {
1415 // Then it won't get written with the others and we need to do it.
1416 try self.writeShdr(self.debug_abbrev_section_index.?);
1417 }
1418 self.debug_abbrev_section_dirty = false;1533 self.debug_abbrev_section_dirty = false;
1419 }1534 }
14201535
1421 if (self.debug_info_header_dirty) {1536 if (self.debug_info_header_dirty) {
1422 // Currently only one compilation unit is supported, so the address range is simply1537 const text_phdr = &self.phdrs.items[self.phdr_zig_load_re_index.?];
1423 // identical to the main program header virtual address and memory size.
1424 const text_phdr = &self.phdrs.items[self.phdr_load_re_index.?];
1425 const low_pc = text_phdr.p_vaddr;1538 const low_pc = text_phdr.p_vaddr;
1426 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;1539 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
1427 try dw.writeDbgInfoHeader(self.base.options.module.?, low_pc, high_pc);1540 try dw.writeDbgInfoHeader(self.base.options.module.?, low_pc, high_pc);
...@@ -1429,14 +1542,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1429,14 +1542,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1429 }1542 }
14301543
1431 if (self.debug_aranges_section_dirty) {1544 if (self.debug_aranges_section_dirty) {
1432 // Currently only one compilation unit is supported, so the address range is simply1545 const text_phdr = &self.phdrs.items[self.phdr_zig_load_re_index.?];
1433 // identical to the main program header virtual address and memory size.
1434 const text_phdr = &self.phdrs.items[self.phdr_load_re_index.?];
1435 try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz);1546 try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz);
1436 if (!self.shdr_table_dirty) {
1437 // Then it won't get written with the others and we need to do it.
1438 try self.writeShdr(self.debug_aranges_section_index.?);
1439 }
1440 self.debug_aranges_section_dirty = false;1547 self.debug_aranges_section_dirty = false;
1441 }1548 }
14421549
...@@ -1444,160 +1551,83 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1444,160 +1551,83 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1444 try dw.writeDbgLineHeader();1551 try dw.writeDbgLineHeader();
1445 self.debug_line_header_dirty = false;1552 self.debug_line_header_dirty = false;
1446 }1553 }
1447 }
1448
1449 if (self.phdr_table_dirty) {
1450 const phsize: u64 = switch (self.ptr_width) {
1451 .p32 => @sizeOf(elf.Elf32_Phdr),
1452 .p64 => @sizeOf(elf.Elf64_Phdr),
1453 };
1454
1455 const phdr_table_index = self.phdr_table_index.?;
1456 const phdr_table = &self.phdrs.items[phdr_table_index];
1457 const phdr_table_load = &self.phdrs.items[self.phdr_table_load_index.?];
1458
1459 const allocated_size = self.allocatedSize(phdr_table.p_offset);
1460 const needed_size = self.phdrs.items.len * phsize;
1461
1462 if (needed_size > allocated_size) {
1463 phdr_table.p_offset = 0; // free the space
1464 phdr_table.p_offset = self.findFreeSpace(needed_size, @as(u32, @intCast(phdr_table.p_align)));
1465 }
1466
1467 phdr_table_load.p_offset = mem.alignBackward(u64, phdr_table.p_offset, phdr_table_load.p_align);
1468 const load_align_offset = phdr_table.p_offset - phdr_table_load.p_offset;
1469 phdr_table_load.p_filesz = load_align_offset + needed_size;
1470 phdr_table_load.p_memsz = load_align_offset + needed_size;
1471
1472 phdr_table.p_filesz = needed_size;
1473 phdr_table.p_vaddr = phdr_table_load.p_vaddr + load_align_offset;
1474 phdr_table.p_paddr = phdr_table_load.p_paddr + load_align_offset;
1475 phdr_table.p_memsz = needed_size;
1476
1477 switch (self.ptr_width) {
1478 .p32 => {
1479 const buf = try gpa.alloc(elf.Elf32_Phdr, self.phdrs.items.len);
1480 defer gpa.free(buf);
14811554
1482 for (buf, 0..) |*phdr, i| {1555 if (self.debug_str_section_index) |shndx| {
1483 phdr.* = phdrTo32(self.phdrs.items[i]);1556 if (self.debug_strtab_dirty or dw.strtab.buffer.items.len != self.shdrs.items[shndx].sh_size) {
1484 if (foreign_endian) {1557 try self.growNonAllocSection(shndx, dw.strtab.buffer.items.len, 1, false);
1485 mem.byteSwapAllFields(elf.Elf32_Phdr, phdr);1558 const shdr = self.shdrs.items[shndx];
1486 }1559 try self.base.file.?.pwriteAll(dw.strtab.buffer.items, shdr.sh_offset);
1487 }1560 self.debug_strtab_dirty = false;
1488 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);1561 }
1489 },
1490 .p64 => {
1491 const buf = try gpa.alloc(elf.Elf64_Phdr, self.phdrs.items.len);
1492 defer gpa.free(buf);
1493
1494 for (buf, 0..) |*phdr, i| {
1495 phdr.* = self.phdrs.items[i];
1496 if (foreign_endian) {
1497 mem.byteSwapAllFields(elf.Elf64_Phdr, phdr);
1498 }
1499 }
1500 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
1501 },
1502 }1562 }
15031563
1504 // We don't actually care if the phdr load section overlaps, only the phdr section matters.1564 self.saveDebugSectionsSizes();
1505 phdr_table_load.p_offset = 0;
1506 phdr_table_load.p_filesz = 0;
1507
1508 self.phdr_table_dirty = false;
1509 }1565 }
15101566
1511 {1567 // Generate and emit non-incremental sections.
1512 const shdr_index = self.shstrtab_section_index.?;1568 try self.initSections();
1513 if (self.shstrtab_dirty or self.shstrtab.buffer.items.len != self.shdrs.items[shdr_index].sh_size) {1569 try self.initSpecialPhdrs();
1514 try self.growNonAllocSection(shdr_index, self.shstrtab.buffer.items.len, 1, false);1570 try self.sortShdrs();
1515 const shstrtab_sect = &self.shdrs.items[shdr_index];1571 for (self.objects.items) |index| {
1516 try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shstrtab_sect.sh_offset);1572 try self.file(index).?.object.addAtomsToOutputSections(self);
1517 self.shstrtab_dirty = false;1573 }
1518 }1574 try self.sortInitFini();
1519 }1575 try self.setDynamicSection(rpath_table.keys());
1576 self.sortDynamicSymtab();
1577 try self.setHashSections();
1578 try self.setVersionSymtab();
1579 try self.updateSectionSizes();
1580
1581 self.allocatePhdrTable();
1582 try self.allocateAllocSections();
1583 try self.sortPhdrs();
1584 try self.allocateNonAllocSections();
1585 self.allocateSpecialPhdrs();
1586 self.allocateAtoms();
1587 self.allocateLinkerDefinedSymbols();
15201588
1521 {1589 // Dump the state for easy debugging.
1522 const shdr_index = self.strtab_section_index.?;1590 // State can be dumped via `--debug-log link_state`.
1523 if (self.strtab_dirty or self.strtab.buffer.items.len != self.shdrs.items[shdr_index].sh_size) {1591 if (build_options.enable_logging) {
1524 try self.growNonAllocSection(shdr_index, self.strtab.buffer.items.len, 1, false);1592 state_log.debug("{}", .{self.dumpState()});
1525 const strtab_sect = self.shdrs.items[shdr_index];
1526 try self.base.file.?.pwriteAll(self.strtab.buffer.items, strtab_sect.sh_offset);
1527 self.strtab_dirty = false;
1528 }
1529 }1593 }
15301594
1531 if (self.dwarf) |dwarf| {1595 // Beyond this point, everything has been allocated a virtual address and we can resolve
1532 const shdr_index = self.debug_str_section_index.?;1596 // the relocations, and commit objects to file.
1533 if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.shdrs.items[shdr_index].sh_size) {1597 if (self.zig_module_index) |index| {
1534 try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false);1598 const zig_module = self.file(index).?.zig_module;
1535 const debug_strtab_sect = self.shdrs.items[shdr_index];1599 for (zig_module.atoms.items) |atom_index| {
1536 try self.base.file.?.pwriteAll(dwarf.strtab.buffer.items, debug_strtab_sect.sh_offset);1600 const atom_ptr = self.atom(atom_index) orelse continue;
1537 self.debug_strtab_dirty = false;1601 if (!atom_ptr.flags.alive) continue;
1602 const out_shndx = atom_ptr.outputShndx() orelse continue;
1603 const shdr = &self.shdrs.items[out_shndx];
1604 if (shdr.sh_type == elf.SHT_NOBITS) continue;
1605 const code = try zig_module.codeAlloc(self, atom_index);
1606 defer gpa.free(code);
1607 const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;
1608 atom_ptr.resolveRelocsAlloc(self, code) catch |err| switch (err) {
1609 // TODO
1610 error.RelaxFail, error.InvalidInstruction, error.CannotEncode => {
1611 log.err("relaxing intructions failed; TODO this should be a fatal linker error", .{});
1612 },
1613 else => |e| return e,
1614 };
1615 try self.base.file.?.pwriteAll(code, file_offset);
1538 }1616 }
1539 }1617 }
15401618
1541 if (self.shdr_table_dirty) {1619 try self.writePhdrTable();
1542 const shsize: u64 = switch (self.ptr_width) {1620 try self.writeShdrTable();
1543 .p32 => @sizeOf(elf.Elf32_Shdr),1621 try self.writeAtoms();
1544 .p64 => @sizeOf(elf.Elf64_Shdr),1622 try self.writeSyntheticSections();
1545 };
1546 const shalign: u16 = switch (self.ptr_width) {
1547 .p32 => @alignOf(elf.Elf32_Shdr),
1548 .p64 => @alignOf(elf.Elf64_Shdr),
1549 };
1550 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
1551 const needed_size = self.shdrs.items.len * shsize;
1552
1553 if (needed_size > allocated_size) {
1554 self.shdr_table_offset = null; // free the space
1555 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
1556 }
1557
1558 switch (self.ptr_width) {
1559 .p32 => {
1560 const buf = try gpa.alloc(elf.Elf32_Shdr, self.shdrs.items.len);
1561 defer gpa.free(buf);
15621623
1563 for (buf, 0..) |*shdr, i| {1624 if (self.entry_index == null and self.base.options.effectiveOutputMode() == .Exe) {
1564 shdr.* = shdrTo32(self.shdrs.items[i]);
1565 log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* });
1566 if (foreign_endian) {
1567 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);
1568 }
1569 }
1570 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
1571 },
1572 .p64 => {
1573 const buf = try gpa.alloc(elf.Elf64_Shdr, self.shdrs.items.len);
1574 defer gpa.free(buf);
1575
1576 for (buf, 0..) |*shdr, i| {
1577 shdr.* = self.shdrs.items[i];
1578 log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* });
1579 if (foreign_endian) {
1580 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);
1581 }
1582 }
1583 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
1584 },
1585 }
1586 self.shdr_table_dirty = false;
1587 }
1588 if (self.entry_addr == null and self.base.options.effectiveOutputMode() == .Exe) {
1589 log.debug("flushing. no_entry_point_found = true", .{});1625 log.debug("flushing. no_entry_point_found = true", .{});
1590 self.error_flags.no_entry_point_found = true;1626 self.error_flags.no_entry_point_found = true;
1591 } else {1627 } else {
1592 log.debug("flushing. no_entry_point_found = false", .{});1628 log.debug("flushing. no_entry_point_found = false", .{});
1593 self.error_flags.no_entry_point_found = false;1629 self.error_flags.no_entry_point_found = false;
1594 try self.writeElfHeader();1630 try self.writeHeader();
1595 }
1596
1597 // Dump the state for easy debugging.
1598 // State can be dumped via `--debug-log link_state`.
1599 if (build_options.enable_logging) {
1600 state_log.debug("{}", .{self.dumpState()});
1601 }1631 }
16021632
1603 // The point of flush() is to commit changes, so in theory, nothing should1633 // The point of flush() is to commit changes, so in theory, nothing should
...@@ -1606,12 +1636,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1606,12 +1636,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1606 // such as debug_line_header_dirty and debug_info_header_dirty.1636 // such as debug_line_header_dirty and debug_info_header_dirty.
1607 assert(!self.debug_abbrev_section_dirty);1637 assert(!self.debug_abbrev_section_dirty);
1608 assert(!self.debug_aranges_section_dirty);1638 assert(!self.debug_aranges_section_dirty);
1609 assert(!self.phdr_table_dirty);
1610 assert(!self.shdr_table_dirty);
1611 assert(!self.shstrtab_dirty);
1612 assert(!self.strtab_dirty);
1613 assert(!self.debug_strtab_dirty);1639 assert(!self.debug_strtab_dirty);
1614 assert(!self.got.dirty);
1615}1640}
16161641
1617const ParseError = error{1642const ParseError = error{
...@@ -1655,6 +1680,8 @@ fn parseLibrary(...@@ -1655,6 +1680,8 @@ fn parseLibrary(
16551680
1656 if (Archive.isArchive(in_file)) {1681 if (Archive.isArchive(in_file)) {
1657 try self.parseArchive(in_file, lib.path, must_link, ctx);1682 try self.parseArchive(in_file, lib.path, must_link, ctx);
1683 } else if (SharedObject.isSharedObject(in_file)) {
1684 try self.parseSharedObject(in_file, lib, ctx);
1658 } else return error.UnknownFileType;1685 } else return error.UnknownFileType;
1659}1686}
16601687
...@@ -1709,6 +1736,34 @@ fn parseArchive(...@@ -1709,6 +1736,34 @@ fn parseArchive(
1709 }1736 }
1710}1737}
17111738
1739fn parseSharedObject(
1740 self: *Elf,
1741 in_file: std.fs.File,
1742 lib: SystemLib,
1743 ctx: *ParseErrorCtx,
1744) ParseError!void {
1745 const tracy = trace(@src());
1746 defer tracy.end();
1747
1748 const gpa = self.base.allocator;
1749 const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
1750 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
1751 self.files.set(index, .{ .shared_object = .{
1752 .path = lib.path,
1753 .data = data,
1754 .index = index,
1755 .needed = lib.needed,
1756 .alive = lib.needed,
1757 } });
1758 try self.shared_objects.append(gpa, index);
1759
1760 const shared_object = self.file(index).?.shared_object;
1761 try shared_object.parse(self);
1762
1763 ctx.detected_cpu_arch = shared_object.header.?.e_machine.toTargetCpuArch().?;
1764 if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch;
1765}
1766
1712/// When resolving symbols, we approach the problem similarly to `mold`.1767/// When resolving symbols, we approach the problem similarly to `mold`.
1713/// 1. Resolve symbols across all objects (including those preemptively extracted archives).1768/// 1. Resolve symbols across all objects (including those preemptively extracted archives).
1714/// 2. Resolve symbols across all shared objects.1769/// 2. Resolve symbols across all shared objects.
...@@ -1716,11 +1771,12 @@ fn parseArchive(...@@ -1716,11 +1771,12 @@ fn parseArchive(
1716/// 4. Reset state of all resolved globals since we will redo this bit on the pruned set.1771/// 4. Reset state of all resolved globals since we will redo this bit on the pruned set.
1717/// 5. Remove references to dead objects/shared objects1772/// 5. Remove references to dead objects/shared objects
1718/// 6. Re-run symbol resolution on pruned objects and shared objects sets.1773/// 6. Re-run symbol resolution on pruned objects and shared objects sets.
1719fn resolveSymbols(self: *Elf) error{Overflow}!void {1774fn resolveSymbols(self: *Elf) void {
1720 // Resolve symbols in the ZigModule. For now, we assume that it's always live.1775 // Resolve symbols in the ZigModule. For now, we assume that it's always live.
1721 if (self.zig_module_index) |index| self.file(index).?.resolveSymbols(self);1776 if (self.zig_module_index) |index| self.file(index).?.resolveSymbols(self);
1722 // Resolve symbols on the set of all objects and shared objects (even if some are unneeded).1777 // Resolve symbols on the set of all objects and shared objects (even if some are unneeded).
1723 for (self.objects.items) |index| self.file(index).?.resolveSymbols(self);1778 for (self.objects.items) |index| self.file(index).?.resolveSymbols(self);
1779 for (self.shared_objects.items) |index| self.file(index).?.resolveSymbols(self);
17241780
1725 // Mark live objects.1781 // Mark live objects.
1726 self.markLive();1782 self.markLive();
...@@ -1728,6 +1784,7 @@ fn resolveSymbols(self: *Elf) error{Overflow}!void {...@@ -1728,6 +1784,7 @@ fn resolveSymbols(self: *Elf) error{Overflow}!void {
1728 // Reset state of all globals after marking live objects.1784 // Reset state of all globals after marking live objects.
1729 if (self.zig_module_index) |index| self.file(index).?.resetGlobals(self);1785 if (self.zig_module_index) |index| self.file(index).?.resetGlobals(self);
1730 for (self.objects.items) |index| self.file(index).?.resetGlobals(self);1786 for (self.objects.items) |index| self.file(index).?.resetGlobals(self);
1787 for (self.shared_objects.items) |index| self.file(index).?.resetGlobals(self);
17311788
1732 // Prune dead objects and shared objects.1789 // Prune dead objects and shared objects.
1733 var i: usize = 0;1790 var i: usize = 0;
...@@ -1737,6 +1794,13 @@ fn resolveSymbols(self: *Elf) error{Overflow}!void {...@@ -1737,6 +1794,13 @@ fn resolveSymbols(self: *Elf) error{Overflow}!void {
1737 _ = self.objects.orderedRemove(i);1794 _ = self.objects.orderedRemove(i);
1738 } else i += 1;1795 } else i += 1;
1739 }1796 }
1797 i = 0;
1798 while (i < self.shared_objects.items.len) {
1799 const index = self.shared_objects.items[i];
1800 if (!self.file(index).?.isAlive()) {
1801 _ = self.shared_objects.orderedRemove(i);
1802 } else i += 1;
1803 }
17401804
1741 // Dedup comdat groups.1805 // Dedup comdat groups.
1742 for (self.objects.items) |index| {1806 for (self.objects.items) |index| {
...@@ -1758,11 +1822,11 @@ fn resolveSymbols(self: *Elf) error{Overflow}!void {...@@ -1758,11 +1822,11 @@ fn resolveSymbols(self: *Elf) error{Overflow}!void {
1758 const cg = self.comdatGroup(cg_index);1822 const cg = self.comdatGroup(cg_index);
1759 const cg_owner = self.comdatGroupOwner(cg.owner);1823 const cg_owner = self.comdatGroupOwner(cg.owner);
1760 if (cg_owner.file != index) {1824 if (cg_owner.file != index) {
1761 for (try object.comdatGroupMembers(cg.shndx)) |shndx| {1825 for (object.comdatGroupMembers(cg.shndx)) |shndx| {
1762 const atom_index = object.atoms.items[shndx];1826 const atom_index = object.atoms.items[shndx];
1763 if (self.atom(atom_index)) |atom_ptr| {1827 if (self.atom(atom_index)) |atom_ptr| {
1764 atom_ptr.flags.alive = false;1828 atom_ptr.flags.alive = false;
1765 // atom_ptr.markFdesDead(self);1829 atom_ptr.markFdesDead(self);
1766 }1830 }
1767 }1831 }
1768 }1832 }
...@@ -1772,6 +1836,7 @@ fn resolveSymbols(self: *Elf) error{Overflow}!void {...@@ -1772,6 +1836,7 @@ fn resolveSymbols(self: *Elf) error{Overflow}!void {
1772 // Re-resolve the symbols.1836 // Re-resolve the symbols.
1773 if (self.zig_module_index) |index| self.file(index).?.resolveSymbols(self);1837 if (self.zig_module_index) |index| self.file(index).?.resolveSymbols(self);
1774 for (self.objects.items) |index| self.file(index).?.resolveSymbols(self);1838 for (self.objects.items) |index| self.file(index).?.resolveSymbols(self);
1839 for (self.shared_objects.items) |index| self.file(index).?.resolveSymbols(self);
1775}1840}
17761841
1777/// Traverses all objects and shared objects marking any object referenced by1842/// Traverses all objects and shared objects marking any object referenced by
...@@ -1784,6 +1849,24 @@ fn markLive(self: *Elf) void {...@@ -1784,6 +1849,24 @@ fn markLive(self: *Elf) void {
1784 const file_ptr = self.file(index).?;1849 const file_ptr = self.file(index).?;
1785 if (file_ptr.isAlive()) file_ptr.markLive(self);1850 if (file_ptr.isAlive()) file_ptr.markLive(self);
1786 }1851 }
1852 for (self.shared_objects.items) |index| {
1853 const file_ptr = self.file(index).?;
1854 if (file_ptr.isAlive()) file_ptr.markLive(self);
1855 }
1856}
1857
1858fn markEhFrameAtomsDead(self: *Elf) void {
1859 for (self.objects.items) |index| {
1860 const file_ptr = self.file(index).?;
1861 if (!file_ptr.isAlive()) continue;
1862 file_ptr.object.markEhFrameAtomsDead(self);
1863 }
1864}
1865
1866fn convertCommonSymbols(self: *Elf) !void {
1867 for (self.objects.items) |index| {
1868 try self.file(index).?.object.convertCommonSymbols(self);
1869 }
1787}1870}
17881871
1789fn markImportsExports(self: *Elf) void {1872fn markImportsExports(self: *Elf) void {
...@@ -1795,10 +1878,10 @@ fn markImportsExports(self: *Elf) void {...@@ -1795,10 +1878,10 @@ fn markImportsExports(self: *Elf) void {
1795 const file_ptr = global.file(elf_file) orelse continue;1878 const file_ptr = global.file(elf_file) orelse continue;
1796 const vis = @as(elf.STV, @enumFromInt(global.elfSym(elf_file).st_other));1879 const vis = @as(elf.STV, @enumFromInt(global.elfSym(elf_file).st_other));
1797 if (vis == .HIDDEN) continue;1880 if (vis == .HIDDEN) continue;
1798 // if (file == .shared and !global.isAbs(self)) {1881 if (file_ptr == .shared_object and !global.isAbs(elf_file)) {
1799 // global.flags.import = true;1882 global.flags.import = true;
1800 // continue;1883 continue;
1801 // }1884 }
1802 if (file_ptr.index() == file_index) {1885 if (file_ptr.index() == file_index) {
1803 global.flags.@"export" = true;1886 global.flags.@"export" = true;
1804 if (elf_file.isDynLib() and vis != .PROTECTED) {1887 if (elf_file.isDynLib() and vis != .PROTECTED) {
...@@ -1809,6 +1892,17 @@ fn markImportsExports(self: *Elf) void {...@@ -1809,6 +1892,17 @@ fn markImportsExports(self: *Elf) void {
1809 }1892 }
1810 }.mark;1893 }.mark;
18111894
1895 if (!self.isDynLib()) {
1896 for (self.shared_objects.items) |index| {
1897 for (self.file(index).?.globals()) |global_index| {
1898 const global = self.symbol(global_index);
1899 const file_ptr = global.file(self) orelse continue;
1900 const vis = @as(elf.STV, @enumFromInt(global.elfSym(self).st_other));
1901 if (file_ptr != .shared_object and vis != .HIDDEN) global.flags.@"export" = true;
1902 }
1903 }
1904 }
1905
1812 if (self.zig_module_index) |index| {1906 if (self.zig_module_index) |index| {
1813 mark(self, index);1907 mark(self, index);
1814 }1908 }
...@@ -1856,65 +1950,51 @@ fn scanRelocs(self: *Elf) !void {...@@ -1856,65 +1950,51 @@ fn scanRelocs(self: *Elf) !void {
18561950
1857 try self.reportUndefined(&undefs);1951 try self.reportUndefined(&undefs);
18581952
1859 for (self.symbols.items, 0..) |*sym, sym_index| {1953 for (self.symbols.items, 0..) |*sym, i| {
1954 const index = @as(u32, @intCast(i));
1955 if (!sym.isLocal() and !sym.flags.has_dynamic) {
1956 log.debug("'{s}' is non-local", .{sym.name(self)});
1957 try self.dynsym.addSymbol(index, self);
1958 }
1860 if (sym.flags.needs_got) {1959 if (sym.flags.needs_got) {
1861 log.debug("'{s}' needs GOT", .{sym.name(self)});1960 log.debug("'{s}' needs GOT", .{sym.name(self)});
1862 // TODO how can we tell we need to write it again, aka the entry is dirty?1961 _ = try self.got.addGotSymbol(index, self);
1863 const gop = try sym.getOrCreateGotEntry(@intCast(sym_index), self);
1864 try self.got.writeEntry(self, gop.index);
1865 }1962 }
1866 }1963 if (sym.flags.needs_plt) {
1867}1964 if (sym.flags.is_canonical) {
18681965 log.debug("'{s}' needs CPLT", .{sym.name(self)});
1869fn allocateObjects(self: *Elf) !void {1966 sym.flags.@"export" = true;
1870 for (self.objects.items) |index| {1967 try self.plt.addSymbol(index, self);
1871 const object = self.file(index).?.object;1968 } else if (sym.flags.needs_got) {
18721969 log.debug("'{s}' needs PLTGOT", .{sym.name(self)});
1873 for (object.atoms.items) |atom_index| {1970 try self.plt_got.addSymbol(index, self);
1874 const atom_ptr = self.atom(atom_index) orelse continue;1971 } else {
1875 if (!atom_ptr.flags.alive or atom_ptr.flags.allocated) continue;1972 log.debug("'{s}' needs PLT", .{sym.name(self)});
1876 try atom_ptr.allocate(self);1973 try self.plt.addSymbol(index, self);
1974 }
1877 }1975 }
18781976 if (sym.flags.needs_copy_rel and !sym.flags.has_copy_rel) {
1879 for (object.locals()) |local_index| {1977 log.debug("'{s}' needs COPYREL", .{sym.name(self)});
1880 const local = self.symbol(local_index);1978 try self.copy_rel.addSymbol(index, self);
1881 const atom_ptr = local.atom(self) orelse continue;
1882 if (!atom_ptr.flags.alive) continue;
1883 local.value = local.elfSym(self).st_value + atom_ptr.value;
1884 }1979 }
18851980 if (sym.flags.needs_tlsgd) {
1886 for (object.globals()) |global_index| {1981 log.debug("'{s}' needs TLSGD", .{sym.name(self)});
1887 const global = self.symbol(global_index);1982 try self.got.addTlsGdSymbol(index, self);
1888 const atom_ptr = global.atom(self) orelse continue;1983 }
1889 if (!atom_ptr.flags.alive) continue;1984 if (sym.flags.needs_gottp) {
1890 if (global.file_index == index) {1985 log.debug("'{s}' needs GOTTP", .{sym.name(self)});
1891 global.value = global.elfSym(self).st_value + atom_ptr.value;1986 try self.got.addGotTpSymbol(index, self);
1892 }1987 }
1988 if (sym.flags.needs_tlsdesc) {
1989 log.debug("'{s}' needs TLSDESC", .{sym.name(self)});
1990 try self.dynsym.addSymbol(index, self);
1991 try self.got.addTlsDescSymbol(index, self);
1893 }1992 }
1894 }1993 }
1895}
1896
1897fn writeObjects(self: *Elf) !void {
1898 const gpa = self.base.allocator;
1899
1900 for (self.objects.items) |index| {
1901 const object = self.file(index).?.object;
1902 for (object.atoms.items) |atom_index| {
1903 const atom_ptr = self.atom(atom_index) orelse continue;
1904 if (!atom_ptr.flags.alive) continue;
1905
1906 const shdr = &self.shdrs.items[atom_ptr.outputShndx().?];
1907 if (shdr.sh_type == elf.SHT_NOBITS) continue;
1908 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; // TODO we don't yet know how to handle non-alloc sections
1909
1910 const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;
1911 log.debug("writing atom({d}) at 0x{x}", .{ atom_ptr.atom_index, file_offset });
1912 const code = try object.codeDecompressAlloc(self, atom_ptr.atom_index);
1913 defer gpa.free(code);
19141994
1915 try atom_ptr.resolveRelocs(self, code);1995 if (self.got.flags.needs_tlsld) {
1916 try self.base.file.?.pwriteAll(code, file_offset);1996 log.debug("program needs TLSLD", .{});
1917 }1997 try self.got.addTlsLdSymbol(self);
1918 }1998 }
1919}1999}
19202000
...@@ -2618,7 +2698,100 @@ fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64)...@@ -2618,7 +2698,100 @@ fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64)
2618 }2698 }
2619}2699}
26202700
2621fn writeElfHeader(self: *Elf) !void {2701fn writeShdrTable(self: *Elf) !void {
2702 const gpa = self.base.allocator;
2703 const target_endian = self.base.options.target.cpu.arch.endian();
2704 const foreign_endian = target_endian != builtin.cpu.arch.endian();
2705 const shsize: u64 = switch (self.ptr_width) {
2706 .p32 => @sizeOf(elf.Elf32_Shdr),
2707 .p64 => @sizeOf(elf.Elf64_Shdr),
2708 };
2709 const shalign: u16 = switch (self.ptr_width) {
2710 .p32 => @alignOf(elf.Elf32_Shdr),
2711 .p64 => @alignOf(elf.Elf64_Shdr),
2712 };
2713
2714 const shoff = self.shdr_table_offset orelse 0;
2715 const needed_size = self.shdrs.items.len * shsize;
2716
2717 if (needed_size > self.allocatedSize(shoff)) {
2718 self.shdr_table_offset = null;
2719 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
2720 }
2721
2722 log.debug("writing section headers from 0x{x} to 0x{x}", .{
2723 self.shdr_table_offset.?,
2724 self.shdr_table_offset.? + needed_size,
2725 });
2726
2727 switch (self.ptr_width) {
2728 .p32 => {
2729 const buf = try gpa.alloc(elf.Elf32_Shdr, self.shdrs.items.len);
2730 defer gpa.free(buf);
2731
2732 for (buf, 0..) |*shdr, i| {
2733 shdr.* = shdrTo32(self.shdrs.items[i]);
2734 if (foreign_endian) {
2735 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);
2736 }
2737 }
2738 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
2739 },
2740 .p64 => {
2741 const buf = try gpa.alloc(elf.Elf64_Shdr, self.shdrs.items.len);
2742 defer gpa.free(buf);
2743
2744 for (buf, 0..) |*shdr, i| {
2745 shdr.* = self.shdrs.items[i];
2746 if (foreign_endian) {
2747 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);
2748 }
2749 }
2750 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
2751 },
2752 }
2753}
2754
2755fn writePhdrTable(self: *Elf) !void {
2756 const gpa = self.base.allocator;
2757 const target_endian = self.base.options.target.cpu.arch.endian();
2758 const foreign_endian = target_endian != builtin.cpu.arch.endian();
2759 const phdr_table = &self.phdrs.items[self.phdr_table_index.?];
2760
2761 log.debug("writing program headers from 0x{x} to 0x{x}", .{
2762 phdr_table.p_offset,
2763 phdr_table.p_offset + phdr_table.p_filesz,
2764 });
2765
2766 switch (self.ptr_width) {
2767 .p32 => {
2768 const buf = try gpa.alloc(elf.Elf32_Phdr, self.phdrs.items.len);
2769 defer gpa.free(buf);
2770
2771 for (buf, 0..) |*phdr, i| {
2772 phdr.* = phdrTo32(self.phdrs.items[i]);
2773 if (foreign_endian) {
2774 mem.byteSwapAllFields(elf.Elf32_Phdr, phdr);
2775 }
2776 }
2777 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
2778 },
2779 .p64 => {
2780 const buf = try gpa.alloc(elf.Elf64_Phdr, self.phdrs.items.len);
2781 defer gpa.free(buf);
2782
2783 for (buf, 0..) |*phdr, i| {
2784 phdr.* = self.phdrs.items[i];
2785 if (foreign_endian) {
2786 mem.byteSwapAllFields(elf.Elf64_Phdr, phdr);
2787 }
2788 }
2789 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
2790 },
2791 }
2792}
2793
2794fn writeHeader(self: *Elf) !void {
2622 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;2795 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
26232796
2624 var index: usize = 0;2797 var index: usize = 0;
...@@ -2649,12 +2822,12 @@ fn writeElfHeader(self: *Elf) !void {...@@ -2649,12 +2822,12 @@ fn writeElfHeader(self: *Elf) !void {
26492822
2650 assert(index == 16);2823 assert(index == 16);
26512824
2652 const elf_type = switch (self.base.options.effectiveOutputMode()) {2825 const elf_type: elf.ET = switch (self.base.options.effectiveOutputMode()) {
2653 .Exe => elf.ET.EXEC,2826 .Exe => if (self.base.options.pic) .DYN else .EXEC,
2654 .Obj => elf.ET.REL,2827 .Obj => .REL,
2655 .Lib => switch (self.base.options.link_mode) {2828 .Lib => switch (self.base.options.link_mode) {
2656 .Static => elf.ET.REL,2829 .Static => @as(elf.ET, .REL),
2657 .Dynamic => elf.ET.DYN,2830 .Dynamic => .DYN,
2658 },2831 },
2659 };2832 };
2660 mem.writeInt(u16, hdr_buf[index..][0..2], @intFromEnum(elf_type), endian);2833 mem.writeInt(u16, hdr_buf[index..][0..2], @intFromEnum(elf_type), endian);
...@@ -2668,8 +2841,7 @@ fn writeElfHeader(self: *Elf) !void {...@@ -2668,8 +2841,7 @@ fn writeElfHeader(self: *Elf) !void {
2668 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);2841 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
2669 index += 4;2842 index += 4;
26702843
2671 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;2844 const e_entry = if (self.entry_index) |entry_index| self.symbol(entry_index).value else 0;
2672
2673 const phdr_table_offset = self.phdrs.items[self.phdr_table_index.?].p_offset;2845 const phdr_table_offset = self.phdrs.items[self.phdr_table_index.?].p_offset;
2674 switch (self.ptr_width) {2846 switch (self.ptr_width) {
2675 .p32 => {2847 .p32 => {
...@@ -2826,24 +2998,24 @@ fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index, code: []const u8)...@@ -2826,24 +2998,24 @@ fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index, code: []const u8)
2826 const decl = mod.declPtr(decl_index);2998 const decl = mod.declPtr(decl_index);
2827 const shdr_index = switch (decl.ty.zigTypeTag(mod)) {2999 const shdr_index = switch (decl.ty.zigTypeTag(mod)) {
2828 // TODO: what if this is a function pointer?3000 // TODO: what if this is a function pointer?
2829 .Fn => self.text_section_index.?,3001 .Fn => self.zig_text_section_index.?,
2830 else => blk: {3002 else => blk: {
2831 if (decl.getOwnedVariable(mod)) |variable| {3003 if (decl.getOwnedVariable(mod)) |variable| {
2832 if (variable.is_const) break :blk self.rodata_section_index.?;3004 if (variable.is_const) break :blk self.zig_rodata_section_index.?;
2833 if (variable.init.toValue().isUndefDeep(mod)) {3005 if (variable.init.toValue().isUndefDeep(mod)) {
2834 const mode = self.base.options.optimize_mode;3006 const mode = self.base.options.optimize_mode;
2835 if (mode == .Debug or mode == .ReleaseSafe) break :blk self.data_section_index.?;3007 if (mode == .Debug or mode == .ReleaseSafe) break :blk self.zig_data_section_index.?;
2836 break :blk self.bss_section_index.?;3008 break :blk self.zig_bss_section_index.?;
2837 }3009 }
2838 // TODO I blatantly copied the logic from the Wasm linker, but is there a less3010 // TODO I blatantly copied the logic from the Wasm linker, but is there a less
2839 // intrusive check for all zeroes than this?3011 // intrusive check for all zeroes than this?
2840 const is_all_zeroes = for (code) |byte| {3012 const is_all_zeroes = for (code) |byte| {
2841 if (byte != 0) break false;3013 if (byte != 0) break false;
2842 } else true;3014 } else true;
2843 if (is_all_zeroes) break :blk self.bss_section_index.?;3015 if (is_all_zeroes) break :blk self.zig_bss_section_index.?;
2844 break :blk self.data_section_index.?;3016 break :blk self.zig_data_section_index.?;
2845 }3017 }
2846 break :blk self.rodata_section_index.?;3018 break :blk self.zig_rodata_section_index.?;
2847 },3019 },
2848 };3020 };
2849 return shdr_index;3021 return shdr_index;
...@@ -2897,8 +3069,9 @@ fn updateDeclCode(...@@ -2897,8 +3069,9 @@ fn updateDeclCode(
2897 esym.st_value = atom_ptr.value;3069 esym.st_value = atom_ptr.value;
28983070
2899 log.debug(" (writing new offset table entry)", .{});3071 log.debug(" (writing new offset table entry)", .{});
3072 assert(sym.flags.has_zig_got);
2900 const extra = sym.extra(self).?;3073 const extra = sym.extra(self).?;
2901 try self.got.writeEntry(self, extra.got);3074 try self.zig_got.writeOne(self, extra.zig_got);
2902 }3075 }
2903 } else if (code.len < old_size) {3076 } else if (code.len < old_size) {
2904 atom_ptr.shrink(self);3077 atom_ptr.shrink(self);
...@@ -2910,9 +3083,8 @@ fn updateDeclCode(...@@ -2910,9 +3083,8 @@ fn updateDeclCode(
2910 sym.value = atom_ptr.value;3083 sym.value = atom_ptr.value;
2911 esym.st_value = atom_ptr.value;3084 esym.st_value = atom_ptr.value;
29123085
2913 sym.flags.needs_got = true;3086 const gop = try sym.getOrCreateZigGotEntry(sym_index, self);
2914 const gop = try sym.getOrCreateGotEntry(sym_index, self);3087 try self.zig_got.writeOne(self, gop.index);
2915 try self.got.writeEntry(self, gop.index);
2916 }3088 }
29173089
2918 if (self.base.child_pid) |pid| {3090 if (self.base.child_pid) |pid| {
...@@ -3016,12 +3188,16 @@ pub fn updateDecl(...@@ -3016,12 +3188,16 @@ pub fn updateDecl(
3016 const decl = mod.declPtr(decl_index);3188 const decl = mod.declPtr(decl_index);
30173189
3018 if (decl.val.getExternFunc(mod)) |_| {3190 if (decl.val.getExternFunc(mod)) |_| {
3019 return; // TODO Should we do more when front-end analyzed extern decl?3191 return;
3020 }3192 }
3021 if (decl.val.getVariable(mod)) |variable| {3193
3022 if (variable.is_extern) {3194 if (decl.isExtern(mod)) {
3023 return; // TODO Should we do more when front-end analyzed extern decl?3195 // Extern variable gets a .got entry only.
3024 }3196 const variable = decl.getOwnedVariable(mod).?;
3197 const name = mod.intern_pool.stringToSlice(decl.name);
3198 const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name);
3199 _ = try self.getGlobalSymbol(name, lib_name);
3200 return;
3025 }3201 }
30263202
3027 const sym_index = try self.getOrCreateMetadataForDecl(decl_index);3203 const sym_index = try self.getOrCreateMetadataForDecl(decl_index);
...@@ -3122,8 +3298,8 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol....@@ -3122,8 +3298,8 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.
3122 };3298 };
31233299
3124 const output_section_index = switch (sym.kind) {3300 const output_section_index = switch (sym.kind) {
3125 .code => self.text_section_index.?,3301 .code => self.zig_text_section_index.?,
3126 .const_data => self.rodata_section_index.?,3302 .const_data => self.zig_rodata_section_index.?,
3127 };3303 };
3128 const local_sym = self.symbol(symbol_index);3304 const local_sym = self.symbol(symbol_index);
3129 const phdr_index = self.phdr_to_shdr_table.get(output_section_index).?;3305 const phdr_index = self.phdr_to_shdr_table.get(output_section_index).?;
...@@ -3146,9 +3322,8 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol....@@ -3146,9 +3322,8 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.
3146 local_sym.value = atom_ptr.value;3322 local_sym.value = atom_ptr.value;
3147 local_esym.st_value = atom_ptr.value;3323 local_esym.st_value = atom_ptr.value;
31483324
3149 local_sym.flags.needs_got = true;3325 const gop = try local_sym.getOrCreateZigGotEntry(symbol_index, self);
3150 const gop = try local_sym.getOrCreateGotEntry(symbol_index, self);3326 try self.zig_got.writeOne(self, gop.index);
3151 try self.got.writeEntry(self, gop.index);
31523327
3153 const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr;3328 const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr;
3154 const file_offset = self.shdrs.items[output_section_index].sh_offset + section_offset;3329 const file_offset = self.shdrs.items[output_section_index].sh_offset + section_offset;
...@@ -3168,7 +3343,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module...@@ -3168,7 +3343,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
3168 const index = unnamed_consts.items.len;3343 const index = unnamed_consts.items.len;
3169 const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index });3344 const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index });
3170 defer gpa.free(name);3345 defer gpa.free(name);
3171 const sym_index = switch (try self.lowerConst(name, typed_value, self.rodata_section_index.?, decl.srcLoc(mod))) {3346 const sym_index = switch (try self.lowerConst(name, typed_value, self.zig_rodata_section_index.?, decl.srcLoc(mod))) {
3172 .ok => |sym_index| sym_index,3347 .ok => |sym_index| sym_index,
3173 .fail => |em| {3348 .fail => |em| {
3174 decl.analysis = .codegen_failure;3349 decl.analysis = .codegen_failure;
...@@ -3265,8 +3440,7 @@ pub fn updateDeclExports(...@@ -3265,8 +3440,7 @@ pub fn updateDeclExports(
3265 const zig_module = self.file(self.zig_module_index.?).?.zig_module;3440 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
3266 const decl = mod.declPtr(decl_index);3441 const decl = mod.declPtr(decl_index);
3267 const decl_sym_index = try self.getOrCreateMetadataForDecl(decl_index);3442 const decl_sym_index = try self.getOrCreateMetadataForDecl(decl_index);
3268 const decl_sym = self.symbol(decl_sym_index);3443 const decl_esym = zig_module.local_esyms.items[self.symbol(decl_sym_index).esym_index];
3269 const decl_esym = zig_module.local_esyms.items[decl_sym.esym_index];
3270 const decl_metadata = self.decls.getPtr(decl_index).?;3444 const decl_metadata = self.decls.getPtr(decl_index).?;
32713445
3272 for (exports) |exp| {3446 for (exports) |exp| {
...@@ -3283,13 +3457,7 @@ pub fn updateDeclExports(...@@ -3283,13 +3457,7 @@ pub fn updateDeclExports(
3283 }3457 }
3284 const stb_bits: u8 = switch (exp.opts.linkage) {3458 const stb_bits: u8 = switch (exp.opts.linkage) {
3285 .Internal => elf.STB_LOCAL,3459 .Internal => elf.STB_LOCAL,
3286 .Strong => blk: {3460 .Strong => elf.STB_GLOBAL,
3287 const entry_name = self.base.options.entry orelse "_start";
3288 if (mem.eql(u8, exp_name, entry_name)) {
3289 self.entry_addr = decl_sym.value;
3290 }
3291 break :blk elf.STB_GLOBAL;
3292 },
3293 .Weak => elf.STB_WEAK,3461 .Weak => elf.STB_WEAK,
3294 .LinkOnce => {3462 .LinkOnce => {
3295 try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);3463 try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);
...@@ -3315,8 +3483,8 @@ pub fn updateDeclExports(...@@ -3315,8 +3483,8 @@ pub fn updateDeclExports(
3315 break :blk sym_index;3483 break :blk sym_index;
3316 };3484 };
3317 const esym = &zig_module.global_esyms.items[sym_index & 0x0fffffff];3485 const esym = &zig_module.global_esyms.items[sym_index & 0x0fffffff];
3318 esym.st_value = decl_sym.value;3486 esym.st_value = self.symbol(decl_sym_index).value;
3319 esym.st_shndx = decl_sym.atom_index;3487 esym.st_shndx = decl_esym.st_shndx;
3320 esym.st_info = (stb_bits << 4) | stt_bits;3488 esym.st_info = (stb_bits << 4) | stt_bits;
3321 esym.st_name = name_off;3489 esym.st_name = name_off;
3322 }3490 }
...@@ -3388,23 +3556,23 @@ fn addLinkerDefinedSymbols(self: *Elf) !void {...@@ -3388,23 +3556,23 @@ fn addLinkerDefinedSymbols(self: *Elf) !void {
3388 self.rela_iplt_start_index = try linker_defined.addGlobal("__rela_iplt_start", self);3556 self.rela_iplt_start_index = try linker_defined.addGlobal("__rela_iplt_start", self);
3389 self.rela_iplt_end_index = try linker_defined.addGlobal("__rela_iplt_end", self);3557 self.rela_iplt_end_index = try linker_defined.addGlobal("__rela_iplt_end", self);
33903558
3391 // for (self.objects.items) |index| {3559 for (self.objects.items) |index| {
3392 // const object = self.getFile(index).?.object;3560 const object = self.file(index).?.object;
3393 // for (object.atoms.items) |atom_index| {3561 for (object.atoms.items) |atom_index| {
3394 // if (self.getStartStopBasename(atom_index)) |name| {3562 if (self.getStartStopBasename(atom_index)) |name| {
3395 // const gpa = self.base.allocator;3563 const gpa = self.base.allocator;
3396 // try self.start_stop_indexes.ensureUnusedCapacity(gpa, 2);3564 try self.start_stop_indexes.ensureUnusedCapacity(gpa, 2);
33973565
3398 // const start = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name});3566 const start = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name});
3399 // defer gpa.free(start);3567 defer gpa.free(start);
3400 // const stop = try std.fmt.allocPrintZ(gpa, "__stop_{s}", .{name});3568 const stop = try std.fmt.allocPrintZ(gpa, "__stop_{s}", .{name});
3401 // defer gpa.free(stop);3569 defer gpa.free(stop);
34023570
3403 // self.start_stop_indexes.appendAssumeCapacity(try internal.addSyntheticGlobal(start, self));3571 self.start_stop_indexes.appendAssumeCapacity(try linker_defined.addGlobal(start, self));
3404 // self.start_stop_indexes.appendAssumeCapacity(try internal.addSyntheticGlobal(stop, self));3572 self.start_stop_indexes.appendAssumeCapacity(try linker_defined.addGlobal(stop, self));
3405 // }3573 }
3406 // }3574 }
3407 // }3575 }
34083576
3409 linker_defined.resolveSymbols(self);3577 linker_defined.resolveSymbols(self);
3410}3578}
...@@ -3474,67 +3642,1224 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void {...@@ -3474,67 +3642,1224 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void {
3474 symbol_ptr.output_section_index = shndx;3642 symbol_ptr.output_section_index = shndx;
3475 }3643 }
34763644
3477 // __dso_handle3645 // __dso_handle
3478 if (self.dso_handle_index) |index| {3646 if (self.dso_handle_index) |index| {
3479 const shdr = &self.shdrs.items[1];3647 const shdr = &self.shdrs.items[1];
3480 const symbol_ptr = self.symbol(index);3648 const symbol_ptr = self.symbol(index);
3481 symbol_ptr.value = shdr.sh_addr;3649 symbol_ptr.value = shdr.sh_addr;
3482 symbol_ptr.output_section_index = 0;3650 symbol_ptr.output_section_index = 0;
3651 }
3652
3653 // __GNU_EH_FRAME_HDR
3654 if (self.eh_frame_hdr_section_index) |shndx| {
3655 const shdr = &self.shdrs.items[shndx];
3656 const symbol_ptr = self.symbol(self.gnu_eh_frame_hdr_index.?);
3657 symbol_ptr.value = shdr.sh_addr;
3658 symbol_ptr.output_section_index = shndx;
3659 }
3660
3661 // __rela_iplt_start, __rela_iplt_end
3662 if (self.rela_dyn_section_index) |shndx| blk: {
3663 if (self.base.options.link_mode != .Static or self.base.options.pie) break :blk;
3664 const shdr = &self.shdrs.items[shndx];
3665 const end_addr = shdr.sh_addr + shdr.sh_size;
3666 const start_addr = end_addr - self.calcNumIRelativeRelocs() * @sizeOf(elf.Elf64_Rela);
3667 const start_sym = self.symbol(self.rela_iplt_start_index.?);
3668 const end_sym = self.symbol(self.rela_iplt_end_index.?);
3669 start_sym.value = start_addr;
3670 start_sym.output_section_index = shndx;
3671 end_sym.value = end_addr;
3672 end_sym.output_section_index = shndx;
3673 }
3674
3675 // _end
3676 {
3677 const end_symbol = self.symbol(self.end_index.?);
3678 for (self.shdrs.items, 0..) |shdr, shndx| {
3679 if (shdr.sh_flags & elf.SHF_ALLOC != 0) {
3680 end_symbol.value = shdr.sh_addr + shdr.sh_size;
3681 end_symbol.output_section_index = @intCast(shndx);
3682 }
3683 }
3684 }
3685
3686 // __start_*, __stop_*
3687 {
3688 var index: usize = 0;
3689 while (index < self.start_stop_indexes.items.len) : (index += 2) {
3690 const start = self.symbol(self.start_stop_indexes.items[index]);
3691 const name = start.name(self);
3692 const stop = self.symbol(self.start_stop_indexes.items[index + 1]);
3693 const shndx = self.sectionByName(name["__start_".len..]).?;
3694 const shdr = &self.shdrs.items[shndx];
3695 start.value = shdr.sh_addr;
3696 start.output_section_index = shndx;
3697 stop.value = shdr.sh_addr + shdr.sh_size;
3698 stop.output_section_index = shndx;
3699 }
3700 }
3701}
3702
3703fn initSections(self: *Elf) !void {
3704 const small_ptr = switch (self.ptr_width) {
3705 .p32 => true,
3706 .p64 => false,
3707 };
3708 const ptr_size = self.ptrWidthBytes();
3709
3710 for (self.objects.items) |index| {
3711 try self.file(index).?.object.initOutputSections(self);
3712 }
3713
3714 const needs_eh_frame = for (self.objects.items) |index| {
3715 if (self.file(index).?.object.cies.items.len > 0) break true;
3716 } else false;
3717 if (needs_eh_frame) {
3718 self.eh_frame_section_index = try self.addSection(.{
3719 .name = ".eh_frame",
3720 .type = elf.SHT_PROGBITS,
3721 .flags = elf.SHF_ALLOC,
3722 .addralign = ptr_size,
3723 });
3724
3725 if (self.base.options.eh_frame_hdr) {
3726 self.eh_frame_hdr_section_index = try self.addSection(.{
3727 .name = ".eh_frame_hdr",
3728 .type = elf.SHT_PROGBITS,
3729 .flags = elf.SHF_ALLOC,
3730 .addralign = 4,
3731 });
3732 }
3733 }
3734
3735 if (self.got.entries.items.len > 0) {
3736 self.got_section_index = try self.addSection(.{
3737 .name = ".got",
3738 .type = elf.SHT_PROGBITS,
3739 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
3740 .addralign = ptr_size,
3741 });
3742 }
3743
3744 const needs_rela_dyn = blk: {
3745 if (self.got.flags.needs_rela or self.got.flags.needs_tlsld or
3746 self.zig_got.flags.needs_rela or self.copy_rel.symbols.items.len > 0) break :blk true;
3747 if (self.zig_module_index) |index| {
3748 if (self.file(index).?.zig_module.num_dynrelocs > 0) break :blk true;
3749 }
3750 for (self.objects.items) |index| {
3751 if (self.file(index).?.object.num_dynrelocs > 0) break :blk true;
3752 }
3753 break :blk false;
3754 };
3755 if (needs_rela_dyn) {
3756 self.rela_dyn_section_index = try self.addSection(.{
3757 .name = ".rela.dyn",
3758 .type = elf.SHT_RELA,
3759 .flags = elf.SHF_ALLOC,
3760 .addralign = @alignOf(elf.Elf64_Rela),
3761 .entsize = @sizeOf(elf.Elf64_Rela),
3762 });
3763 }
3764
3765 if (self.plt.symbols.items.len > 0) {
3766 self.plt_section_index = try self.addSection(.{
3767 .name = ".plt",
3768 .type = elf.SHT_PROGBITS,
3769 .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
3770 .addralign = 16,
3771 });
3772 self.got_plt_section_index = try self.addSection(.{
3773 .name = ".got.plt",
3774 .type = elf.SHT_PROGBITS,
3775 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
3776 .addralign = @alignOf(u64),
3777 });
3778 self.rela_plt_section_index = try self.addSection(.{
3779 .name = ".rela.plt",
3780 .type = elf.SHT_RELA,
3781 .flags = elf.SHF_ALLOC,
3782 .addralign = @alignOf(elf.Elf64_Rela),
3783 .entsize = @sizeOf(elf.Elf64_Rela),
3784 });
3785 }
3786
3787 if (self.plt_got.symbols.items.len > 0) {
3788 self.plt_got_section_index = try self.addSection(.{
3789 .name = ".plt.got",
3790 .type = elf.SHT_PROGBITS,
3791 .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
3792 .addralign = 16,
3793 });
3794 }
3795
3796 if (self.copy_rel.symbols.items.len > 0) {
3797 self.copy_rel_section_index = try self.addSection(.{
3798 .name = ".copyrel",
3799 .type = elf.SHT_NOBITS,
3800 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
3801 });
3802 }
3803
3804 const needs_interp = blk: {
3805 // On Ubuntu with musl-gcc, we get a weird combo of options looking like this:
3806 // -dynamic-linker=<path> -static
3807 // In this case, if we do generate .interp section and segment, we will get
3808 // a segfault in the dynamic linker trying to load a binary that is static
3809 // and doesn't contain .dynamic section.
3810 if (self.isStatic() and !self.base.options.pie) break :blk false;
3811 break :blk self.base.options.dynamic_linker != null;
3812 };
3813 if (needs_interp) {
3814 self.interp_section_index = try self.addSection(.{
3815 .name = ".interp",
3816 .type = elf.SHT_PROGBITS,
3817 .flags = elf.SHF_ALLOC,
3818 .addralign = 1,
3819 });
3820 }
3821
3822 if (self.isDynLib() or self.shared_objects.items.len > 0 or self.base.options.pie) {
3823 self.dynstrtab_section_index = try self.addSection(.{
3824 .name = ".dynstr",
3825 .flags = elf.SHF_ALLOC,
3826 .type = elf.SHT_STRTAB,
3827 .entsize = 1,
3828 .addralign = 1,
3829 });
3830 self.dynamic_section_index = try self.addSection(.{
3831 .name = ".dynamic",
3832 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
3833 .type = elf.SHT_DYNAMIC,
3834 .entsize = @sizeOf(elf.Elf64_Dyn),
3835 .addralign = @alignOf(elf.Elf64_Dyn),
3836 });
3837 self.dynsymtab_section_index = try self.addSection(.{
3838 .name = ".dynsym",
3839 .flags = elf.SHF_ALLOC,
3840 .type = elf.SHT_DYNSYM,
3841 .addralign = @alignOf(elf.Elf64_Sym),
3842 .entsize = @sizeOf(elf.Elf64_Sym),
3843 .info = 1,
3844 });
3845 self.hash_section_index = try self.addSection(.{
3846 .name = ".hash",
3847 .flags = elf.SHF_ALLOC,
3848 .type = elf.SHT_HASH,
3849 .addralign = 4,
3850 .entsize = 4,
3851 });
3852 self.gnu_hash_section_index = try self.addSection(.{
3853 .name = ".gnu.hash",
3854 .flags = elf.SHF_ALLOC,
3855 .type = elf.SHT_GNU_HASH,
3856 .addralign = 8,
3857 });
3858
3859 const needs_versions = for (self.dynsym.entries.items) |entry| {
3860 const sym = self.symbol(entry.symbol_index);
3861 if (sym.flags.import and sym.version_index & elf.VERSYM_VERSION > elf.VER_NDX_GLOBAL) break true;
3862 } else false;
3863 if (needs_versions) {
3864 self.versym_section_index = try self.addSection(.{
3865 .name = ".gnu.version",
3866 .flags = elf.SHF_ALLOC,
3867 .type = elf.SHT_GNU_VERSYM,
3868 .addralign = @alignOf(elf.Elf64_Versym),
3869 .entsize = @sizeOf(elf.Elf64_Versym),
3870 });
3871 self.verneed_section_index = try self.addSection(.{
3872 .name = ".gnu.version_r",
3873 .flags = elf.SHF_ALLOC,
3874 .type = elf.SHT_GNU_VERNEED,
3875 .addralign = @alignOf(elf.Elf64_Verneed),
3876 });
3877 }
3878 }
3879
3880 if (self.symtab_section_index == null) {
3881 self.symtab_section_index = try self.addSection(.{
3882 .name = ".symtab",
3883 .type = elf.SHT_SYMTAB,
3884 .addralign = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym),
3885 .entsize = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym),
3886 });
3887 }
3888 if (self.strtab_section_index == null) {
3889 self.strtab_section_index = try self.addSection(.{
3890 .name = ".strtab",
3891 .type = elf.SHT_STRTAB,
3892 .entsize = 1,
3893 .addralign = 1,
3894 });
3895 }
3896 if (self.shstrtab_section_index == null) {
3897 self.shstrtab_section_index = try self.addSection(.{
3898 .name = ".shstrtab",
3899 .type = elf.SHT_STRTAB,
3900 .entsize = 1,
3901 .addralign = 1,
3902 });
3903 }
3904}
3905
3906fn initSpecialPhdrs(self: *Elf) !void {
3907 if (self.interp_section_index != null) {
3908 self.phdr_interp_index = try self.addPhdr(.{
3909 .type = elf.PT_INTERP,
3910 .flags = elf.PF_R,
3911 .@"align" = 1,
3912 });
3913 }
3914 if (self.dynamic_section_index != null) {
3915 self.phdr_dynamic_index = try self.addPhdr(.{
3916 .type = elf.PT_DYNAMIC,
3917 .flags = elf.PF_R | elf.PF_W,
3918 });
3919 }
3920 if (self.eh_frame_hdr_section_index != null) {
3921 self.phdr_gnu_eh_frame_index = try self.addPhdr(.{
3922 .type = elf.PT_GNU_EH_FRAME,
3923 .flags = elf.PF_R,
3924 });
3925 }
3926 self.phdr_gnu_stack_index = try self.addPhdr(.{
3927 .type = elf.PT_GNU_STACK,
3928 .flags = elf.PF_W | elf.PF_R,
3929 .memsz = self.base.options.stack_size_override orelse 0,
3930 .@"align" = 1,
3931 });
3932
3933 const has_tls = for (self.shdrs.items) |shdr| {
3934 if (shdr.sh_flags & elf.SHF_TLS != 0) break true;
3935 } else false;
3936 if (has_tls) {
3937 self.phdr_tls_index = try self.addPhdr(.{
3938 .type = elf.PT_TLS,
3939 .flags = elf.PF_R,
3940 .@"align" = 1,
3941 });
3942 }
3943}
3944
3945/// We need to sort constructors/destuctors in the following sections:
3946/// * .init_array
3947/// * .fini_array
3948/// * .preinit_array
3949/// * .ctors
3950/// * .dtors
3951/// The prority of inclusion is defined as part of the input section's name. For example, .init_array.10000.
3952/// If no priority value has been specified,
3953/// * for .init_array, .fini_array and .preinit_array, we automatically assign that section max value of maxInt(i32)
3954/// and push it to the back of the queue,
3955/// * for .ctors and .dtors, we automatically assign that section min value of -1
3956/// and push it to the front of the queue,
3957/// crtbegin and ctrend are assigned minInt(i32) and maxInt(i32) respectively.
3958/// Ties are broken by the file prority which corresponds to the inclusion of input sections in this output section
3959/// we are about to sort.
3960fn sortInitFini(self: *Elf) !void {
3961 const gpa = self.base.allocator;
3962
3963 const Entry = struct {
3964 priority: i32,
3965 atom_index: Atom.Index,
3966
3967 pub fn lessThan(ctx: *Elf, lhs: @This(), rhs: @This()) bool {
3968 if (lhs.priority == rhs.priority) {
3969 return ctx.atom(lhs.atom_index).?.priority(ctx) < ctx.atom(rhs.atom_index).?.priority(ctx);
3970 }
3971 return lhs.priority < rhs.priority;
3972 }
3973 };
3974
3975 for (self.shdrs.items, 0..) |*shdr, shndx| {
3976 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
3977
3978 var is_init_fini = false;
3979 var is_ctor_dtor = false;
3980 switch (shdr.sh_type) {
3981 elf.SHT_PREINIT_ARRAY,
3982 elf.SHT_INIT_ARRAY,
3983 elf.SHT_FINI_ARRAY,
3984 => is_init_fini = true,
3985 else => {
3986 const name = self.shstrtab.getAssumeExists(shdr.sh_name);
3987 is_ctor_dtor = mem.indexOf(u8, name, ".ctors") != null or mem.indexOf(u8, name, ".dtors") != null;
3988 },
3989 }
3990
3991 if (!is_init_fini and !is_ctor_dtor) continue;
3992
3993 const atom_list = self.output_sections.getPtr(@intCast(shndx)) orelse continue;
3994
3995 var entries = std.ArrayList(Entry).init(gpa);
3996 try entries.ensureTotalCapacityPrecise(atom_list.items.len);
3997 defer entries.deinit();
3998
3999 for (atom_list.items) |atom_index| {
4000 const atom_ptr = self.atom(atom_index).?;
4001 const object = atom_ptr.file(self).?.object;
4002 const priority = blk: {
4003 if (is_ctor_dtor) {
4004 if (mem.indexOf(u8, object.path, "crtbegin") != null) break :blk std.math.minInt(i32);
4005 if (mem.indexOf(u8, object.path, "crtend") != null) break :blk std.math.maxInt(i32);
4006 }
4007 const default: i32 = if (is_ctor_dtor) -1 else std.math.maxInt(i32);
4008 const name = atom_ptr.name(self);
4009 var it = mem.splitBackwards(u8, name, ".");
4010 const priority = std.fmt.parseUnsigned(u16, it.first(), 10) catch default;
4011 break :blk priority;
4012 };
4013 entries.appendAssumeCapacity(.{ .priority = priority, .atom_index = atom_index });
4014 }
4015
4016 mem.sort(Entry, entries.items, self, Entry.lessThan);
4017
4018 atom_list.clearRetainingCapacity();
4019 for (entries.items) |entry| {
4020 atom_list.appendAssumeCapacity(entry.atom_index);
4021 }
4022 }
4023}
4024
4025fn setDynamicSection(self: *Elf, rpaths: []const []const u8) !void {
4026 if (self.dynamic_section_index == null) return;
4027
4028 for (self.shared_objects.items) |index| {
4029 const shared_object = self.file(index).?.shared_object;
4030 if (!shared_object.alive) continue;
4031 try self.dynamic.addNeeded(shared_object, self);
4032 }
4033
4034 if (self.base.options.soname) |soname| {
4035 try self.dynamic.setSoname(soname, self);
4036 }
4037
4038 try self.dynamic.setRpath(rpaths, self);
4039}
4040
4041fn sortDynamicSymtab(self: *Elf) void {
4042 if (self.gnu_hash_section_index == null) return;
4043 self.dynsym.sort(self);
4044}
4045
4046fn setVersionSymtab(self: *Elf) !void {
4047 if (self.versym_section_index == null) return;
4048 try self.versym.resize(self.base.allocator, self.dynsym.count());
4049 self.versym.items[0] = elf.VER_NDX_LOCAL;
4050 for (self.dynsym.entries.items, 1..) |entry, i| {
4051 const sym = self.symbol(entry.symbol_index);
4052 self.versym.items[i] = sym.version_index;
4053 }
4054
4055 if (self.verneed_section_index) |shndx| {
4056 try self.verneed.generate(self);
4057 const shdr = &self.shdrs.items[shndx];
4058 shdr.sh_info = @as(u32, @intCast(self.verneed.verneed.items.len));
4059 }
4060}
4061
4062fn setHashSections(self: *Elf) !void {
4063 if (self.hash_section_index != null) {
4064 try self.hash.generate(self);
4065 }
4066 if (self.gnu_hash_section_index != null) {
4067 try self.gnu_hash.calcSize(self);
4068 }
4069}
4070
4071fn phdrRank(phdr: elf.Elf64_Phdr) u8 {
4072 switch (phdr.p_type) {
4073 elf.PT_NULL => return 0,
4074 elf.PT_PHDR => return 1,
4075 elf.PT_INTERP => return 2,
4076 elf.PT_LOAD => return 3,
4077 elf.PT_DYNAMIC, elf.PT_TLS => return 4,
4078 elf.PT_GNU_EH_FRAME => return 5,
4079 elf.PT_GNU_STACK => return 6,
4080 else => return 7,
4081 }
4082}
4083
4084fn sortPhdrs(self: *Elf) error{OutOfMemory}!void {
4085 const Entry = struct {
4086 phndx: u16,
4087
4088 pub fn lessThan(elf_file: *Elf, lhs: @This(), rhs: @This()) bool {
4089 const lhs_phdr = elf_file.phdrs.items[lhs.phndx];
4090 const rhs_phdr = elf_file.phdrs.items[rhs.phndx];
4091 const lhs_rank = phdrRank(lhs_phdr);
4092 const rhs_rank = phdrRank(rhs_phdr);
4093 if (lhs_rank == rhs_rank) return lhs_phdr.p_vaddr < rhs_phdr.p_vaddr;
4094 return lhs_rank < rhs_rank;
4095 }
4096 };
4097
4098 const gpa = self.base.allocator;
4099 var entries = try std.ArrayList(Entry).initCapacity(gpa, self.phdrs.items.len);
4100 defer entries.deinit();
4101 for (0..self.phdrs.items.len) |phndx| {
4102 entries.appendAssumeCapacity(.{ .phndx = @as(u16, @intCast(phndx)) });
4103 }
4104
4105 mem.sort(Entry, entries.items, self, Entry.lessThan);
4106
4107 const backlinks = try gpa.alloc(u16, entries.items.len);
4108 defer gpa.free(backlinks);
4109 for (entries.items, 0..) |entry, i| {
4110 backlinks[entry.phndx] = @as(u16, @intCast(i));
4111 }
4112
4113 var slice = try self.phdrs.toOwnedSlice(gpa);
4114 defer gpa.free(slice);
4115
4116 try self.phdrs.ensureTotalCapacityPrecise(gpa, slice.len);
4117 for (entries.items) |sorted| {
4118 self.phdrs.appendAssumeCapacity(slice[sorted.phndx]);
4119 }
4120
4121 for (&[_]*?u16{
4122 &self.phdr_zig_load_re_index,
4123 &self.phdr_zig_got_index,
4124 &self.phdr_zig_load_ro_index,
4125 &self.phdr_zig_load_zerofill_index,
4126 &self.phdr_table_index,
4127 &self.phdr_table_load_index,
4128 &self.phdr_interp_index,
4129 &self.phdr_dynamic_index,
4130 &self.phdr_gnu_eh_frame_index,
4131 &self.phdr_tls_index,
4132 }) |maybe_index| {
4133 if (maybe_index.*) |*index| {
4134 index.* = backlinks[index.*];
4135 }
4136 }
4137
4138 {
4139 var it = self.phdr_to_shdr_table.iterator();
4140 while (it.next()) |entry| {
4141 entry.value_ptr.* = backlinks[entry.value_ptr.*];
4142 }
4143 }
4144}
4145
4146fn shdrRank(self: *Elf, shndx: u16) u8 {
4147 const shdr = self.shdrs.items[shndx];
4148 const name = self.shstrtab.getAssumeExists(shdr.sh_name);
4149 const flags = shdr.sh_flags;
4150
4151 switch (shdr.sh_type) {
4152 elf.SHT_NULL => return 0,
4153 elf.SHT_DYNSYM => return 2,
4154 elf.SHT_HASH => return 3,
4155 elf.SHT_GNU_HASH => return 3,
4156 elf.SHT_GNU_VERSYM => return 4,
4157 elf.SHT_GNU_VERDEF => return 4,
4158 elf.SHT_GNU_VERNEED => return 4,
4159
4160 elf.SHT_PREINIT_ARRAY,
4161 elf.SHT_INIT_ARRAY,
4162 elf.SHT_FINI_ARRAY,
4163 => return 0xf2,
4164
4165 elf.SHT_DYNAMIC => return 0xf3,
4166
4167 elf.SHT_RELA => return 0xf,
4168
4169 elf.SHT_PROGBITS => if (flags & elf.SHF_ALLOC != 0) {
4170 if (flags & elf.SHF_EXECINSTR != 0) {
4171 return 0xf1;
4172 } else if (flags & elf.SHF_WRITE != 0) {
4173 return if (flags & elf.SHF_TLS != 0) 0xf4 else 0xf6;
4174 } else if (mem.eql(u8, name, ".interp")) {
4175 return 1;
4176 } else {
4177 return 0xf0;
4178 }
4179 } else {
4180 if (mem.startsWith(u8, name, ".debug")) {
4181 return 0xf8;
4182 } else {
4183 return 0xf9;
4184 }
4185 },
4186
4187 elf.SHT_NOBITS => return if (flags & elf.SHF_TLS != 0) 0xf5 else 0xf7,
4188 elf.SHT_SYMTAB => return 0xfa,
4189 elf.SHT_STRTAB => return if (mem.eql(u8, name, ".dynstr")) 0x4 else 0xfb,
4190 else => return 0xff,
4191 }
4192}
4193
4194fn sortShdrs(self: *Elf) !void {
4195 const Entry = struct {
4196 shndx: u16,
4197
4198 pub fn lessThan(elf_file: *Elf, lhs: @This(), rhs: @This()) bool {
4199 return elf_file.shdrRank(lhs.shndx) < elf_file.shdrRank(rhs.shndx);
4200 }
4201 };
4202
4203 const gpa = self.base.allocator;
4204 var entries = try std.ArrayList(Entry).initCapacity(gpa, self.shdrs.items.len);
4205 defer entries.deinit();
4206 for (0..self.shdrs.items.len) |shndx| {
4207 entries.appendAssumeCapacity(.{ .shndx = @as(u16, @intCast(shndx)) });
4208 }
4209
4210 mem.sort(Entry, entries.items, self, Entry.lessThan);
4211
4212 const backlinks = try gpa.alloc(u16, entries.items.len);
4213 defer gpa.free(backlinks);
4214 for (entries.items, 0..) |entry, i| {
4215 backlinks[entry.shndx] = @as(u16, @intCast(i));
4216 }
4217
4218 var slice = try self.shdrs.toOwnedSlice(gpa);
4219 defer gpa.free(slice);
4220
4221 try self.shdrs.ensureTotalCapacityPrecise(gpa, slice.len);
4222 for (entries.items) |sorted| {
4223 self.shdrs.appendAssumeCapacity(slice[sorted.shndx]);
4224 }
4225
4226 for (&[_]*?u16{
4227 &self.eh_frame_section_index,
4228 &self.eh_frame_hdr_section_index,
4229 &self.got_section_index,
4230 &self.symtab_section_index,
4231 &self.strtab_section_index,
4232 &self.shstrtab_section_index,
4233 &self.interp_section_index,
4234 &self.dynamic_section_index,
4235 &self.dynsymtab_section_index,
4236 &self.dynstrtab_section_index,
4237 &self.hash_section_index,
4238 &self.gnu_hash_section_index,
4239 &self.plt_section_index,
4240 &self.got_plt_section_index,
4241 &self.plt_got_section_index,
4242 &self.rela_dyn_section_index,
4243 &self.rela_plt_section_index,
4244 &self.copy_rel_section_index,
4245 &self.versym_section_index,
4246 &self.verneed_section_index,
4247 &self.zig_text_section_index,
4248 &self.zig_got_section_index,
4249 &self.zig_rodata_section_index,
4250 &self.zig_data_section_index,
4251 &self.zig_bss_section_index,
4252 &self.debug_str_section_index,
4253 &self.debug_info_section_index,
4254 &self.debug_abbrev_section_index,
4255 &self.debug_aranges_section_index,
4256 &self.debug_line_section_index,
4257 }) |maybe_index| {
4258 if (maybe_index.*) |*index| {
4259 index.* = backlinks[index.*];
4260 }
4261 }
4262
4263 if (self.symtab_section_index) |index| {
4264 const shdr = &self.shdrs.items[index];
4265 shdr.sh_link = self.strtab_section_index.?;
4266 }
4267
4268 if (self.dynamic_section_index) |index| {
4269 const shdr = &self.shdrs.items[index];
4270 shdr.sh_link = self.dynstrtab_section_index.?;
4271 }
4272
4273 if (self.dynsymtab_section_index) |index| {
4274 const shdr = &self.shdrs.items[index];
4275 shdr.sh_link = self.dynstrtab_section_index.?;
4276 }
4277
4278 if (self.hash_section_index) |index| {
4279 const shdr = &self.shdrs.items[index];
4280 shdr.sh_link = self.dynsymtab_section_index.?;
4281 }
4282
4283 if (self.gnu_hash_section_index) |index| {
4284 const shdr = &self.shdrs.items[index];
4285 shdr.sh_link = self.dynsymtab_section_index.?;
4286 }
4287
4288 if (self.versym_section_index) |index| {
4289 const shdr = &self.shdrs.items[index];
4290 shdr.sh_link = self.dynsymtab_section_index.?;
4291 }
4292
4293 if (self.verneed_section_index) |index| {
4294 const shdr = &self.shdrs.items[index];
4295 shdr.sh_link = self.dynstrtab_section_index.?;
4296 }
4297
4298 if (self.rela_dyn_section_index) |index| {
4299 const shdr = &self.shdrs.items[index];
4300 shdr.sh_link = self.dynsymtab_section_index orelse 0;
4301 }
4302
4303 if (self.rela_plt_section_index) |index| {
4304 const shdr = &self.shdrs.items[index];
4305 shdr.sh_link = self.dynsymtab_section_index.?;
4306 shdr.sh_info = self.plt_section_index.?;
4307 }
4308
4309 {
4310 var phdr_to_shdr_table = try self.phdr_to_shdr_table.clone(gpa);
4311 defer phdr_to_shdr_table.deinit(gpa);
4312
4313 self.phdr_to_shdr_table.clearRetainingCapacity();
4314
4315 var it = phdr_to_shdr_table.iterator();
4316 while (it.next()) |entry| {
4317 const shndx = entry.key_ptr.*;
4318 const phndx = entry.value_ptr.*;
4319 self.phdr_to_shdr_table.putAssumeCapacityNoClobber(backlinks[shndx], phndx);
4320 }
4321 }
4322
4323 if (self.zig_module_index) |index| {
4324 const zig_module = self.file(index).?.zig_module;
4325 for (zig_module.atoms.items) |atom_index| {
4326 const atom_ptr = self.atom(atom_index) orelse continue;
4327 if (!atom_ptr.flags.alive) continue;
4328 const out_shndx = atom_ptr.outputShndx() orelse continue;
4329 atom_ptr.output_section_index = backlinks[out_shndx];
4330 }
4331
4332 for (zig_module.locals()) |local_index| {
4333 const local = self.symbol(local_index);
4334 const atom_ptr = local.atom(self) orelse continue;
4335 if (!atom_ptr.flags.alive) continue;
4336 const out_shndx = local.outputShndx() orelse continue;
4337 local.output_section_index = backlinks[out_shndx];
4338 }
4339
4340 for (zig_module.globals()) |global_index| {
4341 const global = self.symbol(global_index);
4342 const atom_ptr = global.atom(self) orelse continue;
4343 if (!atom_ptr.flags.alive) continue;
4344 if (global.file(self).?.index() != index) continue;
4345 const out_shndx = global.outputShndx() orelse continue;
4346 global.output_section_index = backlinks[out_shndx];
4347 }
4348 }
4349}
4350
4351fn saveDebugSectionsSizes(self: *Elf) void {
4352 if (self.debug_info_section_index) |shndx| {
4353 self.debug_info_section_zig_size = self.shdrs.items[shndx].sh_size;
4354 }
4355 if (self.debug_abbrev_section_index) |shndx| {
4356 self.debug_abbrev_section_zig_size = self.shdrs.items[shndx].sh_size;
4357 }
4358 if (self.debug_str_section_index) |shndx| {
4359 self.debug_str_section_zig_size = self.shdrs.items[shndx].sh_size;
4360 }
4361 if (self.debug_aranges_section_index) |shndx| {
4362 self.debug_aranges_section_zig_size = self.shdrs.items[shndx].sh_size;
4363 }
4364 if (self.debug_line_section_index) |shndx| {
4365 self.debug_line_section_zig_size = self.shdrs.items[shndx].sh_size;
4366 }
4367}
4368
4369fn updateSectionSizes(self: *Elf) !void {
4370 for (self.output_sections.keys(), self.output_sections.values()) |shndx, atom_list| {
4371 if (atom_list.items.len == 0) continue;
4372 const shdr = &self.shdrs.items[shndx];
4373 for (atom_list.items) |atom_index| {
4374 const atom_ptr = self.atom(atom_index) orelse continue;
4375 if (!atom_ptr.flags.alive) continue;
4376 const offset = atom_ptr.alignment.forward(shdr.sh_size);
4377 const padding = offset - shdr.sh_size;
4378 atom_ptr.value = offset;
4379 shdr.sh_size += padding + atom_ptr.size;
4380 shdr.sh_addralign = @max(shdr.sh_addralign, atom_ptr.alignment.toByteUnits(1));
4381 }
4382 }
4383
4384 if (self.eh_frame_section_index) |index| {
4385 self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self);
4386 }
4387
4388 if (self.eh_frame_hdr_section_index) |index| {
4389 self.shdrs.items[index].sh_size = eh_frame.calcEhFrameHdrSize(self);
4390 }
4391
4392 if (self.got_section_index) |index| {
4393 self.shdrs.items[index].sh_size = self.got.size(self);
4394 }
4395
4396 if (self.plt_section_index) |index| {
4397 self.shdrs.items[index].sh_size = self.plt.size();
4398 }
4399
4400 if (self.got_plt_section_index) |index| {
4401 self.shdrs.items[index].sh_size = self.got_plt.size(self);
4402 }
4403
4404 if (self.plt_got_section_index) |index| {
4405 self.shdrs.items[index].sh_size = self.plt_got.size();
4406 }
4407
4408 if (self.rela_dyn_section_index) |shndx| {
4409 var num = self.got.numRela(self) + self.copy_rel.numRela() + self.zig_got.numRela();
4410 if (self.zig_module_index) |index| {
4411 num += self.file(index).?.zig_module.num_dynrelocs;
4412 }
4413 for (self.objects.items) |index| {
4414 num += self.file(index).?.object.num_dynrelocs;
4415 }
4416 self.shdrs.items[shndx].sh_size = num * @sizeOf(elf.Elf64_Rela);
4417 }
4418
4419 if (self.rela_plt_section_index) |index| {
4420 self.shdrs.items[index].sh_size = self.plt.numRela() * @sizeOf(elf.Elf64_Rela);
4421 }
4422
4423 if (self.copy_rel_section_index) |index| {
4424 try self.copy_rel.updateSectionSize(index, self);
4425 }
4426
4427 if (self.interp_section_index) |index| {
4428 self.shdrs.items[index].sh_size = self.base.options.dynamic_linker.?.len + 1;
4429 }
4430
4431 if (self.hash_section_index) |index| {
4432 self.shdrs.items[index].sh_size = self.hash.size();
4433 }
4434
4435 if (self.gnu_hash_section_index) |index| {
4436 self.shdrs.items[index].sh_size = self.gnu_hash.size();
4437 }
4438
4439 if (self.dynamic_section_index) |index| {
4440 self.shdrs.items[index].sh_size = self.dynamic.size(self);
4441 }
4442
4443 if (self.dynsymtab_section_index) |index| {
4444 self.shdrs.items[index].sh_size = self.dynsym.size();
4445 }
4446
4447 if (self.dynstrtab_section_index) |index| {
4448 self.shdrs.items[index].sh_size = self.dynstrtab.buffer.items.len;
4449 }
4450
4451 if (self.versym_section_index) |index| {
4452 self.shdrs.items[index].sh_size = self.versym.items.len * @sizeOf(elf.Elf64_Versym);
4453 }
4454
4455 if (self.verneed_section_index) |index| {
4456 self.shdrs.items[index].sh_size = self.verneed.size();
4457 }
4458
4459 if (self.symtab_section_index != null) {
4460 try self.updateSymtabSize();
4461 }
4462
4463 if (self.strtab_section_index) |index| {
4464 // TODO I don't really this here but we need it to add symbol names from GOT and other synthetic
4465 // sections into .strtab for easier debugging.
4466 if (self.zig_got_section_index) |_| {
4467 try self.zig_got.updateStrtab(self);
4468 }
4469 if (self.got_section_index) |_| {
4470 try self.got.updateStrtab(self);
4471 }
4472 if (self.plt_section_index) |_| {
4473 try self.plt.updateStrtab(self);
4474 }
4475 if (self.plt_got_section_index) |_| {
4476 try self.plt_got.updateStrtab(self);
4477 }
4478 self.shdrs.items[index].sh_size = self.strtab.buffer.items.len;
4479 }
4480
4481 if (self.shstrtab_section_index) |index| {
4482 self.shdrs.items[index].sh_size = self.shstrtab.buffer.items.len;
4483 }
4484}
4485
4486fn shdrToPhdrFlags(sh_flags: u64) u32 {
4487 const write = sh_flags & elf.SHF_WRITE != 0;
4488 const exec = sh_flags & elf.SHF_EXECINSTR != 0;
4489 var out_flags: u32 = elf.PF_R;
4490 if (write) out_flags |= elf.PF_W;
4491 if (exec) out_flags |= elf.PF_X;
4492 return out_flags;
4493}
4494
4495/// Calculates how many segments (PT_LOAD progam headers) are required
4496/// to cover the set of sections.
4497/// We permit a maximum of 3**2 number of segments.
4498fn calcNumberOfSegments(self: *Elf) usize {
4499 var covers: [9]bool = [_]bool{false} ** 9;
4500 for (self.shdrs.items, 0..) |shdr, shndx| {
4501 if (shdr.sh_type == elf.SHT_NULL) continue;
4502 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
4503 if (self.isZigSection(@intCast(shndx))) continue;
4504 const flags = shdrToPhdrFlags(shdr.sh_flags);
4505 covers[flags - 1] = true;
4506 }
4507 var count: usize = 0;
4508 for (covers) |cover| {
4509 if (cover) count += 1;
4510 }
4511 return count;
4512}
4513
4514/// Allocates PHDR table in virtual memory and in file.
4515fn allocatePhdrTable(self: *Elf) void {
4516 const new_load_segments = self.calcNumberOfSegments();
4517 const phdr_table = &self.phdrs.items[self.phdr_table_index.?];
4518 const phdr_table_load = &self.phdrs.items[self.phdr_table_load_index.?];
4519
4520 const phsize: u64 = switch (self.ptr_width) {
4521 .p32 => @sizeOf(elf.Elf32_Phdr),
4522 .p64 => @sizeOf(elf.Elf64_Phdr),
4523 };
4524 const needed_size = (self.phdrs.items.len + new_load_segments) * phsize;
4525
4526 if (needed_size > self.allocatedSize(phdr_table.p_offset)) {
4527 phdr_table.p_offset = 0;
4528 phdr_table.p_offset = self.findFreeSpace(needed_size, phdr_table.p_align);
4529 }
4530
4531 phdr_table_load.p_offset = mem.alignBackward(u64, phdr_table.p_offset, phdr_table_load.p_align);
4532 const load_align_offset = phdr_table.p_offset - phdr_table_load.p_offset;
4533 phdr_table_load.p_filesz = load_align_offset + needed_size;
4534 phdr_table_load.p_memsz = load_align_offset + needed_size;
4535
4536 phdr_table.p_filesz = needed_size;
4537 phdr_table.p_vaddr = phdr_table_load.p_vaddr + load_align_offset;
4538 phdr_table.p_paddr = phdr_table_load.p_paddr + load_align_offset;
4539 phdr_table.p_memsz = needed_size;
4540}
4541
4542/// Allocates alloc sections and creates load segments for sections
4543/// extracted from input object files.
4544fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {
4545 // We use this struct to track maximum alignment of all TLS sections.
4546 // According to https://github.com/rui314/mold/commit/bd46edf3f0fe9e1a787ea453c4657d535622e61f in mold,
4547 // in-file offsets have to be aligned against the start of TLS program header.
4548 // If that's not ensured, then in a multi-threaded context, TLS variables across a shared object
4549 // boundary may not get correctly loaded at an aligned address.
4550 const Align = struct {
4551 tls_start_align: u64 = 1,
4552 first_tls_index: ?usize = null,
4553
4554 fn isFirstTlsShdr(this: @This(), other: usize) bool {
4555 if (this.first_tls_index) |index| return index == other;
4556 return false;
4557 }
4558
4559 fn @"align"(this: @This(), index: usize, sh_addralign: u64, addr: u64) u64 {
4560 const alignment = if (this.isFirstTlsShdr(index)) this.tls_start_align else sh_addralign;
4561 return mem.alignForward(u64, addr, alignment);
4562 }
4563 };
4564
4565 var alignment = Align{};
4566 for (self.shdrs.items, 0..) |shdr, i| {
4567 if (shdr.sh_type == elf.SHT_NULL) continue;
4568 if (shdr.sh_flags & elf.SHF_TLS == 0) continue;
4569 if (alignment.first_tls_index == null) alignment.first_tls_index = i;
4570 alignment.tls_start_align = @max(alignment.tls_start_align, shdr.sh_addralign);
4571 }
4572
4573 // Next, calculate segment covers by scanning all alloc sections.
4574 // If a section matches segment flags with the preceeding section,
4575 // we put it in the same segment. Otherwise, we create a new cover.
4576 // This algorithm is simple but suboptimal in terms of space re-use:
4577 // normally we would also take into account any gaps in allocated
4578 // virtual and file offsets. However, the simple one will do for one
4579 // as we are more interested in quick turnaround and compatibility
4580 // with `findFreeSpace` mechanics than anything else.
4581 const Cover = std.ArrayList(u16);
4582 const gpa = self.base.allocator;
4583 var covers: [9]Cover = undefined;
4584 for (&covers) |*cover| {
4585 cover.* = Cover.init(gpa);
4586 }
4587 defer for (&covers) |*cover| {
4588 cover.deinit();
4589 };
4590
4591 for (self.shdrs.items, 0..) |shdr, shndx| {
4592 if (shdr.sh_type == elf.SHT_NULL) continue;
4593 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
4594 if (self.isZigSection(@intCast(shndx))) continue;
4595 const flags = shdrToPhdrFlags(shdr.sh_flags);
4596 try covers[flags - 1].append(@intCast(shndx));
4597 }
4598
4599 // Now we can proceed with allocating the sections in virtual memory.
4600 // As the base address we take the end address of the PHDR table.
4601 // When allocating we first find the largest required alignment
4602 // of any section that is contained in a cover and use it to align
4603 // the start address of the segement (and first section).
4604 const phdr_table = &self.phdrs.items[self.phdr_table_load_index.?];
4605 var addr = phdr_table.p_vaddr + phdr_table.p_memsz;
4606
4607 for (covers) |cover| {
4608 if (cover.items.len == 0) continue;
4609
4610 var @"align": u64 = self.page_size;
4611 for (cover.items) |shndx| {
4612 const shdr = self.shdrs.items[shndx];
4613 if (shdr.sh_type == elf.SHT_NOBITS and shdr.sh_flags & elf.SHF_TLS != 0) continue;
4614 @"align" = @max(@"align", shdr.sh_addralign);
4615 }
4616
4617 addr = mem.alignForward(u64, addr, @"align");
4618
4619 var memsz: u64 = 0;
4620 var filesz: u64 = 0;
4621 var i: usize = 0;
4622 while (i < cover.items.len) : (i += 1) {
4623 const shndx = cover.items[i];
4624 const shdr = &self.shdrs.items[shndx];
4625 if (shdr.sh_type == elf.SHT_NOBITS and shdr.sh_flags & elf.SHF_TLS != 0) {
4626 // .tbss is a little special as it's used only by the loader meaning it doesn't
4627 // need to be actually mmap'ed at runtime. We still need to correctly increment
4628 // the addresses of every TLS zerofill section tho. Thus, we hack it so that
4629 // we increment the start address like normal, however, after we are done,
4630 // the next ALLOC section will get its start address allocated within the same
4631 // range as the .tbss sections. We will get something like this:
4632 //
4633 // ...
4634 // .tbss 0x10
4635 // .tcommon 0x20
4636 // .data 0x10
4637 // ...
4638 var tbss_addr = addr;
4639 while (i < cover.items.len and
4640 self.shdrs.items[cover.items[i]].sh_type == elf.SHT_NOBITS and
4641 self.shdrs.items[cover.items[i]].sh_flags & elf.SHF_TLS != 0) : (i += 1)
4642 {
4643 const tbss_shndx = cover.items[i];
4644 const tbss_shdr = &self.shdrs.items[tbss_shndx];
4645 tbss_addr = alignment.@"align"(tbss_shndx, tbss_shdr.sh_addralign, tbss_addr);
4646 tbss_shdr.sh_addr = tbss_addr;
4647 tbss_addr += tbss_shdr.sh_size;
4648 }
4649 i -= 1;
4650 continue;
4651 }
4652 const next = alignment.@"align"(shndx, shdr.sh_addralign, addr);
4653 const padding = next - addr;
4654 addr = next;
4655 shdr.sh_addr = addr;
4656 if (shdr.sh_type != elf.SHT_NOBITS) {
4657 filesz += padding + shdr.sh_size;
4658 }
4659 memsz += padding + shdr.sh_size;
4660 addr += shdr.sh_size;
4661 }
4662
4663 const first = self.shdrs.items[cover.items[0]];
4664 var off = self.findFreeSpace(filesz, @"align");
4665 const phndx = try self.addPhdr(.{
4666 .type = elf.PT_LOAD,
4667 .offset = off,
4668 .addr = first.sh_addr,
4669 .memsz = memsz,
4670 .filesz = filesz,
4671 .@"align" = @"align",
4672 .flags = shdrToPhdrFlags(first.sh_flags),
4673 });
4674
4675 for (cover.items) |shndx| {
4676 const shdr = &self.shdrs.items[shndx];
4677 if (shdr.sh_type == elf.SHT_NOBITS) continue;
4678 off = alignment.@"align"(shndx, shdr.sh_addralign, off);
4679 shdr.sh_offset = off;
4680 off += shdr.sh_size;
4681 try self.phdr_to_shdr_table.putNoClobber(gpa, shndx, phndx);
4682 }
4683
4684 addr = mem.alignForward(u64, addr, self.page_size);
4685 }
4686}
4687
4688/// Allocates non-alloc sections (debug info, symtabs, etc.).
4689fn allocateNonAllocSections(self: *Elf) !void {
4690 for (self.shdrs.items, 0..) |*shdr, shndx| {
4691 if (shdr.sh_type == elf.SHT_NULL) continue;
4692 if (shdr.sh_flags & elf.SHF_ALLOC != 0) continue;
4693 const needed_size = shdr.sh_size;
4694 if (needed_size > self.allocatedSize(shdr.sh_offset)) {
4695 shdr.sh_size = 0;
4696 const new_offset = self.findFreeSpace(needed_size, shdr.sh_addralign);
4697
4698 if (self.isDebugSection(@intCast(shndx))) {
4699 log.debug("moving {s} from 0x{x} to 0x{x}", .{
4700 self.shstrtab.getAssumeExists(shdr.sh_name),
4701 shdr.sh_offset,
4702 new_offset,
4703 });
4704 const existing_size = blk: {
4705 if (shndx == self.debug_info_section_index.?) break :blk self.debug_info_section_zig_size;
4706 if (shndx == self.debug_abbrev_section_index.?) break :blk self.debug_abbrev_section_zig_size;
4707 if (shndx == self.debug_str_section_index.?) break :blk self.debug_str_section_zig_size;
4708 if (shndx == self.debug_aranges_section_index.?) break :blk self.debug_aranges_section_zig_size;
4709 if (shndx == self.debug_line_section_index.?) break :blk self.debug_line_section_zig_size;
4710 unreachable;
4711 };
4712 const amt = try self.base.file.?.copyRangeAll(
4713 shdr.sh_offset,
4714 self.base.file.?,
4715 new_offset,
4716 existing_size,
4717 );
4718 if (amt != existing_size) return error.InputOutput;
4719 }
4720
4721 shdr.sh_offset = new_offset;
4722 shdr.sh_size = needed_size;
4723 }
4724 }
4725}
4726
4727fn allocateSpecialPhdrs(self: *Elf) void {
4728 for (&[_]struct { ?u16, ?u16 }{
4729 .{ self.phdr_interp_index, self.interp_section_index },
4730 .{ self.phdr_dynamic_index, self.dynamic_section_index },
4731 .{ self.phdr_gnu_eh_frame_index, self.eh_frame_hdr_section_index },
4732 }) |pair| {
4733 if (pair[0]) |index| {
4734 const shdr = self.shdrs.items[pair[1].?];
4735 const phdr = &self.phdrs.items[index];
4736 phdr.p_align = shdr.sh_addralign;
4737 phdr.p_offset = shdr.sh_offset;
4738 phdr.p_vaddr = shdr.sh_addr;
4739 phdr.p_filesz = shdr.sh_size;
4740 phdr.p_memsz = shdr.sh_size;
4741 }
3483 }4742 }
34844743
3485 // __GNU_EH_FRAME_HDR4744 // Set the TLS segment boundaries.
3486 if (self.eh_frame_hdr_section_index) |shndx| {4745 // We assume TLS sections are laid out contiguously and that there is
3487 const shdr = &self.shdrs.items[shndx];4746 // a single TLS segment.
3488 const symbol_ptr = self.symbol(self.gnu_eh_frame_hdr_index.?);4747 if (self.phdr_tls_index) |index| {
3489 symbol_ptr.value = shdr.sh_addr;4748 const slice = self.shdrs.items;
3490 symbol_ptr.output_section_index = shndx;4749 const phdr = &self.phdrs.items[index];
4750 var shndx: u16 = 0;
4751 while (shndx < slice.len) {
4752 const shdr = slice[shndx];
4753 if (shdr.sh_flags & elf.SHF_TLS == 0) {
4754 shndx += 1;
4755 continue;
4756 }
4757 phdr.p_offset = shdr.sh_offset;
4758 phdr.p_vaddr = shdr.sh_addr;
4759 phdr.p_paddr = shdr.sh_addr;
4760 phdr.p_align = shdr.sh_addralign;
4761 shndx += 1;
4762 phdr.p_align = @max(phdr.p_align, shdr.sh_addralign);
4763 if (shdr.sh_type != elf.SHT_NOBITS) {
4764 phdr.p_filesz = shdr.sh_offset + shdr.sh_size - phdr.p_offset;
4765 }
4766 phdr.p_memsz = shdr.sh_addr + shdr.sh_size - phdr.p_vaddr;
4767
4768 while (shndx < slice.len) : (shndx += 1) {
4769 const next = slice[shndx];
4770 if (next.sh_flags & elf.SHF_TLS == 0) break;
4771 phdr.p_align = @max(phdr.p_align, next.sh_addralign);
4772 if (next.sh_type != elf.SHT_NOBITS) {
4773 phdr.p_filesz = next.sh_offset + next.sh_size - phdr.p_offset;
4774 }
4775 phdr.p_memsz = next.sh_addr + next.sh_size - phdr.p_vaddr;
4776 }
4777 }
3491 }4778 }
4779}
34924780
3493 // __rela_iplt_start, __rela_iplt_end4781fn allocateAtoms(self: *Elf) void {
3494 if (self.rela_dyn_section_index) |shndx| blk: {4782 for (self.objects.items) |index| {
3495 if (self.base.options.link_mode != .Static or self.base.options.pie) break :blk;4783 self.file(index).?.object.allocateAtoms(self);
3496 const shdr = &self.shdrs.items[shndx];
3497 const end_addr = shdr.sh_addr + shdr.sh_size;
3498 const start_addr = end_addr - self.calcNumIRelativeRelocs() * @sizeOf(elf.Elf64_Rela);
3499 const start_sym = self.symbol(self.rela_iplt_start_index.?);
3500 const end_sym = self.symbol(self.rela_iplt_end_index.?);
3501 start_sym.value = start_addr;
3502 start_sym.output_section_index = shndx;
3503 end_sym.value = end_addr;
3504 end_sym.output_section_index = shndx;
3505 }4784 }
4785}
35064786
3507 // _end4787fn writeAtoms(self: *Elf) !void {
3508 {4788 const gpa = self.base.allocator;
3509 const end_symbol = self.symbol(self.end_index.?);4789
3510 end_symbol.value = 0;4790 var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Atom.Index)).init(gpa);
3511 for (self.shdrs.items, 0..) |*shdr, shndx| {4791 defer {
3512 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;4792 var it = undefs.iterator();
3513 const phdr_index = self.phdr_to_shdr_table.get(@intCast(shndx)).?;4793 while (it.next()) |entry| {
3514 const phdr = self.phdrs.items[phdr_index];4794 entry.value_ptr.deinit();
3515 const value = phdr.p_vaddr + phdr.p_memsz;
3516 if (end_symbol.value < value) {
3517 end_symbol.value = value;
3518 end_symbol.output_section_index = @intCast(shndx);
3519 }
3520 }4795 }
4796 undefs.deinit();
3521 }4797 }
35224798
3523 // __start_*, __stop_*4799 // TODO iterate over `output_sections` directly
3524 {4800 for (self.shdrs.items, 0..) |shdr, shndx| {
3525 var index: usize = 0;4801 if (shdr.sh_type == elf.SHT_NULL) continue;
3526 while (index < self.start_stop_indexes.items.len) : (index += 2) {4802 if (shdr.sh_type == elf.SHT_NOBITS) continue;
3527 const start = self.symbol(self.start_stop_indexes.items[index]);4803
3528 const name = start.name(self);4804 const atom_list = self.output_sections.get(@intCast(shndx)) orelse continue;
3529 const stop = self.symbol(self.start_stop_indexes.items[index + 1]);4805
3530 const shndx = self.sectionByName(name["__start_".len..]).?;4806 log.debug("writing atoms in '{s}' section", .{self.shstrtab.getAssumeExists(shdr.sh_name)});
3531 const shdr = &self.shdrs.items[shndx];4807
3532 start.value = shdr.sh_addr;4808 // TODO really, really handle debug section separately
3533 start.output_section_index = shndx;4809 const base_offset = if (self.isDebugSection(@intCast(shndx))) blk: {
3534 stop.value = shdr.sh_addr + shdr.sh_size;4810 if (shndx == self.debug_info_section_index.?) break :blk self.debug_info_section_zig_size;
3535 stop.output_section_index = shndx;4811 if (shndx == self.debug_abbrev_section_index.?) break :blk self.debug_abbrev_section_zig_size;
4812 if (shndx == self.debug_str_section_index.?) break :blk self.debug_str_section_zig_size;
4813 if (shndx == self.debug_aranges_section_index.?) break :blk self.debug_aranges_section_zig_size;
4814 if (shndx == self.debug_line_section_index.?) break :blk self.debug_line_section_zig_size;
4815 unreachable;
4816 } else 0;
4817 const sh_offset = shdr.sh_offset + base_offset;
4818 const sh_size = math.cast(usize, shdr.sh_size - base_offset) orelse return error.Overflow;
4819
4820 const buffer = try gpa.alloc(u8, sh_size);
4821 defer gpa.free(buffer);
4822 const padding_byte: u8 = if (shdr.sh_type == elf.SHT_PROGBITS and
4823 shdr.sh_flags & elf.SHF_EXECINSTR != 0)
4824 0xcc // int3
4825 else
4826 0;
4827 @memset(buffer, padding_byte);
4828
4829 for (atom_list.items) |atom_index| {
4830 const atom_ptr = self.atom(atom_index).?;
4831 assert(atom_ptr.flags.alive);
4832
4833 const object = atom_ptr.file(self).?.object;
4834 const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse
4835 return error.Overflow;
4836 const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;
4837
4838 log.debug("writing atom({d}) at 0x{x}", .{ atom_index, sh_offset + offset });
4839
4840 // TODO decompress directly into provided buffer
4841 const out_code = buffer[offset..][0..size];
4842 const in_code = try object.codeDecompressAlloc(self, atom_index);
4843 defer gpa.free(in_code);
4844 @memcpy(out_code, in_code);
4845
4846 if (shdr.sh_flags & elf.SHF_ALLOC == 0) {
4847 try atom_ptr.resolveRelocsNonAlloc(self, out_code, &undefs);
4848 } else {
4849 atom_ptr.resolveRelocsAlloc(self, out_code) catch |err| switch (err) {
4850 // TODO
4851 error.RelaxFail, error.InvalidInstruction, error.CannotEncode => {
4852 log.err("relaxing intructions failed; TODO this should be a fatal linker error", .{});
4853 },
4854 else => |e| return e,
4855 };
4856 }
3536 }4857 }
4858
4859 try self.base.file.?.pwriteAll(buffer, sh_offset);
3537 }4860 }
4861
4862 try self.reportUndefined(&undefs);
3538}4863}
35394864
3540fn updateSymtabSize(self: *Elf) !void {4865fn updateSymtabSize(self: *Elf) !void {
...@@ -3554,11 +4879,32 @@ fn updateSymtabSize(self: *Elf) !void {...@@ -3554,11 +4879,32 @@ fn updateSymtabSize(self: *Elf) !void {
3554 sizes.nglobals += object.output_symtab_size.nglobals;4879 sizes.nglobals += object.output_symtab_size.nglobals;
3555 }4880 }
35564881
4882 for (self.shared_objects.items) |index| {
4883 const shared_object = self.file(index).?.shared_object;
4884 shared_object.updateSymtabSize(self);
4885 sizes.nglobals += shared_object.output_symtab_size.nglobals;
4886 }
4887
4888 if (self.zig_got_section_index) |_| {
4889 self.zig_got.updateSymtabSize(self);
4890 sizes.nlocals += self.zig_got.output_symtab_size.nlocals;
4891 }
4892
3557 if (self.got_section_index) |_| {4893 if (self.got_section_index) |_| {
3558 self.got.updateSymtabSize(self);4894 self.got.updateSymtabSize(self);
3559 sizes.nlocals += self.got.output_symtab_size.nlocals;4895 sizes.nlocals += self.got.output_symtab_size.nlocals;
3560 }4896 }
35614897
4898 if (self.plt_section_index) |_| {
4899 self.plt.updateSymtabSize(self);
4900 sizes.nlocals += self.plt.output_symtab_size.nlocals;
4901 }
4902
4903 if (self.plt_got_section_index) |_| {
4904 self.plt_got.updateSymtabSize(self);
4905 sizes.nlocals += self.plt_got.output_symtab_size.nlocals;
4906 }
4907
3562 if (self.linker_defined_index) |index| {4908 if (self.linker_defined_index) |index| {
3563 const linker_defined = self.file(index).?.linker_defined;4909 const linker_defined = self.file(index).?.linker_defined;
3564 linker_defined.updateSymtabSize(self);4910 linker_defined.updateSymtabSize(self);
...@@ -3567,19 +4913,155 @@ fn updateSymtabSize(self: *Elf) !void {...@@ -3567,19 +4913,155 @@ fn updateSymtabSize(self: *Elf) !void {
35674913
3568 const shdr = &self.shdrs.items[self.symtab_section_index.?];4914 const shdr = &self.shdrs.items[self.symtab_section_index.?];
3569 shdr.sh_info = sizes.nlocals + 1;4915 shdr.sh_info = sizes.nlocals + 1;
3570 self.markDirty(self.symtab_section_index.?, null);4916 shdr.sh_link = self.strtab_section_index.?;
35714917
3572 const sym_size: u64 = switch (self.ptr_width) {4918 const sym_size: u64 = switch (self.ptr_width) {
3573 .p32 => @sizeOf(elf.Elf32_Sym),4919 .p32 => @sizeOf(elf.Elf32_Sym),
3574 .p64 => @sizeOf(elf.Elf64_Sym),4920 .p64 => @sizeOf(elf.Elf64_Sym),
3575 };4921 };
3576 const sym_align: u16 = switch (self.ptr_width) {
3577 .p32 => @alignOf(elf.Elf32_Sym),
3578 .p64 => @alignOf(elf.Elf64_Sym),
3579 };
3580 const needed_size = (sizes.nlocals + sizes.nglobals + 1) * sym_size;4922 const needed_size = (sizes.nlocals + sizes.nglobals + 1) * sym_size;
3581 shdr.sh_size = needed_size;4923 shdr.sh_size = needed_size;
3582 try self.growNonAllocSection(self.symtab_section_index.?, needed_size, sym_align, true);4924}
4925
4926fn writeSyntheticSections(self: *Elf) !void {
4927 const gpa = self.base.allocator;
4928
4929 if (self.interp_section_index) |shndx| {
4930 const shdr = self.shdrs.items[shndx];
4931 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
4932 var buffer = try gpa.alloc(u8, sh_size);
4933 defer gpa.free(buffer);
4934 const dylinker = self.base.options.dynamic_linker.?;
4935 @memcpy(buffer[0..dylinker.len], dylinker);
4936 buffer[dylinker.len] = 0;
4937 try self.base.file.?.pwriteAll(buffer, shdr.sh_offset);
4938 }
4939
4940 if (self.hash_section_index) |shndx| {
4941 const shdr = self.shdrs.items[shndx];
4942 try self.base.file.?.pwriteAll(self.hash.buffer.items, shdr.sh_offset);
4943 }
4944
4945 if (self.gnu_hash_section_index) |shndx| {
4946 const shdr = self.shdrs.items[shndx];
4947 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.gnu_hash.size());
4948 defer buffer.deinit();
4949 try self.gnu_hash.write(self, buffer.writer());
4950 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
4951 }
4952
4953 if (self.versym_section_index) |shndx| {
4954 const shdr = self.shdrs.items[shndx];
4955 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.versym.items), shdr.sh_offset);
4956 }
4957
4958 if (self.verneed_section_index) |shndx| {
4959 const shdr = self.shdrs.items[shndx];
4960 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.verneed.size());
4961 defer buffer.deinit();
4962 try self.verneed.write(buffer.writer());
4963 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
4964 }
4965
4966 if (self.dynamic_section_index) |shndx| {
4967 const shdr = self.shdrs.items[shndx];
4968 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynamic.size(self));
4969 defer buffer.deinit();
4970 try self.dynamic.write(self, buffer.writer());
4971 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
4972 }
4973
4974 if (self.dynsymtab_section_index) |shndx| {
4975 const shdr = self.shdrs.items[shndx];
4976 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynsym.size());
4977 defer buffer.deinit();
4978 try self.dynsym.write(self, buffer.writer());
4979 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
4980 }
4981
4982 if (self.dynstrtab_section_index) |shndx| {
4983 const shdr = self.shdrs.items[shndx];
4984 try self.base.file.?.pwriteAll(self.dynstrtab.buffer.items, shdr.sh_offset);
4985 }
4986
4987 if (self.eh_frame_section_index) |shndx| {
4988 const shdr = self.shdrs.items[shndx];
4989 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
4990 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
4991 defer buffer.deinit();
4992 try eh_frame.writeEhFrame(self, buffer.writer());
4993 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
4994 }
4995
4996 if (self.eh_frame_hdr_section_index) |shndx| {
4997 const shdr = self.shdrs.items[shndx];
4998 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
4999 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
5000 defer buffer.deinit();
5001 try eh_frame.writeEhFrameHdr(self, buffer.writer());
5002 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
5003 }
5004
5005 if (self.got_section_index) |index| {
5006 const shdr = self.shdrs.items[index];
5007 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got.size(self));
5008 defer buffer.deinit();
5009 try self.got.write(self, buffer.writer());
5010 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
5011 }
5012
5013 if (self.rela_dyn_section_index) |shndx| {
5014 const shdr = self.shdrs.items[shndx];
5015 try self.got.addRela(self);
5016 try self.copy_rel.addRela(self);
5017 try self.zig_got.addRela(self);
5018 self.sortRelaDyn();
5019 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_dyn.items), shdr.sh_offset);
5020 }
5021
5022 if (self.plt_section_index) |shndx| {
5023 const shdr = self.shdrs.items[shndx];
5024 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt.size());
5025 defer buffer.deinit();
5026 try self.plt.write(self, buffer.writer());
5027 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
5028 }
5029
5030 if (self.got_plt_section_index) |shndx| {
5031 const shdr = self.shdrs.items[shndx];
5032 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got_plt.size(self));
5033 defer buffer.deinit();
5034 try self.got_plt.write(self, buffer.writer());
5035 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
5036 }
5037
5038 if (self.plt_got_section_index) |shndx| {
5039 const shdr = self.shdrs.items[shndx];
5040 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt_got.size());
5041 defer buffer.deinit();
5042 try self.plt_got.write(self, buffer.writer());
5043 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
5044 }
5045
5046 if (self.rela_plt_section_index) |shndx| {
5047 const shdr = self.shdrs.items[shndx];
5048 try self.plt.addRela(self);
5049 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_plt.items), shdr.sh_offset);
5050 }
5051
5052 if (self.shstrtab_section_index) |index| {
5053 const shdr = self.shdrs.items[index];
5054 try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shdr.sh_offset);
5055 }
5056
5057 if (self.strtab_section_index) |index| {
5058 const shdr = self.shdrs.items[index];
5059 try self.base.file.?.pwriteAll(self.strtab.buffer.items, shdr.sh_offset);
5060 }
5061
5062 if (self.symtab_section_index) |_| {
5063 try self.writeSymtab();
5064 }
3583}5065}
35845066
3585fn writeSymtab(self: *Elf) !void {5067fn writeSymtab(self: *Elf) !void {
...@@ -3617,11 +5099,32 @@ fn writeSymtab(self: *Elf) !void {...@@ -3617,11 +5099,32 @@ fn writeSymtab(self: *Elf) !void {
3617 ctx.iglobal += object.output_symtab_size.nglobals;5099 ctx.iglobal += object.output_symtab_size.nglobals;
3618 }5100 }
36195101
5102 for (self.shared_objects.items) |index| {
5103 const shared_object = self.file(index).?.shared_object;
5104 shared_object.writeSymtab(self, ctx);
5105 ctx.iglobal += shared_object.output_symtab_size.nglobals;
5106 }
5107
5108 if (self.zig_got_section_index) |_| {
5109 try self.zig_got.writeSymtab(self, ctx);
5110 ctx.ilocal += self.zig_got.output_symtab_size.nlocals;
5111 }
5112
3620 if (self.got_section_index) |_| {5113 if (self.got_section_index) |_| {
3621 try self.got.writeSymtab(self, ctx);5114 try self.got.writeSymtab(self, ctx);
3622 ctx.ilocal += self.got.output_symtab_size.nlocals;5115 ctx.ilocal += self.got.output_symtab_size.nlocals;
3623 }5116 }
36245117
5118 if (self.plt_section_index) |_| {
5119 try self.plt.writeSymtab(self, ctx);
5120 ctx.ilocal += self.plt.output_symtab_size.nlocals;
5121 }
5122
5123 if (self.plt_got_section_index) |_| {
5124 try self.plt_got.writeSymtab(self, ctx);
5125 ctx.ilocal += self.plt_got.output_symtab_size.nlocals;
5126 }
5127
3625 if (self.linker_defined_index) |index| {5128 if (self.linker_defined_index) |index| {
3626 const linker_defined = self.file(index).?.linker_defined;5129 const linker_defined = self.file(index).?.linker_defined;
3627 linker_defined.writeSymtab(self, ctx);5130 linker_defined.writeSymtab(self, ctx);
...@@ -3683,29 +5186,6 @@ fn phdrTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr {...@@ -3683,29 +5186,6 @@ fn phdrTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr {
3683 };5186 };
3684}5187}
36855188
3686fn writeShdr(self: *Elf, index: usize) !void {
3687 const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian();
3688 switch (self.ptr_width) {
3689 .p32 => {
3690 var shdr: [1]elf.Elf32_Shdr = undefined;
3691 shdr[0] = shdrTo32(self.shdrs.items[index]);
3692 if (foreign_endian) {
3693 mem.byteSwapAllFields(elf.Elf32_Shdr, &shdr[0]);
3694 }
3695 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr);
3696 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
3697 },
3698 .p64 => {
3699 var shdr = [1]elf.Elf64_Shdr{self.shdrs.items[index]};
3700 if (foreign_endian) {
3701 mem.byteSwapAllFields(elf.Elf64_Shdr, &shdr[0]);
3702 }
3703 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr);
3704 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
3705 },
3706 }
3707}
3708
3709fn shdrTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {5189fn shdrTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
3710 return .{5190 return .{
3711 .sh_name = shdr.sh_name,5191 .sh_name = shdr.sh_name,
...@@ -3979,8 +5459,94 @@ pub fn isStatic(self: Elf) bool {...@@ -3979,8 +5459,94 @@ pub fn isStatic(self: Elf) bool {
3979 return self.base.options.link_mode == .Static;5459 return self.base.options.link_mode == .Static;
3980}5460}
39815461
5462pub fn isExe(self: Elf) bool {
5463 return self.base.options.effectiveOutputMode() == .Exe;
5464}
5465
3982pub fn isDynLib(self: Elf) bool {5466pub fn isDynLib(self: Elf) bool {
3983 return self.base.options.output_mode == .Lib and self.base.options.link_mode == .Dynamic;5467 return self.base.options.effectiveOutputMode() == .Lib and self.base.options.link_mode == .Dynamic;
5468}
5469
5470pub fn isZigSection(self: Elf, shndx: u16) bool {
5471 inline for (&[_]?u16{
5472 self.zig_text_section_index,
5473 self.zig_rodata_section_index,
5474 self.zig_data_section_index,
5475 self.zig_bss_section_index,
5476 self.zig_got_section_index,
5477 }) |maybe_index| {
5478 if (maybe_index) |index| {
5479 if (index == shndx) return true;
5480 }
5481 }
5482 return false;
5483}
5484
5485pub fn isDebugSection(self: Elf, shndx: u16) bool {
5486 inline for (&[_]?u16{
5487 self.debug_info_section_index,
5488 self.debug_abbrev_section_index,
5489 self.debug_str_section_index,
5490 self.debug_aranges_section_index,
5491 self.debug_line_section_index,
5492 }) |maybe_index| {
5493 if (maybe_index) |index| {
5494 if (index == shndx) return true;
5495 }
5496 }
5497 return false;
5498}
5499
5500fn addPhdr(self: *Elf, opts: struct {
5501 type: u32 = 0,
5502 flags: u32 = 0,
5503 @"align": u64 = 0,
5504 offset: u64 = 0,
5505 addr: u64 = 0,
5506 filesz: u64 = 0,
5507 memsz: u64 = 0,
5508}) error{OutOfMemory}!u16 {
5509 const index = @as(u16, @intCast(self.phdrs.items.len));
5510 try self.phdrs.append(self.base.allocator, .{
5511 .p_type = opts.type,
5512 .p_flags = opts.flags,
5513 .p_offset = opts.offset,
5514 .p_vaddr = opts.addr,
5515 .p_paddr = opts.addr,
5516 .p_filesz = opts.filesz,
5517 .p_memsz = opts.memsz,
5518 .p_align = opts.@"align",
5519 });
5520 return index;
5521}
5522
5523pub const AddSectionOpts = struct {
5524 name: [:0]const u8,
5525 type: u32 = elf.SHT_NULL,
5526 flags: u64 = 0,
5527 link: u32 = 0,
5528 info: u32 = 0,
5529 addralign: u64 = 0,
5530 entsize: u64 = 0,
5531};
5532
5533pub fn addSection(self: *Elf, opts: AddSectionOpts) !u16 {
5534 const gpa = self.base.allocator;
5535 const index = @as(u16, @intCast(self.shdrs.items.len));
5536 const shdr = try self.shdrs.addOne(gpa);
5537 shdr.* = .{
5538 .sh_name = try self.shstrtab.insert(gpa, opts.name),
5539 .sh_type = opts.type,
5540 .sh_flags = opts.flags,
5541 .sh_addr = 0,
5542 .sh_offset = 0,
5543 .sh_size = 0,
5544 .sh_link = opts.link,
5545 .sh_info = opts.info,
5546 .sh_addralign = opts.addralign,
5547 .sh_entsize = opts.entsize,
5548 };
5549 return index;
3984}5550}
39855551
3986pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u16 {5552pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u16 {
...@@ -3990,9 +5556,77 @@ pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u16 {...@@ -3990,9 +5556,77 @@ pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u16 {
3990 } else return null;5556 } else return null;
3991}5557}
39925558
3993pub fn calcNumIRelativeRelocs(self: *Elf) u64 {5559const RelaDyn = struct {
3994 _ = self;5560 offset: u64,
3995 unreachable; // TODO5561 sym: u64 = 0,
5562 type: u32,
5563 addend: i64 = 0,
5564};
5565
5566pub fn addRelaDyn(self: *Elf, opts: RelaDyn) !void {
5567 try self.rela_dyn.ensureUnusedCapacity(self.base.alloctor, 1);
5568 self.addRelaDynAssumeCapacity(opts);
5569}
5570
5571pub fn addRelaDynAssumeCapacity(self: *Elf, opts: RelaDyn) void {
5572 self.rela_dyn.appendAssumeCapacity(.{
5573 .r_offset = opts.offset,
5574 .r_info = (opts.sym << 32) | opts.type,
5575 .r_addend = opts.addend,
5576 });
5577}
5578
5579fn sortRelaDyn(self: *Elf) void {
5580 const Sort = struct {
5581 fn rank(rel: elf.Elf64_Rela) u2 {
5582 return switch (rel.r_type()) {
5583 elf.R_X86_64_RELATIVE => 0,
5584 elf.R_X86_64_IRELATIVE => 2,
5585 else => 1,
5586 };
5587 }
5588
5589 pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
5590 _ = ctx;
5591 if (rank(lhs) == rank(rhs)) {
5592 if (lhs.r_sym() == rhs.r_sym()) return lhs.r_offset < rhs.r_offset;
5593 return lhs.r_sym() < rhs.r_sym();
5594 }
5595 return rank(lhs) < rank(rhs);
5596 }
5597 };
5598 mem.sort(elf.Elf64_Rela, self.rela_dyn.items, {}, Sort.lessThan);
5599}
5600
5601fn calcNumIRelativeRelocs(self: *Elf) usize {
5602 var count: usize = self.num_ifunc_dynrelocs;
5603
5604 for (self.got.entries.items) |entry| {
5605 if (entry.tag != .got) continue;
5606 const sym = self.symbol(entry.symbol_index);
5607 if (sym.isIFunc(self)) count += 1;
5608 }
5609
5610 return count;
5611}
5612
5613pub fn isCIdentifier(name: []const u8) bool {
5614 if (name.len == 0) return false;
5615 const first_c = name[0];
5616 if (!std.ascii.isAlphabetic(first_c) and first_c != '_') return false;
5617 for (name[1..]) |c| {
5618 if (!std.ascii.isAlphanumeric(c) and c != '_') return false;
5619 }
5620 return true;
5621}
5622
5623fn getStartStopBasename(self: *Elf, atom_index: Atom.Index) ?[]const u8 {
5624 const atom_ptr = self.atom(atom_index) orelse return null;
5625 const name = atom_ptr.name(self);
5626 if (atom_ptr.inputShdr(self).sh_flags & elf.SHF_ALLOC != 0 and name.len > 0) {
5627 if (isCIdentifier(name)) return name;
5628 }
5629 return null;
3996}5630}
39975631
3998pub fn atom(self: *Elf, atom_index: Atom.Index) ?*Atom {5632pub fn atom(self: *Elf, atom_index: Atom.Index) ?*Atom {
...@@ -4015,6 +5649,7 @@ pub fn file(self: *Elf, index: File.Index) ?File {...@@ -4015,6 +5649,7 @@ pub fn file(self: *Elf, index: File.Index) ?File {
4015 .linker_defined => .{ .linker_defined = &self.files.items(.data)[index].linker_defined },5649 .linker_defined => .{ .linker_defined = &self.files.items(.data)[index].linker_defined },
4016 .zig_module => .{ .zig_module = &self.files.items(.data)[index].zig_module },5650 .zig_module => .{ .zig_module = &self.files.items(.data)[index].zig_module },
4017 .object => .{ .object = &self.files.items(.data)[index].object },5651 .object => .{ .object = &self.files.items(.data)[index].object },
5652 .shared_object => .{ .shared_object = &self.files.items(.data)[index].shared_object },
4018 };5653 };
4019}5654}
40205655
...@@ -4288,52 +5923,78 @@ fn reportParseError(...@@ -4288,52 +5923,78 @@ fn reportParseError(
4288 try err.addNote(self, "while parsing {s}", .{path});5923 try err.addNote(self, "while parsing {s}", .{path});
4289}5924}
42905925
4291fn fmtShdrs(self: *Elf) std.fmt.Formatter(formatShdrs) {5926const FormatShdrCtx = struct {
4292 return .{ .data = self };5927 elf_file: *Elf,
5928 shdr: elf.Elf64_Shdr,
5929};
5930
5931fn fmtShdr(self: *Elf, shdr: elf.Elf64_Shdr) std.fmt.Formatter(formatShdr) {
5932 return .{ .data = .{
5933 .shdr = shdr,
5934 .elf_file = self,
5935 } };
4293}5936}
42945937
4295fn formatShdrs(5938fn formatShdr(
4296 self: *Elf,5939 ctx: FormatShdrCtx,
4297 comptime unused_fmt_string: []const u8,5940 comptime unused_fmt_string: []const u8,
4298 options: std.fmt.FormatOptions,5941 options: std.fmt.FormatOptions,
4299 writer: anytype,5942 writer: anytype,
4300) !void {5943) !void {
4301 _ = options;5944 _ = options;
4302 _ = unused_fmt_string;5945 _ = unused_fmt_string;
4303 for (self.shdrs.items, 0..) |shdr, i| {5946 const shdr = ctx.shdr;
4304 try writer.print("shdr({d}) : phdr({?d}) : {s} : @{x} ({x}) : align({x}) : size({x})\n", .{5947 try writer.print("{s} : @{x} ({x}) : align({x}) : size({x})", .{
4305 i, self.phdr_to_shdr_table.get(@intCast(i)),5948 ctx.elf_file.shstrtab.getAssumeExists(shdr.sh_name), shdr.sh_offset,
4306 self.shstrtab.getAssumeExists(shdr.sh_name), shdr.sh_offset,5949 shdr.sh_addr, shdr.sh_addralign,
4307 shdr.sh_addr, shdr.sh_addralign,5950 shdr.sh_size,
4308 shdr.sh_size,5951 });
4309 });
4310 }
4311}5952}
43125953
4313fn fmtPhdrs(self: *Elf) std.fmt.Formatter(formatPhdrs) {5954const FormatPhdrCtx = struct {
4314 return .{ .data = self };5955 elf_file: *Elf,
5956 phdr: elf.Elf64_Phdr,
5957};
5958
5959fn fmtPhdr(self: *Elf, phdr: elf.Elf64_Phdr) std.fmt.Formatter(formatPhdr) {
5960 return .{ .data = .{
5961 .phdr = phdr,
5962 .elf_file = self,
5963 } };
4315}5964}
43165965
4317fn formatPhdrs(5966fn formatPhdr(
4318 self: *Elf,5967 ctx: FormatPhdrCtx,
4319 comptime unused_fmt_string: []const u8,5968 comptime unused_fmt_string: []const u8,
4320 options: std.fmt.FormatOptions,5969 options: std.fmt.FormatOptions,
4321 writer: anytype,5970 writer: anytype,
4322) !void {5971) !void {
4323 _ = options;5972 _ = options;
4324 _ = unused_fmt_string;5973 _ = unused_fmt_string;
4325 for (self.phdrs.items, 0..) |phdr, i| {5974 const phdr = ctx.phdr;
4326 const write = phdr.p_flags & elf.PF_W != 0;5975 const write = phdr.p_flags & elf.PF_W != 0;
4327 const read = phdr.p_flags & elf.PF_R != 0;5976 const read = phdr.p_flags & elf.PF_R != 0;
4328 const exec = phdr.p_flags & elf.PF_X != 0;5977 const exec = phdr.p_flags & elf.PF_X != 0;
4329 var flags: [3]u8 = [_]u8{'_'} ** 3;5978 var flags: [3]u8 = [_]u8{'_'} ** 3;
4330 if (exec) flags[0] = 'X';5979 if (exec) flags[0] = 'X';
4331 if (write) flags[1] = 'W';5980 if (write) flags[1] = 'W';
4332 if (read) flags[2] = 'R';5981 if (read) flags[2] = 'R';
4333 try writer.print("phdr({d}) : {s} : @{x} ({x}) : align({x}) : filesz({x}) : memsz({x})\n", .{5982 const p_type = switch (phdr.p_type) {
4334 i, flags, phdr.p_offset, phdr.p_vaddr, phdr.p_align, phdr.p_filesz, phdr.p_memsz,5983 elf.PT_LOAD => "LOAD",
4335 });5984 elf.PT_TLS => "TLS",
4336 }5985 elf.PT_GNU_EH_FRAME => "GNU_EH_FRAME",
5986 elf.PT_GNU_STACK => "GNU_STACK",
5987 elf.PT_DYNAMIC => "DYNAMIC",
5988 elf.PT_INTERP => "INTERP",
5989 elf.PT_NULL => "NULL",
5990 elf.PT_PHDR => "PHDR",
5991 elf.PT_NOTE => "NOTE",
5992 else => "UNKNOWN",
5993 };
5994 try writer.print("{s} : {s} : @{x} ({x}) : align({x}) : filesz({x}) : memsz({x})", .{
5995 p_type, flags, phdr.p_offset, phdr.p_vaddr,
5996 phdr.p_align, phdr.p_filesz, phdr.p_memsz,
5997 });
4337}5998}
43385999
4339fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) {6000fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) {
...@@ -4352,7 +6013,10 @@ fn fmtDumpState(...@@ -4352,7 +6013,10 @@ fn fmtDumpState(
4352 if (self.zig_module_index) |index| {6013 if (self.zig_module_index) |index| {
4353 const zig_module = self.file(index).?.zig_module;6014 const zig_module = self.file(index).?.zig_module;
4354 try writer.print("zig_module({d}) : {s}\n", .{ index, zig_module.path });6015 try writer.print("zig_module({d}) : {s}\n", .{ index, zig_module.path });
4355 try writer.print("{}\n", .{zig_module.fmtSymtab(self)});6016 try writer.print("{}{}\n", .{
6017 zig_module.fmtAtoms(self),
6018 zig_module.fmtSymtab(self),
6019 });
4356 }6020 }
43576021
4358 for (self.objects.items) |index| {6022 for (self.objects.items) |index| {
...@@ -4369,16 +6033,35 @@ fn fmtDumpState(...@@ -4369,16 +6033,35 @@ fn fmtDumpState(
4369 });6033 });
4370 }6034 }
43716035
6036 for (self.shared_objects.items) |index| {
6037 const shared_object = self.file(index).?.shared_object;
6038 try writer.print("shared_object({d}) : ", .{index});
6039 try writer.print("{s}", .{shared_object.path});
6040 try writer.print(" : needed({})", .{shared_object.needed});
6041 if (!shared_object.alive) try writer.writeAll(" : [*]");
6042 try writer.writeByte('\n');
6043 try writer.print("{}\n", .{shared_object.fmtSymtab(self)});
6044 }
6045
4372 if (self.linker_defined_index) |index| {6046 if (self.linker_defined_index) |index| {
4373 const linker_defined = self.file(index).?.linker_defined;6047 const linker_defined = self.file(index).?.linker_defined;
4374 try writer.print("linker_defined({d}) : (linker defined)\n", .{index});6048 try writer.print("linker_defined({d}) : (linker defined)\n", .{index});
4375 try writer.print("{}\n", .{linker_defined.fmtSymtab(self)});6049 try writer.print("{}\n", .{linker_defined.fmtSymtab(self)});
4376 }6050 }
4377 try writer.print("{}\n", .{self.got.fmt(self)});6051 try writer.print("{}\n", .{self.got.fmt(self)});
6052 try writer.print("{}\n", .{self.zig_got.fmt(self)});
4378 try writer.writeAll("Output shdrs\n");6053 try writer.writeAll("Output shdrs\n");
4379 try writer.print("{}\n", .{self.fmtShdrs()});6054 for (self.shdrs.items, 0..) |shdr, shndx| {
4380 try writer.writeAll("Output phdrs\n");6055 try writer.print("shdr({d}) : phdr({?d}) : {}\n", .{
4381 try writer.print("{}\n", .{self.fmtPhdrs()});6056 shndx,
6057 self.phdr_to_shdr_table.get(@intCast(shndx)),
6058 self.fmtShdr(shdr),
6059 });
6060 }
6061 try writer.writeAll("\nOutput phdrs\n");
6062 for (self.phdrs.items, 0..) |phdr, phndx| {
6063 try writer.print("phdr{d} : {}\n", .{ phndx, self.fmtPhdr(phdr) });
6064 }
4382}6065}
43836066
4384/// Binary search6067/// Binary search
...@@ -4486,11 +6169,27 @@ pub const null_sym = elf.Elf64_Sym{...@@ -4486,11 +6169,27 @@ pub const null_sym = elf.Elf64_Sym{
4486 .st_size = 0,6169 .st_size = 0,
4487};6170};
44886171
6172pub const null_shdr = elf.Elf64_Shdr{
6173 .sh_name = 0,
6174 .sh_type = 0,
6175 .sh_flags = 0,
6176 .sh_addr = 0,
6177 .sh_offset = 0,
6178 .sh_size = 0,
6179 .sh_link = 0,
6180 .sh_info = 0,
6181 .sh_addralign = 0,
6182 .sh_entsize = 0,
6183};
6184
4489const SystemLib = struct {6185const SystemLib = struct {
4490 needed: bool = false,6186 needed: bool = false,
4491 path: []const u8,6187 path: []const u8,
4492};6188};
44936189
6190pub const R_X86_64_ZIG_GOT32 = elf.R_X86_64_NUM + 1;
6191pub const R_X86_64_ZIG_GOTPCREL = elf.R_X86_64_NUM + 2;
6192
4494const std = @import("std");6193const std = @import("std");
4495const build_options = @import("build_options");6194const build_options = @import("build_options");
4496const builtin = @import("builtin");6195const builtin = @import("builtin");
...@@ -4503,6 +6202,8 @@ const math = std.math;...@@ -4503,6 +6202,8 @@ const math = std.math;
4503const mem = std.mem;6202const mem = std.mem;
45046203
4505const codegen = @import("../codegen.zig");6204const codegen = @import("../codegen.zig");
6205const eh_frame = @import("Elf/eh_frame.zig");
6206const gc = @import("Elf/gc.zig");
4506const glibc = @import("../glibc.zig");6207const glibc = @import("../glibc.zig");
4507const link = @import("../link.zig");6208const link = @import("../link.zig");
4508const lldMain = @import("../main.zig").lldMain;6209const lldMain = @import("../main.zig").lldMain;
...@@ -4517,10 +6218,16 @@ const Archive = @import("Elf/Archive.zig");...@@ -4517,10 +6218,16 @@ const Archive = @import("Elf/Archive.zig");
4517pub const Atom = @import("Elf/Atom.zig");6218pub const Atom = @import("Elf/Atom.zig");
4518const Cache = std.Build.Cache;6219const Cache = std.Build.Cache;
4519const Compilation = @import("../Compilation.zig");6220const Compilation = @import("../Compilation.zig");
6221const CopyRelSection = synthetic_sections.CopyRelSection;
6222const DynamicSection = synthetic_sections.DynamicSection;
6223const DynsymSection = synthetic_sections.DynsymSection;
4520const Dwarf = @import("Dwarf.zig");6224const Dwarf = @import("Dwarf.zig");
4521const Elf = @This();6225const Elf = @This();
4522const File = @import("Elf/file.zig").File;6226const File = @import("Elf/file.zig").File;
6227const GnuHashSection = synthetic_sections.GnuHashSection;
4523const GotSection = synthetic_sections.GotSection;6228const GotSection = synthetic_sections.GotSection;
6229const GotPltSection = synthetic_sections.GotPltSection;
6230const HashSection = synthetic_sections.HashSection;
4524const LinkerDefined = @import("Elf/LinkerDefined.zig");6231const LinkerDefined = @import("Elf/LinkerDefined.zig");
4525const Liveness = @import("../Liveness.zig");6232const Liveness = @import("../Liveness.zig");
4526const LlvmObject = @import("../codegen/llvm.zig").Object;6233const LlvmObject = @import("../codegen/llvm.zig").Object;
...@@ -4528,10 +6235,15 @@ const Module = @import("../Module.zig");...@@ -4528,10 +6235,15 @@ const Module = @import("../Module.zig");
4528const Object = @import("Elf/Object.zig");6235const Object = @import("Elf/Object.zig");
4529const InternPool = @import("../InternPool.zig");6236const InternPool = @import("../InternPool.zig");
4530const Package = @import("../Package.zig");6237const Package = @import("../Package.zig");
6238const PltSection = synthetic_sections.PltSection;
6239const PltGotSection = synthetic_sections.PltGotSection;
6240const SharedObject = @import("Elf/SharedObject.zig");
4531const Symbol = @import("Elf/Symbol.zig");6241const Symbol = @import("Elf/Symbol.zig");
4532const StringTable = @import("strtab.zig").StringTable;6242const StringTable = @import("strtab.zig").StringTable;
4533const TableSection = @import("table_section.zig").TableSection;6243const TableSection = @import("table_section.zig").TableSection;
4534const Type = @import("../type.zig").Type;6244const Type = @import("../type.zig").Type;
4535const TypedValue = @import("../TypedValue.zig");6245const TypedValue = @import("../TypedValue.zig");
4536const Value = @import("../value.zig").Value;6246const Value = @import("../value.zig").Value;
6247const VerneedSection = synthetic_sections.VerneedSection;
6248const ZigGotSection = synthetic_sections.ZigGotSection;
4537const ZigModule = @import("Elf/ZigModule.zig");6249const ZigModule = @import("Elf/ZigModule.zig");
src/link/Elf/Atom.zig+677-81
...@@ -14,13 +14,13 @@ size: u64 = 0,...@@ -14,13 +14,13 @@ size: u64 = 0,
14alignment: Alignment = .@"1",14alignment: Alignment = .@"1",
1515
16/// Index of the input section.16/// Index of the input section.
17input_section_index: Index = 0,17input_section_index: u16 = 0,
1818
19/// Index of the output section.19/// Index of the output section.
20output_section_index: u16 = 0,20output_section_index: u16 = 0,
2121
22/// Index of the input section containing this atom's relocs.22/// Index of the input section containing this atom's relocs.
23relocs_section_index: Index = 0,23relocs_section_index: u16 = 0,
2424
25/// Index of this atom in the linker's atoms table.25/// Index of this atom in the linker's atoms table.
26atom_index: Index = 0,26atom_index: Index = 0,
...@@ -49,9 +49,12 @@ pub fn file(self: Atom, elf_file: *Elf) ?File {...@@ -49,9 +49,12 @@ pub fn file(self: Atom, elf_file: *Elf) ?File {
49 return elf_file.file(self.file_index);49 return elf_file.file(self.file_index);
50}50}
5151
52pub fn inputShdr(self: Atom, elf_file: *Elf) elf.Elf64_Shdr {52pub fn inputShdr(self: Atom, elf_file: *Elf) Object.ElfShdr {
53 const object = self.file(elf_file).?.object;53 return switch (self.file(elf_file).?) {
54 return object.shdrs.items[self.input_section_index];54 .object => |x| x.shdrs.items[self.input_section_index],
55 .zig_module => |x| x.inputShdr(self.atom_index, elf_file),
56 else => unreachable,
57 };
55}58}
5659
57pub fn outputShndx(self: Atom) ?u16 {60pub fn outputShndx(self: Atom) ?u16 {
...@@ -199,7 +202,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {...@@ -199,7 +202,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
199 _ = free_list.swapRemove(i);202 _ = free_list.swapRemove(i);
200 }203 }
201204
202 self.flags.allocated = true;205 self.flags.alive = true;
203}206}
204207
205pub fn shrink(self: *Atom, elf_file: *Elf) void {208pub fn shrink(self: *Atom, elf_file: *Elf) void {
...@@ -216,7 +219,6 @@ pub fn free(self: *Atom, elf_file: *Elf) void {...@@ -216,7 +219,6 @@ pub fn free(self: *Atom, elf_file: *Elf) void {
216 log.debug("freeAtom {d} ({s})", .{ self.atom_index, self.name(elf_file) });219 log.debug("freeAtom {d} ({s})", .{ self.atom_index, self.name(elf_file) });
217220
218 const gpa = elf_file.base.allocator;221 const gpa = elf_file.base.allocator;
219 const zig_module = self.file(elf_file).?.zig_module;
220 const shndx = self.outputShndx().?;222 const shndx = self.outputShndx().?;
221 const meta = elf_file.last_atom_and_free_list_table.getPtr(shndx).?;223 const meta = elf_file.last_atom_and_free_list_table.getPtr(shndx).?;
222 const free_list = &meta.free_list;224 const free_list = &meta.free_list;
...@@ -267,11 +269,13 @@ pub fn free(self: *Atom, elf_file: *Elf) void {...@@ -267,11 +269,13 @@ pub fn free(self: *Atom, elf_file: *Elf) void {
267269
268 // TODO create relocs free list270 // TODO create relocs free list
269 self.freeRelocs(elf_file);271 self.freeRelocs(elf_file);
270 assert(zig_module.atoms.swapRemove(self.atom_index));272 // TODO figure out how to free input section mappind in ZigModule
273 // const zig_module = self.file(elf_file).?.zig_module;
274 // assert(zig_module.atoms.swapRemove(self.atom_index));
271 self.* = .{};275 self.* = .{};
272}276}
273277
274pub fn relocs(self: Atom, elf_file: *Elf) error{Overflow}![]align(1) const elf.Elf64_Rela {278pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
275 return switch (self.file(elf_file).?) {279 return switch (self.file(elf_file).?) {
276 .zig_module => |x| x.relocs.items[self.relocs_section_index].items,280 .zig_module => |x| x.relocs.items[self.relocs_section_index].items,
277 .object => |x| x.getRelocs(self.relocs_section_index),281 .object => |x| x.getRelocs(self.relocs_section_index),
...@@ -279,6 +283,18 @@ pub fn relocs(self: Atom, elf_file: *Elf) error{Overflow}![]align(1) const elf.E...@@ -279,6 +283,18 @@ pub fn relocs(self: Atom, elf_file: *Elf) error{Overflow}![]align(1) const elf.E
279 };283 };
280}284}
281285
286pub fn fdes(self: Atom, elf_file: *Elf) []Fde {
287 if (self.fde_start == self.fde_end) return &[0]Fde{};
288 const object = self.file(elf_file).?.object;
289 return object.fdes.items[self.fde_start..self.fde_end];
290}
291
292pub fn markFdesDead(self: Atom, elf_file: *Elf) void {
293 for (self.fdes(elf_file)) |*fde| {
294 fde.alive = false;
295 }
296}
297
282pub fn addReloc(self: Atom, elf_file: *Elf, reloc: elf.Elf64_Rela) !void {298pub fn addReloc(self: Atom, elf_file: *Elf, reloc: elf.Elf64_Rela) !void {
283 const gpa = elf_file.base.allocator;299 const gpa = elf_file.base.allocator;
284 const file_ptr = self.file(elf_file).?;300 const file_ptr = self.file(elf_file).?;
...@@ -295,17 +311,18 @@ pub fn freeRelocs(self: Atom, elf_file: *Elf) void {...@@ -295,17 +311,18 @@ pub fn freeRelocs(self: Atom, elf_file: *Elf) void {
295 zig_module.relocs.items[self.relocs_section_index].clearRetainingCapacity();311 zig_module.relocs.items[self.relocs_section_index].clearRetainingCapacity();
296}312}
297313
298pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) error{Overflow}!bool {314pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) bool {
299 for (try self.relocs(elf_file)) |rel| {315 for (self.relocs(elf_file)) |rel| {
300 if (rel.r_type() == elf.R_X86_64_GOTTPOFF) return true;316 if (rel.r_type() == elf.R_X86_64_GOTTPOFF) return true;
301 }317 }
302 return false;318 return false;
303}319}
304320
305pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void {321pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void {
322 const is_static = elf_file.isStatic();
306 const is_dyn_lib = elf_file.isDynLib();323 const is_dyn_lib = elf_file.isDynLib();
307 const file_ptr = self.file(elf_file).?;324 const file_ptr = self.file(elf_file).?;
308 const rels = try self.relocs(elf_file);325 const rels = self.relocs(elf_file);
309 var i: usize = 0;326 var i: usize = 0;
310 while (i < rels.len) : (i += 1) {327 while (i < rels.len) : (i += 1) {
311 const rel = rels[i];328 const rel = rels[i];
...@@ -335,17 +352,24 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype...@@ -335,17 +352,24 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
335 // Report an undefined symbol.352 // Report an undefined symbol.
336 try self.reportUndefined(elf_file, symbol, symbol_index, rel, undefs);353 try self.reportUndefined(elf_file, symbol, symbol_index, rel, undefs);
337354
355 if (symbol.isIFunc(elf_file)) {
356 symbol.flags.needs_got = true;
357 symbol.flags.needs_plt = true;
358 }
359
338 // While traversing relocations, mark symbols that require special handling such as360 // While traversing relocations, mark symbols that require special handling such as
339 // pointer indirection via GOT, or a stub trampoline via PLT.361 // pointer indirection via GOT, or a stub trampoline via PLT.
340 switch (rel.r_type()) {362 switch (rel.r_type()) {
341 elf.R_X86_64_64 => {},363 elf.R_X86_64_64 => {
364 try self.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file);
365 },
342366
343 elf.R_X86_64_32,367 elf.R_X86_64_32,
344 elf.R_X86_64_32S,368 elf.R_X86_64_32S,
345 => {},369 => {
370 try self.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file);
371 },
346372
347 elf.R_X86_64_GOT32,
348 elf.R_X86_64_GOT64,
349 elf.R_X86_64_GOTPC32,373 elf.R_X86_64_GOTPC32,
350 elf.R_X86_64_GOTPC64,374 elf.R_X86_64_GOTPC64,
351 elf.R_X86_64_GOTPCREL,375 elf.R_X86_64_GOTPCREL,
...@@ -364,23 +388,14 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype...@@ -364,23 +388,14 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
364 }388 }
365 },389 },
366390
367 elf.R_X86_64_PC32 => {},391 elf.R_X86_64_PC32 => {
368392 try self.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file);
369 elf.R_X86_64_TPOFF32,
370 elf.R_X86_64_TPOFF64,
371 => {
372 if (is_dyn_lib) {
373 // TODO
374 // self.picError(symbol, rel, elf_file);
375 }
376 },393 },
377394
378 elf.R_X86_64_TLSGD => {395 elf.R_X86_64_TLSGD => {
379 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr396 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr
380397
381 if (elf_file.isStatic() or398 if (is_static or (!symbol.flags.import and !is_dyn_lib)) {
382 (!symbol.flags.import and !is_dyn_lib))
383 {
384 // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a399 // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a
385 // We skip the next relocation.400 // We skip the next relocation.
386 i += 1;401 i += 1;
...@@ -392,9 +407,21 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype...@@ -392,9 +407,21 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
392 }407 }
393 },408 },
394409
410 elf.R_X86_64_TLSLD => {
411 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr
412
413 if (is_static or !is_dyn_lib) {
414 // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a
415 // We skip the next relocation.
416 i += 1;
417 } else {
418 elf_file.got.flags.needs_tlsld = true;
419 }
420 },
421
395 elf.R_X86_64_GOTTPOFF => {422 elf.R_X86_64_GOTTPOFF => {
396 const should_relax = blk: {423 const should_relax = blk: {
397 // if (!elf_file.options.relax or is_shared or symbol.flags.import) break :blk false;424 if (is_dyn_lib or symbol.flags.import) break :blk false;
398 if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..])) break :blk false;425 if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..])) break :blk false;
399 break :blk true;426 break :blk true;
400 };427 };
...@@ -403,21 +430,255 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype...@@ -403,21 +430,255 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
403 }430 }
404 },431 },
405432
406 else => {433 elf.R_X86_64_GOTPC32_TLSDESC => {
407 var err = try elf_file.addErrorWithNotes(1);434 const should_relax = is_static or (!is_dyn_lib and !symbol.flags.import);
408 try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {}", .{435 if (!should_relax) {
409 fmtRelocType(rel.r_type()),436 symbol.flags.needs_tlsdesc = true;
410 });437 }
411 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{438 },
412 self.file(elf_file).?.fmtPath(),439
413 self.name(elf_file),440 elf.R_X86_64_TPOFF32,
414 r_offset,441 elf.R_X86_64_TPOFF64,
415 });442 => {
443 if (is_dyn_lib) try self.reportPicError(symbol, rel, elf_file);
416 },444 },
445
446 elf.R_X86_64_GOTOFF64,
447 elf.R_X86_64_DTPOFF32,
448 elf.R_X86_64_DTPOFF64,
449 elf.R_X86_64_SIZE32,
450 elf.R_X86_64_SIZE64,
451 elf.R_X86_64_TLSDESC_CALL,
452 => {},
453
454 // Zig custom relocations
455 Elf.R_X86_64_ZIG_GOT32,
456 Elf.R_X86_64_ZIG_GOTPCREL,
457 => {
458 assert(symbol.flags.has_zig_got);
459 },
460
461 else => try self.reportUnhandledRelocError(rel, elf_file),
417 }462 }
418 }463 }
419}464}
420465
466fn scanReloc(
467 self: Atom,
468 symbol: *Symbol,
469 rel: elf.Elf64_Rela,
470 action: RelocAction,
471 elf_file: *Elf,
472) error{OutOfMemory}!void {
473 const is_writeable = self.inputShdr(elf_file).sh_flags & elf.SHF_WRITE != 0;
474 const num_dynrelocs = switch (self.file(elf_file).?) {
475 .linker_defined => unreachable,
476 .shared_object => unreachable,
477 inline else => |x| &x.num_dynrelocs,
478 };
479
480 switch (action) {
481 .none => {},
482
483 .@"error" => if (symbol.isAbs(elf_file))
484 try self.reportNoPicError(symbol, rel, elf_file)
485 else
486 try self.reportPicError(symbol, rel, elf_file),
487
488 .copyrel => {
489 if (elf_file.base.options.z_nocopyreloc) {
490 if (symbol.isAbs(elf_file))
491 try self.reportNoPicError(symbol, rel, elf_file)
492 else
493 try self.reportPicError(symbol, rel, elf_file);
494 }
495 symbol.flags.needs_copy_rel = true;
496 },
497
498 .dyn_copyrel => {
499 if (is_writeable or elf_file.base.options.z_nocopyreloc) {
500 if (!is_writeable) {
501 if (elf_file.base.options.z_notext) {
502 elf_file.has_text_reloc = true;
503 } else {
504 try self.reportTextRelocError(symbol, rel, elf_file);
505 }
506 }
507 num_dynrelocs.* += 1;
508 } else {
509 symbol.flags.needs_copy_rel = true;
510 }
511 },
512
513 .plt => {
514 symbol.flags.needs_plt = true;
515 },
516
517 .cplt => {
518 symbol.flags.needs_plt = true;
519 symbol.flags.is_canonical = true;
520 },
521
522 .dyn_cplt => {
523 if (is_writeable) {
524 num_dynrelocs.* += 1;
525 } else {
526 symbol.flags.needs_plt = true;
527 symbol.flags.is_canonical = true;
528 }
529 },
530
531 .dynrel, .baserel, .ifunc => {
532 if (!is_writeable) {
533 if (elf_file.base.options.z_notext) {
534 elf_file.has_text_reloc = true;
535 } else {
536 try self.reportTextRelocError(symbol, rel, elf_file);
537 }
538 }
539 num_dynrelocs.* += 1;
540
541 if (action == .ifunc) elf_file.num_ifunc_dynrelocs += 1;
542 },
543 }
544}
545
546const RelocAction = enum {
547 none,
548 @"error",
549 copyrel,
550 dyn_copyrel,
551 plt,
552 dyn_cplt,
553 cplt,
554 dynrel,
555 baserel,
556 ifunc,
557};
558
559fn pcRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction {
560 // zig fmt: off
561 const table: [3][4]RelocAction = .{
562 // Abs Local Import data Import func
563 .{ .@"error", .none, .@"error", .plt }, // Shared object
564 .{ .@"error", .none, .copyrel, .plt }, // PIE
565 .{ .none, .none, .copyrel, .cplt }, // Non-PIE
566 };
567 // zig fmt: on
568 const output = outputType(elf_file);
569 const data = dataType(symbol, elf_file);
570 return table[output][data];
571}
572
573fn absRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction {
574 // zig fmt: off
575 const table: [3][4]RelocAction = .{
576 // Abs Local Import data Import func
577 .{ .none, .@"error", .@"error", .@"error" }, // Shared object
578 .{ .none, .@"error", .@"error", .@"error" }, // PIE
579 .{ .none, .none, .copyrel, .cplt }, // Non-PIE
580 };
581 // zig fmt: on
582 const output = outputType(elf_file);
583 const data = dataType(symbol, elf_file);
584 return table[output][data];
585}
586
587fn dynAbsRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction {
588 if (symbol.isIFunc(elf_file)) return .ifunc;
589 // zig fmt: off
590 const table: [3][4]RelocAction = .{
591 // Abs Local Import data Import func
592 .{ .none, .baserel, .dynrel, .dynrel }, // Shared object
593 .{ .none, .baserel, .dynrel, .dynrel }, // PIE
594 .{ .none, .none, .dyn_copyrel, .dyn_cplt }, // Non-PIE
595 };
596 // zig fmt: on
597 const output = outputType(elf_file);
598 const data = dataType(symbol, elf_file);
599 return table[output][data];
600}
601
602fn outputType(elf_file: *Elf) u2 {
603 return switch (elf_file.base.options.output_mode) {
604 .Obj => unreachable,
605 .Lib => 0,
606 .Exe => if (elf_file.base.options.pie) 1 else 2,
607 };
608}
609
610fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 {
611 if (symbol.isAbs(elf_file)) return 0;
612 if (!symbol.flags.import) return 1;
613 if (symbol.type(elf_file) != elf.STT_FUNC) return 2;
614 return 3;
615}
616
617fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) error{OutOfMemory}!void {
618 var err = try elf_file.addErrorWithNotes(1);
619 try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {} at offset 0x{x}", .{
620 fmtRelocType(rel.r_type()),
621 rel.r_offset,
622 });
623 try err.addNote(elf_file, "in {}:{s}", .{
624 self.file(elf_file).?.fmtPath(),
625 self.name(elf_file),
626 });
627}
628
629fn reportTextRelocError(
630 self: Atom,
631 symbol: *const Symbol,
632 rel: elf.Elf64_Rela,
633 elf_file: *Elf,
634) error{OutOfMemory}!void {
635 var err = try elf_file.addErrorWithNotes(1);
636 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
637 rel.r_offset,
638 symbol.name(elf_file),
639 });
640 try err.addNote(elf_file, "in {}:{s}", .{
641 self.file(elf_file).?.fmtPath(),
642 self.name(elf_file),
643 });
644}
645
646fn reportPicError(
647 self: Atom,
648 symbol: *const Symbol,
649 rel: elf.Elf64_Rela,
650 elf_file: *Elf,
651) error{OutOfMemory}!void {
652 var err = try elf_file.addErrorWithNotes(2);
653 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
654 rel.r_offset,
655 symbol.name(elf_file),
656 });
657 try err.addNote(elf_file, "in {}:{s}", .{
658 self.file(elf_file).?.fmtPath(),
659 self.name(elf_file),
660 });
661 try err.addNote(elf_file, "recompile with -fPIC", .{});
662}
663
664fn reportNoPicError(
665 self: Atom,
666 symbol: *const Symbol,
667 rel: elf.Elf64_Rela,
668 elf_file: *Elf,
669) error{OutOfMemory}!void {
670 var err = try elf_file.addErrorWithNotes(2);
671 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
672 rel.r_offset,
673 symbol.name(elf_file),
674 });
675 try err.addNote(elf_file, "in {}:{s}", .{
676 self.file(elf_file).?.fmtPath(),
677 self.name(elf_file),
678 });
679 try err.addNote(elf_file, "recompile with -fno-PIC", .{});
680}
681
421// This function will report any undefined non-weak symbols that are not imports.682// This function will report any undefined non-weak symbols that are not imports.
422fn reportUndefined(683fn reportUndefined(
423 self: Atom,684 self: Atom,
...@@ -447,15 +708,14 @@ fn reportUndefined(...@@ -447,15 +708,14 @@ fn reportUndefined(
447 }708 }
448}709}
449710
450/// TODO mark relocs dirty711pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
451pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {
452 relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) });712 relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) });
453713
454 const file_ptr = self.file(elf_file).?;714 const file_ptr = self.file(elf_file).?;
455 var stream = std.io.fixedBufferStream(code);715 var stream = std.io.fixedBufferStream(code);
456 const cwriter = stream.writer();716 const cwriter = stream.writer();
457717
458 const rels = try self.relocs(elf_file);718 const rels = self.relocs(elf_file);
459 var i: usize = 0;719 var i: usize = 0;
460 while (i < rels.len) : (i += 1) {720 while (i < rels.len) : (i += 1) {
461 const rel = rels[i];721 const rel = rels[i];
...@@ -488,19 +748,22 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {...@@ -488,19 +748,22 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {
488 null;748 null;
489 break :blk if (shndx) |index| @as(i64, @intCast(elf_file.shdrs.items[index].sh_addr)) else 0;749 break :blk if (shndx) |index| @as(i64, @intCast(elf_file.shdrs.items[index].sh_addr)) else 0;
490 };750 };
751 // Address of the .zig.got table entry if any.
752 const ZIG_GOT = @as(i64, @intCast(target.zigGotAddress(elf_file)));
491 // Relative offset to the start of the global offset table.753 // Relative offset to the start of the global offset table.
492 const G = @as(i64, @intCast(target.gotAddress(elf_file))) - GOT;754 const G = @as(i64, @intCast(target.gotAddress(elf_file))) - GOT;
493 // // Address of the thread pointer.755 // // Address of the thread pointer.
494 const TP = @as(i64, @intCast(elf_file.tpAddress()));756 const TP = @as(i64, @intCast(elf_file.tpAddress()));
495 // // Address of the dynamic thread pointer.757 // Address of the dynamic thread pointer.
496 // const DTP = @as(i64, @intCast(elf_file.dtpAddress()));758 const DTP = @as(i64, @intCast(elf_file.dtpAddress()));
497759
498 relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ({s})", .{760 relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ZG({x}) ({s})", .{
499 fmtRelocType(r_type),761 fmtRelocType(r_type),
500 r_offset,762 r_offset,
501 P,763 P,
502 S + A,764 S + A,
503 G + GOT + A,765 G + GOT + A,
766 ZIG_GOT + A,
504 target.name(elf_file),767 target.name(elf_file),
505 });768 });
506769
...@@ -509,18 +772,20 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {...@@ -509,18 +772,20 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {
509 switch (rel.r_type()) {772 switch (rel.r_type()) {
510 elf.R_X86_64_NONE => unreachable,773 elf.R_X86_64_NONE => unreachable,
511774
512 elf.R_X86_64_64 => try cwriter.writeIntLittle(i64, S + A),775 elf.R_X86_64_64 => {
513776 try self.resolveDynAbsReloc(
514 elf.R_X86_64_32 => try cwriter.writeIntLittle(u32, @as(u32, @truncate(@as(u64, @intCast(S + A))))),777 target,
515 elf.R_X86_64_32S => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A))),778 rel,
779 dynAbsRelocAction(target, elf_file),
780 elf_file,
781 cwriter,
782 );
783 },
516784
517 elf.R_X86_64_PLT32,785 elf.R_X86_64_PLT32,
518 elf.R_X86_64_PC32,786 elf.R_X86_64_PC32,
519 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - P))),787 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - P))),
520788
521 elf.R_X86_64_GOT32 => try cwriter.writeIntLittle(u32, @as(u32, @intCast(G + GOT + A))),
522 elf.R_X86_64_GOT64 => try cwriter.writeIntLittle(u64, @as(u64, @intCast(G + GOT + A))),
523
524 elf.R_X86_64_GOTPCREL => try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A - P))),789 elf.R_X86_64_GOTPCREL => try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A - P))),
525 elf.R_X86_64_GOTPC32 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(GOT + A - P))),790 elf.R_X86_64_GOTPC32 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(GOT + A - P))),
526 elf.R_X86_64_GOTPC64 => try cwriter.writeIntLittle(i64, GOT + A - P),791 elf.R_X86_64_GOTPC64 => try cwriter.writeIntLittle(i64, GOT + A - P),
...@@ -543,18 +808,22 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {...@@ -543,18 +808,22 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {
543 try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A - P)));808 try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A - P)));
544 },809 },
545810
811 elf.R_X86_64_32 => try cwriter.writeIntLittle(u32, @as(u32, @truncate(@as(u64, @intCast(S + A))))),
812 elf.R_X86_64_32S => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A))),
813
546 elf.R_X86_64_TPOFF32 => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A - TP))),814 elf.R_X86_64_TPOFF32 => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A - TP))),
547 elf.R_X86_64_TPOFF64 => try cwriter.writeIntLittle(i64, S + A - TP),815 elf.R_X86_64_TPOFF64 => try cwriter.writeIntLittle(i64, S + A - TP),
548816
817 elf.R_X86_64_DTPOFF32 => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A - DTP))),
818 elf.R_X86_64_DTPOFF64 => try cwriter.writeIntLittle(i64, S + A - DTP),
819
549 elf.R_X86_64_TLSGD => {820 elf.R_X86_64_TLSGD => {
550 if (target.flags.has_tlsgd) {821 if (target.flags.has_tlsgd) {
551 // TODO822 const S_ = @as(i64, @intCast(target.tlsGdAddress(elf_file)));
552 // const S_ = @as(i64, @intCast(target.tlsGdAddress(elf_file)));823 try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P)));
553 // try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P)));
554 } else if (target.flags.has_gottp) {824 } else if (target.flags.has_gottp) {
555 // TODO825 const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file)));
556 // const S_ = @as(i64, @intCast(target.getGotTpAddress(elf_file)));826 try x86_64.relaxTlsGdToIe(self, rels[i .. i + 2], @intCast(S_ - P), elf_file, &stream);
557 // try relaxTlsGdToIe(relocs[i .. i + 2], @intCast(S_ - P), elf_file, &stream);
558 i += 1;827 i += 1;
559 } else {828 } else {
560 try x86_64.relaxTlsGdToLe(829 try x86_64.relaxTlsGdToLe(
...@@ -568,22 +837,245 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {...@@ -568,22 +837,245 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {
568 }837 }
569 },838 },
570839
840 elf.R_X86_64_TLSLD => {
841 if (elf_file.got.tlsld_index) |entry_index| {
842 const tlsld_entry = elf_file.got.entries.items[entry_index];
843 const S_ = @as(i64, @intCast(tlsld_entry.address(elf_file)));
844 try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P)));
845 } else {
846 try x86_64.relaxTlsLdToLe(
847 self,
848 rels[i .. i + 2],
849 @as(i32, @intCast(TP - @as(i64, @intCast(elf_file.tlsAddress())))),
850 elf_file,
851 &stream,
852 );
853 i += 1;
854 }
855 },
856
857 elf.R_X86_64_GOTPC32_TLSDESC => {
858 if (target.flags.has_tlsdesc) {
859 const S_ = @as(i64, @intCast(target.tlsDescAddress(elf_file)));
860 try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P)));
861 } else {
862 try x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]);
863 try cwriter.writeIntLittle(i32, @as(i32, @intCast(S - TP)));
864 }
865 },
866
867 elf.R_X86_64_TLSDESC_CALL => if (!target.flags.has_tlsdesc) {
868 // call -> nop
869 try cwriter.writeAll(&.{ 0x66, 0x90 });
870 },
871
571 elf.R_X86_64_GOTTPOFF => {872 elf.R_X86_64_GOTTPOFF => {
572 if (target.flags.has_gottp) {873 if (target.flags.has_gottp) {
573 // TODO874 const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file)));
574 // const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file)));875 try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P)));
575 // try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P)));
576 } else {876 } else {
577 x86_64.relaxGotTpOff(code[r_offset - 3 ..]) catch unreachable;877 x86_64.relaxGotTpOff(code[r_offset - 3 ..]) catch unreachable;
578 try cwriter.writeIntLittle(i32, @as(i32, @intCast(S - TP)));878 try cwriter.writeIntLittle(i32, @as(i32, @intCast(S - TP)));
579 }879 }
580 },880 },
581881
882 // Zig custom relocations
883 Elf.R_X86_64_ZIG_GOT32 => try cwriter.writeIntLittle(u32, @as(u32, @intCast(ZIG_GOT + A))),
884 Elf.R_X86_64_ZIG_GOTPCREL => try cwriter.writeIntLittle(i32, @as(i32, @intCast(ZIG_GOT + A - P))),
885
582 else => {},886 else => {},
583 }887 }
584 }888 }
585}889}
586890
891fn resolveDynAbsReloc(
892 self: Atom,
893 target: *const Symbol,
894 rel: elf.Elf64_Rela,
895 action: RelocAction,
896 elf_file: *Elf,
897 writer: anytype,
898) !void {
899 const P = self.value + rel.r_offset;
900 const A = rel.r_addend;
901 const S = @as(i64, @intCast(target.address(.{}, elf_file)));
902 const is_writeable = self.inputShdr(elf_file).sh_flags & elf.SHF_WRITE != 0;
903
904 const num_dynrelocs = switch (self.file(elf_file).?) {
905 .linker_defined => unreachable,
906 .shared_object => unreachable,
907 inline else => |x| x.num_dynrelocs,
908 };
909 try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, num_dynrelocs);
910
911 switch (action) {
912 .@"error",
913 .plt,
914 => unreachable,
915
916 .copyrel,
917 .cplt,
918 .none,
919 => try writer.writeIntLittle(i32, @as(i32, @truncate(S + A))),
920
921 .dyn_copyrel => {
922 if (is_writeable or elf_file.base.options.z_nocopyreloc) {
923 elf_file.addRelaDynAssumeCapacity(.{
924 .offset = P,
925 .sym = target.extra(elf_file).?.dynamic,
926 .type = elf.R_X86_64_64,
927 .addend = A,
928 });
929 try applyDynamicReloc(A, elf_file, writer);
930 } else {
931 try writer.writeIntLittle(i32, @as(i32, @truncate(S + A)));
932 }
933 },
934
935 .dyn_cplt => {
936 if (is_writeable) {
937 elf_file.addRelaDynAssumeCapacity(.{
938 .offset = P,
939 .sym = target.extra(elf_file).?.dynamic,
940 .type = elf.R_X86_64_64,
941 .addend = A,
942 });
943 try applyDynamicReloc(A, elf_file, writer);
944 } else {
945 try writer.writeIntLittle(i32, @as(i32, @truncate(S + A)));
946 }
947 },
948
949 .dynrel => {
950 elf_file.addRelaDynAssumeCapacity(.{
951 .offset = P,
952 .sym = target.extra(elf_file).?.dynamic,
953 .type = elf.R_X86_64_64,
954 .addend = A,
955 });
956 try applyDynamicReloc(A, elf_file, writer);
957 },
958
959 .baserel => {
960 elf_file.addRelaDynAssumeCapacity(.{
961 .offset = P,
962 .type = elf.R_X86_64_RELATIVE,
963 .addend = S + A,
964 });
965 try applyDynamicReloc(S + A, elf_file, writer);
966 },
967
968 .ifunc => {
969 const S_ = @as(i64, @intCast(target.address(.{ .plt = false }, elf_file)));
970 elf_file.addRelaDynAssumeCapacity(.{
971 .offset = P,
972 .type = elf.R_X86_64_IRELATIVE,
973 .addend = S_ + A,
974 });
975 try applyDynamicReloc(S_ + A, elf_file, writer);
976 },
977 }
978}
979
980fn applyDynamicReloc(value: i64, elf_file: *Elf, writer: anytype) !void {
981 _ = elf_file;
982 // if (elf_file.options.apply_dynamic_relocs) {
983 try writer.writeIntLittle(i64, value);
984 // }
985}
986
987pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void {
988 relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) });
989
990 const file_ptr = self.file(elf_file).?;
991 var stream = std.io.fixedBufferStream(code);
992 const cwriter = stream.writer();
993
994 const rels = self.relocs(elf_file);
995 var i: usize = 0;
996 while (i < rels.len) : (i += 1) {
997 const rel = rels[i];
998 const r_type = rel.r_type();
999 if (r_type == elf.R_X86_64_NONE) continue;
1000
1001 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
1002
1003 const target_index = switch (file_ptr) {
1004 .zig_module => |x| x.symbol(rel.r_sym()),
1005 .object => |x| x.symbols.items[rel.r_sym()],
1006 else => unreachable,
1007 };
1008 const target = elf_file.symbol(target_index);
1009
1010 // Check for violation of One Definition Rule for COMDATs.
1011 if (target.file(elf_file) == null) {
1012 // TODO convert into an error
1013 log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{
1014 file_ptr.fmtPath(),
1015 self.name(elf_file),
1016 target.name(elf_file),
1017 });
1018 continue;
1019 }
1020
1021 // Report an undefined symbol.
1022 try self.reportUndefined(elf_file, target, target_index, rel, undefs);
1023
1024 // We will use equation format to resolve relocations:
1025 // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/
1026 //
1027 const P = @as(i64, @intCast(self.value + rel.r_offset));
1028 // Addend from the relocation.
1029 const A = rel.r_addend;
1030 // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub.
1031 const S = @as(i64, @intCast(target.address(.{}, elf_file)));
1032 // Address of the global offset table.
1033 const GOT = blk: {
1034 const shndx = if (elf_file.got_plt_section_index) |shndx|
1035 shndx
1036 else if (elf_file.got_section_index) |shndx|
1037 shndx
1038 else
1039 null;
1040 break :blk if (shndx) |index| @as(i64, @intCast(elf_file.shdrs.items[index].sh_addr)) else 0;
1041 };
1042 // Address of the dynamic thread pointer.
1043 const DTP = @as(i64, @intCast(elf_file.dtpAddress()));
1044
1045 relocs_log.debug(" {s}: {x}: [{x} => {x}] ({s})", .{
1046 fmtRelocType(r_type),
1047 rel.r_offset,
1048 P,
1049 S + A,
1050 target.name(elf_file),
1051 });
1052
1053 try stream.seekTo(r_offset);
1054
1055 switch (r_type) {
1056 elf.R_X86_64_NONE => unreachable,
1057 elf.R_X86_64_8 => try cwriter.writeIntLittle(u8, @as(u8, @bitCast(@as(i8, @intCast(S + A))))),
1058 elf.R_X86_64_16 => try cwriter.writeIntLittle(u16, @as(u16, @bitCast(@as(i16, @intCast(S + A))))),
1059 elf.R_X86_64_32 => try cwriter.writeIntLittle(u32, @as(u32, @bitCast(@as(i32, @intCast(S + A))))),
1060 elf.R_X86_64_32S => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A))),
1061 elf.R_X86_64_64 => try cwriter.writeIntLittle(i64, S + A),
1062 elf.R_X86_64_DTPOFF32 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - DTP))),
1063 elf.R_X86_64_DTPOFF64 => try cwriter.writeIntLittle(i64, S + A - DTP),
1064 elf.R_X86_64_GOTOFF64 => try cwriter.writeIntLittle(i64, S + A - GOT),
1065 elf.R_X86_64_GOTPC64 => try cwriter.writeIntLittle(i64, GOT + A),
1066 elf.R_X86_64_SIZE32 => {
1067 const size = @as(i64, @intCast(target.elfSym(elf_file).st_size));
1068 try cwriter.writeIntLittle(u32, @as(u32, @bitCast(@as(i32, @intCast(size + A)))));
1069 },
1070 elf.R_X86_64_SIZE64 => {
1071 const size = @as(i64, @intCast(target.elfSym(elf_file).st_size));
1072 try cwriter.writeIntLittle(i64, @as(i64, @intCast(size + A)));
1073 },
1074 else => try self.reportUnhandledRelocError(rel, elf_file),
1075 }
1076 }
1077}
1078
587pub fn fmtRelocType(r_type: u32) std.fmt.Formatter(formatRelocType) {1079pub fn fmtRelocType(r_type: u32) std.fmt.Formatter(formatRelocType) {
588 return .{ .data = r_type };1080 return .{ .data = r_type };
589}1081}
...@@ -639,6 +1131,9 @@ fn formatRelocType(...@@ -639,6 +1131,9 @@ fn formatRelocType(
639 elf.R_X86_64_GOTPCRELX => "R_X86_64_GOTPCRELX",1131 elf.R_X86_64_GOTPCRELX => "R_X86_64_GOTPCRELX",
640 elf.R_X86_64_REX_GOTPCRELX => "R_X86_64_REX_GOTPCRELX",1132 elf.R_X86_64_REX_GOTPCRELX => "R_X86_64_REX_GOTPCRELX",
641 elf.R_X86_64_NUM => "R_X86_64_NUM",1133 elf.R_X86_64_NUM => "R_X86_64_NUM",
1134 // Zig custom relocations
1135 Elf.R_X86_64_ZIG_GOT32 => "R_X86_64_ZIG_GOT32",
1136 Elf.R_X86_64_ZIG_GOTPCREL => "R_X86_64_ZIG_GOTPCREL",
642 else => "R_X86_64_UNKNOWN",1137 else => "R_X86_64_UNKNOWN",
643 };1138 };
644 try writer.print("{s}", .{str});1139 try writer.print("{s}", .{str});
...@@ -679,39 +1174,32 @@ fn format2(...@@ -679,39 +1174,32 @@ fn format2(
679 _ = unused_fmt_string;1174 _ = unused_fmt_string;
680 const atom = ctx.atom;1175 const atom = ctx.atom;
681 const elf_file = ctx.elf_file;1176 const elf_file = ctx.elf_file;
682 try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x})", .{1177 try writer.print("atom({d}) : {s} : @{x} : shdr({d}) : align({x}) : size({x})", .{
683 atom.atom_index, atom.name(elf_file), atom.value,1178 atom.atom_index, atom.name(elf_file), atom.value,
684 atom.output_section_index, atom.alignment, atom.size,1179 atom.output_section_index, atom.alignment, atom.size,
685 });1180 });
686 // if (atom.fde_start != atom.fde_end) {1181 if (atom.fde_start != atom.fde_end) {
687 // try writer.writeAll(" : fdes{ ");1182 try writer.writeAll(" : fdes{ ");
688 // for (atom.getFdes(elf_file), atom.fde_start..) |fde, i| {1183 for (atom.fdes(elf_file), atom.fde_start..) |fde, i| {
689 // try writer.print("{d}", .{i});1184 try writer.print("{d}", .{i});
690 // if (!fde.alive) try writer.writeAll("([*])");1185 if (!fde.alive) try writer.writeAll("([*])");
691 // if (i < atom.fde_end - 1) try writer.writeAll(", ");1186 if (i < atom.fde_end - 1) try writer.writeAll(", ");
692 // }1187 }
693 // try writer.writeAll(" }");1188 try writer.writeAll(" }");
694 // }1189 }
695 const gc_sections = if (elf_file.base.options.gc_sections) |gc_sections| gc_sections else false;1190 if (!atom.flags.alive) {
696 if (gc_sections and !atom.flags.alive) {
697 try writer.writeAll(" : [*]");1191 try writer.writeAll(" : [*]");
698 }1192 }
699}1193}
7001194
701// TODO this has to be u32 but for now, to avoid redesigning elfSym machinery for1195pub const Index = u32;
702// ZigModule, keep it at u16 with the intention of bumping it to u32 in the near
703// future.
704pub const Index = u16;
7051196
706pub const Flags = packed struct {1197pub const Flags = packed struct {
707 /// Specifies whether this atom is alive or has been garbage collected.1198 /// Specifies whether this atom is alive or has been garbage collected.
708 alive: bool = false,1199 alive: bool = true,
7091200
710 /// Specifies if the atom has been visited during garbage collection.1201 /// Specifies if the atom has been visited during garbage collection.
711 visited: bool = false,1202 visited: bool = false,
712
713 /// Specifies whether this atom has been allocated in the output section.
714 allocated: bool = false,
715};1203};
7161204
717const x86_64 = struct {1205const x86_64 = struct {
...@@ -745,6 +1233,95 @@ const x86_64 = struct {...@@ -745,6 +1233,95 @@ const x86_64 = struct {
745 }1233 }
746 }1234 }
7471235
1236 pub fn relaxTlsGdToIe(
1237 self: Atom,
1238 rels: []align(1) const elf.Elf64_Rela,
1239 value: i32,
1240 elf_file: *Elf,
1241 stream: anytype,
1242 ) !void {
1243 assert(rels.len == 2);
1244 const writer = stream.writer();
1245 switch (rels[1].r_type()) {
1246 elf.R_X86_64_PC32,
1247 elf.R_X86_64_PLT32,
1248 => {
1249 var insts = [_]u8{
1250 0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // movq %fs:0,%rax
1251 0x48, 0x03, 0x05, 0, 0, 0, 0, // add foo@gottpoff(%rip), %rax
1252 };
1253 std.mem.writeIntLittle(i32, insts[12..][0..4], value - 12);
1254 try stream.seekBy(-4);
1255 try writer.writeAll(&insts);
1256 },
1257
1258 else => {
1259 var err = try elf_file.addErrorWithNotes(1);
1260 try err.addMsg(elf_file, "fatal linker error: rewrite {} when followed by {}", .{
1261 fmtRelocType(rels[0].r_type()),
1262 fmtRelocType(rels[1].r_type()),
1263 });
1264 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1265 self.file(elf_file).?.fmtPath(),
1266 self.name(elf_file),
1267 rels[0].r_offset,
1268 });
1269 },
1270 }
1271 }
1272
1273 pub fn relaxTlsLdToLe(
1274 self: Atom,
1275 rels: []align(1) const elf.Elf64_Rela,
1276 value: i32,
1277 elf_file: *Elf,
1278 stream: anytype,
1279 ) !void {
1280 assert(rels.len == 2);
1281 const writer = stream.writer();
1282 switch (rels[1].r_type()) {
1283 elf.R_X86_64_PC32,
1284 elf.R_X86_64_PLT32,
1285 => {
1286 var insts = [_]u8{
1287 0x31, 0xc0, // xor %eax, %eax
1288 0x64, 0x48, 0x8b, 0, // mov %fs:(%rax), %rax
1289 0x48, 0x2d, 0, 0, 0, 0, // sub $tls_size, %rax
1290 };
1291 std.mem.writeIntLittle(i32, insts[8..][0..4], value);
1292 try stream.seekBy(-3);
1293 try writer.writeAll(&insts);
1294 },
1295
1296 elf.R_X86_64_GOTPCREL,
1297 elf.R_X86_64_GOTPCRELX,
1298 => {
1299 var insts = [_]u8{
1300 0x31, 0xc0, // xor %eax, %eax
1301 0x64, 0x48, 0x8b, 0, // mov %fs:(%rax), %rax
1302 0x48, 0x2d, 0, 0, 0, 0, // sub $tls_size, %rax
1303 0x90, // nop
1304 };
1305 std.mem.writeIntLittle(i32, insts[8..][0..4], value);
1306 try stream.seekBy(-3);
1307 try writer.writeAll(&insts);
1308 },
1309
1310 else => {
1311 var err = try elf_file.addErrorWithNotes(1);
1312 try err.addMsg(elf_file, "fatal linker error: rewrite {} when followed by {}", .{
1313 fmtRelocType(rels[0].r_type()),
1314 fmtRelocType(rels[1].r_type()),
1315 });
1316 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1317 self.file(elf_file).?.fmtPath(),
1318 self.name(elf_file),
1319 rels[0].r_offset,
1320 });
1321 },
1322 }
1323 }
1324
748 pub fn canRelaxGotTpOff(code: []const u8) bool {1325 pub fn canRelaxGotTpOff(code: []const u8) bool {
749 const old_inst = disassemble(code) orelse return false;1326 const old_inst = disassemble(code) orelse return false;
750 switch (old_inst.encoding.mnemonic) {1327 switch (old_inst.encoding.mnemonic) {
...@@ -776,6 +1353,22 @@ const x86_64 = struct {...@@ -776,6 +1353,22 @@ const x86_64 = struct {
776 }1353 }
777 }1354 }
7781355
1356 pub fn relaxGotPcTlsDesc(code: []u8) !void {
1357 const old_inst = disassemble(code) orelse return error.RelaxFail;
1358 switch (old_inst.encoding.mnemonic) {
1359 .lea => {
1360 const inst = try Instruction.new(old_inst.prefix, .mov, &.{
1361 old_inst.ops[0],
1362 // TODO: hack to force imm32s in the assembler
1363 .{ .imm = Immediate.s(-129) },
1364 });
1365 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
1366 encode(&.{inst}, code) catch return error.RelaxFail;
1367 },
1368 else => return error.RelaxFail,
1369 }
1370 }
1371
779 pub fn relaxTlsGdToLe(1372 pub fn relaxTlsGdToLe(
780 self: Atom,1373 self: Atom,
781 rels: []align(1) const elf.Elf64_Rela,1374 rels: []align(1) const elf.Elf64_Rela,
...@@ -843,11 +1436,14 @@ const x86_64 = struct {...@@ -843,11 +1436,14 @@ const x86_64 = struct {
843const std = @import("std");1436const std = @import("std");
844const assert = std.debug.assert;1437const assert = std.debug.assert;
845const elf = std.elf;1438const elf = std.elf;
1439const eh_frame = @import("eh_frame.zig");
846const log = std.log.scoped(.link);1440const log = std.log.scoped(.link);
847const relocs_log = std.log.scoped(.link_relocs);1441const relocs_log = std.log.scoped(.link_relocs);
8481442
849const Allocator = std.mem.Allocator;1443const Allocator = std.mem.Allocator;
850const Atom = @This();1444const Atom = @This();
851const Elf = @import("../Elf.zig");1445const Elf = @import("../Elf.zig");
1446const Fde = eh_frame.Fde;
852const File = @import("file.zig").File;1447const File = @import("file.zig").File;
1448const Object = @import("Object.zig");
853const Symbol = @import("Symbol.zig");1449const Symbol = @import("Symbol.zig");
src/link/Elf/Object.zig+150-94
...@@ -4,7 +4,7 @@ data: []const u8,...@@ -4,7 +4,7 @@ data: []const u8,
4index: File.Index,4index: File.Index,
55
6header: ?elf.Elf64_Ehdr = null,6header: ?elf.Elf64_Ehdr = null,
7shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{},7shdrs: std.ArrayListUnmanaged(ElfShdr) = .{},
8strings: StringTable(.object_strings) = .{},8strings: StringTable(.object_strings) = .{},
9symtab: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{},9symtab: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{},
10strtab: []const u8 = &[0]u8{},10strtab: []const u8 = &[0]u8{},
...@@ -59,8 +59,13 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {...@@ -59,8 +59,13 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
59 [*]align(1) const elf.Elf64_Shdr,59 [*]align(1) const elf.Elf64_Shdr,
60 @ptrCast(self.data.ptr + shoff),60 @ptrCast(self.data.ptr + shoff),
61 )[0..self.header.?.e_shnum];61 )[0..self.header.?.e_shnum];
62 try self.shdrs.appendUnalignedSlice(gpa, shdrs);62 try self.shdrs.ensureTotalCapacityPrecise(gpa, shdrs.len);
63 try self.strings.buffer.appendSlice(gpa, try self.shdrContents(self.header.?.e_shstrndx));63
64 for (shdrs) |shdr| {
65 self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr));
66 }
67
68 try self.strings.buffer.appendSlice(gpa, self.shdrContents(self.header.?.e_shstrndx));
6469
65 const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {70 const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
66 elf.SHT_SYMTAB => break @as(u16, @intCast(i)),71 elf.SHT_SYMTAB => break @as(u16, @intCast(i)),
...@@ -71,21 +76,21 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {...@@ -71,21 +76,21 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
71 const shdr = shdrs[index];76 const shdr = shdrs[index];
72 self.first_global = shdr.sh_info;77 self.first_global = shdr.sh_info;
7378
74 const symtab = try self.shdrContents(index);79 const symtab = self.shdrContents(index);
75 const nsyms = @divExact(symtab.len, @sizeOf(elf.Elf64_Sym));80 const nsyms = @divExact(symtab.len, @sizeOf(elf.Elf64_Sym));
76 self.symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(symtab.ptr))[0..nsyms];81 self.symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(symtab.ptr))[0..nsyms];
77 self.strtab = try self.shdrContents(@as(u16, @intCast(shdr.sh_link)));82 self.strtab = self.shdrContents(@as(u16, @intCast(shdr.sh_link)));
78 }83 }
7984
80 try self.initAtoms(elf_file);85 try self.initAtoms(elf_file);
81 try self.initSymtab(elf_file);86 try self.initSymtab(elf_file);
8287
83 // for (self.shdrs.items, 0..) |shdr, i| {88 for (self.shdrs.items, 0..) |shdr, i| {
84 // const atom = elf_file.atom(self.atoms.items[i]) orelse continue;89 const atom = elf_file.atom(self.atoms.items[i]) orelse continue;
85 // if (!atom.alive) continue;90 if (!atom.flags.alive) continue;
86 // if (shdr.sh_type == elf.SHT_X86_64_UNWIND or mem.eql(u8, atom.name(elf_file), ".eh_frame"))91 if (shdr.sh_type == elf.SHT_X86_64_UNWIND or mem.eql(u8, atom.name(elf_file), ".eh_frame"))
87 // try self.parseEhFrame(@as(u16, @intCast(i)), elf_file);92 try self.parseEhFrame(@as(u16, @intCast(i)), elf_file);
88 // }93 }
89}94}
9095
91fn initAtoms(self: *Object, elf_file: *Elf) !void {96fn initAtoms(self: *Object, elf_file: *Elf) !void {
...@@ -115,7 +120,7 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {...@@ -115,7 +120,7 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
115 };120 };
116121
117 const shndx = @as(u16, @intCast(i));122 const shndx = @as(u16, @intCast(i));
118 const group_raw_data = try self.shdrContents(shndx);123 const group_raw_data = self.shdrContents(shndx);
119 const group_nmembers = @divExact(group_raw_data.len, @sizeOf(u32));124 const group_nmembers = @divExact(group_raw_data.len, @sizeOf(u32));
120 const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers];125 const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers];
121126
...@@ -125,7 +130,10 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {...@@ -125,7 +130,10 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
125 continue;130 continue;
126 }131 }
127132
128 const group_signature_off = try self.strings.insert(elf_file.base.allocator, group_signature);133 // Note the assumption about a global strtab used here to disambiguate common
134 // COMDAT owners.
135 const gpa = elf_file.base.allocator;
136 const group_signature_off = try elf_file.strtab.insert(gpa, group_signature);
129 const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature_off);137 const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature_off);
130 const comdat_group_index = try elf_file.addComdatGroup();138 const comdat_group_index = try elf_file.addComdatGroup();
131 const comdat_group = elf_file.comdatGroup(comdat_group_index);139 const comdat_group = elf_file.comdatGroup(comdat_group_index);
...@@ -133,7 +141,7 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {...@@ -133,7 +141,7 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
133 .owner = gop.index,141 .owner = gop.index,
134 .shndx = shndx,142 .shndx = shndx,
135 };143 };
136 try self.comdat_groups.append(elf_file.base.allocator, comdat_group_index);144 try self.comdat_groups.append(gpa, comdat_group_index);
137 },145 },
138146
139 elf.SHT_SYMTAB_SHNDX => @panic("TODO SHT_SYMTAB_SHNDX"),147 elf.SHT_SYMTAB_SHNDX => @panic("TODO SHT_SYMTAB_SHNDX"),
...@@ -168,23 +176,21 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {...@@ -168,23 +176,21 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
168176
169fn addAtom(177fn addAtom(
170 self: *Object,178 self: *Object,
171 shdr: elf.Elf64_Shdr,179 shdr: ElfShdr,
172 shndx: u16,180 shndx: u16,
173 name: [:0]const u8,181 name: [:0]const u8,
174 elf_file: *Elf,182 elf_file: *Elf,
175) error{ OutOfMemory, Overflow }!void {183) error{OutOfMemory}!void {
176 const atom_index = try elf_file.addAtom();184 const atom_index = try elf_file.addAtom();
177 const atom = elf_file.atom(atom_index).?;185 const atom = elf_file.atom(atom_index).?;
178 atom.atom_index = atom_index;186 atom.atom_index = atom_index;
179 atom.name_offset = try elf_file.strtab.insert(elf_file.base.allocator, name);187 atom.name_offset = try elf_file.strtab.insert(elf_file.base.allocator, name);
180 atom.file_index = self.index;188 atom.file_index = self.index;
181 atom.input_section_index = shndx;189 atom.input_section_index = shndx;
182 atom.output_section_index = try self.getOutputSectionIndex(elf_file, shdr);
183 atom.flags.alive = true;
184 self.atoms.items[shndx] = atom_index;190 self.atoms.items[shndx] = atom_index;
185191
186 if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {192 if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {
187 const data = try self.shdrContents(shndx);193 const data = self.shdrContents(shndx);
188 const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;194 const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
189 atom.size = chdr.ch_size;195 atom.size = chdr.ch_size;
190 atom.alignment = Alignment.fromNonzeroByteUnits(chdr.ch_addralign);196 atom.alignment = Alignment.fromNonzeroByteUnits(chdr.ch_addralign);
...@@ -194,10 +200,10 @@ fn addAtom(...@@ -194,10 +200,10 @@ fn addAtom(
194 }200 }
195}201}
196202
197fn getOutputSectionIndex(self: *Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) error{OutOfMemory}!u16 {203fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMemory}!u16 {
198 const name = blk: {204 const name = blk: {
199 const name = self.strings.getAssumeExists(shdr.sh_name);205 const name = self.strings.getAssumeExists(shdr.sh_name);
200 // if (shdr.sh_flags & elf.SHF_MERGE != 0) break :blk name;206 if (shdr.sh_flags & elf.SHF_MERGE != 0) break :blk name;
201 const sh_name_prefixes: []const [:0]const u8 = &.{207 const sh_name_prefixes: []const [:0]const u8 = &.{
202 ".text", ".data.rel.ro", ".data", ".rodata", ".bss.rel.ro", ".bss",208 ".text", ".data.rel.ro", ".data", ".rodata", ".bss.rel.ro", ".bss",
203 ".init_array", ".fini_array", ".tbss", ".tdata", ".gcc_except_table", ".ctors",209 ".init_array", ".fini_array", ".tbss", ".tdata", ".gcc_except_table", ".ctors",
...@@ -208,8 +214,6 @@ fn getOutputSectionIndex(self: *Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) er...@@ -208,8 +214,6 @@ fn getOutputSectionIndex(self: *Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) er
208 break :blk prefix;214 break :blk prefix;
209 }215 }
210 }216 }
211 if (std.mem.eql(u8, name, ".tcommon")) break :blk ".tbss";
212 if (std.mem.eql(u8, name, ".common")) break :blk ".bss";
213 break :blk name;217 break :blk name;
214 };218 };
215 const @"type" = switch (shdr.sh_type) {219 const @"type" = switch (shdr.sh_type) {
...@@ -231,47 +235,23 @@ fn getOutputSectionIndex(self: *Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) er...@@ -231,47 +235,23 @@ fn getOutputSectionIndex(self: *Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) er
231 else => flags,235 else => flags,
232 };236 };
233 };237 };
234 const out_shndx = elf_file.sectionByName(name) orelse blk: {238 const out_shndx = elf_file.sectionByName(name) orelse try elf_file.addSection(.{
235 const is_alloc = flags & elf.SHF_ALLOC != 0;239 .type = @"type",
236 const is_write = flags & elf.SHF_WRITE != 0;240 .flags = flags,
237 const is_exec = flags & elf.SHF_EXECINSTR != 0;241 .name = name,
238 if (!is_alloc) {242 });
239 log.err("{}: output section {s} not found", .{ self.fmtPath(), name });
240 @panic("TODO: missing output section!");
241 }
242 var phdr_flags: u32 = elf.PF_R;
243 if (is_write) phdr_flags |= elf.PF_W;
244 if (is_exec) phdr_flags |= elf.PF_X;
245 const phdr_index = try elf_file.allocateSegment(.{
246 .size = Elf.padToIdeal(shdr.sh_size),
247 .alignment = elf_file.page_size,
248 .flags = phdr_flags,
249 });
250 const shndx = try elf_file.allocateAllocSection(.{
251 .name = name,
252 .phdr_index = phdr_index,
253 .alignment = shdr.sh_addralign,
254 .flags = flags,
255 .type = @"type",
256 });
257 try elf_file.last_atom_and_free_list_table.putNoClobber(elf_file.base.allocator, shndx, .{});
258 break :blk shndx;
259 };
260 return out_shndx;243 return out_shndx;
261}244}
262245
263fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool {246fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool {
264 _ = elf_file;
265 const shdr = self.shdrs.items[index];247 const shdr = self.shdrs.items[index];
266 const name = self.strings.getAssumeExists(shdr.sh_name);248 const name = self.strings.getAssumeExists(shdr.sh_name);
267 const ignore = blk: {249 const ignore = blk: {
268 if (mem.startsWith(u8, name, ".note")) break :blk true;250 if (mem.startsWith(u8, name, ".note")) break :blk true;
269 if (mem.startsWith(u8, name, ".comment")) break :blk true;251 if (mem.startsWith(u8, name, ".comment")) break :blk true;
270 if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true;252 if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true;
271 if (mem.startsWith(u8, name, ".eh_frame")) break :blk true;253 if (elf_file.base.options.strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and
272 // if (elf_file.base.options.strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and254 mem.startsWith(u8, name, ".debug")) break :blk true;
273 // mem.startsWith(u8, name, ".debug")) break :blk true;
274 if (shdr.sh_flags & elf.SHF_ALLOC == 0 and mem.startsWith(u8, name, ".debug")) break :blk true;
275 break :blk false;255 break :blk false;
276 };256 };
277 return ignore;257 return ignore;
...@@ -300,10 +280,6 @@ fn initSymtab(self: *Object, elf_file: *Elf) !void {...@@ -300,10 +280,6 @@ fn initSymtab(self: *Object, elf_file: *Elf) !void {
300 sym_ptr.esym_index = @as(u32, @intCast(i));280 sym_ptr.esym_index = @as(u32, @intCast(i));
301 sym_ptr.atom_index = if (sym.st_shndx == elf.SHN_ABS) 0 else self.atoms.items[sym.st_shndx];281 sym_ptr.atom_index = if (sym.st_shndx == elf.SHN_ABS) 0 else self.atoms.items[sym.st_shndx];
302 sym_ptr.file_index = self.index;282 sym_ptr.file_index = self.index;
303 sym_ptr.output_section_index = if (sym_ptr.atom(elf_file)) |atom_ptr|
304 atom_ptr.outputShndx().?
305 else
306 elf.SHN_UNDEF;
307 }283 }
308284
309 for (self.symtab[first_global..]) |sym| {285 for (self.symtab[first_global..]) |sym| {
...@@ -324,8 +300,8 @@ fn parseEhFrame(self: *Object, shndx: u16, elf_file: *Elf) !void {...@@ -324,8 +300,8 @@ fn parseEhFrame(self: *Object, shndx: u16, elf_file: *Elf) !void {
324 };300 };
325301
326 const gpa = elf_file.base.allocator;302 const gpa = elf_file.base.allocator;
327 const raw = try self.shdrContents(shndx);303 const raw = self.shdrContents(shndx);
328 const relocs = try self.getRelocs(relocs_shndx);304 const relocs = self.getRelocs(relocs_shndx);
329 const fdes_start = self.fdes.items.len;305 const fdes_start = self.fdes.items.len;
330 const cies_start = self.cies.items.len;306 const cies_start = self.cies.items.len;
331307
...@@ -429,7 +405,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void {...@@ -429,7 +405,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void {
429 const shdr = atom.inputShdr(elf_file);405 const shdr = atom.inputShdr(elf_file);
430 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;406 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
431 if (shdr.sh_type == elf.SHT_NOBITS) continue;407 if (shdr.sh_type == elf.SHT_NOBITS) continue;
432 if (try atom.scanRelocsRequiresCode(elf_file)) {408 if (atom.scanRelocsRequiresCode(elf_file)) {
433 // TODO ideally, we don't have to decompress at this stage (should already be done)409 // TODO ideally, we don't have to decompress at this stage (should already be done)
434 // and we just fetch the code slice.410 // and we just fetch the code slice.
435 const code = try self.codeDecompressAlloc(elf_file, atom_index);411 const code = try self.codeDecompressAlloc(elf_file, atom_index);
...@@ -439,7 +415,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void {...@@ -439,7 +415,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void {
439 }415 }
440416
441 for (self.cies.items) |cie| {417 for (self.cies.items) |cie| {
442 for (try cie.relocs(elf_file)) |rel| {418 for (cie.relocs(elf_file)) |rel| {
443 const sym = elf_file.symbol(self.symbols.items[rel.r_sym()]);419 const sym = elf_file.symbol(self.symbols.items[rel.r_sym()]);
444 if (sym.flags.import) {420 if (sym.flags.import) {
445 if (sym.type(elf_file) != elf.STT_FUNC)421 if (sym.type(elf_file) != elf.STT_FUNC)
...@@ -474,15 +450,10 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) void {...@@ -474,15 +450,10 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) void {
474 elf.SHN_ABS, elf.SHN_COMMON => 0,450 elf.SHN_ABS, elf.SHN_COMMON => 0,
475 else => self.atoms.items[esym.st_shndx],451 else => self.atoms.items[esym.st_shndx],
476 };452 };
477 const output_section_index = if (elf_file.atom(atom_index)) |atom|
478 atom.outputShndx().?
479 else
480 elf.SHN_UNDEF;
481 global.value = esym.st_value;453 global.value = esym.st_value;
482 global.atom_index = atom_index;454 global.atom_index = atom_index;
483 global.esym_index = esym_index;455 global.esym_index = esym_index;
484 global.file_index = self.index;456 global.file_index = self.index;
485 global.output_section_index = output_section_index;
486 global.version_index = elf_file.default_sym_version;457 global.version_index = elf_file.default_sym_version;
487 if (esym.st_bind() == elf.STB_WEAK) global.flags.weak = true;458 if (esym.st_bind() == elf.STB_WEAK) global.flags.weak = true;
488 }459 }
...@@ -544,6 +515,15 @@ pub fn markLive(self: *Object, elf_file: *Elf) void {...@@ -544,6 +515,15 @@ pub fn markLive(self: *Object, elf_file: *Elf) void {
544 }515 }
545}516}
546517
518pub fn markEhFrameAtomsDead(self: Object, elf_file: *Elf) void {
519 for (self.atoms.items) |atom_index| {
520 const atom = elf_file.atom(atom_index) orelse continue;
521 const is_eh_frame = atom.inputShdr(elf_file).sh_type == elf.SHT_X86_64_UNWIND or
522 mem.eql(u8, atom.name(elf_file), ".eh_frame");
523 if (atom.flags.alive and is_eh_frame) atom.flags.alive = false;
524 }
525}
526
547pub fn checkDuplicates(self: *Object, elf_file: *Elf) void {527pub fn checkDuplicates(self: *Object, elf_file: *Elf) void {
548 const first_global = self.first_global orelse return;528 const first_global = self.first_global orelse return;
549 for (self.globals(), 0..) |index, i| {529 for (self.globals(), 0..) |index, i| {
...@@ -581,14 +561,14 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {...@@ -581,14 +561,14 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
581 if (this_sym.st_shndx != elf.SHN_COMMON) continue;561 if (this_sym.st_shndx != elf.SHN_COMMON) continue;
582562
583 const global = elf_file.symbol(index);563 const global = elf_file.symbol(index);
584 const global_file = global.getFile(elf_file).?;564 const global_file = global.file(elf_file).?;
585 if (global_file.getIndex() != self.index) {565 if (global_file.index() != self.index) {
586 if (elf_file.options.warn_common) {566 // if (elf_file.options.warn_common) {
587 elf_file.base.warn("{}: multiple common symbols: {s}", .{567 // elf_file.base.warn("{}: multiple common symbols: {s}", .{
588 self.fmtPath(),568 // self.fmtPath(),
589 global.getName(elf_file),569 // global.getName(elf_file),
590 });570 // });
591 }571 // }
592 continue;572 continue;
593 }573 }
594574
...@@ -597,13 +577,13 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {...@@ -597,13 +577,13 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
597 const atom_index = try elf_file.addAtom();577 const atom_index = try elf_file.addAtom();
598 try self.atoms.append(gpa, atom_index);578 try self.atoms.append(gpa, atom_index);
599579
600 const is_tls = global.getType(elf_file) == elf.STT_TLS;580 const is_tls = global.type(elf_file) == elf.STT_TLS;
601 const name = if (is_tls) ".tbss" else ".bss";581 const name = if (is_tls) ".tls_common" else ".common";
602582
603 const atom = elf_file.atom(atom_index).?;583 const atom = elf_file.atom(atom_index).?;
604 atom.atom_index = atom_index;584 atom.atom_index = atom_index;
605 atom.name = try elf_file.strtab.insert(gpa, name);585 atom.name_offset = try elf_file.strtab.insert(gpa, name);
606 atom.file = self.index;586 atom.file_index = self.index;
607 atom.size = this_sym.st_size;587 atom.size = this_sym.st_size;
608 const alignment = this_sym.st_value;588 const alignment = this_sym.st_value;
609 atom.alignment = Alignment.fromNonzeroByteUnits(alignment);589 atom.alignment = Alignment.fromNonzeroByteUnits(alignment);
...@@ -612,26 +592,76 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {...@@ -612,26 +592,76 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
612 if (is_tls) sh_flags |= elf.SHF_TLS;592 if (is_tls) sh_flags |= elf.SHF_TLS;
613 const shndx = @as(u16, @intCast(self.shdrs.items.len));593 const shndx = @as(u16, @intCast(self.shdrs.items.len));
614 const shdr = try self.shdrs.addOne(gpa);594 const shdr = try self.shdrs.addOne(gpa);
595 const sh_size = math.cast(usize, this_sym.st_size) orelse return error.Overflow;
615 shdr.* = .{596 shdr.* = .{
616 .sh_name = try self.strings.insert(gpa, name),597 .sh_name = try self.strings.insert(gpa, name),
617 .sh_type = elf.SHT_NOBITS,598 .sh_type = elf.SHT_NOBITS,
618 .sh_flags = sh_flags,599 .sh_flags = sh_flags,
619 .sh_addr = 0,600 .sh_addr = 0,
620 .sh_offset = 0,601 .sh_offset = 0,
621 .sh_size = this_sym.st_size,602 .sh_size = sh_size,
622 .sh_link = 0,603 .sh_link = 0,
623 .sh_info = 0,604 .sh_info = 0,
624 .sh_addralign = alignment,605 .sh_addralign = alignment,
625 .sh_entsize = 0,606 .sh_entsize = 0,
626 };607 };
627 atom.shndx = shndx;608 atom.input_section_index = shndx;
628609
629 global.value = 0;610 global.value = 0;
630 global.atom = atom_index;611 global.atom_index = atom_index;
631 global.flags.weak = false;612 global.flags.weak = false;
632 }613 }
633}614}
634615
616pub fn initOutputSections(self: Object, elf_file: *Elf) !void {
617 for (self.atoms.items) |atom_index| {
618 const atom = elf_file.atom(atom_index) orelse continue;
619 if (!atom.flags.alive) continue;
620 const shdr = atom.inputShdr(elf_file);
621 _ = try self.initOutputSection(elf_file, shdr);
622 }
623}
624
625pub fn addAtomsToOutputSections(self: *Object, elf_file: *Elf) !void {
626 for (self.atoms.items) |atom_index| {
627 const atom = elf_file.atom(atom_index) orelse continue;
628 if (!atom.flags.alive) continue;
629 const shdr = atom.inputShdr(elf_file);
630 atom.output_section_index = self.initOutputSection(elf_file, shdr) catch unreachable;
631
632 const gpa = elf_file.base.allocator;
633 const gop = try elf_file.output_sections.getOrPut(gpa, atom.output_section_index);
634 if (!gop.found_existing) gop.value_ptr.* = .{};
635 try gop.value_ptr.append(gpa, atom_index);
636 }
637}
638
639pub fn allocateAtoms(self: Object, elf_file: *Elf) void {
640 for (self.atoms.items) |atom_index| {
641 const atom = elf_file.atom(atom_index) orelse continue;
642 if (!atom.flags.alive) continue;
643 const shdr = elf_file.shdrs.items[atom.output_section_index];
644 atom.value += shdr.sh_addr;
645 }
646
647 for (self.locals()) |local_index| {
648 const local = elf_file.symbol(local_index);
649 const atom = local.atom(elf_file) orelse continue;
650 if (!atom.flags.alive) continue;
651 local.value += atom.value;
652 local.output_section_index = atom.output_section_index;
653 }
654
655 for (self.globals()) |global_index| {
656 const global = elf_file.symbol(global_index);
657 const atom = global.atom(elf_file) orelse continue;
658 if (!atom.flags.alive) continue;
659 if (global.file(elf_file).?.index() != self.index) continue;
660 global.value += atom.value;
661 global.output_section_index = atom.output_section_index;
662 }
663}
664
635pub fn updateSymtabSize(self: *Object, elf_file: *Elf) void {665pub fn updateSymtabSize(self: *Object, elf_file: *Elf) void {
636 for (self.locals()) |local_index| {666 for (self.locals()) |local_index| {
637 const local = elf_file.symbol(local_index);667 const local = elf_file.symbol(local_index);
...@@ -682,22 +712,20 @@ pub fn writeSymtab(self: *Object, elf_file: *Elf, ctx: anytype) void {...@@ -682,22 +712,20 @@ pub fn writeSymtab(self: *Object, elf_file: *Elf, ctx: anytype) void {
682 }712 }
683}713}
684714
685pub fn locals(self: *Object) []const Symbol.Index {715pub fn locals(self: Object) []const Symbol.Index {
686 const end = self.first_global orelse self.symbols.items.len;716 const end = self.first_global orelse self.symbols.items.len;
687 return self.symbols.items[0..end];717 return self.symbols.items[0..end];
688}718}
689719
690pub fn globals(self: *Object) []const Symbol.Index {720pub fn globals(self: Object) []const Symbol.Index {
691 const start = self.first_global orelse self.symbols.items.len;721 const start = self.first_global orelse self.symbols.items.len;
692 return self.symbols.items[start..];722 return self.symbols.items[start..];
693}723}
694724
695fn shdrContents(self: Object, index: u32) error{Overflow}![]const u8 {725pub fn shdrContents(self: Object, index: u32) []const u8 {
696 assert(index < self.shdrs.items.len);726 assert(index < self.shdrs.items.len);
697 const shdr = self.shdrs.items[index];727 const shdr = self.shdrs.items[index];
698 const offset = math.cast(usize, shdr.sh_offset) orelse return error.Overflow;728 return self.data[shdr.sh_offset..][0..shdr.sh_size];
699 const size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
700 return self.data[offset..][0..size];
701}729}
702730
703/// Returns atom's code and optionally uncompresses data if required (for compressed sections).731/// Returns atom's code and optionally uncompresses data if required (for compressed sections).
...@@ -706,7 +734,7 @@ pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index)...@@ -706,7 +734,7 @@ pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index)
706 const gpa = elf_file.base.allocator;734 const gpa = elf_file.base.allocator;
707 const atom_ptr = elf_file.atom(atom_index).?;735 const atom_ptr = elf_file.atom(atom_index).?;
708 assert(atom_ptr.file_index == self.index);736 assert(atom_ptr.file_index == self.index);
709 const data = try self.shdrContents(atom_ptr.input_section_index);737 const data = self.shdrContents(atom_ptr.input_section_index);
710 const shdr = atom_ptr.inputShdr(elf_file);738 const shdr = atom_ptr.inputShdr(elf_file);
711 if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {739 if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {
712 const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;740 const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
...@@ -734,8 +762,8 @@ fn getString(self: *Object, off: u32) [:0]const u8 {...@@ -734,8 +762,8 @@ fn getString(self: *Object, off: u32) [:0]const u8 {
734 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0);762 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0);
735}763}
736764
737pub fn comdatGroupMembers(self: *Object, index: u16) error{Overflow}![]align(1) const u32 {765pub fn comdatGroupMembers(self: *Object, index: u16) []align(1) const u32 {
738 const raw = try self.shdrContents(index);766 const raw = self.shdrContents(index);
739 const nmembers = @divExact(raw.len, @sizeOf(u32));767 const nmembers = @divExact(raw.len, @sizeOf(u32));
740 const members = @as([*]align(1) const u32, @ptrCast(raw.ptr))[1..nmembers];768 const members = @as([*]align(1) const u32, @ptrCast(raw.ptr))[1..nmembers];
741 return members;769 return members;
...@@ -745,8 +773,8 @@ pub fn asFile(self: *Object) File {...@@ -745,8 +773,8 @@ pub fn asFile(self: *Object) File {
745 return .{ .object = self };773 return .{ .object = self };
746}774}
747775
748pub fn getRelocs(self: *Object, shndx: u32) error{Overflow}![]align(1) const elf.Elf64_Rela {776pub fn getRelocs(self: *Object, shndx: u32) []align(1) const elf.Elf64_Rela {
749 const raw = try self.shdrContents(shndx);777 const raw = self.shdrContents(shndx);
750 const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela));778 const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela));
751 return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num];779 return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num];
752}780}
...@@ -886,7 +914,7 @@ fn formatComdatGroups(...@@ -886,7 +914,7 @@ fn formatComdatGroups(
886 const cg = elf_file.comdatGroup(cg_index);914 const cg = elf_file.comdatGroup(cg_index);
887 const cg_owner = elf_file.comdatGroupOwner(cg.owner);915 const cg_owner = elf_file.comdatGroupOwner(cg.owner);
888 if (cg_owner.file != object.index) continue;916 if (cg_owner.file != object.index) continue;
889 const cg_members = object.comdatGroupMembers(cg.shndx) catch continue;917 const cg_members = object.comdatGroupMembers(cg.shndx);
890 for (cg_members) |shndx| {918 for (cg_members) |shndx| {
891 const atom_index = object.atoms.items[shndx];919 const atom_index = object.atoms.items[shndx];
892 const atom = elf_file.atom(atom_index) orelse continue;920 const atom = elf_file.atom(atom_index) orelse continue;
...@@ -915,6 +943,34 @@ fn formatPath(...@@ -915,6 +943,34 @@ fn formatPath(
915 } else try writer.writeAll(object.path);943 } else try writer.writeAll(object.path);
916}944}
917945
946pub const ElfShdr = struct {
947 sh_name: u32,
948 sh_type: u32,
949 sh_flags: u64,
950 sh_addr: u64,
951 sh_offset: usize,
952 sh_size: usize,
953 sh_link: u32,
954 sh_info: u32,
955 sh_addralign: u64,
956 sh_entsize: u64,
957
958 pub fn fromElf64Shdr(shdr: elf.Elf64_Shdr) error{Overflow}!ElfShdr {
959 return .{
960 .sh_name = shdr.sh_name,
961 .sh_type = shdr.sh_type,
962 .sh_flags = shdr.sh_flags,
963 .sh_addr = shdr.sh_addr,
964 .sh_offset = math.cast(usize, shdr.sh_offset) orelse return error.Overflow,
965 .sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow,
966 .sh_link = shdr.sh_link,
967 .sh_info = shdr.sh_info,
968 .sh_addralign = shdr.sh_addralign,
969 .sh_entsize = shdr.sh_entsize,
970 };
971 }
972};
973
918const Object = @This();974const Object = @This();
919975
920const std = @import("std");976const std = @import("std");
src/link/Elf/SharedObject.zig created+363
...@@ -0,0 +1,363 @@
1path: []const u8,
2data: []const u8,
3index: File.Index,
4
5header: ?elf.Elf64_Ehdr = null,
6shdrs: std.ArrayListUnmanaged(ElfShdr) = .{},
7symtab: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{},
8strtab: []const u8 = &[0]u8{},
9/// Version symtab contains version strings of the symbols if present.
10versyms: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{},
11verstrings: std.ArrayListUnmanaged(u32) = .{},
12
13dynamic_sect_index: ?u16 = null,
14versym_sect_index: ?u16 = null,
15verdef_sect_index: ?u16 = null,
16
17symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
18aliases: ?std.ArrayListUnmanaged(u32) = null,
19
20needed: bool,
21alive: bool,
22
23output_symtab_size: Elf.SymtabSize = .{},
24
25pub fn isSharedObject(file: std.fs.File) bool {
26 const reader = file.reader();
27 const header = reader.readStruct(elf.Elf64_Ehdr) catch return false;
28 defer file.seekTo(0) catch {};
29 if (!mem.eql(u8, header.e_ident[0..4], "\x7fELF")) return false;
30 if (header.e_ident[elf.EI_VERSION] != 1) return false;
31 if (header.e_type != elf.ET.DYN) return false;
32 return true;
33}
34
35pub fn deinit(self: *SharedObject, allocator: Allocator) void {
36 allocator.free(self.data);
37 self.versyms.deinit(allocator);
38 self.verstrings.deinit(allocator);
39 self.symbols.deinit(allocator);
40 if (self.aliases) |*aliases| aliases.deinit(allocator);
41 self.shdrs.deinit(allocator);
42}
43
44pub fn parse(self: *SharedObject, elf_file: *Elf) !void {
45 const gpa = elf_file.base.allocator;
46 var stream = std.io.fixedBufferStream(self.data);
47 const reader = stream.reader();
48
49 self.header = try reader.readStruct(elf.Elf64_Ehdr);
50 const shoff = std.math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;
51
52 var dynsym_index: ?u16 = null;
53 const shdrs = @as(
54 [*]align(1) const elf.Elf64_Shdr,
55 @ptrCast(self.data.ptr + shoff),
56 )[0..self.header.?.e_shnum];
57 try self.shdrs.ensureTotalCapacityPrecise(gpa, shdrs.len);
58
59 for (shdrs, 0..) |shdr, i| {
60 self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr));
61 switch (shdr.sh_type) {
62 elf.SHT_DYNSYM => dynsym_index = @as(u16, @intCast(i)),
63 elf.SHT_DYNAMIC => self.dynamic_sect_index = @as(u16, @intCast(i)),
64 elf.SHT_GNU_VERSYM => self.versym_sect_index = @as(u16, @intCast(i)),
65 elf.SHT_GNU_VERDEF => self.verdef_sect_index = @as(u16, @intCast(i)),
66 else => {},
67 }
68 }
69
70 if (dynsym_index) |index| {
71 const shdr = self.shdrs.items[index];
72 const symtab = self.shdrContents(index);
73 const nsyms = @divExact(symtab.len, @sizeOf(elf.Elf64_Sym));
74 self.symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(symtab.ptr))[0..nsyms];
75 self.strtab = self.shdrContents(@as(u16, @intCast(shdr.sh_link)));
76 }
77
78 try self.parseVersions(elf_file);
79 try self.initSymtab(elf_file);
80}
81
82fn parseVersions(self: *SharedObject, elf_file: *Elf) !void {
83 const gpa = elf_file.base.allocator;
84
85 try self.verstrings.resize(gpa, 2);
86 self.verstrings.items[elf.VER_NDX_LOCAL] = 0;
87 self.verstrings.items[elf.VER_NDX_GLOBAL] = 0;
88
89 if (self.verdef_sect_index) |shndx| {
90 const verdefs = self.shdrContents(shndx);
91 const nverdefs = self.verdefNum();
92 try self.verstrings.resize(gpa, self.verstrings.items.len + nverdefs);
93
94 var i: u32 = 0;
95 var offset: u32 = 0;
96 while (i < nverdefs) : (i += 1) {
97 const verdef = @as(*align(1) const elf.Elf64_Verdef, @ptrCast(verdefs.ptr + offset)).*;
98 defer offset += verdef.vd_next;
99 if (verdef.vd_flags == elf.VER_FLG_BASE) continue; // Skip BASE entry
100 const vda_name = if (verdef.vd_cnt > 0)
101 @as(*align(1) const elf.Elf64_Verdaux, @ptrCast(verdefs.ptr + offset + verdef.vd_aux)).vda_name
102 else
103 0;
104 self.verstrings.items[verdef.vd_ndx] = vda_name;
105 }
106 }
107
108 try self.versyms.ensureTotalCapacityPrecise(gpa, self.symtab.len);
109
110 if (self.versym_sect_index) |shndx| {
111 const versyms_raw = self.shdrContents(shndx);
112 const nversyms = @divExact(versyms_raw.len, @sizeOf(elf.Elf64_Versym));
113 const versyms = @as([*]align(1) const elf.Elf64_Versym, @ptrCast(versyms_raw.ptr))[0..nversyms];
114 for (versyms) |ver| {
115 const normalized_ver = if (ver & elf.VERSYM_VERSION >= self.verstrings.items.len - 1)
116 elf.VER_NDX_GLOBAL
117 else
118 ver;
119 self.versyms.appendAssumeCapacity(normalized_ver);
120 }
121 } else for (0..self.symtab.len) |_| {
122 self.versyms.appendAssumeCapacity(elf.VER_NDX_GLOBAL);
123 }
124}
125
126fn initSymtab(self: *SharedObject, elf_file: *Elf) !void {
127 const gpa = elf_file.base.allocator;
128
129 try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.len);
130
131 for (self.symtab, 0..) |sym, i| {
132 const hidden = self.versyms.items[i] & elf.VERSYM_HIDDEN != 0;
133 const name = self.getString(sym.st_name);
134 // We need to garble up the name so that we don't pick this symbol
135 // during symbol resolution. Thank you GNU!
136 const off = if (hidden) blk: {
137 const full_name = try std.fmt.allocPrint(gpa, "{s}@{s}", .{
138 name,
139 self.versionString(self.versyms.items[i]),
140 });
141 defer gpa.free(full_name);
142 break :blk try elf_file.strtab.insert(gpa, full_name);
143 } else try elf_file.strtab.insert(gpa, name);
144 const gop = try elf_file.getOrPutGlobal(off);
145 self.symbols.addOneAssumeCapacity().* = gop.index;
146 }
147}
148
149pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) void {
150 for (self.globals(), 0..) |index, i| {
151 const esym_index = @as(u32, @intCast(i));
152 const this_sym = self.symtab[esym_index];
153
154 if (this_sym.st_shndx == elf.SHN_UNDEF) continue;
155
156 const global = elf_file.symbol(index);
157 if (self.asFile().symbolRank(this_sym, false) < global.symbolRank(elf_file)) {
158 global.value = this_sym.st_value;
159 global.atom_index = 0;
160 global.esym_index = esym_index;
161 global.version_index = self.versyms.items[esym_index];
162 global.file_index = self.index;
163 }
164 }
165}
166
167pub fn resetGlobals(self: *SharedObject, elf_file: *Elf) void {
168 for (self.globals()) |index| {
169 const global = elf_file.symbol(index);
170 const off = global.name_offset;
171 global.* = .{};
172 global.name_offset = off;
173 }
174}
175
176pub fn markLive(self: *SharedObject, elf_file: *Elf) void {
177 for (self.globals(), 0..) |index, i| {
178 const sym = self.symtab[i];
179 if (sym.st_shndx != elf.SHN_UNDEF) continue;
180
181 const global = elf_file.symbol(index);
182 const file = global.file(elf_file) orelse continue;
183 const should_drop = switch (file) {
184 .shared_object => |sh| !sh.needed and sym.st_bind() == elf.STB_WEAK,
185 else => false,
186 };
187 if (!should_drop and !file.isAlive()) {
188 file.setAlive();
189 file.markLive(elf_file);
190 }
191 }
192}
193
194pub fn updateSymtabSize(self: *SharedObject, elf_file: *Elf) void {
195 for (self.globals()) |global_index| {
196 const global = elf_file.symbol(global_index);
197 if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
198 if (global.isLocal()) continue;
199 global.flags.output_symtab = true;
200 self.output_symtab_size.nglobals += 1;
201 }
202}
203
204pub fn writeSymtab(self: *SharedObject, elf_file: *Elf, ctx: anytype) void {
205 var iglobal = ctx.iglobal;
206 for (self.globals()) |global_index| {
207 const global = elf_file.symbol(global_index);
208 if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
209 if (!global.flags.output_symtab) continue;
210 global.setOutputSym(elf_file, &ctx.symtab[iglobal]);
211 iglobal += 1;
212 }
213}
214
215pub fn globals(self: SharedObject) []const Symbol.Index {
216 return self.symbols.items;
217}
218
219pub fn shdrContents(self: SharedObject, index: u16) []const u8 {
220 const shdr = self.shdrs.items[index];
221 return self.data[shdr.sh_offset..][0..shdr.sh_size];
222}
223
224pub fn getString(self: SharedObject, off: u32) [:0]const u8 {
225 assert(off < self.strtab.len);
226 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0);
227}
228
229pub fn versionString(self: SharedObject, index: elf.Elf64_Versym) [:0]const u8 {
230 const off = self.verstrings.items[index & elf.VERSYM_VERSION];
231 return self.getString(off);
232}
233
234pub fn asFile(self: *SharedObject) File {
235 return .{ .shared_object = self };
236}
237
238fn dynamicTable(self: *SharedObject) []align(1) const elf.Elf64_Dyn {
239 const shndx = self.dynamic_sect_index orelse return &[0]elf.Elf64_Dyn{};
240 const raw = self.shdrContents(shndx);
241 const num = @divExact(raw.len, @sizeOf(elf.Elf64_Dyn));
242 return @as([*]align(1) const elf.Elf64_Dyn, @ptrCast(raw.ptr))[0..num];
243}
244
245fn verdefNum(self: *SharedObject) u32 {
246 const entries = self.dynamicTable();
247 for (entries) |entry| switch (entry.d_tag) {
248 elf.DT_VERDEFNUM => return @as(u32, @intCast(entry.d_val)),
249 else => {},
250 };
251 return 0;
252}
253
254pub fn soname(self: *SharedObject) []const u8 {
255 const entries = self.dynamicTable();
256 for (entries) |entry| switch (entry.d_tag) {
257 elf.DT_SONAME => return self.getString(@as(u32, @intCast(entry.d_val))),
258 else => {},
259 };
260 return std.fs.path.basename(self.path);
261}
262
263pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void {
264 assert(self.aliases == null);
265
266 const SortAlias = struct {
267 pub fn lessThan(ctx: *Elf, lhs: Symbol.Index, rhs: Symbol.Index) bool {
268 const lhs_sym = ctx.symbol(lhs).elfSym(ctx);
269 const rhs_sym = ctx.symbol(rhs).elfSym(ctx);
270 return lhs_sym.st_value < rhs_sym.st_value;
271 }
272 };
273
274 const gpa = elf_file.base.allocator;
275 var aliases = std.ArrayList(Symbol.Index).init(gpa);
276 defer aliases.deinit();
277 try aliases.ensureTotalCapacityPrecise(self.globals().len);
278
279 for (self.globals()) |index| {
280 const global = elf_file.symbol(index);
281 const global_file = global.file(elf_file) orelse continue;
282 if (global_file.index() != self.index) continue;
283 aliases.appendAssumeCapacity(index);
284 }
285
286 std.mem.sort(u32, aliases.items, elf_file, SortAlias.lessThan);
287
288 self.aliases = aliases.moveToUnmanaged();
289}
290
291pub fn symbolAliases(self: *SharedObject, index: u32, elf_file: *Elf) []const u32 {
292 assert(self.aliases != null);
293
294 const symbol = elf_file.symbol(index).elfSym(elf_file);
295 const aliases = self.aliases.?;
296
297 const start = for (aliases.items, 0..) |alias, i| {
298 const alias_sym = elf_file.symbol(alias).elfSym(elf_file);
299 if (symbol.st_value == alias_sym.st_value) break i;
300 } else aliases.items.len;
301
302 const end = for (aliases.items[start..], 0..) |alias, i| {
303 const alias_sym = elf_file.symbol(alias).elfSym(elf_file);
304 if (symbol.st_value < alias_sym.st_value) break i + start;
305 } else aliases.items.len;
306
307 return aliases.items[start..end];
308}
309
310pub fn format(
311 self: SharedObject,
312 comptime unused_fmt_string: []const u8,
313 options: std.fmt.FormatOptions,
314 writer: anytype,
315) !void {
316 _ = self;
317 _ = unused_fmt_string;
318 _ = options;
319 _ = writer;
320 @compileError("do not format shared objects directly");
321}
322
323pub fn fmtSymtab(self: SharedObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
324 return .{ .data = .{
325 .shared = self,
326 .elf_file = elf_file,
327 } };
328}
329
330const FormatContext = struct {
331 shared: SharedObject,
332 elf_file: *Elf,
333};
334
335fn formatSymtab(
336 ctx: FormatContext,
337 comptime unused_fmt_string: []const u8,
338 options: std.fmt.FormatOptions,
339 writer: anytype,
340) !void {
341 _ = unused_fmt_string;
342 _ = options;
343 const shared = ctx.shared;
344 try writer.writeAll(" globals\n");
345 for (shared.symbols.items) |index| {
346 const global = ctx.elf_file.symbol(index);
347 try writer.print(" {}\n", .{global.fmt(ctx.elf_file)});
348 }
349}
350
351const SharedObject = @This();
352
353const std = @import("std");
354const assert = std.debug.assert;
355const elf = std.elf;
356const log = std.log.scoped(.elf);
357const mem = std.mem;
358
359const Allocator = mem.Allocator;
360const Elf = @import("../Elf.zig");
361const ElfShdr = @import("Object.zig").ElfShdr;
362const File = @import("file.zig").File;
363const Symbol = @import("Symbol.zig");
src/link/Elf/Symbol.zig+119-78
...@@ -32,7 +32,7 @@ extra_index: u32 = 0,...@@ -32,7 +32,7 @@ extra_index: u32 = 0,
3232
33pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool {33pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool {
34 const file_ptr = symbol.file(elf_file).?;34 const file_ptr = symbol.file(elf_file).?;
35 // if (file_ptr == .shared) return symbol.sourceSymbol(elf_file).st_shndx == elf.SHN_ABS;35 if (file_ptr == .shared_object) return symbol.elfSym(elf_file).st_shndx == elf.SHN_ABS;
36 return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.outputShndx() == null and36 return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.outputShndx() == null and
37 file_ptr != .linker_defined;37 file_ptr != .linker_defined;
38}38}
...@@ -51,10 +51,10 @@ pub fn isIFunc(symbol: Symbol, elf_file: *Elf) bool {...@@ -51,10 +51,10 @@ pub fn isIFunc(symbol: Symbol, elf_file: *Elf) bool {
51}51}
5252
53pub fn @"type"(symbol: Symbol, elf_file: *Elf) u4 {53pub fn @"type"(symbol: Symbol, elf_file: *Elf) u4 {
54 const s_sym = symbol.elfSym(elf_file);54 const esym = symbol.elfSym(elf_file);
55 // const file_ptr = symbol.file(elf_file).?;55 const file_ptr = symbol.file(elf_file).?;
56 // if (s_sym.st_type() == elf.STT_GNU_IFUNC and file_ptr == .shared) return elf.STT_FUNC;56 if (esym.st_type() == elf.STT_GNU_IFUNC and file_ptr == .shared_object) return elf.STT_FUNC;
57 return s_sym.st_type();57 return esym.st_type();
58}58}
5959
60pub fn name(symbol: Symbol, elf_file: *Elf) [:0]const u8 {60pub fn name(symbol: Symbol, elf_file: *Elf) [:0]const u8 {
...@@ -74,7 +74,7 @@ pub fn elfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym {...@@ -74,7 +74,7 @@ pub fn elfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym {
74 switch (file_ptr) {74 switch (file_ptr) {
75 .zig_module => |x| return x.elfSym(symbol.esym_index).*,75 .zig_module => |x| return x.elfSym(symbol.esym_index).*,
76 .linker_defined => |x| return x.symtab.items[symbol.esym_index],76 .linker_defined => |x| return x.symtab.items[symbol.esym_index],
77 .object => |x| return x.symtab[symbol.esym_index],77 inline else => |x| return x.symtab[symbol.esym_index],
78 }78 }
79}79}
8080
...@@ -88,23 +88,18 @@ pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 {...@@ -88,23 +88,18 @@ pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 {
88 return file_ptr.symbolRank(sym, in_archive);88 return file_ptr.symbolRank(sym, in_archive);
89}89}
9090
91pub fn address(symbol: Symbol, opts: struct {91pub fn address(symbol: Symbol, opts: struct { plt: bool = true }, elf_file: *Elf) u64 {
92 plt: bool = true,92 if (symbol.flags.has_copy_rel) {
93}, elf_file: *Elf) u64 {93 return symbol.copyRelAddress(elf_file);
94 _ = elf_file;94 }
95 _ = opts;95 if (symbol.flags.has_plt and opts.plt) {
96 // if (symbol.flags.copy_rel) {96 if (!symbol.flags.is_canonical and symbol.flags.has_got) {
97 // return elf_file.sectionAddress(elf_file.copy_rel_sect_index.?) + symbol.value;97 // We have a non-lazy bound function pointer, use that!
98 // }98 return symbol.pltGotAddress(elf_file);
99 // if (symbol.flags.plt and opts.plt) {99 }
100 // const extra = symbol.getExtra(elf_file).?;100 // Lazy-bound function it is!
101 // if (!symbol.flags.is_canonical and symbol.flags.got) {101 return symbol.pltAddress(elf_file);
102 // // We have a non-lazy bound function pointer, use that!102 }
103 // return elf_file.getPltGotEntryAddress(extra.plt_got);
104 // }
105 // // Lazy-bound function it is!
106 // return elf_file.getPltEntryAddress(extra.plt);
107 // }
108 return symbol.value;103 return symbol.value;
109}104}
110105
...@@ -115,48 +110,83 @@ pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 {...@@ -115,48 +110,83 @@ pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 {
115 return entry.address(elf_file);110 return entry.address(elf_file);
116}111}
117112
118const GetOrCreateGotEntryResult = struct {113pub fn pltGotAddress(symbol: Symbol, elf_file: *Elf) u64 {
114 if (!(symbol.flags.has_plt and symbol.flags.has_got)) return 0;
115 const extras = symbol.extra(elf_file).?;
116 const shdr = elf_file.shdrs.items[elf_file.plt_got_section_index.?];
117 return shdr.sh_addr + extras.plt_got * 16;
118}
119
120pub fn pltAddress(symbol: Symbol, elf_file: *Elf) u64 {
121 if (!symbol.flags.has_plt) return 0;
122 const extras = symbol.extra(elf_file).?;
123 const shdr = elf_file.shdrs.items[elf_file.plt_section_index.?];
124 return shdr.sh_addr + extras.plt * 16 + PltSection.preamble_size;
125}
126
127pub fn gotPltAddress(symbol: Symbol, elf_file: *Elf) u64 {
128 if (!symbol.flags.has_plt) return 0;
129 const extras = symbol.extra(elf_file).?;
130 const shdr = elf_file.shdrs.items[elf_file.got_plt_section_index.?];
131 return shdr.sh_addr + extras.plt * 8 + GotPltSection.preamble_size;
132}
133
134pub fn copyRelAddress(symbol: Symbol, elf_file: *Elf) u64 {
135 if (!symbol.flags.has_copy_rel) return 0;
136 const shdr = elf_file.shdrs.items[elf_file.copy_rel_section_index.?];
137 return shdr.sh_addr + symbol.value;
138}
139
140pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 {
141 if (!symbol.flags.has_tlsgd) return 0;
142 const extras = symbol.extra(elf_file).?;
143 const entry = elf_file.got.entries.items[extras.tlsgd];
144 return entry.address(elf_file);
145}
146
147pub fn gotTpAddress(symbol: Symbol, elf_file: *Elf) u64 {
148 if (!symbol.flags.has_gottp) return 0;
149 const extras = symbol.extra(elf_file).?;
150 const entry = elf_file.got.entries.items[extras.gottp];
151 return entry.address(elf_file);
152}
153
154pub fn tlsDescAddress(symbol: Symbol, elf_file: *Elf) u64 {
155 if (!symbol.flags.has_tlsdesc) return 0;
156 const extras = symbol.extra(elf_file).?;
157 const entry = elf_file.got.entries.items[extras.tlsdesc];
158 return entry.address(elf_file);
159}
160
161const GetOrCreateZigGotEntryResult = struct {
119 found_existing: bool,162 found_existing: bool,
120 index: GotSection.Index,163 index: ZigGotSection.Index,
121};164};
122165
123pub fn getOrCreateGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf) !GetOrCreateGotEntryResult {166pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf) !GetOrCreateZigGotEntryResult {
124 assert(symbol.flags.needs_got);167 if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).?.zig_got };
125 if (symbol.flags.has_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).?.got };168 const index = try elf_file.zig_got.addSymbol(symbol_index, elf_file);
126 const index = try elf_file.got.addGotSymbol(symbol_index, elf_file);
127 symbol.flags.has_got = true;
128 return .{ .found_existing = false, .index = index };169 return .{ .found_existing = false, .index = index };
129}170}
130171
131// pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 {172pub fn zigGotAddress(symbol: Symbol, elf_file: *Elf) u64 {
132// if (!symbol.flags.tlsgd) return 0;173 if (!symbol.flags.has_zig_got) return 0;
133// const extra = symbol.getExtra(elf_file).?;174 const extras = symbol.extra(elf_file).?;
134// return elf_file.getGotEntryAddress(extra.tlsgd);175 return elf_file.zig_got.entryAddress(extras.zig_got, elf_file);
135// }176}
136177
137// pub fn gotTpAddress(symbol: Symbol, elf_file: *Elf) u64 {178pub fn dsoAlignment(symbol: Symbol, elf_file: *Elf) !u64 {
138// if (!symbol.flags.gottp) return 0;179 const file_ptr = symbol.file(elf_file) orelse return 0;
139// const extra = symbol.getExtra(elf_file).?;180 assert(file_ptr == .shared_object);
140// return elf_file.getGotEntryAddress(extra.gottp);181 const shared_object = file_ptr.shared_object;
141// }182 const esym = symbol.elfSym(elf_file);
142183 const shdr = shared_object.shdrs.items[esym.st_shndx];
143// pub fn tlsDescAddress(symbol: Symbol, elf_file: *Elf) u64 {184 const alignment = @max(1, shdr.sh_addralign);
144// if (!symbol.flags.tlsdesc) return 0;185 return if (esym.st_value == 0)
145// const extra = symbol.getExtra(elf_file).?;186 alignment
146// return elf_file.getGotEntryAddress(extra.tlsdesc);187 else
147// }188 @min(alignment, try std.math.powi(u64, 2, @ctz(esym.st_value)));
148189}
149// pub fn alignment(symbol: Symbol, elf_file: *Elf) !u64 {
150// const file = symbol.getFile(elf_file) orelse return 0;
151// const shared = file.shared;
152// const s_sym = symbol.getSourceSymbol(elf_file);
153// const shdr = shared.getShdrs()[s_sym.st_shndx];
154// const alignment = @max(1, shdr.sh_addralign);
155// return if (s_sym.st_value == 0)
156// alignment
157// else
158// @min(alignment, try std.math.powi(u64, 2, @ctz(s_sym.st_value)));
159// }
160190
161pub fn addExtra(symbol: *Symbol, extras: Extra, elf_file: *Elf) !void {191pub fn addExtra(symbol: *Symbol, extras: Extra, elf_file: *Elf) !void {
162 symbol.extra_index = try elf_file.addSymbolExtra(extras);192 symbol.extra_index = try elf_file.addSymbolExtra(extras);
...@@ -180,22 +210,22 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {...@@ -180,22 +210,22 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
180 const st_bind: u8 = blk: {210 const st_bind: u8 = blk: {
181 if (symbol.isLocal()) break :blk 0;211 if (symbol.isLocal()) break :blk 0;
182 if (symbol.flags.weak) break :blk elf.STB_WEAK;212 if (symbol.flags.weak) break :blk elf.STB_WEAK;
183 // if (file_ptr == .shared) break :blk elf.STB_GLOBAL;213 if (file_ptr == .shared_object) break :blk elf.STB_GLOBAL;
184 break :blk esym.st_bind();214 break :blk esym.st_bind();
185 };215 };
186 const st_shndx = blk: {216 const st_shndx = blk: {
187 // if (symbol.flags.copy_rel) break :blk elf_file.copy_rel_sect_index.?;217 if (symbol.flags.has_copy_rel) break :blk elf_file.copy_rel_section_index.?;
188 // if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF;218 if (file_ptr == .shared_object or esym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF;
189 if (symbol.atom(elf_file) == null and file_ptr != .linker_defined)219 if (symbol.atom(elf_file) == null and file_ptr != .linker_defined)
190 break :blk elf.SHN_ABS;220 break :blk elf.SHN_ABS;
191 break :blk symbol.outputShndx() orelse elf.SHN_UNDEF;221 break :blk symbol.outputShndx() orelse elf.SHN_UNDEF;
192 };222 };
193 const st_value = blk: {223 const st_value = blk: {
194 // if (symbol.flags.copy_rel) break :blk symbol.address(.{}, elf_file);224 if (symbol.flags.has_copy_rel) break :blk symbol.address(.{}, elf_file);
195 // if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) {225 if (file_ptr == .shared_object or esym.st_shndx == elf.SHN_UNDEF) {
196 // if (symbol.flags.is_canonical) break :blk symbol.address(.{}, elf_file);226 if (symbol.flags.is_canonical) break :blk symbol.address(.{}, elf_file);
197 // break :blk 0;227 break :blk 0;
198 // }228 }
199 if (st_shndx == elf.SHN_ABS) break :blk symbol.value;229 if (st_shndx == elf.SHN_ABS) break :blk symbol.value;
200 const shdr = &elf_file.shdrs.items[st_shndx];230 const shdr = &elf_file.shdrs.items[st_shndx];
201 if (shdr.sh_flags & elf.SHF_TLS != 0 and file_ptr != .linker_defined)231 if (shdr.sh_flags & elf.SHF_TLS != 0 and file_ptr != .linker_defined)
...@@ -251,9 +281,10 @@ fn formatName(...@@ -251,9 +281,10 @@ fn formatName(
251 switch (symbol.version_index & elf.VERSYM_VERSION) {281 switch (symbol.version_index & elf.VERSYM_VERSION) {
252 elf.VER_NDX_LOCAL, elf.VER_NDX_GLOBAL => {},282 elf.VER_NDX_LOCAL, elf.VER_NDX_GLOBAL => {},
253 else => {283 else => {
254 unreachable;284 const file_ptr = symbol.file(elf_file).?;
255 // const shared = symbol.getFile(elf_file).?.shared;285 assert(file_ptr == .shared_object);
256 // try writer.print("@{s}", .{shared.getVersionString(symbol.version_index)});286 const shared_object = file_ptr.shared_object;
287 try writer.print("@{s}", .{shared_object.versionString(symbol.version_index)});
257 },288 },
258 }289 }
259}290}
...@@ -283,7 +314,7 @@ fn format2(...@@ -283,7 +314,7 @@ fn format2(
283 try writer.writeAll(" : absolute");314 try writer.writeAll(" : absolute");
284 }315 }
285 } else if (symbol.outputShndx()) |shndx| {316 } else if (symbol.outputShndx()) |shndx| {
286 try writer.print(" : sect({d})", .{shndx});317 try writer.print(" : shdr({d})", .{shndx});
287 }318 }
288 if (symbol.atom(ctx.elf_file)) |atom_ptr| {319 if (symbol.atom(ctx.elf_file)) |atom_ptr| {
289 try writer.print(" : atom({d})", .{atom_ptr.atom_index});320 try writer.print(" : atom({d})", .{atom_ptr.atom_index});
...@@ -309,23 +340,25 @@ pub const Flags = packed struct {...@@ -309,23 +340,25 @@ pub const Flags = packed struct {
309 /// Whether this symbol is weak.340 /// Whether this symbol is weak.
310 weak: bool = false,341 weak: bool = false,
311342
312 /// Whether the symbol makes into the output symtab or not.343 /// Whether the symbol makes into the output symtab.
313 output_symtab: bool = false,344 output_symtab: bool = false,
314345
346 /// Whether the symbol has entry in dynamic symbol table.
347 has_dynamic: bool = false,
348
315 /// Whether the symbol contains GOT indirection.349 /// Whether the symbol contains GOT indirection.
316 needs_got: bool = false,350 needs_got: bool = false,
317 has_got: bool = false,351 has_got: bool = false,
318352
319 /// Whether the symbol contains PLT indirection.353 /// Whether the symbol contains PLT indirection.
320 needs_plt: bool = false,354 needs_plt: bool = false,
321 plt: bool = false,355 has_plt: bool = false,
322 /// Whether the PLT entry is canonical.356 /// Whether the PLT entry is canonical.
323 is_canonical: bool = false,357 is_canonical: bool = false,
324358
325 /// Whether the symbol contains COPYREL directive.359 /// Whether the symbol contains COPYREL directive.
326 copy_rel: bool = false,360 needs_copy_rel: bool = false,
327 has_copy_rel: bool = false,361 has_copy_rel: bool = false,
328 has_dynamic: bool = false,
329362
330 /// Whether the symbol contains TLSGD indirection.363 /// Whether the symbol contains TLSGD indirection.
331 needs_tlsgd: bool = false,364 needs_tlsgd: bool = false,
...@@ -336,7 +369,11 @@ pub const Flags = packed struct {...@@ -336,7 +369,11 @@ pub const Flags = packed struct {
336 has_gottp: bool = false,369 has_gottp: bool = false,
337370
338 /// Whether the symbol contains TLSDESC indirection.371 /// Whether the symbol contains TLSDESC indirection.
339 tlsdesc: bool = false,372 needs_tlsdesc: bool = false,
373 has_tlsdesc: bool = false,
374
375 /// Whether the symbol contains .zig.got indirection.
376 has_zig_got: bool = false,
340};377};
341378
342pub const Extra = struct {379pub const Extra = struct {
...@@ -348,6 +385,7 @@ pub const Extra = struct {...@@ -348,6 +385,7 @@ pub const Extra = struct {
348 tlsgd: u32 = 0,385 tlsgd: u32 = 0,
349 gottp: u32 = 0,386 gottp: u32 = 0,
350 tlsdesc: u32 = 0,387 tlsdesc: u32 = 0,
388 zig_got: u32 = 0,
351};389};
352390
353pub const Index = u32;391pub const Index = u32;
...@@ -361,8 +399,11 @@ const Atom = @import("Atom.zig");...@@ -361,8 +399,11 @@ const Atom = @import("Atom.zig");
361const Elf = @import("../Elf.zig");399const Elf = @import("../Elf.zig");
362const File = @import("file.zig").File;400const File = @import("file.zig").File;
363const GotSection = synthetic_sections.GotSection;401const GotSection = synthetic_sections.GotSection;
402const GotPltSection = synthetic_sections.GotPltSection;
364const LinkerDefined = @import("LinkerDefined.zig");403const LinkerDefined = @import("LinkerDefined.zig");
365// const Object = @import("Object.zig");404const Object = @import("Object.zig");
366// const SharedObject = @import("SharedObject.zig");405const PltSection = synthetic_sections.PltSection;
406const SharedObject = @import("SharedObject.zig");
367const Symbol = @This();407const Symbol = @This();
408const ZigGotSection = synthetic_sections.ZigGotSection;
368const ZigModule = @import("ZigModule.zig");409const ZigModule = @import("ZigModule.zig");
src/link/Elf/ZigModule.zig+36-10
...@@ -13,9 +13,11 @@ local_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},...@@ -13,9 +13,11 @@ local_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
13global_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},13global_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
14globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{},14globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{},
1515
16atoms: std.AutoArrayHashMapUnmanaged(Atom.Index, void) = .{},16atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
17relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{},17relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{},
1818
19num_dynrelocs: u32 = 0,
20
19output_symtab_size: Elf.SymtabSize = .{},21output_symtab_size: Elf.SymtabSize = .{},
2022
21pub fn deinit(self: *ZigModule, allocator: Allocator) void {23pub fn deinit(self: *ZigModule, allocator: Allocator) void {
...@@ -56,7 +58,8 @@ pub fn addAtom(self: *ZigModule, elf_file: *Elf) !Symbol.Index {...@@ -56,7 +58,8 @@ pub fn addAtom(self: *ZigModule, elf_file: *Elf) !Symbol.Index {
56 const symbol_index = try elf_file.addSymbol();58 const symbol_index = try elf_file.addSymbol();
57 const esym_index = try self.addLocalEsym(gpa);59 const esym_index = try self.addLocalEsym(gpa);
5860
59 try self.atoms.putNoClobber(gpa, atom_index, {});61 const shndx = @as(u16, @intCast(self.atoms.items.len));
62 try self.atoms.append(gpa, atom_index);
60 try self.local_symbols.append(gpa, symbol_index);63 try self.local_symbols.append(gpa, symbol_index);
6164
62 const atom_ptr = elf_file.atom(atom_index).?;65 const atom_ptr = elf_file.atom(atom_index).?;
...@@ -67,10 +70,10 @@ pub fn addAtom(self: *ZigModule, elf_file: *Elf) !Symbol.Index {...@@ -67,10 +70,10 @@ pub fn addAtom(self: *ZigModule, elf_file: *Elf) !Symbol.Index {
67 symbol_ptr.atom_index = atom_index;70 symbol_ptr.atom_index = atom_index;
6871
69 const esym = &self.local_esyms.items[esym_index];72 const esym = &self.local_esyms.items[esym_index];
70 esym.st_shndx = atom_index;73 esym.st_shndx = shndx;
71 symbol_ptr.esym_index = esym_index;74 symbol_ptr.esym_index = esym_index;
7275
73 const relocs_index = @as(Atom.Index, @intCast(self.relocs.items.len));76 const relocs_index = @as(u16, @intCast(self.relocs.items.len));
74 const relocs = try self.relocs.addOne(gpa);77 const relocs = try self.relocs.addOne(gpa);
75 relocs.* = .{};78 relocs.* = .{};
76 atom_ptr.relocs_section_index = relocs_index;79 atom_ptr.relocs_section_index = relocs_index;
...@@ -78,6 +81,22 @@ pub fn addAtom(self: *ZigModule, elf_file: *Elf) !Symbol.Index {...@@ -78,6 +81,22 @@ pub fn addAtom(self: *ZigModule, elf_file: *Elf) !Symbol.Index {
78 return symbol_index;81 return symbol_index;
79}82}
8083
84/// TODO actually create fake input shdrs and return that instead.
85pub fn inputShdr(self: ZigModule, atom_index: Atom.Index, elf_file: *Elf) Object.ElfShdr {
86 _ = self;
87 const shdr = shdr: {
88 const atom = elf_file.atom(atom_index) orelse break :shdr Elf.null_shdr;
89 const shndx = atom.outputShndx() orelse break :shdr Elf.null_shdr;
90 var shdr = elf_file.shdrs.items[shndx];
91 shdr.sh_addr = 0;
92 shdr.sh_offset = 0;
93 shdr.sh_size = atom.size;
94 shdr.sh_addralign = atom.alignment.toByteUnits(1);
95 break :shdr shdr;
96 };
97 return Object.ElfShdr.fromElf64Shdr(shdr) catch unreachable;
98}
99
81pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void {100pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void {
82 for (self.globals(), 0..) |index, i| {101 for (self.globals(), 0..) |index, i| {
83 const esym_index = @as(Symbol.Index, @intCast(i)) | 0x10000000;102 const esym_index = @as(Symbol.Index, @intCast(i)) | 0x10000000;
...@@ -86,7 +105,7 @@ pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void {...@@ -86,7 +105,7 @@ pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void {
86 if (esym.st_shndx == elf.SHN_UNDEF) continue;105 if (esym.st_shndx == elf.SHN_UNDEF) continue;
87106
88 if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) {107 if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) {
89 const atom_index = esym.st_shndx;108 const atom_index = self.atoms.items[esym.st_shndx];
90 const atom = elf_file.atom(atom_index) orelse continue;109 const atom = elf_file.atom(atom_index) orelse continue;
91 if (!atom.flags.alive) continue;110 if (!atom.flags.alive) continue;
92 }111 }
...@@ -95,7 +114,7 @@ pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void {...@@ -95,7 +114,7 @@ pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void {
95 if (self.asFile().symbolRank(esym, false) < global.symbolRank(elf_file)) {114 if (self.asFile().symbolRank(esym, false) < global.symbolRank(elf_file)) {
96 const atom_index = switch (esym.st_shndx) {115 const atom_index = switch (esym.st_shndx) {
97 elf.SHN_ABS, elf.SHN_COMMON => 0,116 elf.SHN_ABS, elf.SHN_COMMON => 0,
98 else => esym.st_shndx,117 else => self.atoms.items[esym.st_shndx],
99 };118 };
100 const output_section_index = if (elf_file.atom(atom_index)) |atom|119 const output_section_index = if (elf_file.atom(atom_index)) |atom|
101 atom.outputShndx().?120 atom.outputShndx().?
...@@ -141,10 +160,12 @@ pub fn claimUnresolved(self: *ZigModule, elf_file: *Elf) void {...@@ -141,10 +160,12 @@ pub fn claimUnresolved(self: *ZigModule, elf_file: *Elf) void {
141}160}
142161
143pub fn scanRelocs(self: *ZigModule, elf_file: *Elf, undefs: anytype) !void {162pub fn scanRelocs(self: *ZigModule, elf_file: *Elf, undefs: anytype) !void {
144 for (self.atoms.keys()) |atom_index| {163 for (self.atoms.items) |atom_index| {
145 const atom = elf_file.atom(atom_index) orelse continue;164 const atom = elf_file.atom(atom_index) orelse continue;
146 if (!atom.flags.alive) continue;165 if (!atom.flags.alive) continue;
147 if (try atom.scanRelocsRequiresCode(elf_file)) {166 const shdr = atom.inputShdr(elf_file);
167 if (shdr.sh_type == elf.SHT_NOBITS) continue;
168 if (atom.scanRelocsRequiresCode(elf_file)) {
148 // TODO ideally we don't have to fetch the code here.169 // TODO ideally we don't have to fetch the code here.
149 // Perhaps it would make sense to save the code until flushModule where we170 // Perhaps it would make sense to save the code until flushModule where we
150 // would free all of generated code?171 // would free all of generated code?
...@@ -272,7 +293,10 @@ pub fn codeAlloc(self: ZigModule, elf_file: *Elf, atom_index: Atom.Index) ![]u8...@@ -272,7 +293,10 @@ pub fn codeAlloc(self: ZigModule, elf_file: *Elf, atom_index: Atom.Index) ![]u8
272 const code = try gpa.alloc(u8, size);293 const code = try gpa.alloc(u8, size);
273 errdefer gpa.free(code);294 errdefer gpa.free(code);
274 const amt = try elf_file.base.file.?.preadAll(code, file_offset);295 const amt = try elf_file.base.file.?.preadAll(code, file_offset);
275 if (amt != code.len) return error.InputOutput;296 if (amt != code.len) {
297 log.err("fetching code for {s} failed", .{atom.name(elf_file)});
298 return error.InputOutput;
299 }
276 return code;300 return code;
277}301}
278302
...@@ -324,7 +348,7 @@ fn formatAtoms(...@@ -324,7 +348,7 @@ fn formatAtoms(
324 _ = unused_fmt_string;348 _ = unused_fmt_string;
325 _ = options;349 _ = options;
326 try writer.writeAll(" atoms\n");350 try writer.writeAll(" atoms\n");
327 for (ctx.self.atoms.keys()) |atom_index| {351 for (ctx.self.atoms.items) |atom_index| {
328 const atom = ctx.elf_file.atom(atom_index) orelse continue;352 const atom = ctx.elf_file.atom(atom_index) orelse continue;
329 try writer.print(" {}\n", .{atom.fmt(ctx.elf_file)});353 try writer.print(" {}\n", .{atom.fmt(ctx.elf_file)});
330 }354 }
...@@ -333,11 +357,13 @@ fn formatAtoms(...@@ -333,11 +357,13 @@ fn formatAtoms(
333const assert = std.debug.assert;357const assert = std.debug.assert;
334const std = @import("std");358const std = @import("std");
335const elf = std.elf;359const elf = std.elf;
360const log = std.log.scoped(.link);
336361
337const Allocator = std.mem.Allocator;362const Allocator = std.mem.Allocator;
338const Atom = @import("Atom.zig");363const Atom = @import("Atom.zig");
339const Elf = @import("../Elf.zig");364const Elf = @import("../Elf.zig");
340const File = @import("file.zig").File;365const File = @import("file.zig").File;
341const Module = @import("../../Module.zig");366const Module = @import("../../Module.zig");
367const Object = @import("Object.zig");
342const Symbol = @import("Symbol.zig");368const Symbol = @import("Symbol.zig");
343const ZigModule = @This();369const ZigModule = @This();
src/link/Elf/eh_frame.zig+39-41
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1pub const Fde = struct {1pub const Fde = struct {
2 /// Includes 4byte size cell.2 /// Includes 4byte size cell.
3 offset: u64,3 offset: usize,
4 size: u64,4 size: usize,
5 cie_index: u32,5 cie_index: u32,
6 rel_index: u32 = 0,6 rel_index: u32 = 0,
7 rel_num: u32 = 0,7 rel_num: u32 = 0,
...@@ -20,9 +20,9 @@ pub const Fde = struct {...@@ -20,9 +20,9 @@ pub const Fde = struct {
20 return base + fde.out_offset;20 return base + fde.out_offset;
21 }21 }
2222
23 pub fn data(fde: Fde, elf_file: *Elf) error{Overflow}![]const u8 {23 pub fn data(fde: Fde, elf_file: *Elf) []const u8 {
24 const object = elf_file.file(fde.file_index).?.object;24 const object = elf_file.file(fde.file_index).?.object;
25 const contents = try object.shdrContents(fde.input_section_index);25 const contents = object.shdrContents(fde.input_section_index);
26 return contents[fde.offset..][0..fde.calcSize()];26 return contents[fde.offset..][0..fde.calcSize()];
27 }27 }
2828
...@@ -32,24 +32,25 @@ pub const Fde = struct {...@@ -32,24 +32,25 @@ pub const Fde = struct {
32 }32 }
3333
34 pub fn ciePointer(fde: Fde, elf_file: *Elf) u32 {34 pub fn ciePointer(fde: Fde, elf_file: *Elf) u32 {
35 return std.mem.readIntLittle(u32, fde.data(elf_file)[4..8]);35 const fde_data = fde.data(elf_file);
36 return std.mem.readIntLittle(u32, fde_data[4..8]);
36 }37 }
3738
38 pub fn calcSize(fde: Fde) u64 {39 pub fn calcSize(fde: Fde) usize {
39 return fde.size + 4;40 return fde.size + 4;
40 }41 }
4142
42 pub fn atom(fde: Fde, elf_file: *Elf) error{Overflow}!*Atom {43 pub fn atom(fde: Fde, elf_file: *Elf) *Atom {
43 const object = elf_file.file(fde.file_index).?.object;44 const object = elf_file.file(fde.file_index).?.object;
44 const rel = (try fde.relocs(elf_file))[0];45 const rel = fde.relocs(elf_file)[0];
45 const sym = object.symtab[rel.r_sym()];46 const sym = object.symtab[rel.r_sym()];
46 const atom_index = object.atoms.items[sym.st_shndx];47 const atom_index = object.atoms.items[sym.st_shndx];
47 return elf_file.atom(atom_index).?;48 return elf_file.atom(atom_index).?;
48 }49 }
4950
50 pub fn relocs(fde: Fde, elf_file: *Elf) error{Overflow}![]align(1) const elf.Elf64_Rela {51 pub fn relocs(fde: Fde, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
51 const object = elf_file.file(fde.file_index).?.object;52 const object = elf_file.file(fde.file_index).?.object;
52 return (try object.getRelocs(fde.rel_section_index))[fde.rel_index..][0..fde.rel_num];53 return object.getRelocs(fde.rel_section_index)[fde.rel_index..][0..fde.rel_num];
53 }54 }
5455
55 pub fn format(56 pub fn format(
...@@ -88,10 +89,7 @@ pub const Fde = struct {...@@ -88,10 +89,7 @@ pub const Fde = struct {
88 const fde = ctx.fde;89 const fde = ctx.fde;
89 const elf_file = ctx.elf_file;90 const elf_file = ctx.elf_file;
90 const base_addr = fde.address(elf_file);91 const base_addr = fde.address(elf_file);
91 const atom_name = if (fde.atom(elf_file)) |atom_ptr|92 const atom_name = fde.atom(elf_file).name(elf_file);
92 atom_ptr.name(elf_file)
93 else |_|
94 "";
95 try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{93 try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{
96 base_addr + fde.out_offset,94 base_addr + fde.out_offset,
97 fde.calcSize(),95 fde.calcSize(),
...@@ -104,8 +102,8 @@ pub const Fde = struct {...@@ -104,8 +102,8 @@ pub const Fde = struct {
104102
105pub const Cie = struct {103pub const Cie = struct {
106 /// Includes 4byte size cell.104 /// Includes 4byte size cell.
107 offset: u64,105 offset: usize,
108 size: u64,106 size: usize,
109 rel_index: u32 = 0,107 rel_index: u32 = 0,
110 rel_num: u32 = 0,108 rel_num: u32 = 0,
111 rel_section_index: u32 = 0,109 rel_section_index: u32 = 0,
...@@ -123,26 +121,26 @@ pub const Cie = struct {...@@ -123,26 +121,26 @@ pub const Cie = struct {
123 return base + cie.out_offset;121 return base + cie.out_offset;
124 }122 }
125123
126 pub fn data(cie: Cie, elf_file: *Elf) error{Overflow}![]const u8 {124 pub fn data(cie: Cie, elf_file: *Elf) []const u8 {
127 const object = elf_file.file(cie.file_index).?.object;125 const object = elf_file.file(cie.file_index).?.object;
128 const contents = try object.shdrContents(cie.input_section_index);126 const contents = object.shdrContents(cie.input_section_index);
129 return contents[cie.offset..][0..cie.calcSize()];127 return contents[cie.offset..][0..cie.calcSize()];
130 }128 }
131129
132 pub fn calcSize(cie: Cie) u64 {130 pub fn calcSize(cie: Cie) usize {
133 return cie.size + 4;131 return cie.size + 4;
134 }132 }
135133
136 pub fn relocs(cie: Cie, elf_file: *Elf) error{Overflow}![]align(1) const elf.Elf64_Rela {134 pub fn relocs(cie: Cie, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
137 const object = elf_file.file(cie.file_index).?.object;135 const object = elf_file.file(cie.file_index).?.object;
138 return (try object.getRelocs(cie.rel_section_index))[cie.rel_index..][0..cie.rel_num];136 return object.getRelocs(cie.rel_section_index)[cie.rel_index..][0..cie.rel_num];
139 }137 }
140138
141 pub fn eql(cie: Cie, other: Cie, elf_file: *Elf) error{Overflow}!bool {139 pub fn eql(cie: Cie, other: Cie, elf_file: *Elf) bool {
142 if (!std.mem.eql(u8, try cie.data(elf_file), try other.data(elf_file))) return false;140 if (!std.mem.eql(u8, cie.data(elf_file), other.data(elf_file))) return false;
143141
144 const cie_relocs = try cie.relocs(elf_file);142 const cie_relocs = cie.relocs(elf_file);
145 const other_relocs = try other.relocs(elf_file);143 const other_relocs = other.relocs(elf_file);
146 if (cie_relocs.len != other_relocs.len) return false;144 if (cie_relocs.len != other_relocs.len) return false;
147145
148 for (cie_relocs, other_relocs) |cie_rel, other_rel| {146 for (cie_relocs, other_relocs) |cie_rel, other_rel| {
...@@ -152,8 +150,8 @@ pub const Cie = struct {...@@ -152,8 +150,8 @@ pub const Cie = struct {
152150
153 const cie_object = elf_file.file(cie.file_index).?.object;151 const cie_object = elf_file.file(cie.file_index).?.object;
154 const other_object = elf_file.file(other.file_index).?.object;152 const other_object = elf_file.file(other.file_index).?.object;
155 const cie_sym = cie_object.symbol(cie_rel.r_sym(), elf_file);153 const cie_sym = cie_object.symbols.items[cie_rel.r_sym()];
156 const other_sym = other_object.symbol(other_rel.r_sym(), elf_file);154 const other_sym = other_object.symbols.items[other_rel.r_sym()];
157 if (!std.mem.eql(u8, std.mem.asBytes(&cie_sym), std.mem.asBytes(&other_sym))) return false;155 if (!std.mem.eql(u8, std.mem.asBytes(&cie_sym), std.mem.asBytes(&other_sym))) return false;
158 }156 }
159 return true;157 return true;
...@@ -205,12 +203,12 @@ pub const Cie = struct {...@@ -205,12 +203,12 @@ pub const Cie = struct {
205203
206pub const Iterator = struct {204pub const Iterator = struct {
207 data: []const u8,205 data: []const u8,
208 pos: u64 = 0,206 pos: usize = 0,
209207
210 pub const Record = struct {208 pub const Record = struct {
211 tag: enum { fde, cie },209 tag: enum { fde, cie },
212 offset: u64,210 offset: usize,
213 size: u64,211 size: usize,
214 };212 };
215213
216 pub fn next(it: *Iterator) !?Record {214 pub fn next(it: *Iterator) !?Record {
...@@ -235,7 +233,7 @@ pub const Iterator = struct {...@@ -235,7 +233,7 @@ pub const Iterator = struct {
235};233};
236234
237pub fn calcEhFrameSize(elf_file: *Elf) !usize {235pub fn calcEhFrameSize(elf_file: *Elf) !usize {
238 var offset: u64 = 0;236 var offset: usize = 0;
239237
240 var cies = std.ArrayList(Cie).init(elf_file.base.allocator);238 var cies = std.ArrayList(Cie).init(elf_file.base.allocator);
241 defer cies.deinit();239 defer cies.deinit();
...@@ -285,7 +283,7 @@ pub fn calcEhFrameHdrSize(elf_file: *Elf) usize {...@@ -285,7 +283,7 @@ pub fn calcEhFrameHdrSize(elf_file: *Elf) usize {
285}283}
286284
287fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file: *Elf, contents: []u8) !void {285fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file: *Elf, contents: []u8) !void {
288 const offset = rel.r_offset - rec.offset;286 const offset = std.math.cast(usize, rel.r_offset - rec.offset) orelse return error.Overflow;
289 const P = @as(i64, @intCast(rec.address(elf_file) + offset));287 const P = @as(i64, @intCast(rec.address(elf_file) + offset));
290 const S = @as(i64, @intCast(sym.address(.{}, elf_file)));288 const S = @as(i64, @intCast(sym.address(.{}, elf_file)));
291 const A = rel.r_addend;289 const A = rel.r_addend;
...@@ -319,11 +317,11 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {...@@ -319,11 +317,11 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
319 for (object.cies.items) |cie| {317 for (object.cies.items) |cie| {
320 if (!cie.alive) continue;318 if (!cie.alive) continue;
321319
322 const contents = try gpa.dupe(u8, try cie.data(elf_file));320 const contents = try gpa.dupe(u8, cie.data(elf_file));
323 defer gpa.free(contents);321 defer gpa.free(contents);
324322
325 for (try cie.relocs(elf_file)) |rel| {323 for (cie.relocs(elf_file)) |rel| {
326 const sym = object.symbol(rel.r_sym(), elf_file);324 const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
327 try resolveReloc(cie, sym, rel, elf_file, contents);325 try resolveReloc(cie, sym, rel, elf_file, contents);
328 }326 }
329327
...@@ -337,7 +335,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {...@@ -337,7 +335,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
337 for (object.fdes.items) |fde| {335 for (object.fdes.items) |fde| {
338 if (!fde.alive) continue;336 if (!fde.alive) continue;
339337
340 const contents = try gpa.dupe(u8, try fde.data(elf_file));338 const contents = try gpa.dupe(u8, fde.data(elf_file));
341 defer gpa.free(contents);339 defer gpa.free(contents);
342340
343 std.mem.writeIntLittle(341 std.mem.writeIntLittle(
...@@ -346,8 +344,8 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {...@@ -346,8 +344,8 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
346 @as(i32, @truncate(@as(i64, @intCast(fde.out_offset + 4)) - @as(i64, @intCast(fde.cie(elf_file).out_offset)))),344 @as(i32, @truncate(@as(i64, @intCast(fde.out_offset + 4)) - @as(i64, @intCast(fde.cie(elf_file).out_offset)))),
347 );345 );
348346
349 for (try fde.relocs(elf_file)) |rel| {347 for (fde.relocs(elf_file)) |rel| {
350 const sym = object.symbol(rel.r_sym(), elf_file);348 const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
351 try resolveReloc(fde, sym, rel, elf_file, contents);349 try resolveReloc(fde, sym, rel, elf_file, contents);
352 }350 }
353351
...@@ -395,10 +393,10 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void {...@@ -395,10 +393,10 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void {
395 for (object.fdes.items) |fde| {393 for (object.fdes.items) |fde| {
396 if (!fde.alive) continue;394 if (!fde.alive) continue;
397395
398 const relocs = try fde.relocs(elf_file);396 const relocs = fde.relocs(elf_file);
399 assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips...397 assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips...
400 const rel = relocs[0];398 const rel = relocs[0];
401 const sym = object.symbol(rel.r_sym(), elf_file);399 const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
402 const P = @as(i64, @intCast(fde.address(elf_file)));400 const P = @as(i64, @intCast(fde.address(elf_file)));
403 const S = @as(i64, @intCast(sym.address(.{}, elf_file)));401 const S = @as(i64, @intCast(sym.address(.{}, elf_file)));
404 const A = rel.r_addend;402 const A = rel.r_addend;
...@@ -416,7 +414,7 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void {...@@ -416,7 +414,7 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void {
416 try writer.writeAll(std.mem.sliceAsBytes(entries.items));414 try writer.writeAll(std.mem.sliceAsBytes(entries.items));
417}415}
418416
419const eh_frame_hdr_header_size: u64 = 12;417const eh_frame_hdr_header_size: usize = 12;
420418
421const EH_PE = struct {419const EH_PE = struct {
422 pub const absptr = 0x00;420 pub const absptr = 0x00;
src/link/Elf/file.zig+8-7
...@@ -2,7 +2,7 @@ pub const File = union(enum) {...@@ -2,7 +2,7 @@ pub const File = union(enum) {
2 zig_module: *ZigModule,2 zig_module: *ZigModule,
3 linker_defined: *LinkerDefined,3 linker_defined: *LinkerDefined,
4 object: *Object,4 object: *Object,
5 // shared_object: *SharedObject,5 shared_object: *SharedObject,
66
7 pub fn index(file: File) Index {7 pub fn index(file: File) Index {
8 return switch (file) {8 return switch (file) {
...@@ -26,7 +26,7 @@ pub const File = union(enum) {...@@ -26,7 +26,7 @@ pub const File = union(enum) {
26 .zig_module => |x| try writer.print("{s}", .{x.path}),26 .zig_module => |x| try writer.print("{s}", .{x.path}),
27 .linker_defined => try writer.writeAll("(linker defined)"),27 .linker_defined => try writer.writeAll("(linker defined)"),
28 .object => |x| try writer.print("{}", .{x.fmtPath()}),28 .object => |x| try writer.print("{}", .{x.fmtPath()}),
29 // .shared_object => |x| try writer.writeAll(x.path),29 .shared_object => |x| try writer.writeAll(x.path),
30 }30 }
31 }31 }
3232
...@@ -49,8 +49,7 @@ pub const File = union(enum) {...@@ -49,8 +49,7 @@ pub const File = union(enum) {
49 pub fn symbolRank(file: File, sym: elf.Elf64_Sym, in_archive: bool) u32 {49 pub fn symbolRank(file: File, sym: elf.Elf64_Sym, in_archive: bool) u32 {
50 const base: u3 = blk: {50 const base: u3 = blk: {
51 if (sym.st_shndx == elf.SHN_COMMON) break :blk if (in_archive) 6 else 5;51 if (sym.st_shndx == elf.SHN_COMMON) break :blk if (in_archive) 6 else 5;
52 // if (file == .shared or in_archive) break :blk switch (sym.st_bind()) {52 if (file == .shared_object or in_archive) break :blk switch (sym.st_bind()) {
53 if (in_archive) break :blk switch (sym.st_bind()) {
54 elf.STB_GLOBAL => 3,53 elf.STB_GLOBAL => 3,
55 else => 4,54 else => 4,
56 };55 };
...@@ -92,7 +91,8 @@ pub const File = union(enum) {...@@ -92,7 +91,8 @@ pub const File = union(enum) {
92 pub fn atoms(file: File) []const Atom.Index {91 pub fn atoms(file: File) []const Atom.Index {
93 return switch (file) {92 return switch (file) {
94 .linker_defined => unreachable,93 .linker_defined => unreachable,
95 .zig_module => |x| x.atoms.keys(),94 .shared_object => unreachable,
95 .zig_module => |x| x.atoms.items,
96 .object => |x| x.atoms.items,96 .object => |x| x.atoms.items,
97 };97 };
98 }98 }
...@@ -100,6 +100,7 @@ pub const File = union(enum) {...@@ -100,6 +100,7 @@ pub const File = union(enum) {
100 pub fn locals(file: File) []const Symbol.Index {100 pub fn locals(file: File) []const Symbol.Index {
101 return switch (file) {101 return switch (file) {
102 .linker_defined => unreachable,102 .linker_defined => unreachable,
103 .shared_object => unreachable,
103 inline else => |x| x.locals(),104 inline else => |x| x.locals(),
104 };105 };
105 }106 }
...@@ -117,7 +118,7 @@ pub const File = union(enum) {...@@ -117,7 +118,7 @@ pub const File = union(enum) {
117 zig_module: ZigModule,118 zig_module: ZigModule,
118 linker_defined: LinkerDefined,119 linker_defined: LinkerDefined,
119 object: Object,120 object: Object,
120 // shared_object: SharedObject,121 shared_object: SharedObject,
121 };122 };
122};123};
123124
...@@ -129,6 +130,6 @@ const Atom = @import("Atom.zig");...@@ -129,6 +130,6 @@ const Atom = @import("Atom.zig");
129const Elf = @import("../Elf.zig");130const Elf = @import("../Elf.zig");
130const LinkerDefined = @import("LinkerDefined.zig");131const LinkerDefined = @import("LinkerDefined.zig");
131const Object = @import("Object.zig");132const Object = @import("Object.zig");
132// const SharedObject = @import("SharedObject.zig");133const SharedObject = @import("SharedObject.zig");
133const Symbol = @import("Symbol.zig");134const Symbol = @import("Symbol.zig");
134const ZigModule = @import("ZigModule.zig");135const ZigModule = @import("ZigModule.zig");
src/link/Elf/gc.zig created+161
...@@ -0,0 +1,161 @@
1pub fn gcAtoms(elf_file: *Elf) !void {
2 var roots = std.ArrayList(*Atom).init(elf_file.base.allocator);
3 defer roots.deinit();
4 try collectRoots(&roots, elf_file);
5 mark(roots, elf_file);
6 prune(elf_file);
7}
8
9fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void {
10 if (elf_file.entry_index) |index| {
11 const global = elf_file.symbol(index);
12 try markSymbol(global, roots, elf_file);
13 }
14
15 for (elf_file.objects.items) |index| {
16 for (elf_file.file(index).?.object.globals()) |global_index| {
17 const global = elf_file.symbol(global_index);
18 if (global.file(elf_file)) |file| {
19 if (file.index() == index and global.flags.@"export")
20 try markSymbol(global, roots, elf_file);
21 }
22 }
23 }
24
25 for (elf_file.objects.items) |index| {
26 const object = elf_file.file(index).?.object;
27
28 for (object.atoms.items) |atom_index| {
29 const atom = elf_file.atom(atom_index) orelse continue;
30 if (!atom.flags.alive) continue;
31
32 const shdr = atom.inputShdr(elf_file);
33 const name = atom.name(elf_file);
34 const is_gc_root = blk: {
35 if (shdr.sh_flags & elf.SHF_GNU_RETAIN != 0) break :blk true;
36 if (shdr.sh_type == elf.SHT_NOTE) break :blk true;
37 if (shdr.sh_type == elf.SHT_PREINIT_ARRAY) break :blk true;
38 if (shdr.sh_type == elf.SHT_INIT_ARRAY) break :blk true;
39 if (shdr.sh_type == elf.SHT_FINI_ARRAY) break :blk true;
40 if (mem.startsWith(u8, name, ".ctors")) break :blk true;
41 if (mem.startsWith(u8, name, ".dtors")) break :blk true;
42 if (mem.startsWith(u8, name, ".init")) break :blk true;
43 if (mem.startsWith(u8, name, ".fini")) break :blk true;
44 if (Elf.isCIdentifier(name)) break :blk true;
45 break :blk false;
46 };
47 if (is_gc_root and markAtom(atom)) try roots.append(atom);
48 if (shdr.sh_flags & elf.SHF_ALLOC == 0) atom.flags.visited = true;
49 }
50
51 // Mark every atom referenced by CIE as alive.
52 for (object.cies.items) |cie| {
53 for (cie.relocs(elf_file)) |rel| {
54 const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
55 try markSymbol(sym, roots, elf_file);
56 }
57 }
58 }
59}
60
61fn markSymbol(sym: *Symbol, roots: *std.ArrayList(*Atom), elf_file: *Elf) !void {
62 const atom = sym.atom(elf_file) orelse return;
63 if (markAtom(atom)) try roots.append(atom);
64}
65
66fn markAtom(atom: *Atom) bool {
67 const already_visited = atom.flags.visited;
68 atom.flags.visited = true;
69 return atom.flags.alive and !already_visited;
70}
71
72fn markLive(atom: *Atom, elf_file: *Elf) void {
73 if (@import("build_options").enable_logging) track_live_level.incr();
74
75 assert(atom.flags.visited);
76 const object = atom.file(elf_file).?.object;
77
78 for (atom.fdes(elf_file)) |fde| {
79 for (fde.relocs(elf_file)[1..]) |rel| {
80 const target_sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
81 const target_atom = target_sym.atom(elf_file) orelse continue;
82 target_atom.flags.alive = true;
83 gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index });
84 if (markAtom(target_atom)) markLive(target_atom, elf_file);
85 }
86 }
87
88 for (atom.relocs(elf_file)) |rel| {
89 const target_sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
90 const target_atom = target_sym.atom(elf_file) orelse continue;
91 target_atom.flags.alive = true;
92 gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index });
93 if (markAtom(target_atom)) markLive(target_atom, elf_file);
94 }
95}
96
97fn mark(roots: std.ArrayList(*Atom), elf_file: *Elf) void {
98 for (roots.items) |root| {
99 gc_track_live_log.debug("root atom({d})", .{root.atom_index});
100 markLive(root, elf_file);
101 }
102}
103
104fn prune(elf_file: *Elf) void {
105 for (elf_file.objects.items) |index| {
106 for (elf_file.file(index).?.object.atoms.items) |atom_index| {
107 const atom = elf_file.atom(atom_index) orelse continue;
108 if (atom.flags.alive and !atom.flags.visited) {
109 atom.flags.alive = false;
110 atom.markFdesDead(elf_file);
111 }
112 }
113 }
114}
115
116pub fn dumpPrunedAtoms(elf_file: *Elf) !void {
117 const stderr = std.io.getStdErr().writer();
118 for (elf_file.objects.items) |index| {
119 for (elf_file.file(index).?.object.atoms.items) |atom_index| {
120 const atom = elf_file.atom(atom_index) orelse continue;
121 if (!atom.flags.alive)
122 // TODO should we simply print to stderr?
123 try stderr.print("link: removing unused section '{s}' in file '{}'\n", .{
124 atom.name(elf_file),
125 atom.file(elf_file).?.fmtPath(),
126 });
127 }
128 }
129}
130
131const Level = struct {
132 value: usize = 0,
133
134 fn incr(self: *@This()) void {
135 self.value += 1;
136 }
137
138 pub fn format(
139 self: *const @This(),
140 comptime unused_fmt_string: []const u8,
141 options: std.fmt.FormatOptions,
142 writer: anytype,
143 ) !void {
144 _ = unused_fmt_string;
145 _ = options;
146 try writer.writeByteNTimes(' ', self.value);
147 }
148};
149
150var track_live_level: Level = .{};
151
152const std = @import("std");
153const assert = std.debug.assert;
154const elf = std.elf;
155const gc_track_live_log = std.log.scoped(.gc_track_live);
156const mem = std.mem;
157
158const Allocator = mem.Allocator;
159const Atom = @import("Atom.zig");
160const Elf = @import("../Elf.zig");
161const Symbol = @import("Symbol.zig");
src/link/Elf/synthetic_sections.zig+1427-329
...@@ -1,133 +1,297 @@...@@ -1,133 +1,297 @@
1pub const GotSection = struct {1pub const DynamicSection = struct {
2 entries: std.ArrayListUnmanaged(Entry) = .{},2 soname: ?u32 = null,
3 needs_rela: bool = false,3 needed: std.ArrayListUnmanaged(u32) = .{},
4 dirty: bool = false,4 rpath: u32 = 0,
5 output_symtab_size: Elf.SymtabSize = .{},
65
7 pub const Index = u32;6 pub fn deinit(dt: *DynamicSection, allocator: Allocator) void {
7 dt.needed.deinit(allocator);
8 }
89
9 const Tag = enum {10 pub fn addNeeded(dt: *DynamicSection, shared: *SharedObject, elf_file: *Elf) !void {
10 got,11 const gpa = elf_file.base.allocator;
11 tlsld,12 const off = try elf_file.dynstrtab.insert(gpa, shared.soname());
12 tlsgd,13 try dt.needed.append(gpa, off);
13 gottp,14 }
14 tlsdesc,
15 };
1615
17 const Entry = struct {16 pub fn setRpath(dt: *DynamicSection, rpath_list: []const []const u8, elf_file: *Elf) !void {
18 tag: Tag,17 if (rpath_list.len == 0) return;
19 symbol_index: Symbol.Index,18 const gpa = elf_file.base.allocator;
20 cell_index: Index,19 var rpath = std.ArrayList(u8).init(gpa);
20 defer rpath.deinit();
21 for (rpath_list, 0..) |path, i| {
22 if (i > 0) try rpath.append(':');
23 try rpath.appendSlice(path);
24 }
25 dt.rpath = try elf_file.dynstrtab.insert(gpa, rpath.items);
26 }
2127
22 /// Returns how many indexes in the GOT this entry uses.28 pub fn setSoname(dt: *DynamicSection, soname: []const u8, elf_file: *Elf) !void {
23 pub inline fn len(entry: Entry) usize {29 dt.soname = try elf_file.dynstrtab.insert(elf_file.base.allocator, soname);
24 return switch (entry.tag) {30 }
25 .got, .gottp => 1,31
26 .tlsld, .tlsgd, .tlsdesc => 2,32 fn getFlags(dt: DynamicSection, elf_file: *Elf) ?u64 {
27 };33 _ = dt;
34 var flags: u64 = 0;
35 if (elf_file.base.options.z_now) {
36 flags |= elf.DF_BIND_NOW;
37 }
38 for (elf_file.got.entries.items) |entry| switch (entry.tag) {
39 .gottp => {
40 flags |= elf.DF_STATIC_TLS;
41 break;
42 },
43 else => {},
44 };
45 if (elf_file.has_text_reloc) {
46 flags |= elf.DF_TEXTREL;
47 }
48 return if (flags > 0) flags else null;
49 }
50
51 fn getFlags1(dt: DynamicSection, elf_file: *Elf) ?u64 {
52 _ = dt;
53 var flags_1: u64 = 0;
54 if (elf_file.base.options.z_now) {
55 flags_1 |= elf.DF_1_NOW;
28 }56 }
57 if (elf_file.base.options.pie) {
58 flags_1 |= elf.DF_1_PIE;
59 }
60 // if (elf_file.base.options.z_nodlopen) {
61 // flags_1 |= elf.DF_1_NOOPEN;
62 // }
63 return if (flags_1 > 0) flags_1 else null;
64 }
2965
30 pub fn address(entry: Entry, elf_file: *Elf) u64 {66 pub fn size(dt: DynamicSection, elf_file: *Elf) usize {
31 const ptr_bytes = @as(u64, elf_file.archPtrWidthBytes());67 var nentries: usize = 0;
32 const shdr = &elf_file.shdrs.items[elf_file.got_section_index.?];68 nentries += dt.needed.items.len; // NEEDED
33 return shdr.sh_addr + @as(u64, entry.cell_index) * ptr_bytes;69 if (dt.soname != null) nentries += 1; // SONAME
70 if (dt.rpath > 0) nentries += 1; // RUNPATH
71 if (elf_file.sectionByName(".init") != null) nentries += 1; // INIT
72 if (elf_file.sectionByName(".fini") != null) nentries += 1; // FINI
73 if (elf_file.sectionByName(".init_array") != null) nentries += 2; // INIT_ARRAY
74 if (elf_file.sectionByName(".fini_array") != null) nentries += 2; // FINI_ARRAY
75 if (elf_file.rela_dyn_section_index != null) nentries += 3; // RELA
76 if (elf_file.rela_plt_section_index != null) nentries += 3; // JMPREL
77 if (elf_file.got_plt_section_index != null) nentries += 1; // PLTGOT
78 nentries += 1; // HASH
79 if (elf_file.gnu_hash_section_index != null) nentries += 1; // GNU_HASH
80 if (elf_file.has_text_reloc) nentries += 1; // TEXTREL
81 nentries += 1; // SYMTAB
82 nentries += 1; // SYMENT
83 nentries += 1; // STRTAB
84 nentries += 1; // STRSZ
85 if (elf_file.versym_section_index != null) nentries += 1; // VERSYM
86 if (elf_file.verneed_section_index != null) nentries += 2; // VERNEED
87 if (dt.getFlags(elf_file) != null) nentries += 1; // FLAGS
88 if (dt.getFlags1(elf_file) != null) nentries += 1; // FLAGS_1
89 if (!elf_file.isDynLib()) nentries += 1; // DEBUG
90 nentries += 1; // NULL
91 return nentries * @sizeOf(elf.Elf64_Dyn);
92 }
93
94 pub fn write(dt: DynamicSection, elf_file: *Elf, writer: anytype) !void {
95 // NEEDED
96 for (dt.needed.items) |off| {
97 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_NEEDED, .d_val = off });
98 }
99
100 if (dt.soname) |off| {
101 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_SONAME, .d_val = off });
102 }
103
104 // RUNPATH
105 // TODO add option in Options to revert to old RPATH tag
106 if (dt.rpath > 0) {
107 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_RUNPATH, .d_val = dt.rpath });
108 }
109
110 // INIT
111 if (elf_file.sectionByName(".init")) |shndx| {
112 const addr = elf_file.shdrs.items[shndx].sh_addr;
113 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_INIT, .d_val = addr });
114 }
115
116 // FINI
117 if (elf_file.sectionByName(".fini")) |shndx| {
118 const addr = elf_file.shdrs.items[shndx].sh_addr;
119 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_FINI, .d_val = addr });
120 }
121
122 // INIT_ARRAY
123 if (elf_file.sectionByName(".init_array")) |shndx| {
124 const shdr = elf_file.shdrs.items[shndx];
125 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_INIT_ARRAY, .d_val = shdr.sh_addr });
126 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_INIT_ARRAYSZ, .d_val = shdr.sh_size });
127 }
128
129 // FINI_ARRAY
130 if (elf_file.sectionByName(".fini_array")) |shndx| {
131 const shdr = elf_file.shdrs.items[shndx];
132 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_FINI_ARRAY, .d_val = shdr.sh_addr });
133 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_FINI_ARRAYSZ, .d_val = shdr.sh_size });
134 }
135
136 // RELA
137 if (elf_file.rela_dyn_section_index) |shndx| {
138 const shdr = elf_file.shdrs.items[shndx];
139 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_RELA, .d_val = shdr.sh_addr });
140 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_RELASZ, .d_val = shdr.sh_size });
141 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_RELAENT, .d_val = shdr.sh_entsize });
142 }
143
144 // JMPREL
145 if (elf_file.rela_plt_section_index) |shndx| {
146 const shdr = elf_file.shdrs.items[shndx];
147 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_JMPREL, .d_val = shdr.sh_addr });
148 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_PLTRELSZ, .d_val = shdr.sh_size });
149 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_PLTREL, .d_val = elf.DT_RELA });
150 }
151
152 // PLTGOT
153 if (elf_file.got_plt_section_index) |shndx| {
154 const addr = elf_file.shdrs.items[shndx].sh_addr;
155 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_PLTGOT, .d_val = addr });
156 }
157
158 {
159 assert(elf_file.hash_section_index != null);
160 const addr = elf_file.shdrs.items[elf_file.hash_section_index.?].sh_addr;
161 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_HASH, .d_val = addr });
162 }
163
164 if (elf_file.gnu_hash_section_index) |shndx| {
165 const addr = elf_file.shdrs.items[shndx].sh_addr;
166 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_GNU_HASH, .d_val = addr });
34 }167 }
168
169 // TEXTREL
170 if (elf_file.has_text_reloc) {
171 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_TEXTREL, .d_val = 0 });
172 }
173
174 // SYMTAB + SYMENT
175 {
176 assert(elf_file.dynsymtab_section_index != null);
177 const shdr = elf_file.shdrs.items[elf_file.dynsymtab_section_index.?];
178 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_SYMTAB, .d_val = shdr.sh_addr });
179 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_SYMENT, .d_val = shdr.sh_entsize });
180 }
181
182 // STRTAB + STRSZ
183 {
184 assert(elf_file.dynstrtab_section_index != null);
185 const shdr = elf_file.shdrs.items[elf_file.dynstrtab_section_index.?];
186 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_STRTAB, .d_val = shdr.sh_addr });
187 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_STRSZ, .d_val = shdr.sh_size });
188 }
189
190 // VERSYM
191 if (elf_file.versym_section_index) |shndx| {
192 const addr = elf_file.shdrs.items[shndx].sh_addr;
193 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_VERSYM, .d_val = addr });
194 }
195
196 // VERNEED + VERNEEDNUM
197 if (elf_file.verneed_section_index) |shndx| {
198 const addr = elf_file.shdrs.items[shndx].sh_addr;
199 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_VERNEED, .d_val = addr });
200 try writer.writeStruct(elf.Elf64_Dyn{
201 .d_tag = elf.DT_VERNEEDNUM,
202 .d_val = elf_file.verneed.verneed.items.len,
203 });
204 }
205
206 // FLAGS
207 if (dt.getFlags(elf_file)) |flags| {
208 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_FLAGS, .d_val = flags });
209 }
210 // FLAGS_1
211 if (dt.getFlags1(elf_file)) |flags_1| {
212 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_FLAGS_1, .d_val = flags_1 });
213 }
214
215 // DEBUG
216 if (!elf_file.isDynLib()) try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_DEBUG, .d_val = 0 });
217
218 // NULL
219 try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_NULL, .d_val = 0 });
220 }
221};
222
223pub const ZigGotSection = struct {
224 entries: std.ArrayListUnmanaged(Symbol.Index) = .{},
225 output_symtab_size: Elf.SymtabSize = .{},
226 flags: Flags = .{},
227
228 const Flags = packed struct {
229 needs_rela: bool = false, // TODO in prep for PIC/PIE and base relocations
230 dirty: bool = false,
35 };231 };
36232
37 pub fn deinit(got: *GotSection, allocator: Allocator) void {233 pub const Index = u32;
38 got.entries.deinit(allocator);234
235 pub fn deinit(zig_got: *ZigGotSection, allocator: Allocator) void {
236 zig_got.entries.deinit(allocator);
39 }237 }
40238
41 fn allocateEntry(got: *GotSection, allocator: Allocator) !Index {239 fn allocateEntry(zig_got: *ZigGotSection, allocator: Allocator) !Index {
42 try got.entries.ensureUnusedCapacity(allocator, 1);240 try zig_got.entries.ensureUnusedCapacity(allocator, 1);
43 // TODO add free list241 // TODO add free list
44 const index = @as(Index, @intCast(got.entries.items.len));242 const index = @as(Index, @intCast(zig_got.entries.items.len));
45 const entry = got.entries.addOneAssumeCapacity();243 _ = zig_got.entries.addOneAssumeCapacity();
46 const cell_index: Index = if (index > 0) blk: {244 zig_got.flags.dirty = true;
47 const last = got.entries.items[index - 1];
48 break :blk last.cell_index + @as(Index, @intCast(last.len()));
49 } else 0;
50 entry.* = .{ .tag = undefined, .symbol_index = undefined, .cell_index = cell_index };
51 got.dirty = true;
52 return index;245 return index;
53 }246 }
54247
55 pub fn addGotSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index {248 pub fn addSymbol(zig_got: *ZigGotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index {
56 const index = try got.allocateEntry(elf_file.base.allocator);249 const index = try zig_got.allocateEntry(elf_file.base.allocator);
57 const entry = &got.entries.items[index];250 const entry = &zig_got.entries.items[index];
58 entry.tag = .got;251 entry.* = sym_index;
59 entry.symbol_index = sym_index;
60 const symbol = elf_file.symbol(sym_index);252 const symbol = elf_file.symbol(sym_index);
61 if (symbol.flags.import or symbol.isIFunc(elf_file) or (elf_file.base.options.pic and !symbol.isAbs(elf_file)))253 symbol.flags.has_zig_got = true;
62 got.needs_rela = true;254 if (elf_file.base.options.pic) {
255 zig_got.flags.needs_rela = true;
256 }
63 if (symbol.extra(elf_file)) |extra| {257 if (symbol.extra(elf_file)) |extra| {
64 var new_extra = extra;258 var new_extra = extra;
65 new_extra.got = index;259 new_extra.zig_got = index;
66 symbol.setExtra(new_extra, elf_file);260 symbol.setExtra(new_extra, elf_file);
67 } else try symbol.addExtra(.{ .got = index }, elf_file);261 } else try symbol.addExtra(.{ .zig_got = index }, elf_file);
68 return index;262 return index;
69 }263 }
70264
71 // pub fn addTlsGdSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void {265 pub fn entryOffset(zig_got: ZigGotSection, index: Index, elf_file: *Elf) u64 {
72 // const index = got.next_index;266 _ = zig_got;
73 // const symbol = elf_file.getSymbol(sym_index);267 const entry_size = elf_file.archPtrWidthBytes();
74 // if (symbol.flags.import or elf_file.options.output_mode == .lib) got.needs_rela = true;268 const shdr = elf_file.shdrs.items[elf_file.zig_got_section_index.?];
75 // if (symbol.getExtra(elf_file)) |extra| {269 return shdr.sh_offset + @as(u64, entry_size) * index;
76 // var new_extra = extra;270 }
77 // new_extra.tlsgd = index;
78 // symbol.setExtra(new_extra, elf_file);
79 // } else try symbol.addExtra(.{ .tlsgd = index }, elf_file);
80 // try got.symbols.append(elf_file.base.allocator, .{ .tlsgd = sym_index });
81 // got.next_index += 2;
82 // }
83
84 // pub fn addGotTpSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void {
85 // const index = got.next_index;
86 // const symbol = elf_file.getSymbol(sym_index);
87 // if (symbol.flags.import or elf_file.options.output_mode == .lib) got.needs_rela = true;
88 // if (symbol.getExtra(elf_file)) |extra| {
89 // var new_extra = extra;
90 // new_extra.gottp = index;
91 // symbol.setExtra(new_extra, elf_file);
92 // } else try symbol.addExtra(.{ .gottp = index }, elf_file);
93 // try got.symbols.append(elf_file.base.allocator, .{ .gottp = sym_index });
94 // got.next_index += 1;
95 // }
96
97 // pub fn addTlsDescSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void {
98 // const index = got.next_index;
99 // const symbol = elf_file.getSymbol(sym_index);
100 // got.needs_rela = true;
101 // if (symbol.getExtra(elf_file)) |extra| {
102 // var new_extra = extra;
103 // new_extra.tlsdesc = index;
104 // symbol.setExtra(new_extra, elf_file);
105 // } else try symbol.addExtra(.{ .tlsdesc = index }, elf_file);
106 // try got.symbols.append(elf_file.base.allocator, .{ .tlsdesc = sym_index });
107 // got.next_index += 2;
108 // }
109271
110 pub fn size(got: GotSection, elf_file: *Elf) usize {272 pub fn entryAddress(zig_got: ZigGotSection, index: Index, elf_file: *Elf) u64 {
111 var s: usize = 0;273 _ = zig_got;
112 for (got.entries.items) |entry| {274 const entry_size = elf_file.archPtrWidthBytes();
113 s += elf_file.archPtrWidthBytes() * entry.len();275 const shdr = elf_file.shdrs.items[elf_file.zig_got_section_index.?];
114 }276 return shdr.sh_addr + @as(u64, entry_size) * index;
115 return s;
116 }277 }
117278
118 pub fn writeEntry(got: *GotSection, elf_file: *Elf, index: Index) !void {279 pub fn size(zig_got: ZigGotSection, elf_file: *Elf) usize {
119 const entry_size: u16 = elf_file.archPtrWidthBytes();280 return elf_file.archPtrWidthBytes() * zig_got.entries.items.len;
120 if (got.dirty) {281 }
121 const needed_size = got.size(elf_file);282
122 try elf_file.growAllocSection(elf_file.got_section_index.?, needed_size);283 pub fn writeOne(zig_got: *ZigGotSection, elf_file: *Elf, index: Index) !void {
123 got.dirty = false;284 if (zig_got.flags.dirty) {
285 const needed_size = zig_got.size(elf_file);
286 try elf_file.growAllocSection(elf_file.zig_got_section_index.?, needed_size);
287 zig_got.flags.dirty = false;
124 }288 }
289 const entry_size: u16 = elf_file.archPtrWidthBytes();
125 const endian = elf_file.base.options.target.cpu.arch.endian();290 const endian = elf_file.base.options.target.cpu.arch.endian();
126 const entry = got.entries.items[index];291 const off = zig_got.entryOffset(index, elf_file);
127 const shdr = &elf_file.shdrs.items[elf_file.got_section_index.?];292 const vaddr = zig_got.entryAddress(index, elf_file);
128 const off = shdr.sh_offset + @as(u64, entry_size) * entry.cell_index;293 const entry = zig_got.entries.items[index];
129 const vaddr = shdr.sh_addr + @as(u64, entry_size) * entry.cell_index;294 const value = elf_file.symbol(entry).value;
130 const value = elf_file.symbol(entry.symbol_index).value;
131 switch (entry_size) {295 switch (entry_size) {
132 2 => {296 2 => {
133 var buf: [2]u8 = undefined;297 var buf: [2]u8 = undefined;
...@@ -169,237 +333,61 @@ pub const GotSection = struct {...@@ -169,237 +333,61 @@ pub const GotSection = struct {
169 }333 }
170 }334 }
171335
172 pub fn writeAllEntries(got: GotSection, elf_file: *Elf, writer: anytype) !void {336 pub fn writeAll(zig_got: ZigGotSection, elf_file: *Elf, writer: anytype) !void {
173 assert(!got.dirty);337 for (zig_got.entries.items) |entry| {
174 const entry_size: u16 = elf_file.archPtrWidthBytes();338 const symbol = elf_file.symbol(entry);
175 const endian = elf_file.base.options.target.cpu.arch.endian();339 const value = symbol.address(.{ .plt = false }, elf_file);
176 for (got.entries.items) |entry| {340 try writeInt(value, elf_file, writer);
177 const value = elf_file.symbol(entry.symbol_index).value;
178 switch (entry_size) {
179 2 => try writer.writeInt(u16, @intCast(value), endian),
180 4 => try writer.writeInt(u32, @intCast(value), endian),
181 8 => try writer.writeInt(u64, @intCast(value), endian),
182 else => unreachable,
183 }
184 }341 }
185 }342 }
186343
187 // pub fn write(got: GotSection, elf_file: *Elf, writer: anytype) !void {344 pub fn numRela(zig_got: ZigGotSection) usize {
188 // const is_shared = elf_file.options.output_mode == .lib;345 return zig_got.entries.items.len;
189 // const apply_relocs = elf_file.options.apply_dynamic_relocs;346 }
190
191 // for (got.symbols.items) |sym| {
192 // const symbol = elf_file.getSymbol(sym.getIndex());
193 // switch (sym) {
194 // .got => {
195 // const value: u64 = blk: {
196 // const value = symbol.getAddress(.{ .plt = false }, elf_file);
197 // if (symbol.flags.import) break :blk 0;
198 // if (symbol.isIFunc(elf_file))
199 // break :blk if (apply_relocs) value else 0;
200 // if (elf_file.options.pic and !symbol.isAbs(elf_file))
201 // break :blk if (apply_relocs) value else 0;
202 // break :blk value;
203 // };
204 // try writer.writeIntLittle(u64, value);
205 // },
206
207 // .tlsgd => {
208 // if (symbol.flags.import) {
209 // try writer.writeIntLittle(u64, 0);
210 // try writer.writeIntLittle(u64, 0);
211 // } else {
212 // try writer.writeIntLittle(u64, if (is_shared) @as(u64, 0) else 1);
213 // const offset = symbol.getAddress(.{}, elf_file) - elf_file.getDtpAddress();
214 // try writer.writeIntLittle(u64, offset);
215 // }
216 // },
217
218 // .gottp => {
219 // if (symbol.flags.import) {
220 // try writer.writeIntLittle(u64, 0);
221 // } else if (is_shared) {
222 // const offset = if (apply_relocs)
223 // symbol.getAddress(.{}, elf_file) - elf_file.getTlsAddress()
224 // else
225 // 0;
226 // try writer.writeIntLittle(u64, offset);
227 // } else {
228 // const offset = @as(i64, @intCast(symbol.getAddress(.{}, elf_file))) -
229 // @as(i64, @intCast(elf_file.getTpAddress()));
230 // try writer.writeIntLittle(u64, @as(u64, @bitCast(offset)));
231 // }
232 // },
233
234 // .tlsdesc => {
235 // try writer.writeIntLittle(u64, 0);
236 // try writer.writeIntLittle(u64, 0);
237 // },
238 // }
239 // }
240
241 // if (got.emit_tlsld) {
242 // try writer.writeIntLittle(u64, if (is_shared) @as(u64, 0) else 1);
243 // try writer.writeIntLittle(u64, 0);
244 // }
245 // }
246
247 // pub fn addRela(got: GotSection, elf_file: *Elf) !void {
248 // const is_shared = elf_file.options.output_mode == .lib;
249 // try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, got.numRela(elf_file));
250
251 // for (got.symbols.items) |sym| {
252 // const symbol = elf_file.getSymbol(sym.getIndex());
253 // const extra = symbol.getExtra(elf_file).?;
254
255 // switch (sym) {
256 // .got => {
257 // const offset = symbol.gotAddress(elf_file);
258
259 // if (symbol.flags.import) {
260 // elf_file.addRelaDynAssumeCapacity(.{
261 // .offset = offset,
262 // .sym = extra.dynamic,
263 // .type = elf.R_X86_64_GLOB_DAT,
264 // });
265 // continue;
266 // }
267
268 // if (symbol.isIFunc(elf_file)) {
269 // elf_file.addRelaDynAssumeCapacity(.{
270 // .offset = offset,
271 // .type = elf.R_X86_64_IRELATIVE,
272 // .addend = @intCast(symbol.getAddress(.{ .plt = false }, elf_file)),
273 // });
274 // continue;
275 // }
276
277 // if (elf_file.options.pic and !symbol.isAbs(elf_file)) {
278 // elf_file.addRelaDynAssumeCapacity(.{
279 // .offset = offset,
280 // .type = elf.R_X86_64_RELATIVE,
281 // .addend = @intCast(symbol.getAddress(.{ .plt = false }, elf_file)),
282 // });
283 // }
284 // },
285
286 // .tlsgd => {
287 // const offset = symbol.getTlsGdAddress(elf_file);
288 // if (symbol.flags.import) {
289 // elf_file.addRelaDynAssumeCapacity(.{
290 // .offset = offset,
291 // .sym = extra.dynamic,
292 // .type = elf.R_X86_64_DTPMOD64,
293 // });
294 // elf_file.addRelaDynAssumeCapacity(.{
295 // .offset = offset + 8,
296 // .sym = extra.dynamic,
297 // .type = elf.R_X86_64_DTPOFF64,
298 // });
299 // } else if (is_shared) {
300 // elf_file.addRelaDynAssumeCapacity(.{
301 // .offset = offset,
302 // .sym = extra.dynamic,
303 // .type = elf.R_X86_64_DTPMOD64,
304 // });
305 // }
306 // },
307
308 // .gottp => {
309 // const offset = symbol.getGotTpAddress(elf_file);
310 // if (symbol.flags.import) {
311 // elf_file.addRelaDynAssumeCapacity(.{
312 // .offset = offset,
313 // .sym = extra.dynamic,
314 // .type = elf.R_X86_64_TPOFF64,
315 // });
316 // } else if (is_shared) {
317 // elf_file.addRelaDynAssumeCapacity(.{
318 // .offset = offset,
319 // .type = elf.R_X86_64_TPOFF64,
320 // .addend = @intCast(symbol.getAddress(.{}, elf_file) - elf_file.getTlsAddress()),
321 // });
322 // }
323 // },
324
325 // .tlsdesc => {
326 // const offset = symbol.getTlsDescAddress(elf_file);
327 // elf_file.addRelaDynAssumeCapacity(.{
328 // .offset = offset,
329 // .sym = extra.dynamic,
330 // .type = elf.R_X86_64_TLSDESC,
331 // });
332 // },
333 // }
334 // }
335
336 // if (is_shared and got.emit_tlsld) {
337 // const offset = elf_file.getTlsLdAddress();
338 // elf_file.addRelaDynAssumeCapacity(.{
339 // .offset = offset,
340 // .type = elf.R_X86_64_DTPMOD64,
341 // });
342 // }
343 // }
344
345 // pub fn numRela(got: GotSection, elf_file: *Elf) usize {
346 // const is_shared = elf_file.options.output_mode == .lib;
347 // var num: usize = 0;
348 // for (got.symbols.items) |sym| {
349 // const symbol = elf_file.symbol(sym.index());
350 // switch (sym) {
351 // .got => if (symbol.flags.import or
352 // symbol.isIFunc(elf_file) or (elf_file.options.pic and !symbol.isAbs(elf_file)))
353 // {
354 // num += 1;
355 // },
356
357 // .tlsgd => if (symbol.flags.import) {
358 // num += 2;
359 // } else if (is_shared) {
360 // num += 1;
361 // },
362
363 // .gottp => if (symbol.flags.import or is_shared) {
364 // num += 1;
365 // },
366
367 // .tlsdesc => num += 1,
368 // }
369 // }
370 // if (is_shared and got.emit_tlsld) num += 1;
371 // return num;
372 // }
373347
374 pub fn updateSymtabSize(got: *GotSection, elf_file: *Elf) void {348 pub fn addRela(zig_got: ZigGotSection, elf_file: *Elf) !void {
349 try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, zig_got.numRela());
350 for (zig_got.entries.items) |entry| {
351 const symbol = elf_file.symbol(entry);
352 const offset = symbol.zigGotAddress(elf_file);
353 elf_file.addRelaDynAssumeCapacity(.{
354 .offset = offset,
355 .type = elf.R_X86_64_RELATIVE,
356 .addend = @intCast(symbol.address(.{ .plt = false }, elf_file)),
357 });
358 }
359 }
360
361 pub fn updateSymtabSize(zig_got: *ZigGotSection, elf_file: *Elf) void {
375 _ = elf_file;362 _ = elf_file;
376 got.output_symtab_size.nlocals = @as(u32, @intCast(got.entries.items.len));363 zig_got.output_symtab_size.nlocals = @as(u32, @intCast(zig_got.entries.items.len));
377 }364 }
378365
379 pub fn writeSymtab(got: GotSection, elf_file: *Elf, ctx: anytype) !void {366 pub fn updateStrtab(zig_got: ZigGotSection, elf_file: *Elf) !void {
380 const gpa = elf_file.base.allocator;367 const gpa = elf_file.base.allocator;
381 for (got.entries.items, ctx.ilocal..) |entry, ilocal| {368 for (zig_got.entries.items) |entry| {
382 const suffix = switch (entry.tag) {369 const symbol_name = elf_file.symbol(entry).name(elf_file);
383 .tlsld => "$tlsld",370 const name = try std.fmt.allocPrint(gpa, "{s}$ziggot", .{symbol_name});
384 .tlsgd => "$tlsgd",371 defer gpa.free(name);
385 .got => "$got",372 _ = try elf_file.strtab.insert(gpa, name);
386 .gottp => "$gottp",373 }
387 .tlsdesc => "$tlsdesc",374 }
388 };375
389 const symbol = elf_file.symbol(entry.symbol_index);376 pub fn writeSymtab(zig_got: ZigGotSection, elf_file: *Elf, ctx: anytype) !void {
390 const name = try std.fmt.allocPrint(gpa, "{s}{s}", .{ symbol.name(elf_file), suffix });377 const gpa = elf_file.base.allocator;
378 for (zig_got.entries.items, ctx.ilocal.., 0..) |entry, ilocal, index| {
379 const symbol = elf_file.symbol(entry);
380 const symbol_name = symbol.name(elf_file);
381 const name = try std.fmt.allocPrint(gpa, "{s}$ziggot", .{symbol_name});
391 defer gpa.free(name);382 defer gpa.free(name);
392 const st_name = try elf_file.strtab.insert(gpa, name);383 const st_name = try elf_file.strtab.insert(gpa, name);
393 const st_value = switch (entry.tag) {384 const st_value = zig_got.entryAddress(@intCast(index), elf_file);
394 .got => symbol.gotAddress(elf_file),385 const st_size = elf_file.archPtrWidthBytes();
395 else => unreachable,
396 };
397 const st_size: u64 = entry.len() * elf_file.archPtrWidthBytes();
398 ctx.symtab[ilocal] = .{386 ctx.symtab[ilocal] = .{
399 .st_name = st_name,387 .st_name = st_name,
400 .st_info = elf.STT_OBJECT,388 .st_info = elf.STT_OBJECT,
401 .st_other = 0,389 .st_other = 0,
402 .st_shndx = elf_file.got_section_index.?,390 .st_shndx = elf_file.zig_got_section_index.?,
403 .st_value = st_value,391 .st_value = st_value,
404 .st_size = st_size,392 .st_size = st_size,
405 };393 };
...@@ -407,12 +395,12 @@ pub const GotSection = struct {...@@ -407,12 +395,12 @@ pub const GotSection = struct {
407 }395 }
408396
409 const FormatCtx = struct {397 const FormatCtx = struct {
410 got: GotSection,398 zig_got: ZigGotSection,
411 elf_file: *Elf,399 elf_file: *Elf,
412 };400 };
413401
414 pub fn fmt(got: GotSection, elf_file: *Elf) std.fmt.Formatter(format2) {402 pub fn fmt(zig_got: ZigGotSection, elf_file: *Elf) std.fmt.Formatter(format2) {
415 return .{ .data = .{ .got = got, .elf_file = elf_file } };403 return .{ .data = .{ .zig_got = zig_got, .elf_file = elf_file } };
416 }404 }
417405
418 pub fn format2(406 pub fn format2(
...@@ -423,13 +411,13 @@ pub const GotSection = struct {...@@ -423,13 +411,13 @@ pub const GotSection = struct {
423 ) !void {411 ) !void {
424 _ = options;412 _ = options;
425 _ = unused_fmt_string;413 _ = unused_fmt_string;
426 try writer.writeAll("GOT\n");414 try writer.writeAll(".zig.got\n");
427 for (ctx.got.entries.items) |entry| {415 for (ctx.zig_got.entries.items, 0..) |entry, index| {
428 const symbol = ctx.elf_file.symbol(entry.symbol_index);416 const symbol = ctx.elf_file.symbol(entry);
429 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{417 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
430 entry.cell_index,418 index,
431 entry.address(ctx.elf_file),419 ctx.zig_got.entryAddress(@intCast(index), ctx.elf_file),
432 entry.symbol_index,420 entry,
433 symbol.address(.{}, ctx.elf_file),421 symbol.address(.{}, ctx.elf_file),
434 symbol.name(ctx.elf_file),422 symbol.name(ctx.elf_file),
435 });423 });
...@@ -437,12 +425,1122 @@ pub const GotSection = struct {...@@ -437,12 +425,1122 @@ pub const GotSection = struct {
437 }425 }
438};426};
439427
428pub const GotSection = struct {
429 entries: std.ArrayListUnmanaged(Entry) = .{},
430 output_symtab_size: Elf.SymtabSize = .{},
431 tlsld_index: ?u32 = null,
432 flags: Flags = .{},
433
434 pub const Index = u32;
435
436 const Flags = packed struct {
437 needs_rela: bool = false,
438 needs_tlsld: bool = false,
439 };
440
441 const Tag = enum {
442 got,
443 tlsld,
444 tlsgd,
445 gottp,
446 tlsdesc,
447 };
448
449 const Entry = struct {
450 tag: Tag,
451 symbol_index: Symbol.Index,
452 cell_index: Index,
453
454 /// Returns how many indexes in the GOT this entry uses.
455 pub inline fn len(entry: Entry) usize {
456 return switch (entry.tag) {
457 .got, .gottp => 1,
458 .tlsld, .tlsgd, .tlsdesc => 2,
459 };
460 }
461
462 pub fn address(entry: Entry, elf_file: *Elf) u64 {
463 const ptr_bytes = @as(u64, elf_file.archPtrWidthBytes());
464 const shdr = &elf_file.shdrs.items[elf_file.got_section_index.?];
465 return shdr.sh_addr + @as(u64, entry.cell_index) * ptr_bytes;
466 }
467 };
468
469 pub fn deinit(got: *GotSection, allocator: Allocator) void {
470 got.entries.deinit(allocator);
471 }
472
473 fn allocateEntry(got: *GotSection, allocator: Allocator) !Index {
474 try got.entries.ensureUnusedCapacity(allocator, 1);
475 // TODO add free list
476 const index = @as(Index, @intCast(got.entries.items.len));
477 const entry = got.entries.addOneAssumeCapacity();
478 const cell_index: Index = if (index > 0) blk: {
479 const last = got.entries.items[index - 1];
480 break :blk last.cell_index + @as(Index, @intCast(last.len()));
481 } else 0;
482 entry.* = .{ .tag = undefined, .symbol_index = undefined, .cell_index = cell_index };
483 return index;
484 }
485
486 pub fn addGotSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index {
487 const index = try got.allocateEntry(elf_file.base.allocator);
488 const entry = &got.entries.items[index];
489 entry.tag = .got;
490 entry.symbol_index = sym_index;
491 const symbol = elf_file.symbol(sym_index);
492 symbol.flags.has_got = true;
493 if (symbol.flags.import or symbol.isIFunc(elf_file) or
494 (elf_file.base.options.pic and !symbol.isAbs(elf_file)))
495 {
496 got.flags.needs_rela = true;
497 }
498 if (symbol.extra(elf_file)) |extra| {
499 var new_extra = extra;
500 new_extra.got = index;
501 symbol.setExtra(new_extra, elf_file);
502 } else try symbol.addExtra(.{ .got = index }, elf_file);
503 return index;
504 }
505
506 pub fn addTlsLdSymbol(got: *GotSection, elf_file: *Elf) !void {
507 assert(got.flags.needs_tlsld);
508 const index = try got.allocateEntry(elf_file.base.allocator);
509 const entry = &got.entries.items[index];
510 entry.tag = .tlsld;
511 entry.symbol_index = undefined; // unused
512 got.flags.needs_rela = true;
513 got.tlsld_index = index;
514 }
515
516 pub fn addTlsGdSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void {
517 const index = try got.allocateEntry(elf_file.base.allocator);
518 const entry = &got.entries.items[index];
519 entry.tag = .tlsgd;
520 entry.symbol_index = sym_index;
521 const symbol = elf_file.symbol(sym_index);
522 symbol.flags.has_tlsgd = true;
523 if (symbol.flags.import or elf_file.isDynLib()) got.flags.needs_rela = true;
524 if (symbol.extra(elf_file)) |extra| {
525 var new_extra = extra;
526 new_extra.tlsgd = index;
527 symbol.setExtra(new_extra, elf_file);
528 } else try symbol.addExtra(.{ .tlsgd = index }, elf_file);
529 }
530
531 pub fn addGotTpSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void {
532 const index = try got.allocateEntry(elf_file.base.allocator);
533 const entry = &got.entries.items[index];
534 entry.tag = .gottp;
535 entry.symbol_index = sym_index;
536 const symbol = elf_file.symbol(sym_index);
537 symbol.flags.has_gottp = true;
538 if (symbol.flags.import or elf_file.isDynLib()) got.flags.needs_rela = true;
539 if (symbol.extra(elf_file)) |extra| {
540 var new_extra = extra;
541 new_extra.gottp = index;
542 symbol.setExtra(new_extra, elf_file);
543 } else try symbol.addExtra(.{ .gottp = index }, elf_file);
544 }
545
546 pub fn addTlsDescSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void {
547 const index = try got.allocateEntry(elf_file.base.allocator);
548 const entry = &got.entries.items[index];
549 entry.tag = .tlsdesc;
550 entry.symbol_index = sym_index;
551 const symbol = elf_file.symbol(sym_index);
552 symbol.flags.has_tlsdesc = true;
553 got.flags.needs_rela = true;
554 if (symbol.extra(elf_file)) |extra| {
555 var new_extra = extra;
556 new_extra.tlsdesc = index;
557 symbol.setExtra(new_extra, elf_file);
558 } else try symbol.addExtra(.{ .tlsdesc = index }, elf_file);
559 }
560
561 pub fn size(got: GotSection, elf_file: *Elf) usize {
562 var s: usize = 0;
563 for (got.entries.items) |entry| {
564 s += elf_file.archPtrWidthBytes() * entry.len();
565 }
566 return s;
567 }
568
569 pub fn write(got: GotSection, elf_file: *Elf, writer: anytype) !void {
570 const is_dyn_lib = elf_file.isDynLib();
571 const apply_relocs = true; // TODO add user option for this
572
573 for (got.entries.items) |entry| {
574 const symbol = switch (entry.tag) {
575 .tlsld => null,
576 inline else => elf_file.symbol(entry.symbol_index),
577 };
578 switch (entry.tag) {
579 .got => {
580 const value = blk: {
581 const value = symbol.?.address(.{ .plt = false }, elf_file);
582 if (symbol.?.flags.import) break :blk 0;
583 if (symbol.?.isIFunc(elf_file))
584 break :blk if (apply_relocs) value else 0;
585 if (elf_file.base.options.pic and !symbol.?.isAbs(elf_file))
586 break :blk if (apply_relocs) value else 0;
587 break :blk value;
588 };
589 try writeInt(value, elf_file, writer);
590 },
591 .tlsld => {
592 try writeInt(if (is_dyn_lib) @as(u64, 0) else 1, elf_file, writer);
593 try writeInt(0, elf_file, writer);
594 },
595 .tlsgd => {
596 if (symbol.?.flags.import) {
597 try writeInt(0, elf_file, writer);
598 try writeInt(0, elf_file, writer);
599 } else {
600 try writeInt(if (is_dyn_lib) @as(u64, 0) else 1, elf_file, writer);
601 const offset = symbol.?.address(.{}, elf_file) - elf_file.dtpAddress();
602 try writeInt(offset, elf_file, writer);
603 }
604 },
605 .gottp => {
606 if (symbol.?.flags.import) {
607 try writeInt(0, elf_file, writer);
608 } else if (is_dyn_lib) {
609 const offset = if (apply_relocs)
610 symbol.?.address(.{}, elf_file) - elf_file.tlsAddress()
611 else
612 0;
613 try writeInt(offset, elf_file, writer);
614 } else {
615 const offset = @as(i64, @intCast(symbol.?.address(.{}, elf_file))) -
616 @as(i64, @intCast(elf_file.tpAddress()));
617 try writeInt(offset, elf_file, writer);
618 }
619 },
620 .tlsdesc => {
621 try writeInt(0, elf_file, writer);
622 try writeInt(0, elf_file, writer);
623 },
624 }
625 }
626 }
627
628 pub fn addRela(got: GotSection, elf_file: *Elf) !void {
629 const is_dyn_lib = elf_file.isDynLib();
630 try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, got.numRela(elf_file));
631
632 for (got.entries.items) |entry| {
633 const symbol = switch (entry.tag) {
634 .tlsld => null,
635 inline else => elf_file.symbol(entry.symbol_index),
636 };
637 const extra = if (symbol) |s| s.extra(elf_file).? else null;
638
639 switch (entry.tag) {
640 .got => {
641 const offset = symbol.?.gotAddress(elf_file);
642 if (symbol.?.flags.import) {
643 elf_file.addRelaDynAssumeCapacity(.{
644 .offset = offset,
645 .sym = extra.?.dynamic,
646 .type = elf.R_X86_64_GLOB_DAT,
647 });
648 continue;
649 }
650 if (symbol.?.isIFunc(elf_file)) {
651 elf_file.addRelaDynAssumeCapacity(.{
652 .offset = offset,
653 .type = elf.R_X86_64_IRELATIVE,
654 .addend = @intCast(symbol.?.address(.{ .plt = false }, elf_file)),
655 });
656 continue;
657 }
658 if (elf_file.base.options.pic and !symbol.?.isAbs(elf_file)) {
659 elf_file.addRelaDynAssumeCapacity(.{
660 .offset = offset,
661 .type = elf.R_X86_64_RELATIVE,
662 .addend = @intCast(symbol.?.address(.{ .plt = false }, elf_file)),
663 });
664 }
665 },
666
667 .tlsld => {
668 if (is_dyn_lib) {
669 const offset = entry.address(elf_file);
670 elf_file.addRelaDynAssumeCapacity(.{
671 .offset = offset,
672 .type = elf.R_X86_64_DTPMOD64,
673 });
674 }
675 },
676
677 .tlsgd => {
678 const offset = symbol.?.tlsGdAddress(elf_file);
679 if (symbol.?.flags.import) {
680 elf_file.addRelaDynAssumeCapacity(.{
681 .offset = offset,
682 .sym = extra.?.dynamic,
683 .type = elf.R_X86_64_DTPMOD64,
684 });
685 elf_file.addRelaDynAssumeCapacity(.{
686 .offset = offset + 8,
687 .sym = extra.?.dynamic,
688 .type = elf.R_X86_64_DTPOFF64,
689 });
690 } else if (is_dyn_lib) {
691 elf_file.addRelaDynAssumeCapacity(.{
692 .offset = offset,
693 .sym = extra.?.dynamic,
694 .type = elf.R_X86_64_DTPMOD64,
695 });
696 }
697 },
698
699 .gottp => {
700 const offset = symbol.?.gotTpAddress(elf_file);
701 if (symbol.?.flags.import) {
702 elf_file.addRelaDynAssumeCapacity(.{
703 .offset = offset,
704 .sym = extra.?.dynamic,
705 .type = elf.R_X86_64_TPOFF64,
706 });
707 } else if (is_dyn_lib) {
708 elf_file.addRelaDynAssumeCapacity(.{
709 .offset = offset,
710 .type = elf.R_X86_64_TPOFF64,
711 .addend = @intCast(symbol.?.address(.{}, elf_file) - elf_file.tlsAddress()),
712 });
713 }
714 },
715
716 .tlsdesc => {
717 const offset = symbol.?.tlsDescAddress(elf_file);
718 elf_file.addRelaDynAssumeCapacity(.{
719 .offset = offset,
720 .sym = extra.?.dynamic,
721 .type = elf.R_X86_64_TLSDESC,
722 });
723 },
724 }
725 }
726 }
727
728 pub fn numRela(got: GotSection, elf_file: *Elf) usize {
729 const is_dyn_lib = elf_file.isDynLib();
730 var num: usize = 0;
731 for (got.entries.items) |entry| {
732 const symbol = switch (entry.tag) {
733 .tlsld => null,
734 inline else => elf_file.symbol(entry.symbol_index),
735 };
736 switch (entry.tag) {
737 .got => if (symbol.?.flags.import or
738 symbol.?.isIFunc(elf_file) or (elf_file.base.options.pic and !symbol.?.isAbs(elf_file)))
739 {
740 num += 1;
741 },
742
743 .tlsld => if (is_dyn_lib) {
744 num += 1;
745 },
746
747 .tlsgd => if (symbol.?.flags.import) {
748 num += 2;
749 } else if (is_dyn_lib) {
750 num += 1;
751 },
752
753 .gottp => if (symbol.?.flags.import or is_dyn_lib) {
754 num += 1;
755 },
756
757 .tlsdesc => num += 1,
758 }
759 }
760 return num;
761 }
762
763 pub fn updateSymtabSize(got: *GotSection, elf_file: *Elf) void {
764 _ = elf_file;
765 got.output_symtab_size.nlocals = @as(u32, @intCast(got.entries.items.len));
766 }
767
768 pub fn updateStrtab(got: GotSection, elf_file: *Elf) !void {
769 const gpa = elf_file.base.allocator;
770 for (got.entries.items) |entry| {
771 const symbol_name = switch (entry.tag) {
772 .tlsld => "",
773 inline else => elf_file.symbol(entry.symbol_index).name(elf_file),
774 };
775 const name = try std.fmt.allocPrint(gpa, "{s}${s}", .{ symbol_name, @tagName(entry.tag) });
776 defer gpa.free(name);
777 _ = try elf_file.strtab.insert(gpa, name);
778 }
779 }
780
781 pub fn writeSymtab(got: GotSection, elf_file: *Elf, ctx: anytype) !void {
782 const gpa = elf_file.base.allocator;
783 for (got.entries.items, ctx.ilocal..) |entry, ilocal| {
784 const symbol = switch (entry.tag) {
785 .tlsld => null,
786 inline else => elf_file.symbol(entry.symbol_index),
787 };
788 const symbol_name = switch (entry.tag) {
789 .tlsld => "",
790 inline else => symbol.?.name(elf_file),
791 };
792 const name = try std.fmt.allocPrint(gpa, "{s}${s}", .{ symbol_name, @tagName(entry.tag) });
793 defer gpa.free(name);
794 const st_name = try elf_file.strtab.insert(gpa, name);
795 const st_value = entry.address(elf_file);
796 const st_size: u64 = entry.len() * elf_file.archPtrWidthBytes();
797 ctx.symtab[ilocal] = .{
798 .st_name = st_name,
799 .st_info = elf.STT_OBJECT,
800 .st_other = 0,
801 .st_shndx = elf_file.got_section_index.?,
802 .st_value = st_value,
803 .st_size = st_size,
804 };
805 }
806 }
807
808 const FormatCtx = struct {
809 got: GotSection,
810 elf_file: *Elf,
811 };
812
813 pub fn fmt(got: GotSection, elf_file: *Elf) std.fmt.Formatter(format2) {
814 return .{ .data = .{ .got = got, .elf_file = elf_file } };
815 }
816
817 pub fn format2(
818 ctx: FormatCtx,
819 comptime unused_fmt_string: []const u8,
820 options: std.fmt.FormatOptions,
821 writer: anytype,
822 ) !void {
823 _ = options;
824 _ = unused_fmt_string;
825 try writer.writeAll("GOT\n");
826 for (ctx.got.entries.items) |entry| {
827 const symbol = ctx.elf_file.symbol(entry.symbol_index);
828 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
829 entry.cell_index,
830 entry.address(ctx.elf_file),
831 entry.symbol_index,
832 symbol.address(.{}, ctx.elf_file),
833 symbol.name(ctx.elf_file),
834 });
835 }
836 }
837};
838
839pub const PltSection = struct {
840 symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
841 output_symtab_size: Elf.SymtabSize = .{},
842
843 pub const preamble_size = 32;
844
845 pub fn deinit(plt: *PltSection, allocator: Allocator) void {
846 plt.symbols.deinit(allocator);
847 }
848
849 pub fn addSymbol(plt: *PltSection, sym_index: Symbol.Index, elf_file: *Elf) !void {
850 const index = @as(u32, @intCast(plt.symbols.items.len));
851 const symbol = elf_file.symbol(sym_index);
852 symbol.flags.has_plt = true;
853 if (symbol.extra(elf_file)) |extra| {
854 var new_extra = extra;
855 new_extra.plt = index;
856 symbol.setExtra(new_extra, elf_file);
857 } else try symbol.addExtra(.{ .plt = index }, elf_file);
858 try plt.symbols.append(elf_file.base.allocator, sym_index);
859 }
860
861 pub fn size(plt: PltSection) usize {
862 return preamble_size + plt.symbols.items.len * 16;
863 }
864
865 pub fn write(plt: PltSection, elf_file: *Elf, writer: anytype) !void {
866 const plt_addr = elf_file.shdrs.items[elf_file.plt_section_index.?].sh_addr;
867 const got_plt_addr = elf_file.shdrs.items[elf_file.got_plt_section_index.?].sh_addr;
868 var preamble = [_]u8{
869 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
870 0x41, 0x53, // push r11
871 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // push qword ptr [rip] -> .got.plt[1]
872 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got.plt[2]
873 };
874 var disp = @as(i64, @intCast(got_plt_addr + 8)) - @as(i64, @intCast(plt_addr + 8)) - 4;
875 mem.writeIntLittle(i32, preamble[8..][0..4], @as(i32, @intCast(disp)));
876 disp = @as(i64, @intCast(got_plt_addr + 16)) - @as(i64, @intCast(plt_addr + 14)) - 4;
877 mem.writeIntLittle(i32, preamble[14..][0..4], @as(i32, @intCast(disp)));
878 try writer.writeAll(&preamble);
879 try writer.writeByteNTimes(0xcc, preamble_size - preamble.len);
880
881 for (plt.symbols.items, 0..) |sym_index, i| {
882 const sym = elf_file.symbol(sym_index);
883 const target_addr = sym.gotPltAddress(elf_file);
884 const source_addr = sym.pltAddress(elf_file);
885 disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 12)) - 4;
886 var entry = [_]u8{
887 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
888 0x41, 0xbb, 0x00, 0x00, 0x00, 0x00, // mov r11d, N
889 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got.plt[N]
890 };
891 mem.writeIntLittle(i32, entry[6..][0..4], @as(i32, @intCast(i)));
892 mem.writeIntLittle(i32, entry[12..][0..4], @as(i32, @intCast(disp)));
893 try writer.writeAll(&entry);
894 }
895 }
896
897 pub fn addRela(plt: PltSection, elf_file: *Elf) !void {
898 try elf_file.rela_plt.ensureUnusedCapacity(elf_file.base.allocator, plt.numRela());
899 for (plt.symbols.items) |sym_index| {
900 const sym = elf_file.symbol(sym_index);
901 assert(sym.flags.import);
902 const extra = sym.extra(elf_file).?;
903 const r_offset = sym.gotPltAddress(elf_file);
904 const r_sym: u64 = extra.dynamic;
905 const r_type: u32 = elf.R_X86_64_JUMP_SLOT;
906 elf_file.rela_plt.appendAssumeCapacity(.{
907 .r_offset = r_offset,
908 .r_info = (r_sym << 32) | r_type,
909 .r_addend = 0,
910 });
911 }
912 }
913
914 pub fn numRela(plt: PltSection) usize {
915 return plt.symbols.items.len;
916 }
917
918 pub fn updateSymtabSize(plt: *PltSection, elf_file: *Elf) void {
919 _ = elf_file;
920 plt.output_symtab_size.nlocals = @as(u32, @intCast(plt.symbols.items.len));
921 }
922
923 pub fn updateStrtab(plt: PltSection, elf_file: *Elf) !void {
924 const gpa = elf_file.base.allocator;
925 for (plt.symbols.items) |sym_index| {
926 const sym = elf_file.symbol(sym_index);
927 const name = try std.fmt.allocPrint(gpa, "{s}$plt", .{sym.name(elf_file)});
928 defer gpa.free(name);
929 _ = try elf_file.strtab.insert(gpa, name);
930 }
931 }
932
933 pub fn writeSymtab(plt: PltSection, elf_file: *Elf, ctx: anytype) !void {
934 const gpa = elf_file.base.allocator;
935
936 var ilocal = ctx.ilocal;
937 for (plt.symbols.items) |sym_index| {
938 const sym = elf_file.symbol(sym_index);
939 const name = try std.fmt.allocPrint(gpa, "{s}$plt", .{sym.name(elf_file)});
940 defer gpa.free(name);
941 const st_name = try elf_file.strtab.insert(gpa, name);
942 ctx.symtab[ilocal] = .{
943 .st_name = st_name,
944 .st_info = elf.STT_FUNC,
945 .st_other = 0,
946 .st_shndx = elf_file.plt_section_index.?,
947 .st_value = sym.pltAddress(elf_file),
948 .st_size = 16,
949 };
950 ilocal += 1;
951 }
952 }
953};
954
955pub const GotPltSection = struct {
956 pub const preamble_size = 24;
957
958 pub fn size(got_plt: GotPltSection, elf_file: *Elf) usize {
959 _ = got_plt;
960 return preamble_size + elf_file.plt.symbols.items.len * 8;
961 }
962
963 pub fn write(got_plt: GotPltSection, elf_file: *Elf, writer: anytype) !void {
964 _ = got_plt;
965 {
966 // [0]: _DYNAMIC
967 const symbol = elf_file.symbol(elf_file.dynamic_index.?);
968 try writer.writeIntLittle(u64, symbol.value);
969 }
970 // [1]: 0x0
971 // [2]: 0x0
972 try writer.writeIntLittle(u64, 0x0);
973 try writer.writeIntLittle(u64, 0x0);
974 if (elf_file.plt_section_index) |shndx| {
975 const plt_addr = elf_file.shdrs.items[shndx].sh_addr;
976 for (0..elf_file.plt.symbols.items.len) |_| {
977 // [N]: .plt
978 try writer.writeIntLittle(u64, plt_addr);
979 }
980 }
981 }
982};
983
984pub const PltGotSection = struct {
985 symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
986 output_symtab_size: Elf.SymtabSize = .{},
987
988 pub fn deinit(plt_got: *PltGotSection, allocator: Allocator) void {
989 plt_got.symbols.deinit(allocator);
990 }
991
992 pub fn addSymbol(plt_got: *PltGotSection, sym_index: Symbol.Index, elf_file: *Elf) !void {
993 const index = @as(u32, @intCast(plt_got.symbols.items.len));
994 const symbol = elf_file.symbol(sym_index);
995 symbol.flags.has_plt = true;
996 symbol.flags.has_got = true;
997 if (symbol.extra(elf_file)) |extra| {
998 var new_extra = extra;
999 new_extra.plt_got = index;
1000 symbol.setExtra(new_extra, elf_file);
1001 } else try symbol.addExtra(.{ .plt_got = index }, elf_file);
1002 try plt_got.symbols.append(elf_file.base.allocator, sym_index);
1003 }
1004
1005 pub fn size(plt_got: PltGotSection) usize {
1006 return plt_got.symbols.items.len * 16;
1007 }
1008
1009 pub fn write(plt_got: PltGotSection, elf_file: *Elf, writer: anytype) !void {
1010 for (plt_got.symbols.items) |sym_index| {
1011 const sym = elf_file.symbol(sym_index);
1012 const target_addr = sym.gotAddress(elf_file);
1013 const source_addr = sym.pltGotAddress(elf_file);
1014 const disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 6)) - 4;
1015 var entry = [_]u8{
1016 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
1017 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got[N]
1018 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
1019 };
1020 mem.writeIntLittle(i32, entry[6..][0..4], @as(i32, @intCast(disp)));
1021 try writer.writeAll(&entry);
1022 }
1023 }
1024
1025 pub fn updateSymtabSize(plt_got: *PltGotSection, elf_file: *Elf) void {
1026 _ = elf_file;
1027 plt_got.output_symtab_size.nlocals = @as(u32, @intCast(plt_got.symbols.items.len));
1028 }
1029
1030 pub fn updateStrtab(plt_got: PltGotSection, elf_file: *Elf) !void {
1031 const gpa = elf_file.base.allocator;
1032 for (plt_got.symbols.items) |sym_index| {
1033 const sym = elf_file.symbol(sym_index);
1034 const name = try std.fmt.allocPrint(gpa, "{s}$pltgot", .{sym.name(elf_file)});
1035 defer gpa.free(name);
1036 _ = try elf_file.strtab.insert(gpa, name);
1037 }
1038 }
1039
1040 pub fn writeSymtab(plt_got: PltGotSection, elf_file: *Elf, ctx: anytype) !void {
1041 const gpa = elf_file.base.allocator;
1042 var ilocal = ctx.ilocal;
1043 for (plt_got.symbols.items) |sym_index| {
1044 const sym = elf_file.symbol(sym_index);
1045 const name = try std.fmt.allocPrint(gpa, "{s}$pltgot", .{sym.name(elf_file)});
1046 defer gpa.free(name);
1047 const st_name = try elf_file.strtab.insert(gpa, name);
1048 ctx.symtab[ilocal] = .{
1049 .st_name = st_name,
1050 .st_info = elf.STT_FUNC,
1051 .st_other = 0,
1052 .st_shndx = elf_file.plt_got_section_index.?,
1053 .st_value = sym.pltGotAddress(elf_file),
1054 .st_size = 16,
1055 };
1056 ilocal += 1;
1057 }
1058 }
1059};
1060
1061pub const CopyRelSection = struct {
1062 symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
1063
1064 pub fn deinit(copy_rel: *CopyRelSection, allocator: Allocator) void {
1065 copy_rel.symbols.deinit(allocator);
1066 }
1067
1068 pub fn addSymbol(copy_rel: *CopyRelSection, sym_index: Symbol.Index, elf_file: *Elf) !void {
1069 const index = @as(u32, @intCast(copy_rel.symbols.items.len));
1070 const symbol = elf_file.symbol(sym_index);
1071 symbol.flags.import = true;
1072 symbol.flags.@"export" = true;
1073 symbol.flags.has_copy_rel = true;
1074 symbol.flags.weak = false;
1075
1076 if (symbol.extra(elf_file)) |extra| {
1077 var new_extra = extra;
1078 new_extra.copy_rel = index;
1079 symbol.setExtra(new_extra, elf_file);
1080 } else try symbol.addExtra(.{ .copy_rel = index }, elf_file);
1081 try copy_rel.symbols.append(elf_file.base.allocator, sym_index);
1082
1083 const shared_object = symbol.file(elf_file).?.shared_object;
1084 if (shared_object.aliases == null) {
1085 try shared_object.initSymbolAliases(elf_file);
1086 }
1087
1088 const aliases = shared_object.symbolAliases(sym_index, elf_file);
1089 for (aliases) |alias| {
1090 if (alias == sym_index) continue;
1091 const alias_sym = elf_file.symbol(alias);
1092 alias_sym.flags.import = true;
1093 alias_sym.flags.@"export" = true;
1094 alias_sym.flags.has_copy_rel = true;
1095 alias_sym.flags.needs_copy_rel = true;
1096 alias_sym.flags.weak = false;
1097 try elf_file.dynsym.addSymbol(alias, elf_file);
1098 }
1099 }
1100
1101 pub fn updateSectionSize(copy_rel: CopyRelSection, shndx: u16, elf_file: *Elf) !void {
1102 const shdr = &elf_file.shdrs.items[shndx];
1103 for (copy_rel.symbols.items) |sym_index| {
1104 const symbol = elf_file.symbol(sym_index);
1105 const shared_object = symbol.file(elf_file).?.shared_object;
1106 const alignment = try symbol.dsoAlignment(elf_file);
1107 symbol.value = mem.alignForward(u64, shdr.sh_size, alignment);
1108 shdr.sh_addralign = @max(shdr.sh_addralign, alignment);
1109 shdr.sh_size = symbol.value + symbol.elfSym(elf_file).st_size;
1110
1111 const aliases = shared_object.symbolAliases(sym_index, elf_file);
1112 for (aliases) |alias| {
1113 if (alias == sym_index) continue;
1114 const alias_sym = elf_file.symbol(alias);
1115 alias_sym.value = symbol.value;
1116 }
1117 }
1118 }
1119
1120 pub fn addRela(copy_rel: CopyRelSection, elf_file: *Elf) !void {
1121 try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, copy_rel.numRela());
1122 for (copy_rel.symbols.items) |sym_index| {
1123 const sym = elf_file.symbol(sym_index);
1124 assert(sym.flags.import and sym.flags.has_copy_rel);
1125 const extra = sym.extra(elf_file).?;
1126 elf_file.addRelaDynAssumeCapacity(.{
1127 .offset = sym.address(.{}, elf_file),
1128 .sym = extra.dynamic,
1129 .type = elf.R_X86_64_COPY,
1130 });
1131 }
1132 }
1133
1134 pub fn numRela(copy_rel: CopyRelSection) usize {
1135 return copy_rel.symbols.items.len;
1136 }
1137};
1138
1139pub const DynsymSection = struct {
1140 entries: std.ArrayListUnmanaged(Entry) = .{},
1141
1142 pub const Entry = struct {
1143 /// Index of the symbol which gets privilege of getting a dynamic treatment
1144 symbol_index: Symbol.Index,
1145 /// Offset into .dynstrtab
1146 off: u32,
1147 };
1148
1149 pub fn deinit(dynsym: *DynsymSection, allocator: Allocator) void {
1150 dynsym.entries.deinit(allocator);
1151 }
1152
1153 pub fn addSymbol(dynsym: *DynsymSection, sym_index: Symbol.Index, elf_file: *Elf) !void {
1154 const gpa = elf_file.base.allocator;
1155 const index = @as(u32, @intCast(dynsym.entries.items.len + 1));
1156 const sym = elf_file.symbol(sym_index);
1157 sym.flags.has_dynamic = true;
1158 if (sym.extra(elf_file)) |extra| {
1159 var new_extra = extra;
1160 new_extra.dynamic = index;
1161 sym.setExtra(new_extra, elf_file);
1162 } else try sym.addExtra(.{ .dynamic = index }, elf_file);
1163 const off = try elf_file.dynstrtab.insert(gpa, sym.name(elf_file));
1164 try dynsym.entries.append(gpa, .{ .symbol_index = sym_index, .off = off });
1165 }
1166
1167 pub fn sort(dynsym: *DynsymSection, elf_file: *Elf) void {
1168 const Sort = struct {
1169 pub fn lessThan(ctx: *Elf, lhs: Entry, rhs: Entry) bool {
1170 const lhs_sym = ctx.symbol(lhs.symbol_index);
1171 const rhs_sym = ctx.symbol(rhs.symbol_index);
1172
1173 if (lhs_sym.flags.@"export" != rhs_sym.flags.@"export") {
1174 return rhs_sym.flags.@"export";
1175 }
1176
1177 // TODO cache hash values
1178 const nbuckets = ctx.gnu_hash.num_buckets;
1179 const lhs_hash = GnuHashSection.hasher(lhs_sym.name(ctx)) % nbuckets;
1180 const rhs_hash = GnuHashSection.hasher(rhs_sym.name(ctx)) % nbuckets;
1181
1182 if (lhs_hash == rhs_hash)
1183 return lhs_sym.extra(ctx).?.dynamic < rhs_sym.extra(ctx).?.dynamic;
1184 return lhs_hash < rhs_hash;
1185 }
1186 };
1187
1188 var num_exports: u32 = 0;
1189 for (dynsym.entries.items) |entry| {
1190 const sym = elf_file.symbol(entry.symbol_index);
1191 if (sym.flags.@"export") num_exports += 1;
1192 }
1193
1194 elf_file.gnu_hash.num_buckets = @divTrunc(num_exports, GnuHashSection.load_factor) + 1;
1195
1196 std.mem.sort(Entry, dynsym.entries.items, elf_file, Sort.lessThan);
1197
1198 for (dynsym.entries.items, 1..) |entry, index| {
1199 const sym = elf_file.symbol(entry.symbol_index);
1200 var extra = sym.extra(elf_file).?;
1201 extra.dynamic = @as(u32, @intCast(index));
1202 sym.setExtra(extra, elf_file);
1203 }
1204 }
1205
1206 pub fn size(dynsym: DynsymSection) usize {
1207 return dynsym.count() * @sizeOf(elf.Elf64_Sym);
1208 }
1209
1210 pub fn count(dynsym: DynsymSection) u32 {
1211 return @as(u32, @intCast(dynsym.entries.items.len + 1));
1212 }
1213
1214 pub fn write(dynsym: DynsymSection, elf_file: *Elf, writer: anytype) !void {
1215 try writer.writeStruct(Elf.null_sym);
1216 for (dynsym.entries.items) |entry| {
1217 const sym = elf_file.symbol(entry.symbol_index);
1218 var out_sym: elf.Elf64_Sym = Elf.null_sym;
1219 sym.setOutputSym(elf_file, &out_sym);
1220 out_sym.st_name = entry.off;
1221 try writer.writeStruct(out_sym);
1222 }
1223 }
1224};
1225
1226pub const HashSection = struct {
1227 buffer: std.ArrayListUnmanaged(u8) = .{},
1228
1229 pub fn deinit(hs: *HashSection, allocator: Allocator) void {
1230 hs.buffer.deinit(allocator);
1231 }
1232
1233 pub fn generate(hs: *HashSection, elf_file: *Elf) !void {
1234 if (elf_file.dynsym.count() == 1) return;
1235
1236 const gpa = elf_file.base.allocator;
1237 const nsyms = elf_file.dynsym.count();
1238
1239 var buckets = try gpa.alloc(u32, nsyms);
1240 defer gpa.free(buckets);
1241 @memset(buckets, 0);
1242
1243 var chains = try gpa.alloc(u32, nsyms);
1244 defer gpa.free(chains);
1245 @memset(chains, 0);
1246
1247 for (elf_file.dynsym.entries.items, 1..) |entry, i| {
1248 const name = elf_file.dynstrtab.getAssumeExists(entry.off);
1249 const hash = hasher(name) % buckets.len;
1250 chains[@as(u32, @intCast(i))] = buckets[hash];
1251 buckets[hash] = @as(u32, @intCast(i));
1252 }
1253
1254 try hs.buffer.ensureTotalCapacityPrecise(gpa, (2 + nsyms * 2) * 4);
1255 hs.buffer.writer(gpa).writeIntLittle(u32, @as(u32, @intCast(nsyms))) catch unreachable;
1256 hs.buffer.writer(gpa).writeIntLittle(u32, @as(u32, @intCast(nsyms))) catch unreachable;
1257 hs.buffer.writer(gpa).writeAll(mem.sliceAsBytes(buckets)) catch unreachable;
1258 hs.buffer.writer(gpa).writeAll(mem.sliceAsBytes(chains)) catch unreachable;
1259 }
1260
1261 pub inline fn size(hs: HashSection) usize {
1262 return hs.buffer.items.len;
1263 }
1264
1265 pub fn hasher(name: [:0]const u8) u32 {
1266 var h: u32 = 0;
1267 var g: u32 = 0;
1268 for (name) |c| {
1269 h = (h << 4) + c;
1270 g = h & 0xf0000000;
1271 if (g > 0) h ^= g >> 24;
1272 h &= ~g;
1273 }
1274 return h;
1275 }
1276};
1277
1278pub const GnuHashSection = struct {
1279 num_buckets: u32 = 0,
1280 num_bloom: u32 = 1,
1281 num_exports: u32 = 0,
1282
1283 pub const load_factor = 8;
1284 pub const header_size = 16;
1285 pub const bloom_shift = 26;
1286
1287 fn getExports(elf_file: *Elf) []const DynsymSection.Entry {
1288 const start = for (elf_file.dynsym.entries.items, 0..) |entry, i| {
1289 const sym = elf_file.symbol(entry.symbol_index);
1290 if (sym.flags.@"export") break i;
1291 } else elf_file.dynsym.entries.items.len;
1292 return elf_file.dynsym.entries.items[start..];
1293 }
1294
1295 inline fn bitCeil(x: u64) u64 {
1296 if (@popCount(x) == 1) return x;
1297 return @as(u64, @intCast(@as(u128, 1) << (64 - @clz(x))));
1298 }
1299
1300 pub fn calcSize(hash: *GnuHashSection, elf_file: *Elf) !void {
1301 hash.num_exports = @as(u32, @intCast(getExports(elf_file).len));
1302 if (hash.num_exports > 0) {
1303 const num_bits = hash.num_exports * 12;
1304 hash.num_bloom = @as(u32, @intCast(bitCeil(@divTrunc(num_bits, 64))));
1305 }
1306 }
1307
1308 pub fn size(hash: GnuHashSection) usize {
1309 return header_size + hash.num_bloom * 8 + hash.num_buckets * 4 + hash.num_exports * 4;
1310 }
1311
1312 pub fn write(hash: GnuHashSection, elf_file: *Elf, writer: anytype) !void {
1313 const exports = getExports(elf_file);
1314 const export_off = elf_file.dynsym.count() - hash.num_exports;
1315
1316 var counting = std.io.countingWriter(writer);
1317 const cwriter = counting.writer();
1318
1319 try cwriter.writeIntLittle(u32, hash.num_buckets);
1320 try cwriter.writeIntLittle(u32, export_off);
1321 try cwriter.writeIntLittle(u32, hash.num_bloom);
1322 try cwriter.writeIntLittle(u32, bloom_shift);
1323
1324 const gpa = elf_file.base.allocator;
1325 const hashes = try gpa.alloc(u32, exports.len);
1326 defer gpa.free(hashes);
1327 const indices = try gpa.alloc(u32, exports.len);
1328 defer gpa.free(indices);
1329
1330 // Compose and write the bloom filter
1331 const bloom = try gpa.alloc(u64, hash.num_bloom);
1332 defer gpa.free(bloom);
1333 @memset(bloom, 0);
1334
1335 for (exports, 0..) |entry, i| {
1336 const sym = elf_file.symbol(entry.symbol_index);
1337 const h = hasher(sym.name(elf_file));
1338 hashes[i] = h;
1339 indices[i] = h % hash.num_buckets;
1340 const idx = @divTrunc(h, 64) % hash.num_bloom;
1341 bloom[idx] |= @as(u64, 1) << @as(u6, @intCast(h % 64));
1342 bloom[idx] |= @as(u64, 1) << @as(u6, @intCast((h >> bloom_shift) % 64));
1343 }
1344
1345 try cwriter.writeAll(mem.sliceAsBytes(bloom));
1346
1347 // Fill in the hash bucket indices
1348 const buckets = try gpa.alloc(u32, hash.num_buckets);
1349 defer gpa.free(buckets);
1350 @memset(buckets, 0);
1351
1352 for (0..hash.num_exports) |i| {
1353 if (buckets[indices[i]] == 0) {
1354 buckets[indices[i]] = @as(u32, @intCast(i + export_off));
1355 }
1356 }
1357
1358 try cwriter.writeAll(mem.sliceAsBytes(buckets));
1359
1360 // Finally, write the hash table
1361 const table = try gpa.alloc(u32, hash.num_exports);
1362 defer gpa.free(table);
1363 @memset(table, 0);
1364
1365 for (0..hash.num_exports) |i| {
1366 const h = hashes[i];
1367 if (i == exports.len - 1 or indices[i] != indices[i + 1]) {
1368 table[i] = h | 1;
1369 } else {
1370 table[i] = h & ~@as(u32, 1);
1371 }
1372 }
1373
1374 try cwriter.writeAll(mem.sliceAsBytes(table));
1375
1376 assert(counting.bytes_written == hash.size());
1377 }
1378
1379 pub fn hasher(name: [:0]const u8) u32 {
1380 var h: u32 = 5381;
1381 for (name) |c| {
1382 h = (h << 5) +% h +% c;
1383 }
1384 return h;
1385 }
1386};
1387
1388pub const VerneedSection = struct {
1389 verneed: std.ArrayListUnmanaged(elf.Elf64_Verneed) = .{},
1390 vernaux: std.ArrayListUnmanaged(elf.Elf64_Vernaux) = .{},
1391 index: elf.Elf64_Versym = elf.VER_NDX_GLOBAL + 1,
1392
1393 pub fn deinit(vern: *VerneedSection, allocator: Allocator) void {
1394 vern.verneed.deinit(allocator);
1395 vern.vernaux.deinit(allocator);
1396 }
1397
1398 pub fn generate(vern: *VerneedSection, elf_file: *Elf) !void {
1399 const dynsyms = elf_file.dynsym.entries.items;
1400 var versyms = elf_file.versym.items;
1401
1402 const VersionedSymbol = struct {
1403 /// Index in the output version table
1404 index: usize,
1405 /// Index of the defining this symbol version shared object file
1406 shared_object: File.Index,
1407 /// Version index
1408 version_index: elf.Elf64_Versym,
1409
1410 fn soname(this: @This(), ctx: *Elf) []const u8 {
1411 const shared_object = ctx.file(this.shared_object).?.shared_object;
1412 return shared_object.soname();
1413 }
1414
1415 fn versionString(this: @This(), ctx: *Elf) [:0]const u8 {
1416 const shared_object = ctx.file(this.shared_object).?.shared_object;
1417 return shared_object.versionString(this.version_index);
1418 }
1419
1420 pub fn lessThan(ctx: *Elf, lhs: @This(), rhs: @This()) bool {
1421 if (lhs.shared_object == rhs.shared_object) return lhs.version_index < rhs.version_index;
1422 return mem.lessThan(u8, lhs.soname(ctx), rhs.soname(ctx));
1423 }
1424 };
1425
1426 const gpa = elf_file.base.allocator;
1427 var verneed = std.ArrayList(VersionedSymbol).init(gpa);
1428 defer verneed.deinit();
1429 try verneed.ensureTotalCapacity(dynsyms.len);
1430
1431 for (dynsyms, 1..) |entry, i| {
1432 const symbol = elf_file.symbol(entry.symbol_index);
1433 if (symbol.flags.import and symbol.version_index & elf.VERSYM_VERSION > elf.VER_NDX_GLOBAL) {
1434 const shared_object = symbol.file(elf_file).?.shared_object;
1435 verneed.appendAssumeCapacity(.{
1436 .index = i,
1437 .shared_object = shared_object.index,
1438 .version_index = symbol.version_index,
1439 });
1440 }
1441 }
1442
1443 mem.sort(VersionedSymbol, verneed.items, elf_file, VersionedSymbol.lessThan);
1444
1445 var last = verneed.items[0];
1446 var last_verneed = try vern.addVerneed(last.soname(elf_file), elf_file);
1447 var last_vernaux = try vern.addVernaux(last_verneed, last.versionString(elf_file), elf_file);
1448 versyms[last.index] = last_vernaux.vna_other;
1449
1450 for (verneed.items[1..]) |ver| {
1451 if (ver.shared_object == last.shared_object) {
1452 if (ver.version_index != last.version_index) {
1453 last_vernaux = try vern.addVernaux(last_verneed, ver.versionString(elf_file), elf_file);
1454 }
1455 } else {
1456 last_verneed = try vern.addVerneed(ver.soname(elf_file), elf_file);
1457 last_vernaux = try vern.addVernaux(last_verneed, ver.versionString(elf_file), elf_file);
1458 }
1459 last = ver;
1460 versyms[ver.index] = last_vernaux.vna_other;
1461 }
1462
1463 // Fixup offsets
1464 var count: usize = 0;
1465 var verneed_off: u32 = 0;
1466 var vernaux_off: u32 = @as(u32, @intCast(vern.verneed.items.len)) * @sizeOf(elf.Elf64_Verneed);
1467 for (vern.verneed.items, 0..) |*vsym, vsym_i| {
1468 if (vsym_i < vern.verneed.items.len - 1) vsym.vn_next = @sizeOf(elf.Elf64_Verneed);
1469 vsym.vn_aux = vernaux_off - verneed_off;
1470 var inner_off: u32 = 0;
1471 for (vern.vernaux.items[count..][0..vsym.vn_cnt], 0..) |*vaux, vaux_i| {
1472 if (vaux_i < vsym.vn_cnt - 1) vaux.vna_next = @sizeOf(elf.Elf64_Vernaux);
1473 inner_off += @sizeOf(elf.Elf64_Vernaux);
1474 }
1475 vernaux_off += inner_off;
1476 verneed_off += @sizeOf(elf.Elf64_Verneed);
1477 count += vsym.vn_cnt;
1478 }
1479 }
1480
1481 fn addVerneed(vern: *VerneedSection, soname: []const u8, elf_file: *Elf) !*elf.Elf64_Verneed {
1482 const gpa = elf_file.base.allocator;
1483 const sym = try vern.verneed.addOne(gpa);
1484 sym.* = .{
1485 .vn_version = 1,
1486 .vn_cnt = 0,
1487 .vn_file = try elf_file.dynstrtab.insert(gpa, soname),
1488 .vn_aux = 0,
1489 .vn_next = 0,
1490 };
1491 return sym;
1492 }
1493
1494 fn addVernaux(
1495 vern: *VerneedSection,
1496 verneed_sym: *elf.Elf64_Verneed,
1497 version: [:0]const u8,
1498 elf_file: *Elf,
1499 ) !elf.Elf64_Vernaux {
1500 const gpa = elf_file.base.allocator;
1501 const sym = try vern.vernaux.addOne(gpa);
1502 sym.* = .{
1503 .vna_hash = HashSection.hasher(version),
1504 .vna_flags = 0,
1505 .vna_other = vern.index,
1506 .vna_name = try elf_file.dynstrtab.insert(gpa, version),
1507 .vna_next = 0,
1508 };
1509 verneed_sym.vn_cnt += 1;
1510 vern.index += 1;
1511 return sym.*;
1512 }
1513
1514 pub fn size(vern: VerneedSection) usize {
1515 return vern.verneed.items.len * @sizeOf(elf.Elf64_Verneed) + vern.vernaux.items.len * @sizeOf(elf.Elf64_Vernaux);
1516 }
1517
1518 pub fn write(vern: VerneedSection, writer: anytype) !void {
1519 try writer.writeAll(mem.sliceAsBytes(vern.verneed.items));
1520 try writer.writeAll(mem.sliceAsBytes(vern.vernaux.items));
1521 }
1522};
1523
1524fn writeInt(value: anytype, elf_file: *Elf, writer: anytype) !void {
1525 const entry_size = elf_file.archPtrWidthBytes();
1526 const endian = elf_file.base.options.target.cpu.arch.endian();
1527 switch (entry_size) {
1528 2 => try writer.writeInt(u16, @intCast(value), endian),
1529 4 => try writer.writeInt(u32, @intCast(value), endian),
1530 8 => try writer.writeInt(u64, @intCast(value), endian),
1531 else => unreachable,
1532 }
1533}
1534
440const assert = std.debug.assert;1535const assert = std.debug.assert;
441const builtin = @import("builtin");1536const builtin = @import("builtin");
442const elf = std.elf;1537const elf = std.elf;
1538const mem = std.mem;
443const log = std.log.scoped(.link);1539const log = std.log.scoped(.link);
444const std = @import("std");1540const std = @import("std");
4451541
446const Allocator = std.mem.Allocator;1542const Allocator = std.mem.Allocator;
447const Elf = @import("../Elf.zig");1543const Elf = @import("../Elf.zig");
1544const File = @import("file.zig").File;
1545const SharedObject = @import("SharedObject.zig");
448const Symbol = @import("Symbol.zig");1546const Symbol = @import("Symbol.zig");
test/link/elf.zig+2850-55
...@@ -11,83 +11,2642 @@ pub fn build(b: *Build) void {...@@ -11,83 +11,2642 @@ pub fn build(b: *Build) void {
11 .os_tag = .linux,11 .os_tag = .linux,
12 .abi = .musl,12 .abi = .musl,
13 };13 };
14 const glibc_target = CrossTarget{
15 .cpu_arch = .x86_64,
16 .os_tag = .linux,
17 .abi = .gnu,
18 };
19
20 // Exercise linker with self-hosted backend (no LLVM)
21 elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false }));
22 elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = glibc_target }));
23 elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = musl_target }));
24
25 // Exercise linker with LLVM backend
26 // musl tests
27 elf_step.dependOn(testAbsSymbols(b, .{ .target = musl_target }));
28 elf_step.dependOn(testCommonSymbols(b, .{ .target = musl_target }));
29 elf_step.dependOn(testCommonSymbolsInArchive(b, .{ .target = musl_target }));
30 elf_step.dependOn(testEmptyObject(b, .{ .target = musl_target }));
31 elf_step.dependOn(testEntryPoint(b, .{ .target = musl_target }));
32 elf_step.dependOn(testGcSections(b, .{ .target = musl_target }));
33 elf_step.dependOn(testImageBase(b, .{ .target = musl_target }));
34 elf_step.dependOn(testInitArrayOrder(b, .{ .target = musl_target }));
35 elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = musl_target }));
36 // https://github.com/ziglang/zig/issues/17449
37 // elf_step.dependOn(testLargeBss(b, .{ .target = musl_target }));
38 elf_step.dependOn(testLinkingC(b, .{ .target = musl_target }));
39 elf_step.dependOn(testLinkingCpp(b, .{ .target = musl_target }));
40 elf_step.dependOn(testLinkingZig(b, .{ .target = musl_target }));
41 // https://github.com/ziglang/zig/issues/17451
42 // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = musl_target }));
43 elf_step.dependOn(testTlsStatic(b, .{ .target = musl_target }));
44 elf_step.dependOn(testStrip(b, .{ .target = musl_target }));
45
46 // glibc tests
47 elf_step.dependOn(testAsNeeded(b, .{ .target = glibc_target }));
48 // https://github.com/ziglang/zig/issues/17430
49 // elf_step.dependOn(testCanonicalPlt(b, .{ .target = glibc_target }));
50 elf_step.dependOn(testCopyrel(b, .{ .target = glibc_target }));
51 // https://github.com/ziglang/zig/issues/17430
52 // elf_step.dependOn(testCopyrelAlias(b, .{ .target = glibc_target }));
53 // https://github.com/ziglang/zig/issues/17430
54 // elf_step.dependOn(testCopyrelAlignment(b, .{ .target = glibc_target }));
55 elf_step.dependOn(testDsoPlt(b, .{ .target = glibc_target }));
56 elf_step.dependOn(testDsoUndef(b, .{ .target = glibc_target }));
57 elf_step.dependOn(testExportDynamic(b, .{ .target = glibc_target }));
58 elf_step.dependOn(testExportSymbolsFromExe(b, .{ .target = glibc_target }));
59 // https://github.com/ziglang/zig/issues/17430
60 // elf_step.dependOn(testFuncAddress(b, .{ .target = glibc_target }));
61 elf_step.dependOn(testHiddenWeakUndef(b, .{ .target = glibc_target }));
62 elf_step.dependOn(testIFuncAlias(b, .{ .target = glibc_target }));
63 // https://github.com/ziglang/zig/issues/17430
64 // elf_step.dependOn(testIFuncDlopen(b, .{ .target = glibc_target }));
65 elf_step.dependOn(testIFuncDso(b, .{ .target = glibc_target }));
66 elf_step.dependOn(testIFuncDynamic(b, .{ .target = glibc_target }));
67 elf_step.dependOn(testIFuncExport(b, .{ .target = glibc_target }));
68 elf_step.dependOn(testIFuncFuncPtr(b, .{ .target = glibc_target }));
69 elf_step.dependOn(testIFuncNoPlt(b, .{ .target = glibc_target }));
70 // https://github.com/ziglang/zig/issues/17430 ??
71 // elf_step.dependOn(testIFuncStatic(b, .{ .target = glibc_target }));
72 // elf_step.dependOn(testIFuncStaticPie(b, .{ .target = glibc_target }));
73 elf_step.dependOn(testInitArrayOrder(b, .{ .target = glibc_target }));
74 elf_step.dependOn(testLargeAlignmentDso(b, .{ .target = glibc_target }));
75 elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = glibc_target }));
76 elf_step.dependOn(testLargeBss(b, .{ .target = glibc_target }));
77 elf_step.dependOn(testLinkOrder(b, .{ .target = glibc_target }));
78 // https://github.com/ziglang/zig/issues/17451
79 // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = glibc_target }));
80 elf_step.dependOn(testPie(b, .{ .target = glibc_target }));
81 elf_step.dependOn(testPltGot(b, .{ .target = glibc_target }));
82 elf_step.dependOn(testPreinitArray(b, .{ .target = glibc_target }));
83 elf_step.dependOn(testSharedAbsSymbol(b, .{ .target = glibc_target }));
84 elf_step.dependOn(testTlsDfStaticTls(b, .{ .target = glibc_target }));
85 elf_step.dependOn(testTlsDso(b, .{ .target = glibc_target }));
86 elf_step.dependOn(testTlsGd(b, .{ .target = glibc_target }));
87 elf_step.dependOn(testTlsGdNoPlt(b, .{ .target = glibc_target }));
88 elf_step.dependOn(testTlsGdToIe(b, .{ .target = glibc_target }));
89 elf_step.dependOn(testTlsIe(b, .{ .target = glibc_target }));
90 elf_step.dependOn(testTlsLargeAlignment(b, .{ .target = glibc_target }));
91 elf_step.dependOn(testTlsLargeTbss(b, .{ .target = glibc_target }));
92 elf_step.dependOn(testTlsLargeStaticImage(b, .{ .target = glibc_target }));
93 elf_step.dependOn(testTlsLd(b, .{ .target = glibc_target }));
94 elf_step.dependOn(testTlsLdDso(b, .{ .target = glibc_target }));
95 elf_step.dependOn(testTlsLdNoPlt(b, .{ .target = glibc_target }));
96 // https://github.com/ziglang/zig/issues/17430
97 // elf_step.dependOn(testTlsNoPic(b, .{ .target = glibc_target }));
98 elf_step.dependOn(testTlsOffsetAlignment(b, .{ .target = glibc_target }));
99 elf_step.dependOn(testTlsPic(b, .{ .target = glibc_target }));
100 elf_step.dependOn(testTlsSmallAlignment(b, .{ .target = glibc_target }));
101 elf_step.dependOn(testWeakExports(b, .{ .target = glibc_target }));
102 elf_step.dependOn(testWeakUndefsDso(b, .{ .target = glibc_target }));
103 elf_step.dependOn(testZNow(b, .{ .target = glibc_target }));
104 elf_step.dependOn(testZStackSize(b, .{ .target = glibc_target }));
105 elf_step.dependOn(testZText(b, .{ .target = glibc_target }));
106}
107
108fn testAbsSymbols(b: *Build, opts: Options) *Step {
109 const test_step = addTestStep(b, "abs-symbols", opts);
110
111 const obj = addObject(b, "obj", opts);
112 addAsmSourceBytes(obj,
113 \\.globl foo
114 \\foo = 0x800008
115 );
116
117 const exe = addExecutable(b, "test", opts);
118 addCSourceBytes(exe,
119 \\#include <signal.h>
120 \\#include <stdio.h>
121 \\#include <stdlib.h>
122 \\#include <ucontext.h>
123 \\#include <assert.h>
124 \\void handler(int signum, siginfo_t *info, void *ptr) {
125 \\ assert((size_t)info->si_addr == 0x800008);
126 \\ exit(0);
127 \\}
128 \\extern int foo;
129 \\int main() {
130 \\ struct sigaction act;
131 \\ act.sa_flags = SA_SIGINFO | SA_RESETHAND;
132 \\ act.sa_sigaction = handler;
133 \\ sigemptyset(&act.sa_mask);
134 \\ sigaction(SIGSEGV, &act, 0);
135 \\ foo = 5;
136 \\ return 0;
137 \\}
138 , &.{});
139 exe.addObject(obj);
140 exe.linkLibC();
141
142 const run = addRunArtifact(exe);
143 run.expectExitCode(0);
144 test_step.dependOn(&run.step);
145
146 return test_step;
147}
148
149fn testAsNeeded(b: *Build, opts: Options) *Step {
150 const test_step = addTestStep(b, "as-needed", opts);
151
152 const main_o = addObject(b, "main", opts);
153 addCSourceBytes(main_o,
154 \\#include <stdio.h>
155 \\int baz();
156 \\int main() {
157 \\ printf("%d\n", baz());
158 \\ return 0;
159 \\}
160 , &.{});
161 main_o.linkLibC();
162
163 const libfoo = addSharedLibrary(b, "foo", opts);
164 addCSourceBytes(libfoo, "int foo() { return 42; }", &.{});
165
166 const libbar = addSharedLibrary(b, "bar", opts);
167 addCSourceBytes(libbar, "int bar() { return 42; }", &.{});
168
169 const libbaz = addSharedLibrary(b, "baz", opts);
170 addCSourceBytes(libbaz,
171 \\int foo();
172 \\int baz() { return foo(); }
173 , &.{});
174
175 {
176 const exe = addExecutable(b, "test", opts);
177 exe.addObject(main_o);
178 exe.linkSystemLibrary2("foo", .{ .needed = true });
179 exe.addLibraryPath(libfoo.getEmittedBinDirectory());
180 exe.addRPath(libfoo.getEmittedBinDirectory());
181 exe.linkSystemLibrary2("bar", .{ .needed = true });
182 exe.addLibraryPath(libbar.getEmittedBinDirectory());
183 exe.addRPath(libbar.getEmittedBinDirectory());
184 exe.linkSystemLibrary2("baz", .{ .needed = true });
185 exe.addLibraryPath(libbaz.getEmittedBinDirectory());
186 exe.addRPath(libbaz.getEmittedBinDirectory());
187 exe.linkLibC();
188
189 const run = addRunArtifact(exe);
190 run.expectStdOutEqual("42\n");
191 test_step.dependOn(&run.step);
192
193 const check = exe.checkObject();
194 check.checkInDynamicSection();
195 check.checkExact("NEEDED libfoo.so");
196 check.checkExact("NEEDED libbar.so");
197 check.checkExact("NEEDED libbaz.so");
198 test_step.dependOn(&check.step);
199 }
200
201 {
202 const exe = addExecutable(b, "test", opts);
203 exe.addObject(main_o);
204 exe.linkSystemLibrary2("foo", .{ .needed = false });
205 exe.addLibraryPath(libfoo.getEmittedBinDirectory());
206 exe.addRPath(libfoo.getEmittedBinDirectory());
207 exe.linkSystemLibrary2("bar", .{ .needed = false });
208 exe.addLibraryPath(libbar.getEmittedBinDirectory());
209 exe.addRPath(libbar.getEmittedBinDirectory());
210 exe.linkSystemLibrary2("baz", .{ .needed = false });
211 exe.addLibraryPath(libbaz.getEmittedBinDirectory());
212 exe.addRPath(libbaz.getEmittedBinDirectory());
213 exe.linkLibC();
214
215 const run = addRunArtifact(exe);
216 run.expectStdOutEqual("42\n");
217 test_step.dependOn(&run.step);
218
219 const check = exe.checkObject();
220 check.checkInDynamicSection();
221 check.checkNotPresent("NEEDED libbar.so");
222 check.checkInDynamicSection();
223 check.checkExact("NEEDED libfoo.so");
224 check.checkExact("NEEDED libbaz.so");
225 test_step.dependOn(&check.step);
226 }
227
228 return test_step;
229}
230
231fn testCanonicalPlt(b: *Build, opts: Options) *Step {
232 const test_step = addTestStep(b, "canonical-plt", opts);
233
234 const dso = addSharedLibrary(b, "a", opts);
235 addCSourceBytes(dso,
236 \\void *foo() {
237 \\ return foo;
238 \\}
239 \\void *bar() {
240 \\ return bar;
241 \\}
242 , &.{});
243
244 const b_o = addObject(b, "obj", opts);
245 addCSourceBytes(b_o,
246 \\void *bar();
247 \\void *baz() {
248 \\ return bar;
249 \\}
250 , &.{});
251 b_o.force_pic = true;
252
253 const main_o = addObject(b, "main", opts);
254 addCSourceBytes(main_o,
255 \\#include <assert.h>
256 \\void *foo();
257 \\void *bar();
258 \\void *baz();
259 \\int main() {
260 \\ assert(foo == foo());
261 \\ assert(bar == bar());
262 \\ assert(bar == baz());
263 \\ return 0;
264 \\}
265 , &.{});
266 main_o.linkLibC();
267 main_o.force_pic = false;
268
269 const exe = addExecutable(b, "main", opts);
270 exe.addObject(main_o);
271 exe.addObject(b_o);
272 exe.linkLibrary(dso);
273 exe.linkLibC();
274 exe.pie = false;
275
276 const run = addRunArtifact(exe);
277 run.expectExitCode(0);
278 test_step.dependOn(&run.step);
279
280 return test_step;
281}
282
283fn testCommonSymbols(b: *Build, opts: Options) *Step {
284 const test_step = addTestStep(b, "common-symbols", opts);
285
286 const exe = addExecutable(b, "test", opts);
287 addCSourceBytes(exe,
288 \\int foo;
289 \\int bar;
290 \\int baz = 42;
291 , &.{"-fcommon"});
292 addCSourceBytes(exe,
293 \\#include<stdio.h>
294 \\int foo;
295 \\int bar = 5;
296 \\int baz;
297 \\int main() {
298 \\ printf("%d %d %d\n", foo, bar, baz);
299 \\}
300 , &.{"-fcommon"});
301 exe.linkLibC();
302
303 const run = addRunArtifact(exe);
304 run.expectStdOutEqual("0 5 42\n");
305 test_step.dependOn(&run.step);
306
307 return test_step;
308}
309
310fn testCommonSymbolsInArchive(b: *Build, opts: Options) *Step {
311 const test_step = addTestStep(b, "common-symbols-in-archive", opts);
312
313 const a_o = addObject(b, "a", opts);
314 addCSourceBytes(a_o,
315 \\#include <stdio.h>
316 \\int foo;
317 \\int bar;
318 \\extern int baz;
319 \\__attribute__((weak)) int two();
320 \\int main() {
321 \\ printf("%d %d %d %d\n", foo, bar, baz, two ? two() : -1);
322 \\}
323 , &.{"-fcommon"});
324 a_o.linkLibC();
325
326 const b_o = addObject(b, "b", opts);
327 addCSourceBytes(b_o, "int foo = 5;", &.{"-fcommon"});
328
329 {
330 const c_o = addObject(b, "c", opts);
331 addCSourceBytes(c_o,
332 \\int bar;
333 \\int two() { return 2; }
334 , &.{"-fcommon"});
335
336 const d_o = addObject(b, "d", opts);
337 addCSourceBytes(d_o, "int baz;", &.{"-fcommon"});
338
339 const lib = addStaticLibrary(b, "lib", opts);
340 lib.addObject(b_o);
341 lib.addObject(c_o);
342 lib.addObject(d_o);
343
344 const exe = addExecutable(b, "test", opts);
345 exe.addObject(a_o);
346 exe.linkLibrary(lib);
347 exe.linkLibC();
348
349 const run = addRunArtifact(exe);
350 run.expectStdOutEqual("5 0 0 -1\n");
351 test_step.dependOn(&run.step);
352 }
353
354 {
355 const e_o = addObject(b, "e", opts);
356 addCSourceBytes(e_o,
357 \\int bar = 0;
358 \\int baz = 7;
359 \\int two() { return 2; }
360 , &.{"-fcommon"});
361
362 const lib = addStaticLibrary(b, "lib", opts);
363 lib.addObject(b_o);
364 lib.addObject(e_o);
365
366 const exe = addExecutable(b, "test", opts);
367 exe.addObject(a_o);
368 exe.linkLibrary(lib);
369 exe.linkLibC();
370
371 const run = addRunArtifact(exe);
372 run.expectStdOutEqual("5 0 7 2\n");
373 test_step.dependOn(&run.step);
374 }
375
376 return test_step;
377}
378
379fn testCopyrel(b: *Build, opts: Options) *Step {
380 const test_step = addTestStep(b, "copyrel", opts);
381
382 const dso = addSharedLibrary(b, "a", opts);
383 addCSourceBytes(dso,
384 \\int foo = 3;
385 \\int bar = 5;
386 , &.{});
387
388 const exe = addExecutable(b, "main", opts);
389 addCSourceBytes(exe,
390 \\#include<stdio.h>
391 \\extern int foo, bar;
392 \\int main() {
393 \\ printf("%d %d\n", foo, bar);
394 \\ return 0;
395 \\}
396 , &.{});
397 exe.linkLibrary(dso);
398 exe.linkLibC();
399
400 const run = addRunArtifact(exe);
401 run.expectStdOutEqual("3 5\n");
402 test_step.dependOn(&run.step);
403
404 return test_step;
405}
406
407fn testCopyrelAlias(b: *Build, opts: Options) *Step {
408 const test_step = addTestStep(b, "copyrel-alias", opts);
409
410 const dso = addSharedLibrary(b, "a", opts);
411 addCSourceBytes(dso,
412 \\int bruh = 31;
413 \\int foo = 42;
414 \\extern int bar __attribute__((alias("foo")));
415 \\extern int baz __attribute__((alias("foo")));
416 , &.{});
417
418 const exe = addExecutable(b, "main", opts);
419 addCSourceBytes(exe,
420 \\#include<stdio.h>
421 \\extern int foo;
422 \\extern int *get_bar();
423 \\int main() {
424 \\ printf("%d %d %d\n", foo, *get_bar(), &foo == get_bar());
425 \\ return 0;
426 \\}
427 , &.{});
428 addCSourceBytes(exe,
429 \\extern int bar;
430 \\int *get_bar() { return &bar; }
431 , &.{});
432 exe.linkLibrary(dso);
433 exe.linkLibC();
434 exe.force_pic = false;
435 exe.pie = false;
436
437 const run = addRunArtifact(exe);
438 run.expectStdOutEqual("42 42 1\n");
439 test_step.dependOn(&run.step);
440
441 return test_step;
442}
443
444fn testCopyrelAlignment(b: *Build, opts: Options) *Step {
445 const test_step = addTestStep(b, "copyrel-alignment", opts);
446
447 const a_so = addSharedLibrary(b, "a", opts);
448 addCSourceBytes(a_so, "__attribute__((aligned(32))) int foo = 5;", &.{});
449
450 const b_so = addSharedLibrary(b, "b", opts);
451 addCSourceBytes(b_so, "__attribute__((aligned(8))) int foo = 5;", &.{});
452
453 const c_so = addSharedLibrary(b, "c", opts);
454 addCSourceBytes(c_so, "__attribute__((aligned(256))) int foo = 5;", &.{});
455
456 const obj = addObject(b, "main", opts);
457 addCSourceBytes(obj,
458 \\#include <stdio.h>
459 \\extern int foo;
460 \\int main() { printf("%d\n", foo); }
461 , &.{});
462 obj.linkLibC();
463 obj.force_pic = false;
464
465 const exp_stdout = "5\n";
466
467 {
468 const exe = addExecutable(b, "main", opts);
469 exe.addObject(obj);
470 exe.linkLibrary(a_so);
471 exe.linkLibC();
472 exe.pie = false;
473
474 const run = addRunArtifact(exe);
475 run.expectStdOutEqual(exp_stdout);
476 test_step.dependOn(&run.step);
477
478 const check = exe.checkObject();
479 check.checkStart();
480 check.checkExact("section headers");
481 check.checkExact("name .copyrel");
482 check.checkExact("addralign 20");
483 test_step.dependOn(&check.step);
484 }
485
486 {
487 const exe = addExecutable(b, "main", opts);
488 exe.addObject(obj);
489 exe.linkLibrary(b_so);
490 exe.linkLibC();
491 exe.pie = false;
492
493 const run = addRunArtifact(exe);
494 run.expectStdOutEqual(exp_stdout);
495 test_step.dependOn(&run.step);
496
497 const check = exe.checkObject();
498 check.checkStart();
499 check.checkExact("section headers");
500 check.checkExact("name .copyrel");
501 check.checkExact("addralign 8");
502 test_step.dependOn(&check.step);
503 }
504
505 {
506 const exe = addExecutable(b, "main", opts);
507 exe.addObject(obj);
508 exe.linkLibrary(c_so);
509 exe.linkLibC();
510 exe.pie = false;
511
512 const run = addRunArtifact(exe);
513 run.expectStdOutEqual(exp_stdout);
514 test_step.dependOn(&run.step);
515
516 const check = exe.checkObject();
517 check.checkStart();
518 check.checkExact("section headers");
519 check.checkExact("name .copyrel");
520 check.checkExact("addralign 100");
521 test_step.dependOn(&check.step);
522 }
523
524 return test_step;
525}
526
527fn testDsoPlt(b: *Build, opts: Options) *Step {
528 const test_step = addTestStep(b, "dso-plt", opts);
529
530 const dso = addSharedLibrary(b, "dso", opts);
531 addCSourceBytes(dso,
532 \\#include<stdio.h>
533 \\void world() {
534 \\ printf("world\n");
535 \\}
536 \\void real_hello() {
537 \\ printf("Hello ");
538 \\ world();
539 \\}
540 \\void hello() {
541 \\ real_hello();
542 \\}
543 , &.{});
544 dso.linkLibC();
545
546 const exe = addExecutable(b, "test", opts);
547 addCSourceBytes(exe,
548 \\#include<stdio.h>
549 \\void world() {
550 \\ printf("WORLD\n");
551 \\}
552 \\void hello();
553 \\int main() {
554 \\ hello();
555 \\}
556 , &.{});
557 exe.linkLibrary(dso);
558 exe.linkLibC();
559
560 const run = addRunArtifact(exe);
561 run.expectStdOutEqual("Hello WORLD\n");
562 test_step.dependOn(&run.step);
563
564 return test_step;
565}
566
567fn testDsoUndef(b: *Build, opts: Options) *Step {
568 const test_step = addTestStep(b, "dso-undef", opts);
569
570 const dso = addSharedLibrary(b, "dso", opts);
571 addCSourceBytes(dso,
572 \\extern int foo;
573 \\int bar = 5;
574 \\int baz() { return foo; }
575 , &.{});
576 dso.linkLibC();
577
578 const obj = addObject(b, "obj", opts);
579 addCSourceBytes(obj, "int foo = 3;", &.{});
580
581 const lib = addStaticLibrary(b, "lib", opts);
582 lib.addObject(obj);
583
584 const exe = addExecutable(b, "test", opts);
585 exe.linkLibrary(dso);
586 exe.linkLibrary(lib);
587 addCSourceBytes(exe,
588 \\extern int bar;
589 \\int main() {
590 \\ return bar - 5;
591 \\}
592 , &.{});
593 exe.linkLibC();
594
595 const run = addRunArtifact(exe);
596 run.expectExitCode(0);
597 test_step.dependOn(&run.step);
598
599 const check = exe.checkObject();
600 check.checkInDynamicSymtab();
601 check.checkContains("foo");
602 test_step.dependOn(&check.step);
603
604 return test_step;
605}
606
607fn testEmptyObject(b: *Build, opts: Options) *Step {
608 const test_step = addTestStep(b, "empty-object", opts);
609
610 const exe = addExecutable(b, "test", opts);
611 addCSourceBytes(exe, "int main() { return 0; }", &.{});
612 addCSourceBytes(exe, "", &.{});
613 exe.linkLibC();
614
615 const run = addRunArtifact(exe);
616 run.expectExitCode(0);
617 test_step.dependOn(&run.step);
618
619 return test_step;
620}
621
622fn testEntryPoint(b: *Build, opts: Options) *Step {
623 const test_step = addTestStep(b, "entry-point", opts);
624
625 const a_o = addObject(b, "a", opts);
626 addAsmSourceBytes(a_o,
627 \\.globl foo, bar
628 \\foo = 0x1000
629 \\bar = 0x2000
630 );
631
632 const b_o = addObject(b, "b", opts);
633 addCSourceBytes(b_o, "int main() { return 0; }", &.{});
634
635 {
636 const exe = addExecutable(b, "main", opts);
637 exe.addObject(a_o);
638 exe.addObject(b_o);
639 exe.entry_symbol_name = "foo";
640
641 const check = exe.checkObject();
642 check.checkStart();
643 check.checkExact("header");
644 check.checkExact("entry 1000");
645 test_step.dependOn(&check.step);
646 }
647
648 {
649 // TODO looks like not assigning a unique name to this executable will
650 // cause an artifact collision taking the cached executable from the above
651 // step instead of generating a new one.
652 const exe = addExecutable(b, "other", opts);
653 exe.addObject(a_o);
654 exe.addObject(b_o);
655 exe.entry_symbol_name = "bar";
656
657 const check = exe.checkObject();
658 check.checkStart();
659 check.checkExact("header");
660 check.checkExact("entry 2000");
661 test_step.dependOn(&check.step);
662 }
663
664 return test_step;
665}
666
667fn testExportDynamic(b: *Build, opts: Options) *Step {
668 const test_step = addTestStep(b, "export-dynamic", opts);
669
670 const obj = addObject(b, "obj", opts);
671 addAsmSourceBytes(obj,
672 \\.text
673 \\ .globl foo
674 \\ .hidden foo
675 \\foo:
676 \\ nop
677 \\ .globl bar
678 \\bar:
679 \\ nop
680 \\ .globl _start
681 \\_start:
682 \\ nop
683 );
684
685 const dso = addSharedLibrary(b, "a", opts);
686 addCSourceBytes(dso, "int baz = 10;", &.{});
687
688 const exe = addExecutable(b, "main", opts);
689 addCSourceBytes(exe,
690 \\extern int baz;
691 \\int callBaz() {
692 \\ return baz;
693 \\}
694 , &.{});
695 exe.addObject(obj);
696 exe.linkLibrary(dso);
697 exe.rdynamic = true;
698
699 const check = exe.checkObject();
700 check.checkInDynamicSymtab();
701 check.checkContains("bar");
702 check.checkInDynamicSymtab();
703 check.checkContains("_start");
704 test_step.dependOn(&check.step);
705
706 return test_step;
707}
708
709fn testExportSymbolsFromExe(b: *Build, opts: Options) *Step {
710 const test_step = addTestStep(b, "export-symbols-from-exe", opts);
711
712 const dso = addSharedLibrary(b, "a", opts);
713 addCSourceBytes(dso,
714 \\void expfn1();
715 \\void expfn2() {}
716 \\
717 \\void foo() {
718 \\ expfn1();
719 \\}
720 , &.{});
721
722 const exe = addExecutable(b, "main", opts);
723 addCSourceBytes(exe,
724 \\void expfn1() {}
725 \\void expfn2() {}
726 \\void foo();
727 \\
728 \\int main() {
729 \\ expfn1();
730 \\ expfn2();
731 \\ foo();
732 \\}
733 , &.{});
734 exe.linkLibrary(dso);
735 exe.linkLibC();
736
737 const check = exe.checkObject();
738 check.checkInDynamicSymtab();
739 check.checkContains("expfn2");
740 check.checkInDynamicSymtab();
741 check.checkContains("expfn1");
742 test_step.dependOn(&check.step);
743
744 return test_step;
745}
746
747fn testFuncAddress(b: *Build, opts: Options) *Step {
748 const test_step = addTestStep(b, "func-address", opts);
749
750 const dso = addSharedLibrary(b, "a", opts);
751 addCSourceBytes(dso, "void fn() {}", &.{});
752
753 const exe = addExecutable(b, "main", opts);
754 addCSourceBytes(exe,
755 \\#include <assert.h>
756 \\typedef void Func();
757 \\void fn();
758 \\Func *const ptr = fn;
759 \\int main() {
760 \\ assert(fn == ptr);
761 \\}
762 , &.{});
763 exe.linkLibrary(dso);
764 exe.force_pic = false;
765 exe.pie = false;
766
767 const run = addRunArtifact(exe);
768 run.expectExitCode(0);
769 test_step.dependOn(&run.step);
770
771 return test_step;
772}
773
774fn testGcSections(b: *Build, opts: Options) *Step {
775 const test_step = addTestStep(b, "gc-sections", opts);
776
777 const obj = addObject(b, "obj", opts);
778 addCppSourceBytes(obj,
779 \\#include <stdio.h>
780 \\int two() { return 2; }
781 \\int live_var1 = 1;
782 \\int live_var2 = two();
783 \\int dead_var1 = 3;
784 \\int dead_var2 = 4;
785 \\void live_fn1() {}
786 \\void live_fn2() { live_fn1(); }
787 \\void dead_fn1() {}
788 \\void dead_fn2() { dead_fn1(); }
789 \\int main() {
790 \\ printf("%d %d\n", live_var1, live_var2);
791 \\ live_fn2();
792 \\}
793 , &.{});
794 obj.link_function_sections = true;
795 obj.link_data_sections = true;
796 obj.linkLibC();
797 obj.linkLibCpp();
798
799 {
800 const exe = addExecutable(b, "test", opts);
801 exe.addObject(obj);
802 exe.link_gc_sections = false;
803 exe.linkLibC();
804 exe.linkLibCpp();
805
806 const run = addRunArtifact(exe);
807 run.expectStdOutEqual("1 2\n");
808 test_step.dependOn(&run.step);
809
810 const check = exe.checkObject();
811 check.checkInSymtab();
812 check.checkContains("live_var1");
813 check.checkInSymtab();
814 check.checkContains("live_var2");
815 check.checkInSymtab();
816 check.checkContains("dead_var1");
817 check.checkInSymtab();
818 check.checkContains("dead_var2");
819 check.checkInSymtab();
820 check.checkContains("live_fn1");
821 check.checkInSymtab();
822 check.checkContains("live_fn2");
823 check.checkInSymtab();
824 check.checkContains("dead_fn1");
825 check.checkInSymtab();
826 check.checkContains("dead_fn2");
827 test_step.dependOn(&check.step);
828 }
829
830 {
831 const exe = addExecutable(b, "test", opts);
832 exe.addObject(obj);
833 exe.link_gc_sections = true;
834 exe.linkLibC();
835 exe.linkLibCpp();
836
837 const run = addRunArtifact(exe);
838 run.expectStdOutEqual("1 2\n");
839 test_step.dependOn(&run.step);
840
841 const check = exe.checkObject();
842 check.checkInSymtab();
843 check.checkContains("live_var1");
844 check.checkInSymtab();
845 check.checkContains("live_var2");
846 check.checkInSymtab();
847 check.checkNotPresent("dead_var1");
848 check.checkInSymtab();
849 check.checkNotPresent("dead_var2");
850 check.checkInSymtab();
851 check.checkContains("live_fn1");
852 check.checkInSymtab();
853 check.checkContains("live_fn2");
854 check.checkInSymtab();
855 check.checkNotPresent("dead_fn1");
856 check.checkInSymtab();
857 check.checkNotPresent("dead_fn2");
858 test_step.dependOn(&check.step);
859 }
860
861 return test_step;
862}
863
864fn testHiddenWeakUndef(b: *Build, opts: Options) *Step {
865 const test_step = addTestStep(b, "hidden-weak-undef", opts);
866
867 const dso = addSharedLibrary(b, "a", opts);
868 addCSourceBytes(dso,
869 \\__attribute__((weak, visibility("hidden"))) void foo();
870 \\void bar() { foo(); }
871 , &.{});
872
873 const check = dso.checkObject();
874 check.checkInDynamicSymtab();
875 check.checkNotPresent("foo");
876 check.checkInDynamicSymtab();
877 check.checkContains("bar");
878 test_step.dependOn(&check.step);
879
880 return test_step;
881}
882
883fn testIFuncAlias(b: *Build, opts: Options) *Step {
884 const test_step = addTestStep(b, "ifunc-alias", opts);
885
886 const exe = addExecutable(b, "main", opts);
887 addCSourceBytes(exe,
888 \\#include <assert.h>
889 \\void foo() {}
890 \\int bar() __attribute__((ifunc("resolve_bar")));
891 \\void *resolve_bar() { return foo; }
892 \\void *bar2 = bar;
893 \\int main() {
894 \\ assert(bar == bar2);
895 \\}
896 , &.{});
897 exe.force_pic = true;
898 exe.linkLibC();
899
900 const run = addRunArtifact(exe);
901 run.expectExitCode(0);
902 test_step.dependOn(&run.step);
903
904 return test_step;
905}
906
907fn testIFuncDlopen(b: *Build, opts: Options) *Step {
908 const test_step = addTestStep(b, "ifunc-dlopen", opts);
909
910 const dso = addSharedLibrary(b, "a", opts);
911 addCSourceBytes(dso,
912 \\__attribute__((ifunc("resolve_foo")))
913 \\void foo(void);
914 \\static void real_foo(void) {
915 \\}
916 \\typedef void Func();
917 \\static Func *resolve_foo(void) {
918 \\ return real_foo;
919 \\}
920 , &.{});
921
922 const exe = addExecutable(b, "main", opts);
923 addCSourceBytes(exe,
924 \\#include <dlfcn.h>
925 \\#include <assert.h>
926 \\#include <stdlib.h>
927 \\typedef void Func();
928 \\void foo(void);
929 \\int main() {
930 \\ void *handle = dlopen(NULL, RTLD_NOW);
931 \\ Func *p = dlsym(handle, "foo");
932 \\
933 \\ foo();
934 \\ p();
935 \\ assert(foo == p);
936 \\}
937 , &.{});
938 exe.linkLibrary(dso);
939 exe.linkLibC();
940 exe.linkSystemLibrary2("dl", .{});
941 exe.force_pic = false;
942 exe.pie = false;
943
944 const run = addRunArtifact(exe);
945 run.expectExitCode(0);
946 test_step.dependOn(&run.step);
947
948 return test_step;
949}
950
951fn testIFuncDso(b: *Build, opts: Options) *Step {
952 const test_step = addTestStep(b, "ifunc-dso", opts);
953
954 const dso = addSharedLibrary(b, "a", opts);
955 addCSourceBytes(dso,
956 \\#include<stdio.h>
957 \\__attribute__((ifunc("resolve_foobar")))
958 \\void foobar(void);
959 \\static void real_foobar(void) {
960 \\ printf("Hello world\n");
961 \\}
962 \\typedef void Func();
963 \\static Func *resolve_foobar(void) {
964 \\ return real_foobar;
965 \\}
966 , &.{});
967 dso.linkLibC();
968
969 const exe = addExecutable(b, "main", opts);
970 addCSourceBytes(exe,
971 \\void foobar(void);
972 \\int main() {
973 \\ foobar();
974 \\}
975 , &.{});
976 exe.linkLibrary(dso);
977
978 const run = addRunArtifact(exe);
979 run.expectStdOutEqual("Hello world\n");
980 test_step.dependOn(&run.step);
981
982 return test_step;
983}
984
985fn testIFuncDynamic(b: *Build, opts: Options) *Step {
986 const test_step = addTestStep(b, "ifunc-dynamic", opts);
987
988 const main_c =
989 \\#include <stdio.h>
990 \\__attribute__((ifunc("resolve_foobar")))
991 \\static void foobar(void);
992 \\static void real_foobar(void) {
993 \\ printf("Hello world\n");
994 \\}
995 \\typedef void Func();
996 \\static Func *resolve_foobar(void) {
997 \\ return real_foobar;
998 \\}
999 \\int main() {
1000 \\ foobar();
1001 \\}
1002 ;
1003
1004 {
1005 const exe = addExecutable(b, "main", opts);
1006 addCSourceBytes(exe, main_c, &.{});
1007 exe.linkLibC();
1008 exe.link_z_lazy = true;
1009
1010 const run = addRunArtifact(exe);
1011 run.expectStdOutEqual("Hello world\n");
1012 test_step.dependOn(&run.step);
1013 }
1014 {
1015 const exe = addExecutable(b, "other", opts);
1016 addCSourceBytes(exe, main_c, &.{});
1017 exe.linkLibC();
1018
1019 const run = addRunArtifact(exe);
1020 run.expectStdOutEqual("Hello world\n");
1021 test_step.dependOn(&run.step);
1022 }
1023
1024 return test_step;
1025}
1026
1027fn testIFuncExport(b: *Build, opts: Options) *Step {
1028 const test_step = addTestStep(b, "ifunc-export", opts);
1029
1030 const dso = addSharedLibrary(b, "a", opts);
1031 addCSourceBytes(dso,
1032 \\#include <stdio.h>
1033 \\__attribute__((ifunc("resolve_foobar")))
1034 \\void foobar(void);
1035 \\void real_foobar(void) {
1036 \\ printf("Hello world\n");
1037 \\}
1038 \\typedef void Func();
1039 \\Func *resolve_foobar(void) {
1040 \\ return real_foobar;
1041 \\}
1042 , &.{});
1043 dso.linkLibC();
1044
1045 const check = dso.checkObject();
1046 check.checkInDynamicSymtab();
1047 check.checkContains("IFUNC GLOBAL DEFAULT foobar");
1048 test_step.dependOn(&check.step);
1049
1050 return test_step;
1051}
1052
1053fn testIFuncFuncPtr(b: *Build, opts: Options) *Step {
1054 const test_step = addTestStep(b, "ifunc-func-ptr", opts);
1055
1056 const exe = addExecutable(b, "main", opts);
1057 addCSourceBytes(exe,
1058 \\typedef int Fn();
1059 \\int foo() __attribute__((ifunc("resolve_foo")));
1060 \\int real_foo() { return 3; }
1061 \\Fn *resolve_foo(void) {
1062 \\ return real_foo;
1063 \\}
1064 , &.{});
1065 addCSourceBytes(exe,
1066 \\typedef int Fn();
1067 \\int foo();
1068 \\Fn *get_foo() { return foo; }
1069 , &.{});
1070 addCSourceBytes(exe,
1071 \\#include <stdio.h>
1072 \\typedef int Fn();
1073 \\Fn *get_foo();
1074 \\int main() {
1075 \\ Fn *f = get_foo();
1076 \\ printf("%d\n", f());
1077 \\}
1078 , &.{});
1079 exe.force_pic = true;
1080 exe.linkLibC();
1081
1082 const run = addRunArtifact(exe);
1083 run.expectStdOutEqual("3\n");
1084 test_step.dependOn(&run.step);
1085
1086 return test_step;
1087}
1088
1089fn testIFuncNoPlt(b: *Build, opts: Options) *Step {
1090 const test_step = addTestStep(b, "ifunc-noplt", opts);
1091
1092 const exe = addExecutable(b, "main", opts);
1093 addCSourceBytes(exe,
1094 \\#include <stdio.h>
1095 \\__attribute__((ifunc("resolve_foo")))
1096 \\void foo(void);
1097 \\void hello(void) {
1098 \\ printf("Hello world\n");
1099 \\}
1100 \\typedef void Fn();
1101 \\Fn *resolve_foo(void) {
1102 \\ return hello;
1103 \\}
1104 \\int main() {
1105 \\ foo();
1106 \\}
1107 , &.{"-fno-plt"});
1108 exe.force_pic = true;
1109 exe.linkLibC();
1110
1111 const run = addRunArtifact(exe);
1112 run.expectStdOutEqual("Hello world\n");
1113 test_step.dependOn(&run.step);
1114
1115 return test_step;
1116}
1117
1118fn testIFuncStatic(b: *Build, opts: Options) *Step {
1119 const test_step = addTestStep(b, "ifunc-static", opts);
1120
1121 const exe = addExecutable(b, "main", opts);
1122 addCSourceBytes(exe,
1123 \\#include <stdio.h>
1124 \\void foo() __attribute__((ifunc("resolve_foo")));
1125 \\void hello() {
1126 \\ printf("Hello world\n");
1127 \\}
1128 \\void *resolve_foo() {
1129 \\ return hello;
1130 \\}
1131 \\int main() {
1132 \\ foo();
1133 \\ return 0;
1134 \\}
1135 , &.{});
1136 exe.linkLibC();
1137 exe.linkage = .static;
1138
1139 const run = addRunArtifact(exe);
1140 run.expectStdOutEqual("Hello world\n");
1141 test_step.dependOn(&run.step);
1142
1143 return test_step;
1144}
1145
1146fn testIFuncStaticPie(b: *Build, opts: Options) *Step {
1147 const test_step = addTestStep(b, "ifunc-static-pie", opts);
1148
1149 const exe = addExecutable(b, "main", opts);
1150 addCSourceBytes(exe,
1151 \\#include <stdio.h>
1152 \\void foo() __attribute__((ifunc("resolve_foo")));
1153 \\void hello() {
1154 \\ printf("Hello world\n");
1155 \\}
1156 \\void *resolve_foo() {
1157 \\ return hello;
1158 \\}
1159 \\int main() {
1160 \\ foo();
1161 \\ return 0;
1162 \\}
1163 , &.{});
1164 exe.linkage = .static;
1165 exe.force_pic = true;
1166 exe.pie = true;
1167 exe.linkLibC();
1168
1169 const run = addRunArtifact(exe);
1170 run.expectStdOutEqual("Hello world\n");
1171 test_step.dependOn(&run.step);
1172
1173 const check = exe.checkObject();
1174 check.checkStart();
1175 check.checkExact("header");
1176 check.checkExact("type DYN");
1177 check.checkStart();
1178 check.checkExact("section headers");
1179 check.checkExact("name .dynamic");
1180 check.checkStart();
1181 check.checkExact("section headers");
1182 check.checkNotPresent("name .interp");
1183 test_step.dependOn(&check.step);
1184
1185 return test_step;
1186}
1187
1188fn testImageBase(b: *Build, opts: Options) *Step {
1189 const test_step = addTestStep(b, "image-base", opts);
1190
1191 {
1192 const exe = addExecutable(b, "main1", opts);
1193 addCSourceBytes(exe,
1194 \\#include <stdio.h>
1195 \\int main() {
1196 \\ printf("Hello World!\n");
1197 \\ return 0;
1198 \\}
1199 , &.{});
1200 exe.linkLibC();
1201 exe.image_base = 0x8000000;
1202
1203 const run = addRunArtifact(exe);
1204 run.expectStdOutEqual("Hello World!\n");
1205 test_step.dependOn(&run.step);
1206
1207 const check = exe.checkObject();
1208 check.checkStart();
1209 check.checkExact("header");
1210 check.checkExtract("entry {addr}");
1211 check.checkComputeCompare("addr", .{ .op = .gte, .value = .{ .literal = 0x8000000 } });
1212 test_step.dependOn(&check.step);
1213 }
1214
1215 {
1216 const exe = addExecutable(b, "main2", opts);
1217 addCSourceBytes(exe, "void _start() {}", &.{});
1218 exe.image_base = 0xffffffff8000000;
1219
1220 const check = exe.checkObject();
1221 check.checkStart();
1222 check.checkExact("header");
1223 check.checkExtract("entry {addr}");
1224 check.checkComputeCompare("addr", .{ .op = .gte, .value = .{ .literal = 0xffffffff8000000 } });
1225 test_step.dependOn(&check.step);
1226 }
1227
1228 return test_step;
1229}
1230
1231fn testImportingDataDynamic(b: *Build, opts: Options) *Step {
1232 const test_step = addTestStep(b, "importing-data-dynamic", opts);
1233
1234 const dso = addSharedLibrary(b, "a", .{
1235 .target = opts.target,
1236 .optimize = opts.optimize,
1237 .use_llvm = true,
1238 });
1239 addCSourceBytes(dso, "int foo = 42;", &.{});
1240
1241 const main = addExecutable(b, "main", opts);
1242 addZigSourceBytes(main,
1243 \\extern var foo: i32;
1244 \\pub fn main() void {
1245 \\ @import("std").debug.print("{d}\n", .{foo});
1246 \\}
1247 );
1248 main.pie = true;
1249 main.strip = true; // TODO temp hack
1250 main.linkLibrary(dso);
1251 main.linkLibC();
1252
1253 const run = addRunArtifact(main);
1254 run.expectStdErrEqual("42\n");
1255 test_step.dependOn(&run.step);
1256
1257 return test_step;
1258}
1259
1260fn testImportingDataStatic(b: *Build, opts: Options) *Step {
1261 const test_step = addTestStep(b, "importing-data-static", opts);
1262
1263 const obj = addObject(b, "a", .{
1264 .target = opts.target,
1265 .optimize = opts.optimize,
1266 .use_llvm = true,
1267 });
1268 addCSourceBytes(obj, "int foo = 42;", &.{});
1269
1270 const lib = addStaticLibrary(b, "a", .{
1271 .target = opts.target,
1272 .optimize = opts.optimize,
1273 .use_llvm = true,
1274 });
1275 lib.addObject(obj);
1276
1277 const main = addExecutable(b, "main", opts);
1278 addZigSourceBytes(main,
1279 \\extern var foo: i32;
1280 \\pub fn main() void {
1281 \\ @import("std").debug.print("{d}\n", .{foo});
1282 \\}
1283 );
1284 main.strip = true; // TODO temp hack
1285 main.linkLibrary(lib);
1286 main.linkLibC();
1287
1288 const run = addRunArtifact(main);
1289 run.expectStdErrEqual("42\n");
1290 test_step.dependOn(&run.step);
1291
1292 return test_step;
1293}
1294
1295fn testInitArrayOrder(b: *Build, opts: Options) *Step {
1296 const test_step = addTestStep(b, "init-array-order", opts);
1297
1298 const a_o = addObject(b, "a", opts);
1299 addCSourceBytes(a_o,
1300 \\#include <stdio.h>
1301 \\__attribute__((constructor(10000))) void init4() { printf("1"); }
1302 , &.{});
1303 a_o.linkLibC();
1304
1305 const b_o = addObject(b, "b", opts);
1306 addCSourceBytes(b_o,
1307 \\#include <stdio.h>
1308 \\__attribute__((constructor(1000))) void init3() { printf("2"); }
1309 , &.{});
1310 b_o.linkLibC();
1311
1312 const c_o = addObject(b, "c", opts);
1313 addCSourceBytes(c_o,
1314 \\#include <stdio.h>
1315 \\__attribute__((constructor)) void init1() { printf("3"); }
1316 , &.{});
1317 c_o.linkLibC();
1318
1319 const d_o = addObject(b, "d", opts);
1320 addCSourceBytes(d_o,
1321 \\#include <stdio.h>
1322 \\__attribute__((constructor)) void init2() { printf("4"); }
1323 , &.{});
1324 d_o.linkLibC();
1325
1326 const e_o = addObject(b, "e", opts);
1327 addCSourceBytes(e_o,
1328 \\#include <stdio.h>
1329 \\__attribute__((destructor(10000))) void fini4() { printf("5"); }
1330 , &.{});
1331 e_o.linkLibC();
1332
1333 const f_o = addObject(b, "f", opts);
1334 addCSourceBytes(f_o,
1335 \\#include <stdio.h>
1336 \\__attribute__((destructor(1000))) void fini3() { printf("6"); }
1337 , &.{});
1338 f_o.linkLibC();
1339
1340 const g_o = addObject(b, "g", opts);
1341 addCSourceBytes(g_o,
1342 \\#include <stdio.h>
1343 \\__attribute__((destructor)) void fini1() { printf("7"); }
1344 , &.{});
1345 g_o.linkLibC();
1346
1347 const h_o = addObject(b, "h", opts);
1348 addCSourceBytes(h_o,
1349 \\#include <stdio.h>
1350 \\__attribute__((destructor)) void fini2() { printf("8"); }
1351 , &.{});
1352 h_o.linkLibC();
1353
1354 const exe = addExecutable(b, "main", opts);
1355 addCSourceBytes(exe, "int main() { return 0; }", &.{});
1356 exe.addObject(a_o);
1357 exe.addObject(b_o);
1358 exe.addObject(c_o);
1359 exe.addObject(d_o);
1360 exe.addObject(e_o);
1361 exe.addObject(f_o);
1362 exe.addObject(g_o);
1363 exe.addObject(h_o);
1364
1365 if (opts.target.isGnuLibC()) {
1366 // TODO I think we need to clarify our use of `-fPIC -fPIE` flags for different targets
1367 exe.pie = true;
1368 }
1369
1370 const run = addRunArtifact(exe);
1371 run.expectStdOutEqual("21348756");
1372 test_step.dependOn(&run.step);
1373
1374 return test_step;
1375}
1376
1377fn testLargeAlignmentDso(b: *Build, opts: Options) *Step {
1378 const test_step = addTestStep(b, "large-alignment-dso", opts);
1379
1380 const dso = addSharedLibrary(b, "dso", opts);
1381 addCSourceBytes(dso,
1382 \\#include <stdio.h>
1383 \\#include <stdint.h>
1384 \\void hello() __attribute__((aligned(32768), section(".hello")));
1385 \\void world() __attribute__((aligned(32768), section(".world")));
1386 \\void hello() {
1387 \\ printf("Hello");
1388 \\}
1389 \\void world() {
1390 \\ printf(" world");
1391 \\}
1392 \\void greet() {
1393 \\ hello();
1394 \\ world();
1395 \\}
1396 , &.{});
1397 dso.link_function_sections = true;
1398 dso.linkLibC();
1399
1400 const check = dso.checkObject();
1401 check.checkInSymtab();
1402 check.checkExtract("{addr1} {size1} {shndx1} FUNC GLOBAL DEFAULT hello");
1403 check.checkInSymtab();
1404 check.checkExtract("{addr2} {size2} {shndx2} FUNC GLOBAL DEFAULT world");
1405 check.checkComputeCompare("addr1 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
1406 check.checkComputeCompare("addr2 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
1407 test_step.dependOn(&check.step);
1408
1409 const exe = addExecutable(b, "test", opts);
1410 addCSourceBytes(exe,
1411 \\void greet();
1412 \\int main() { greet(); }
1413 , &.{});
1414 exe.linkLibrary(dso);
1415 exe.linkLibC();
1416
1417 const run = addRunArtifact(exe);
1418 run.expectStdOutEqual("Hello world");
1419 test_step.dependOn(&run.step);
1420
1421 return test_step;
1422}
1423
1424fn testLargeAlignmentExe(b: *Build, opts: Options) *Step {
1425 const test_step = addTestStep(b, "large-alignment-exe", opts);
1426
1427 const exe = addExecutable(b, "test", opts);
1428 addCSourceBytes(exe,
1429 \\#include <stdio.h>
1430 \\#include <stdint.h>
1431 \\
1432 \\void hello() __attribute__((aligned(32768), section(".hello")));
1433 \\void world() __attribute__((aligned(32768), section(".world")));
1434 \\
1435 \\void hello() {
1436 \\ printf("Hello");
1437 \\}
1438 \\
1439 \\void world() {
1440 \\ printf(" world");
1441 \\}
1442 \\
1443 \\int main() {
1444 \\ hello();
1445 \\ world();
1446 \\}
1447 , &.{});
1448 exe.link_function_sections = true;
1449 exe.linkLibC();
1450
1451 const check = exe.checkObject();
1452 check.checkInSymtab();
1453 check.checkExtract("{addr1} {size1} {shndx1} FUNC LOCAL DEFAULT hello");
1454 check.checkInSymtab();
1455 check.checkExtract("{addr2} {size2} {shndx2} FUNC LOCAL DEFAULT world");
1456 check.checkComputeCompare("addr1 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
1457 check.checkComputeCompare("addr2 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
1458 test_step.dependOn(&check.step);
1459
1460 const run = addRunArtifact(exe);
1461 run.expectStdOutEqual("Hello world");
1462 test_step.dependOn(&run.step);
1463
1464 return test_step;
1465}
1466
1467fn testLargeBss(b: *Build, opts: Options) *Step {
1468 const test_step = addTestStep(b, "large-bss", opts);
1469
1470 const exe = addExecutable(b, "main", opts);
1471 addCSourceBytes(exe,
1472 \\char arr[0x100000000];
1473 \\int main() {
1474 \\ return arr[2000];
1475 \\}
1476 , &.{});
1477 exe.linkLibC();
1478
1479 const run = addRunArtifact(exe);
1480 run.expectExitCode(0);
1481 test_step.dependOn(&run.step);
1482
1483 return test_step;
1484}
1485
1486fn testLinkOrder(b: *Build, opts: Options) *Step {
1487 const test_step = addTestStep(b, "link-order", opts);
1488
1489 const obj = addObject(b, "obj", opts);
1490 addCSourceBytes(obj, "void foo() {}", &.{});
1491 obj.force_pic = true;
1492
1493 const dso = addSharedLibrary(b, "a", opts);
1494 dso.addObject(obj);
1495
1496 const lib = addStaticLibrary(b, "b", opts);
1497 lib.addObject(obj);
1498
1499 const main_o = addObject(b, "main", opts);
1500 addCSourceBytes(main_o,
1501 \\void foo();
1502 \\int main() {
1503 \\ foo();
1504 \\}
1505 , &.{});
1506
1507 // https://github.com/ziglang/zig/issues/17450
1508 // {
1509 // const exe = addExecutable(b, "main1", opts);
1510 // exe.addObject(main_o);
1511 // exe.linkSystemLibrary2("a", .{});
1512 // exe.addLibraryPath(dso.getEmittedBinDirectory());
1513 // exe.addRPath(dso.getEmittedBinDirectory());
1514 // exe.linkSystemLibrary2("b", .{});
1515 // exe.addLibraryPath(lib.getEmittedBinDirectory());
1516 // exe.addRPath(lib.getEmittedBinDirectory());
1517 // exe.linkLibC();
1518
1519 // const check = exe.checkObject();
1520 // check.checkInDynamicSection();
1521 // check.checkContains("libb.so");
1522 // test_step.dependOn(&check.step);
1523 // }
1524
1525 {
1526 const exe = addExecutable(b, "main2", opts);
1527 exe.addObject(main_o);
1528 exe.linkSystemLibrary2("b", .{});
1529 exe.addLibraryPath(lib.getEmittedBinDirectory());
1530 exe.addRPath(lib.getEmittedBinDirectory());
1531 exe.linkSystemLibrary2("a", .{});
1532 exe.addLibraryPath(dso.getEmittedBinDirectory());
1533 exe.addRPath(dso.getEmittedBinDirectory());
1534 exe.linkLibC();
1535
1536 const check = exe.checkObject();
1537 check.checkInDynamicSection();
1538 check.checkNotPresent("libb.so");
1539 test_step.dependOn(&check.step);
1540 }
1541
1542 return test_step;
1543}
1544
1545fn testLinkingC(b: *Build, opts: Options) *Step {
1546 const test_step = addTestStep(b, "linking-c", opts);
1547
1548 const exe = addExecutable(b, "test", opts);
1549 addCSourceBytes(exe,
1550 \\#include <stdio.h>
1551 \\int main() {
1552 \\ printf("Hello World!\n");
1553 \\ return 0;
1554 \\}
1555 , &.{});
1556 exe.linkLibC();
1557
1558 const run = addRunArtifact(exe);
1559 run.expectStdOutEqual("Hello World!\n");
1560 test_step.dependOn(&run.step);
1561
1562 const check = exe.checkObject();
1563 check.checkStart();
1564 check.checkExact("header");
1565 check.checkExact("type EXEC");
1566 check.checkStart();
1567 check.checkExact("section headers");
1568 check.checkNotPresent("name .dynamic");
1569 test_step.dependOn(&check.step);
1570
1571 return test_step;
1572}
1573
1574fn testLinkingCpp(b: *Build, opts: Options) *Step {
1575 const test_step = addTestStep(b, "linking-cpp", opts);
1576
1577 const exe = addExecutable(b, "test", opts);
1578 addCppSourceBytes(exe,
1579 \\#include <iostream>
1580 \\int main() {
1581 \\ std::cout << "Hello World!" << std::endl;
1582 \\ return 0;
1583 \\}
1584 , &.{});
1585 exe.linkLibC();
1586 exe.linkLibCpp();
1587
1588 const run = addRunArtifact(exe);
1589 run.expectStdOutEqual("Hello World!\n");
1590 test_step.dependOn(&run.step);
1591
1592 const check = exe.checkObject();
1593 check.checkStart();
1594 check.checkExact("header");
1595 check.checkExact("type EXEC");
1596 check.checkStart();
1597 check.checkExact("section headers");
1598 check.checkNotPresent("name .dynamic");
1599 test_step.dependOn(&check.step);
1600
1601 return test_step;
1602}
1603
1604fn testLinkingZig(b: *Build, opts: Options) *Step {
1605 const test_step = addTestStep(b, "linking-zig-static", opts);
1606
1607 const exe = addExecutable(b, "test", opts);
1608 addZigSourceBytes(exe,
1609 \\pub fn main() void {
1610 \\ @import("std").debug.print("Hello World!\n", .{});
1611 \\}
1612 );
1613
1614 const run = addRunArtifact(exe);
1615 run.expectStdErrEqual("Hello World!\n");
1616 test_step.dependOn(&run.step);
1617
1618 const check = exe.checkObject();
1619 check.checkStart();
1620 check.checkExact("header");
1621 check.checkExact("type EXEC");
1622 check.checkStart();
1623 check.checkExact("section headers");
1624 check.checkNotPresent("name .dynamic");
1625 test_step.dependOn(&check.step);
1626
1627 return test_step;
1628}
1629
1630fn testNoEhFrameHdr(b: *Build, opts: Options) *Step {
1631 const test_step = addTestStep(b, "no-eh-frame-hdr", opts);
1632
1633 const exe = addExecutable(b, "main", opts);
1634 addCSourceBytes(exe, "int main() { return 0; }", &.{});
1635 exe.link_eh_frame_hdr = false;
1636 exe.linkLibC();
1637
1638 const check = exe.checkObject();
1639 check.checkStart();
1640 check.checkExact("section headers");
1641 check.checkNotPresent("name .eh_frame_hdr");
1642 test_step.dependOn(&check.step);
1643
1644 return test_step;
1645}
1646
1647fn testPie(b: *Build, opts: Options) *Step {
1648 const test_step = addTestStep(b, "hello-pie", opts);
1649
1650 const exe = addExecutable(b, "main", opts);
1651 addCSourceBytes(exe,
1652 \\#include <stdio.h>
1653 \\int main() {
1654 \\ printf("Hello!\n");
1655 \\ return 0;
1656 \\}
1657 , &.{});
1658 exe.linkLibC();
1659 exe.force_pic = true;
1660 exe.pie = true;
1661
1662 const run = addRunArtifact(exe);
1663 run.expectStdOutEqual("Hello!\n");
1664 test_step.dependOn(&run.step);
1665
1666 const check = exe.checkObject();
1667 check.checkStart();
1668 check.checkExact("header");
1669 check.checkExact("type DYN");
1670 check.checkStart();
1671 check.checkExact("section headers");
1672 check.checkExact("name .dynamic");
1673 test_step.dependOn(&check.step);
1674
1675 return test_step;
1676}
1677
1678fn testPltGot(b: *Build, opts: Options) *Step {
1679 const test_step = addTestStep(b, "plt-got", opts);
1680
1681 const dso = addSharedLibrary(b, "a", opts);
1682 addCSourceBytes(dso,
1683 \\#include <stdio.h>
1684 \\void ignore(void *foo) {}
1685 \\void hello() {
1686 \\ printf("Hello world\n");
1687 \\}
1688 , &.{});
1689 dso.linkLibC();
1690
1691 const exe = addExecutable(b, "main", opts);
1692 addCSourceBytes(exe,
1693 \\void ignore(void *);
1694 \\int hello();
1695 \\void foo() { ignore(hello); }
1696 \\int main() { hello(); }
1697 , &.{});
1698 exe.linkLibrary(dso);
1699 exe.force_pic = true;
1700 exe.linkLibC();
1701
1702 const run = addRunArtifact(exe);
1703 run.expectStdOutEqual("Hello world\n");
1704 test_step.dependOn(&run.step);
1705
1706 return test_step;
1707}
1708
1709fn testPreinitArray(b: *Build, opts: Options) *Step {
1710 const test_step = addTestStep(b, "preinit-array", opts);
1711
1712 {
1713 const obj = addObject(b, "obj", opts);
1714 addCSourceBytes(obj, "void _start() {}", &.{});
1715
1716 const exe = addExecutable(b, "main1", opts);
1717 exe.addObject(obj);
1718
1719 const check = exe.checkObject();
1720 check.checkInDynamicSection();
1721 check.checkNotPresent("PREINIT_ARRAY");
1722 }
1723
1724 {
1725 const exe = addExecutable(b, "main2", opts);
1726 addCSourceBytes(exe,
1727 \\void preinit_fn() {}
1728 \\int main() {}
1729 \\__attribute__((section(".preinit_array")))
1730 \\void *preinit[] = { preinit_fn };
1731 , &.{});
1732 exe.linkLibC();
1733
1734 const check = exe.checkObject();
1735 check.checkInDynamicSection();
1736 check.checkContains("PREINIT_ARRAY");
1737 }
1738
1739 return test_step;
1740}
1741
1742fn testSharedAbsSymbol(b: *Build, opts: Options) *Step {
1743 const test_step = addTestStep(b, "shared-abs-symbol", opts);
1744
1745 const dso = addSharedLibrary(b, "a", opts);
1746 addAsmSourceBytes(dso,
1747 \\.globl foo
1748 \\foo = 3;
1749 );
1750
1751 const obj = addObject(b, "obj", opts);
1752 addCSourceBytes(obj,
1753 \\#include <stdio.h>
1754 \\extern char foo;
1755 \\int main() { printf("foo=%p\n", &foo); }
1756 , &.{});
1757 obj.force_pic = true;
1758 obj.linkLibC();
1759
1760 {
1761 const exe = addExecutable(b, "main1", opts);
1762 exe.addObject(obj);
1763 exe.linkLibrary(dso);
1764 exe.pie = true;
1765
1766 const run = addRunArtifact(exe);
1767 run.expectStdOutEqual("foo=0x3\n");
1768 test_step.dependOn(&run.step);
1769
1770 const check = exe.checkObject();
1771 check.checkStart();
1772 check.checkExact("header");
1773 check.checkExact("type DYN");
1774 // TODO fix/improve in CheckObject
1775 // check.checkInSymtab();
1776 // check.checkNotPresent("foo");
1777 test_step.dependOn(&check.step);
1778 }
1779
1780 // https://github.com/ziglang/zig/issues/17430
1781 // {
1782 // const exe = addExecutable(b, "main2", opts);
1783 // exe.addObject(obj);
1784 // exe.linkLibrary(dso);
1785 // exe.pie = false;
1786
1787 // const run = addRunArtifact(exe);
1788 // run.expectStdOutEqual("foo=0x3\n");
1789 // test_step.dependOn(&run.step);
1790
1791 // const check = exe.checkObject();
1792 // check.checkStart();
1793 // check.checkExact("header");
1794 // check.checkExact("type EXEC");
1795 // // TODO fix/improve in CheckObject
1796 // // check.checkInSymtab();
1797 // // check.checkNotPresent("foo");
1798 // test_step.dependOn(&check.step);
1799 // }
1800
1801 return test_step;
1802}
1803
1804fn testStrip(b: *Build, opts: Options) *Step {
1805 const test_step = addTestStep(b, "strip", opts);
1806
1807 const obj = addObject(b, "obj", opts);
1808 addCSourceBytes(obj,
1809 \\#include <stdio.h>
1810 \\int main() {
1811 \\ printf("Hello!\n");
1812 \\ return 0;
1813 \\}
1814 , &.{});
1815 obj.linkLibC();
1816
1817 {
1818 const exe = addExecutable(b, "main1", opts);
1819 exe.addObject(obj);
1820 exe.strip = false;
1821 exe.linkLibC();
1822
1823 const check = exe.checkObject();
1824 check.checkStart();
1825 check.checkExact("section headers");
1826 check.checkExact("name .debug_info");
1827 test_step.dependOn(&check.step);
1828 }
1829
1830 {
1831 const exe = addExecutable(b, "main2", opts);
1832 exe.addObject(obj);
1833 exe.strip = true;
1834 exe.linkLibC();
1835
1836 const check = exe.checkObject();
1837 check.checkStart();
1838 check.checkExact("section headers");
1839 check.checkNotPresent("name .debug_info");
1840 test_step.dependOn(&check.step);
1841 }
1842
1843 return test_step;
1844}
1845
1846fn testTlsDfStaticTls(b: *Build, opts: Options) *Step {
1847 const test_step = addTestStep(b, "tls-df-static-tls", opts);
1848
1849 const obj = addObject(b, "obj", opts);
1850 addCSourceBytes(obj,
1851 \\static _Thread_local int foo = 5;
1852 \\void mutate() { ++foo; }
1853 \\int bar() { return foo; }
1854 , &.{"-ftls-model=initial-exec"});
1855 obj.force_pic = true;
1856
1857 {
1858 const dso = addSharedLibrary(b, "a", opts);
1859 dso.addObject(obj);
1860 // dso.link_relax = true;
1861
1862 const check = dso.checkObject();
1863 check.checkInDynamicSection();
1864 check.checkContains("STATIC_TLS");
1865 test_step.dependOn(&check.step);
1866 }
1867
1868 // TODO add -Wl,--no-relax
1869 // {
1870 // const dso = addSharedLibrary(b, "a", opts);
1871 // dso.addObject(obj);
1872 // dso.link_relax = false;
1873
1874 // const check = dso.checkObject();
1875 // check.checkInDynamicSection();
1876 // check.checkContains("STATIC_TLS");
1877 // test_step.dependOn(&check.step);
1878 // }
1879
1880 return test_step;
1881}
1882
1883fn testTlsDso(b: *Build, opts: Options) *Step {
1884 const test_step = addTestStep(b, "tls-dso", opts);
1885
1886 const dso = addSharedLibrary(b, "a", opts);
1887 addCSourceBytes(dso,
1888 \\extern _Thread_local int foo;
1889 \\_Thread_local int bar;
1890 \\int get_foo1() { return foo; }
1891 \\int get_bar1() { return bar; }
1892 , &.{});
1893
1894 const exe = addExecutable(b, "main", opts);
1895 addCSourceBytes(exe,
1896 \\#include <stdio.h>
1897 \\_Thread_local int foo;
1898 \\extern _Thread_local int bar;
1899 \\int get_foo1();
1900 \\int get_bar1();
1901 \\int get_foo2() { return foo; }
1902 \\int get_bar2() { return bar; }
1903 \\int main() {
1904 \\ foo = 5;
1905 \\ bar = 3;
1906 \\ printf("%d %d %d %d %d %d\n",
1907 \\ foo, bar,
1908 \\ get_foo1(), get_bar1(),
1909 \\ get_foo2(), get_bar2());
1910 \\ return 0;
1911 \\}
1912 , &.{});
1913 exe.linkLibrary(dso);
1914 exe.linkLibC();
1915
1916 const run = addRunArtifact(exe);
1917 run.expectStdOutEqual("5 3 5 3 5 3\n");
1918 test_step.dependOn(&run.step);
1919
1920 return test_step;
1921}
1922
1923fn testTlsGd(b: *Build, opts: Options) *Step {
1924 const test_step = addTestStep(b, "tls-gd", opts);
1925
1926 const main_o = addObject(b, "main", opts);
1927 addCSourceBytes(main_o,
1928 \\#include <stdio.h>
1929 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
1930 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x2;
1931 \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x3;
1932 \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x4;
1933 \\int get_x5();
1934 \\int get_x6();
1935 \\int main() {
1936 \\ x2 = 2;
1937 \\ printf("%d %d %d %d %d %d\n", x1, x2, x3, x4, get_x5(), get_x6());
1938 \\ return 0;
1939 \\}
1940 , &.{});
1941 main_o.linkLibC();
1942 main_o.force_pic = true;
1943
1944 const a_o = addObject(b, "a", opts);
1945 addCSourceBytes(a_o,
1946 \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3 = 3;
1947 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x5 = 5;
1948 \\int get_x5() { return x5; }
1949 , &.{});
1950 a_o.force_pic = true;
1951
1952 const b_o = addObject(b, "b", opts);
1953 addCSourceBytes(b_o,
1954 \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x4 = 4;
1955 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x6 = 6;
1956 \\int get_x6() { return x6; }
1957 , &.{});
1958 b_o.force_pic = true;
1959
1960 const exp_stdout = "1 2 3 4 5 6\n";
1961
1962 const dso1 = addSharedLibrary(b, "a", opts);
1963 dso1.addObject(a_o);
1964
1965 const dso2 = addSharedLibrary(b, "b", opts);
1966 dso2.addObject(b_o);
1967 // dso2.link_relax = false; // TODO
1968
1969 {
1970 const exe = addExecutable(b, "main1", opts);
1971 exe.addObject(main_o);
1972 exe.linkLibrary(dso1);
1973 exe.linkLibrary(dso2);
1974
1975 const run = addRunArtifact(exe);
1976 run.expectStdOutEqual(exp_stdout);
1977 test_step.dependOn(&run.step);
1978 }
1979
1980 {
1981 const exe = addExecutable(b, "main2", opts);
1982 exe.addObject(main_o);
1983 // exe.link_relax = false; // TODO
1984 exe.linkLibrary(dso1);
1985 exe.linkLibrary(dso2);
1986
1987 const run = addRunArtifact(exe);
1988 run.expectStdOutEqual(exp_stdout);
1989 test_step.dependOn(&run.step);
1990 }
1991
1992 // https://github.com/ziglang/zig/issues/17430 ??
1993 // {
1994 // const exe = addExecutable(b, "main3", opts);
1995 // exe.addObject(main_o);
1996 // exe.linkLibrary(dso1);
1997 // exe.linkLibrary(dso2);
1998 // exe.linkage = .static;
1999
2000 // const run = addRunArtifact(exe);
2001 // run.expectStdOutEqual(exp_stdout);
2002 // test_step.dependOn(&run.step);
2003 // }
2004
2005 // {
2006 // const exe = addExecutable(b, "main4", opts);
2007 // exe.addObject(main_o);
2008 // // exe.link_relax = false; // TODO
2009 // exe.linkLibrary(dso1);
2010 // exe.linkLibrary(dso2);
2011 // exe.linkage = .static;
2012
2013 // const run = addRunArtifact(exe);
2014 // run.expectStdOutEqual(exp_stdout);
2015 // test_step.dependOn(&run.step);
2016 // }
2017
2018 return test_step;
2019}
2020
2021fn testTlsGdNoPlt(b: *Build, opts: Options) *Step {
2022 const test_step = addTestStep(b, "tls-gd-no-plt", opts);
2023
2024 const obj = addObject(b, "obj", opts);
2025 addCSourceBytes(obj,
2026 \\#include <stdio.h>
2027 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
2028 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x2;
2029 \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x3;
2030 \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x4;
2031 \\int get_x5();
2032 \\int get_x6();
2033 \\int main() {
2034 \\ x2 = 2;
2035 \\
2036 \\ printf("%d %d %d %d %d %d\n", x1, x2, x3, x4, get_x5(), get_x6());
2037 \\ return 0;
2038 \\}
2039 , &.{"-fno-plt"});
2040 obj.force_pic = true;
2041 obj.linkLibC();
2042
2043 const a_so = addSharedLibrary(b, "a", opts);
2044 addCSourceBytes(a_so,
2045 \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3 = 3;
2046 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x5 = 5;
2047 \\int get_x5() { return x5; }
2048 , &.{"-fno-plt"});
2049
2050 const b_so = addSharedLibrary(b, "b", opts);
2051 addCSourceBytes(b_so,
2052 \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x4 = 4;
2053 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x6 = 6;
2054 \\int get_x6() { return x6; }
2055 , &.{"-fno-plt"});
2056 // b_so.link_relax = false; // TODO
2057
2058 {
2059 const exe = addExecutable(b, "main1", opts);
2060 exe.addObject(obj);
2061 exe.linkLibrary(a_so);
2062 exe.linkLibrary(b_so);
2063 exe.linkLibC();
2064
2065 const run = addRunArtifact(exe);
2066 run.expectStdOutEqual("1 2 3 4 5 6\n");
2067 test_step.dependOn(&run.step);
2068 }
2069
2070 {
2071 const exe = addExecutable(b, "main2", opts);
2072 exe.addObject(obj);
2073 exe.linkLibrary(a_so);
2074 exe.linkLibrary(b_so);
2075 exe.linkLibC();
2076 // exe.link_relax = false; // TODO
2077
2078 const run = addRunArtifact(exe);
2079 run.expectStdOutEqual("1 2 3 4 5 6\n");
2080 test_step.dependOn(&run.step);
2081 }
2082
2083 return test_step;
2084}
2085
2086fn testTlsGdToIe(b: *Build, opts: Options) *Step {
2087 const test_step = addTestStep(b, "tls-gd-to-ie", opts);
2088
2089 const a_o = addObject(b, "a", opts);
2090 addCSourceBytes(a_o,
2091 \\#include <stdio.h>
2092 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
2093 \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x2 = 2;
2094 \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3;
2095 \\int foo() {
2096 \\ x3 = 3;
2097 \\
2098 \\ printf("%d %d %d\n", x1, x2, x3);
2099 \\ return 0;
2100 \\}
2101 , &.{});
2102 a_o.linkLibC();
2103 a_o.force_pic = true;
2104
2105 const b_o = addObject(b, "b", opts);
2106 addCSourceBytes(b_o,
2107 \\int foo();
2108 \\int main() { foo(); }
2109 , &.{});
2110 b_o.force_pic = true;
2111
2112 {
2113 const dso = addSharedLibrary(b, "a", opts);
2114 dso.addObject(a_o);
2115
2116 const exe = addExecutable(b, "main", opts);
2117 exe.addObject(b_o);
2118 exe.linkLibrary(dso);
2119 exe.linkLibC();
2120
2121 const run = addRunArtifact(exe);
2122 run.expectStdOutEqual("1 2 3\n");
2123 test_step.dependOn(&run.step);
2124 }
2125
2126 {
2127 const dso = addSharedLibrary(b, "a", opts);
2128 dso.addObject(a_o);
2129 // dso.link_relax = false; // TODO
2130
2131 const exe = addExecutable(b, "main", opts);
2132 exe.addObject(b_o);
2133 exe.linkLibrary(dso);
2134 exe.linkLibC();
2135
2136 const run = addRunArtifact(exe);
2137 run.expectStdOutEqual("1 2 3\n");
2138 test_step.dependOn(&run.step);
2139 }
2140
2141 // {
2142 // const dso = addSharedLibrary(b, "a", opts);
2143 // dso.addObject(a_o);
2144 // dso.link_z_nodlopen = true;
2145
2146 // const exe = addExecutable(b, "main", opts);
2147 // exe.addObject(b_o);
2148 // exe.linkLibrary(dso);
2149
2150 // const run = addRunArtifact(exe);
2151 // run.expectStdOutEqual("1 2 3\n");
2152 // test_step.dependOn(&run.step);
2153 // }
2154
2155 // {
2156 // const dso = addSharedLibrary(b, "a", opts);
2157 // dso.addObject(a_o);
2158 // dso.link_relax = false;
2159 // dso.link_z_nodlopen = true;
2160
2161 // const exe = addExecutable(b, "main", opts);
2162 // exe.addObject(b_o);
2163 // exe.linkLibrary(dso);
2164
2165 // const run = addRunArtifact(exe);
2166 // run.expectStdOutEqual("1 2 3\n");
2167 // test_step.dependOn(&run.step);
2168 // }
2169
2170 return test_step;
2171}
142172
15 // Exercise linker with self-hosted backend (no LLVM)2173fn testTlsIe(b: *Build, opts: Options) *Step {
16 elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false }));2174 const test_step = addTestStep(b, "tls-ie", opts);
172175
18 // Exercise linker with LLVM backend2176 const dso = addSharedLibrary(b, "a", opts);
19 elf_step.dependOn(testEmptyObject(b, .{ .target = musl_target }));2177 addCSourceBytes(dso,
20 elf_step.dependOn(testLinkingC(b, .{ .target = musl_target }));2178 \\#include <stdio.h>
21 elf_step.dependOn(testLinkingZig(b, .{ .target = musl_target }));2179 \\__attribute__((tls_model("initial-exec"))) static _Thread_local int foo;
22 elf_step.dependOn(testTlsStatic(b, .{ .target = musl_target }));2180 \\__attribute__((tls_model("initial-exec"))) static _Thread_local int bar;
2181 \\void set() {
2182 \\ foo = 3;
2183 \\ bar = 5;
2184 \\}
2185 \\void print() {
2186 \\ printf("%d %d ", foo, bar);
2187 \\}
2188 , &.{});
2189 dso.linkLibC();
2190
2191 const main_o = addObject(b, "main", opts);
2192 addCSourceBytes(main_o,
2193 \\#include <stdio.h>
2194 \\_Thread_local int baz;
2195 \\void set();
2196 \\void print();
2197 \\int main() {
2198 \\ baz = 7;
2199 \\ print();
2200 \\ set();
2201 \\ print();
2202 \\ printf("%d\n", baz);
2203 \\}
2204 , &.{});
2205 main_o.linkLibC();
2206
2207 const exp_stdout = "0 0 3 5 7\n";
2208
2209 {
2210 const exe = addExecutable(b, "main", opts);
2211 exe.addObject(main_o);
2212 exe.linkLibrary(dso);
2213 exe.linkLibC();
2214
2215 const run = addRunArtifact(exe);
2216 run.expectStdOutEqual(exp_stdout);
2217 test_step.dependOn(&run.step);
2218 }
2219
2220 {
2221 const exe = addExecutable(b, "main", opts);
2222 exe.addObject(main_o);
2223 exe.linkLibrary(dso);
2224 exe.linkLibC();
2225 // exe.link_relax = false; // TODO
2226
2227 const run = addRunArtifact(exe);
2228 run.expectStdOutEqual(exp_stdout);
2229 test_step.dependOn(&run.step);
2230 }
2231
2232 return test_step;
23}2233}
242234
25fn testEmptyObject(b: *Build, opts: Options) *Step {2235fn testTlsLargeAlignment(b: *Build, opts: Options) *Step {
26 const test_step = addTestStep(b, "empty-object", opts);2236 const test_step = addTestStep(b, "tls-large-alignment", opts);
2237
2238 const a_o = addObject(b, "a", opts);
2239 addCSourceBytes(a_o,
2240 \\__attribute__((section(".tdata1")))
2241 \\_Thread_local int x = 42;
2242 , &.{"-std=c11"});
2243 a_o.force_pic = true;
2244
2245 const b_o = addObject(b, "b", opts);
2246 addCSourceBytes(b_o,
2247 \\__attribute__((section(".tdata2")))
2248 \\_Alignas(256) _Thread_local int y[] = { 1, 2, 3 };
2249 , &.{"-std=c11"});
2250 b_o.force_pic = true;
2251
2252 const c_o = addObject(b, "c", opts);
2253 addCSourceBytes(c_o,
2254 \\#include <stdio.h>
2255 \\extern _Thread_local int x;
2256 \\extern _Thread_local int y[];
2257 \\int main() {
2258 \\ printf("%d %d %d %d\n", x, y[0], y[1], y[2]);
2259 \\}
2260 , &.{});
2261 c_o.force_pic = true;
2262 c_o.linkLibC();
2263
2264 {
2265 const dso = addSharedLibrary(b, "a", opts);
2266 dso.addObject(a_o);
2267 dso.addObject(b_o);
272268
28 const exe = addExecutable(b, opts);2269 const exe = addExecutable(b, "main", opts);
29 addCSourceBytes(exe, "int main() { return 0; }");2270 exe.addObject(c_o);
30 addCSourceBytes(exe, "");2271 exe.linkLibrary(dso);
31 exe.is_linking_libc = true;2272 exe.linkLibC();
2273
2274 const run = addRunArtifact(exe);
2275 run.expectStdOutEqual("42 1 2 3\n");
2276 test_step.dependOn(&run.step);
2277 }
2278
2279 {
2280 const exe = addExecutable(b, "main", opts);
2281 exe.addObject(a_o);
2282 exe.addObject(b_o);
2283 exe.addObject(c_o);
2284 exe.linkLibC();
2285
2286 const run = addRunArtifact(exe);
2287 run.expectStdOutEqual("42 1 2 3\n");
2288 test_step.dependOn(&run.step);
2289 }
2290
2291 return test_step;
2292}
2293
2294fn testTlsLargeTbss(b: *Build, opts: Options) *Step {
2295 const test_step = addTestStep(b, "tls-large-tbss", opts);
2296
2297 const exe = addExecutable(b, "main", opts);
2298 addAsmSourceBytes(exe,
2299 \\.globl x, y
2300 \\.section .tbss,"awT",@nobits
2301 \\x:
2302 \\.zero 1024
2303 \\.section .tcommon,"awT",@nobits
2304 \\y:
2305 \\.zero 1024
2306 );
2307 addCSourceBytes(exe,
2308 \\#include <stdio.h>
2309 \\extern _Thread_local char x[1024000];
2310 \\extern _Thread_local char y[1024000];
2311 \\int main() {
2312 \\ x[0] = 3;
2313 \\ x[1023] = 5;
2314 \\ printf("%d %d %d %d %d %d\n", x[0], x[1], x[1023], y[0], y[1], y[1023]);
2315 \\}
2316 , &.{});
2317 exe.linkLibC();
322318
33 const run = addRunArtifact(exe);2319 const run = addRunArtifact(exe);
34 run.expectExitCode(0);2320 run.expectStdOutEqual("3 0 5 0 0 0\n");
35 test_step.dependOn(&run.step);2321 test_step.dependOn(&run.step);
362322
37 return test_step;2323 return test_step;
38}2324}
392325
40fn testLinkingC(b: *Build, opts: Options) *Step {2326fn testTlsLargeStaticImage(b: *Build, opts: Options) *Step {
41 const test_step = addTestStep(b, "linking-c-static", opts);2327 const test_step = addTestStep(b, "tls-large-static-image", opts);
422328
43 const exe = addExecutable(b, opts);2329 const exe = addExecutable(b, "main", opts);
2330 addCSourceBytes(exe, "_Thread_local int x[] = { 1, 2, 3, [10000] = 5 };", &.{});
44 addCSourceBytes(exe,2331 addCSourceBytes(exe,
45 \\#include <stdio.h>2332 \\#include <stdio.h>
2333 \\extern _Thread_local int x[];
46 \\int main() {2334 \\int main() {
47 \\ printf("Hello World!\n");2335 \\ printf("%d %d %d %d %d\n", x[0], x[1], x[2], x[3], x[10000]);
2336 \\}
2337 , &.{});
2338 exe.force_pic = true;
2339 exe.linkLibC();
2340
2341 const run = addRunArtifact(exe);
2342 run.expectStdOutEqual("1 2 3 0 5\n");
2343 test_step.dependOn(&run.step);
2344
2345 return test_step;
2346}
2347
2348fn testTlsLd(b: *Build, opts: Options) *Step {
2349 const test_step = addTestStep(b, "tls-ld", opts);
2350
2351 const main_o = addObject(b, "main", opts);
2352 addCSourceBytes(main_o,
2353 \\#include <stdio.h>
2354 \\extern _Thread_local int foo;
2355 \\static _Thread_local int bar;
2356 \\int *get_foo_addr() { return &foo; }
2357 \\int *get_bar_addr() { return &bar; }
2358 \\int main() {
2359 \\ bar = 5;
2360 \\ printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
48 \\ return 0;2361 \\ return 0;
49 \\}2362 \\}
50 );2363 , &.{"-ftls-model=local-dynamic"});
51 exe.is_linking_libc = true;2364 main_o.force_pic = true;
2365 main_o.linkLibC();
2366
2367 const a_o = addObject(b, "a", opts);
2368 addCSourceBytes(a_o, "_Thread_local int foo = 3;", &.{"-ftls-model=local-dynamic"});
2369 a_o.force_pic = true;
2370
2371 const exp_stdout = "3 5 3 5\n";
2372
2373 {
2374 const exe = addExecutable(b, "main", opts);
2375 exe.addObject(main_o);
2376 exe.addObject(a_o);
2377 exe.linkLibC();
2378
2379 const run = addRunArtifact(exe);
2380 run.expectStdOutEqual(exp_stdout);
2381 test_step.dependOn(&run.step);
2382 }
2383
2384 {
2385 const exe = addExecutable(b, "main", opts);
2386 exe.addObject(main_o);
2387 exe.addObject(a_o);
2388 exe.linkLibC();
2389 // exe.link_relax = false; // TODO
2390
2391 const run = addRunArtifact(exe);
2392 run.expectStdOutEqual(exp_stdout);
2393 test_step.dependOn(&run.step);
2394 }
2395
2396 return test_step;
2397}
2398
2399fn testTlsLdDso(b: *Build, opts: Options) *Step {
2400 const test_step = addTestStep(b, "tls-ld-dso", opts);
2401
2402 const dso = addSharedLibrary(b, "a", opts);
2403 addCSourceBytes(dso,
2404 \\static _Thread_local int def, def1;
2405 \\int f0() { return ++def; }
2406 \\int f1() { return ++def1 + def; }
2407 , &.{"-ftls-model=local-dynamic"});
2408
2409 const exe = addExecutable(b, "main", opts);
2410 addCSourceBytes(exe,
2411 \\#include <stdio.h>
2412 \\extern int f0();
2413 \\extern int f1();
2414 \\int main() {
2415 \\ int x = f0();
2416 \\ int y = f1();
2417 \\ printf("%d %d\n", x, y);
2418 \\ return 0;
2419 \\}
2420 , &.{});
2421 exe.linkLibrary(dso);
2422 exe.linkLibC();
522423
53 const run = addRunArtifact(exe);2424 const run = addRunArtifact(exe);
54 run.expectStdOutEqual("Hello World!\n");2425 run.expectStdOutEqual("1 2\n");
55 test_step.dependOn(&run.step);2426 test_step.dependOn(&run.step);
562427
57 const check = exe.checkObject();2428 return test_step;
58 check.checkStart();2429}
59 check.checkExact("header");2430
60 check.checkExact("type EXEC");2431fn testTlsLdNoPlt(b: *Build, opts: Options) *Step {
61 check.checkStart();2432 const test_step = addTestStep(b, "tls-ld-no-plt", opts);
62 check.checkExact("section headers");2433
63 check.checkNotPresent("name .dynamic");2434 const a_o = addObject(b, "a", opts);
64 test_step.dependOn(&check.step);2435 addCSourceBytes(a_o,
2436 \\#include <stdio.h>
2437 \\extern _Thread_local int foo;
2438 \\static _Thread_local int bar;
2439 \\int *get_foo_addr() { return &foo; }
2440 \\int *get_bar_addr() { return &bar; }
2441 \\int main() {
2442 \\ bar = 5;
2443 \\
2444 \\ printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
2445 \\ return 0;
2446 \\}
2447 , &.{ "-ftls-model=local-dynamic", "-fno-plt" });
2448 a_o.linkLibC();
2449 a_o.force_pic = true;
2450
2451 const b_o = addObject(b, "b", opts);
2452 addCSourceBytes(b_o, "_Thread_local int foo = 3;", &.{ "-ftls-model=local-dynamic", "-fno-plt" });
2453 b_o.force_pic = true;
2454
2455 {
2456 const exe = addExecutable(b, "main", opts);
2457 exe.addObject(a_o);
2458 exe.addObject(b_o);
2459 exe.linkLibC();
2460
2461 const run = addRunArtifact(exe);
2462 run.expectStdOutEqual("3 5 3 5\n");
2463 test_step.dependOn(&run.step);
2464 }
2465
2466 {
2467 const exe = addExecutable(b, "main", opts);
2468 exe.addObject(a_o);
2469 exe.addObject(b_o);
2470 exe.linkLibC();
2471 // exe.link_relax = false; // TODO
2472
2473 const run = addRunArtifact(exe);
2474 run.expectStdOutEqual("3 5 3 5\n");
2475 test_step.dependOn(&run.step);
2476 }
652477
66 return test_step;2478 return test_step;
67}2479}
682480
69fn testLinkingZig(b: *Build, opts: Options) *Step {2481fn testTlsNoPic(b: *Build, opts: Options) *Step {
70 const test_step = addTestStep(b, "linking-zig-static", opts);2482 const test_step = addTestStep(b, "tls-no-pic", opts);
712483
72 const exe = addExecutable(b, opts);2484 const exe = addExecutable(b, "main", opts);
73 addZigSourceBytes(exe,2485 addCSourceBytes(exe,
74 \\pub fn main() void {2486 \\#include <stdio.h>
75 \\ @import("std").debug.print("Hello World!\n", .{});2487 \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo;
2488 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int bar;
2489 \\int *get_foo_addr() { return &foo; }
2490 \\int *get_bar_addr() { return &bar; }
2491 \\int main() {
2492 \\ foo = 3;
2493 \\ bar = 5;
2494 \\
2495 \\ printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
2496 \\ return 0;
76 \\}2497 \\}
77 );2498 , .{});
2499 addCSourceBytes(exe,
2500 \\__attribute__((tls_model("global-dynamic"))) _Thread_local int foo;
2501 , &.{});
2502 exe.force_pic = false;
2503 exe.linkLibC();
782504
79 const run = addRunArtifact(exe);2505 const run = addRunArtifact(exe);
80 run.expectStdErrEqual("Hello World!\n");2506 run.expectStdOutEqual("3 5 3 5\n");
81 test_step.dependOn(&run.step);2507 test_step.dependOn(&run.step);
822508
83 const check = exe.checkObject();2509 return test_step;
84 check.checkStart();2510}
85 check.checkExact("header");2511
86 check.checkExact("type EXEC");2512fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step {
87 check.checkStart();2513 const test_step = addTestStep(b, "tls-offset-alignment", opts);
88 check.checkExact("section headers");2514
89 check.checkNotPresent("name .dynamic");2515 const dso = addSharedLibrary(b, "a", opts);
90 test_step.dependOn(&check.step);2516 addCSourceBytes(dso,
2517 \\#include <assert.h>
2518 \\#include <stdlib.h>
2519 \\
2520 \\// .tdata
2521 \\_Thread_local int x = 42;
2522 \\// .tbss
2523 \\__attribute__ ((aligned(64)))
2524 \\_Thread_local int y = 0;
2525 \\
2526 \\void *verify(void *unused) {
2527 \\ assert((unsigned long)(&y) % 64 == 0);
2528 \\ return NULL;
2529 \\}
2530 , &.{});
2531 dso.linkLibC();
2532
2533 const exe = addExecutable(b, "main", opts);
2534 addCSourceBytes(exe,
2535 \\#include <pthread.h>
2536 \\#include <dlfcn.h>
2537 \\#include <assert.h>
2538 \\void *(*verify)(void *);
2539 \\
2540 \\int main() {
2541 \\ void *handle = dlopen("liba.so", RTLD_NOW);
2542 \\ assert(handle);
2543 \\ *(void**)(&verify) = dlsym(handle, "verify");
2544 \\ assert(verify);
2545 \\
2546 \\ pthread_t thread;
2547 \\
2548 \\ verify(NULL);
2549 \\
2550 \\ pthread_create(&thread, NULL, verify, NULL);
2551 \\ pthread_join(thread, NULL);
2552 \\}
2553 , &.{});
2554 exe.addRPath(dso.getEmittedBinDirectory());
2555 exe.linkLibC();
2556 exe.force_pic = true;
2557
2558 const run = addRunArtifact(exe);
2559 run.expectExitCode(0);
2560 test_step.dependOn(&run.step);
2561
2562 return test_step;
2563}
2564
2565fn testTlsPic(b: *Build, opts: Options) *Step {
2566 const test_step = addTestStep(b, "tls-pic", opts);
2567
2568 const obj = addObject(b, "obj", opts);
2569 addCSourceBytes(obj,
2570 \\#include <stdio.h>
2571 \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo;
2572 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int bar;
2573 \\int *get_foo_addr() { return &foo; }
2574 \\int *get_bar_addr() { return &bar; }
2575 \\int main() {
2576 \\ bar = 5;
2577 \\
2578 \\ printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
2579 \\ return 0;
2580 \\}
2581 , &.{});
2582 obj.linkLibC();
2583 obj.force_pic = true;
2584
2585 const exe = addExecutable(b, "main", opts);
2586 addCSourceBytes(exe,
2587 \\__attribute__((tls_model("global-dynamic"))) _Thread_local int foo = 3;
2588 , &.{});
2589 exe.addObject(obj);
2590 exe.linkLibC();
2591
2592 const run = addRunArtifact(exe);
2593 run.expectStdOutEqual("3 5 3 5\n");
2594 test_step.dependOn(&run.step);
2595
2596 return test_step;
2597}
2598
2599fn testTlsSmallAlignment(b: *Build, opts: Options) *Step {
2600 const test_step = addTestStep(b, "tls-small-alignment", opts);
2601
2602 const a_o = addObject(b, "a", opts);
2603 addAsmSourceBytes(a_o,
2604 \\.text
2605 \\.byte 0
2606 );
2607 a_o.force_pic = true;
2608
2609 const b_o = addObject(b, "b", opts);
2610 addCSourceBytes(b_o, "_Thread_local char x = 42;", &.{"-std=c11"});
2611 b_o.force_pic = true;
2612
2613 const c_o = addObject(b, "c", opts);
2614 addCSourceBytes(c_o,
2615 \\#include <stdio.h>
2616 \\extern _Thread_local char x;
2617 \\int main() {
2618 \\ printf("%d\n", x);
2619 \\}
2620 , &.{});
2621 c_o.linkLibC();
2622 c_o.force_pic = true;
2623
2624 {
2625 const exe = addExecutable(b, "main", opts);
2626 exe.addObject(a_o);
2627 exe.addObject(b_o);
2628 exe.addObject(c_o);
2629 exe.linkLibC();
2630
2631 const run = addRunArtifact(exe);
2632 run.expectStdOutEqual("42\n");
2633 test_step.dependOn(&run.step);
2634 }
2635
2636 {
2637 const dso = addSharedLibrary(b, "a", opts);
2638 dso.addObject(a_o);
2639 dso.addObject(b_o);
2640
2641 const exe = addExecutable(b, "main", opts);
2642 exe.addObject(c_o);
2643 exe.linkLibrary(dso);
2644 exe.linkLibC();
2645
2646 const run = addRunArtifact(exe);
2647 run.expectStdOutEqual("42\n");
2648 test_step.dependOn(&run.step);
2649 }
912650
92 return test_step;2651 return test_step;
93}2652}
...@@ -95,7 +2654,7 @@ fn testLinkingZig(b: *Build, opts: Options) *Step {...@@ -95,7 +2654,7 @@ fn testLinkingZig(b: *Build, opts: Options) *Step {
95fn testTlsStatic(b: *Build, opts: Options) *Step {2654fn testTlsStatic(b: *Build, opts: Options) *Step {
96 const test_step = addTestStep(b, "tls-static", opts);2655 const test_step = addTestStep(b, "tls-static", opts);
972656
98 const exe = addExecutable(b, opts);2657 const exe = addExecutable(b, "test", opts);
99 addCSourceBytes(exe,2658 addCSourceBytes(exe,
100 \\#include <stdio.h>2659 \\#include <stdio.h>
101 \\_Thread_local int a = 10;2660 \\_Thread_local int a = 10;
...@@ -109,8 +2668,8 @@ fn testTlsStatic(b: *Build, opts: Options) *Step {...@@ -109,8 +2668,8 @@ fn testTlsStatic(b: *Build, opts: Options) *Step {
109 \\ printf("%d %d %c\n", a, b, c);2668 \\ printf("%d %d %c\n", a, b, c);
110 \\ return 0;2669 \\ return 0;
111 \\}2670 \\}
112 );2671 , &.{});
113 exe.is_linking_libc = true;2672 exe.linkLibC();
1142673
115 const run = addRunArtifact(exe);2674 const run = addRunArtifact(exe);
116 run.expectStdOutEqual(2675 run.expectStdOutEqual(
...@@ -123,6 +2682,205 @@ fn testTlsStatic(b: *Build, opts: Options) *Step {...@@ -123,6 +2682,205 @@ fn testTlsStatic(b: *Build, opts: Options) *Step {
123 return test_step;2682 return test_step;
124}2683}
1252684
2685fn testWeakExports(b: *Build, opts: Options) *Step {
2686 const test_step = addTestStep(b, "weak-exports", opts);
2687
2688 const obj = addObject(b, "obj", opts);
2689 addCSourceBytes(obj,
2690 \\#include <stdio.h>
2691 \\__attribute__((weak)) int foo();
2692 \\int main() {
2693 \\ printf("%d\n", foo ? foo() : 3);
2694 \\}
2695 , &.{});
2696 obj.linkLibC();
2697 obj.force_pic = true;
2698
2699 {
2700 const dso = addSharedLibrary(b, "a", opts);
2701 dso.addObject(obj);
2702 dso.linkLibC();
2703
2704 const check = dso.checkObject();
2705 check.checkInDynamicSymtab();
2706 check.checkContains("UND NOTYPE WEAK DEFAULT foo");
2707 test_step.dependOn(&check.step);
2708 }
2709
2710 {
2711 const exe = addExecutable(b, "main", opts);
2712 exe.addObject(obj);
2713 exe.linkLibC();
2714
2715 const check = exe.checkObject();
2716 check.checkInDynamicSymtab();
2717 check.checkNotPresent("UND NOTYPE WEAK DEFAULT foo");
2718 test_step.dependOn(&check.step);
2719
2720 const run = addRunArtifact(exe);
2721 run.expectStdOutEqual("3\n");
2722 test_step.dependOn(&run.step);
2723 }
2724
2725 return test_step;
2726}
2727
2728fn testWeakUndefsDso(b: *Build, opts: Options) *Step {
2729 const test_step = addTestStep(b, "weak-undef-dso", opts);
2730
2731 const dso = addSharedLibrary(b, "a", opts);
2732 addCSourceBytes(dso,
2733 \\__attribute__((weak)) int foo();
2734 \\int bar() { return foo ? foo() : -1; }
2735 , &.{});
2736
2737 {
2738 const exe = addExecutable(b, "main", opts);
2739 addCSourceBytes(exe,
2740 \\#include <stdio.h>
2741 \\int bar();
2742 \\int main() { printf("bar=%d\n", bar()); }
2743 , &.{});
2744 exe.linkLibrary(dso);
2745 exe.linkLibC();
2746
2747 const run = addRunArtifact(exe);
2748 run.expectStdOutEqual("bar=-1\n");
2749 test_step.dependOn(&run.step);
2750 }
2751
2752 {
2753 const exe = addExecutable(b, "main", opts);
2754 addCSourceBytes(exe,
2755 \\#include <stdio.h>
2756 \\int foo() { return 5; }
2757 \\int bar();
2758 \\int main() { printf("bar=%d\n", bar()); }
2759 , &.{});
2760 exe.linkLibrary(dso);
2761 exe.linkLibC();
2762
2763 const run = addRunArtifact(exe);
2764 run.expectStdOutEqual("bar=5\n");
2765 test_step.dependOn(&run.step);
2766 }
2767
2768 return test_step;
2769}
2770
2771fn testZNow(b: *Build, opts: Options) *Step {
2772 const test_step = addTestStep(b, "z-now", opts);
2773
2774 const obj = addObject(b, "obj", opts);
2775 addCSourceBytes(obj, "int main() { return 0; }", &.{});
2776 obj.force_pic = true;
2777
2778 {
2779 const dso = addSharedLibrary(b, "a", opts);
2780 dso.addObject(obj);
2781
2782 const check = dso.checkObject();
2783 check.checkInDynamicSection();
2784 check.checkContains("NOW");
2785 test_step.dependOn(&check.step);
2786 }
2787
2788 {
2789 const dso = addSharedLibrary(b, "a", opts);
2790 dso.addObject(obj);
2791 dso.link_z_lazy = true;
2792
2793 const check = dso.checkObject();
2794 check.checkInDynamicSection();
2795 check.checkNotPresent("NOW");
2796 test_step.dependOn(&check.step);
2797 }
2798
2799 return test_step;
2800}
2801
2802fn testZStackSize(b: *Build, opts: Options) *Step {
2803 const test_step = addTestStep(b, "z-stack-size", opts);
2804
2805 const exe = addExecutable(b, "main", opts);
2806 addCSourceBytes(exe, "int main() { return 0; }", &.{});
2807 exe.stack_size = 0x800000;
2808 exe.linkLibC();
2809
2810 const check = exe.checkObject();
2811 check.checkStart();
2812 check.checkExact("program headers");
2813 check.checkExact("type GNU_STACK");
2814 check.checkExact("memsz 800000");
2815 test_step.dependOn(&check.step);
2816
2817 return test_step;
2818}
2819
2820fn testZText(b: *Build, opts: Options) *Step {
2821 const test_step = addTestStep(b, "z-text", opts);
2822
2823 // Previously, following mold, this test tested text relocs present in a PIE executable.
2824 // However, as we want to cover musl AND glibc, it is now modified to test presence of
2825 // text relocs in a DSO which is then linked with an executable.
2826 // According to Rich and this thread https://www.openwall.com/lists/musl/2020/09/25/4
2827 // musl supports only a very limited number of text relocations and only in DSOs (and
2828 // rightly so!).
2829
2830 const a_o = addObject(b, "a", opts);
2831 addAsmSourceBytes(a_o,
2832 \\.globl fn1
2833 \\fn1:
2834 \\ sub $8, %rsp
2835 \\ movabs ptr, %rax
2836 \\ call *%rax
2837 \\ add $8, %rsp
2838 \\ ret
2839 );
2840
2841 const b_o = addObject(b, "b", opts);
2842 addCSourceBytes(b_o,
2843 \\int fn1();
2844 \\int fn2() {
2845 \\ return 3;
2846 \\}
2847 \\void *ptr = fn2;
2848 \\int fnn() {
2849 \\ return fn1();
2850 \\}
2851 , &.{});
2852 b_o.force_pic = true;
2853
2854 const dso = addSharedLibrary(b, "a", opts);
2855 dso.addObject(a_o);
2856 dso.addObject(b_o);
2857 dso.link_z_notext = true;
2858
2859 const exe = addExecutable(b, "main", opts);
2860 addCSourceBytes(exe,
2861 \\#include <stdio.h>
2862 \\int fnn();
2863 \\int main() {
2864 \\ printf("%d\n", fnn());
2865 \\}
2866 , &.{});
2867 exe.linkLibrary(dso);
2868 exe.linkLibC();
2869
2870 const run = addRunArtifact(exe);
2871 run.expectStdOutEqual("3\n");
2872 test_step.dependOn(&run.step);
2873
2874 // Check for DT_TEXTREL in a DSO
2875 const check = dso.checkObject();
2876 check.checkInDynamicSection();
2877 // check.checkExact("TEXTREL 0"); // TODO fix in CheckObject parser
2878 check.checkContains("FLAGS TEXTREL");
2879 test_step.dependOn(&check.step);
2880
2881 return test_step;
2882}
2883
126const Options = struct {2884const Options = struct {
127 target: CrossTarget = .{ .cpu_arch = .x86_64, .os_tag = .linux },2885 target: CrossTarget = .{ .cpu_arch = .x86_64, .os_tag = .linux },
128 optimize: std.builtin.OptimizeMode = .Debug,2886 optimize: std.builtin.OptimizeMode = .Debug,
...@@ -141,9 +2899,39 @@ fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {...@@ -141,9 +2899,39 @@ fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {
141 return b.step(name, "");2899 return b.step(name, "");
142}2900}
1432901
144fn addExecutable(b: *Build, opts: Options) *Compile {2902fn addExecutable(b: *Build, name: []const u8, opts: Options) *Compile {
145 return b.addExecutable(.{2903 return b.addExecutable(.{
146 .name = "test",2904 .name = name,
2905 .target = opts.target,
2906 .optimize = opts.optimize,
2907 .use_llvm = opts.use_llvm,
2908 .use_lld = false,
2909 });
2910}
2911
2912fn addObject(b: *Build, name: []const u8, opts: Options) *Compile {
2913 return b.addObject(.{
2914 .name = name,
2915 .target = opts.target,
2916 .optimize = opts.optimize,
2917 .use_llvm = opts.use_llvm,
2918 .use_lld = false,
2919 });
2920}
2921
2922fn addStaticLibrary(b: *Build, name: []const u8, opts: Options) *Compile {
2923 return b.addStaticLibrary(.{
2924 .name = name,
2925 .target = opts.target,
2926 .optimize = opts.optimize,
2927 .use_llvm = opts.use_llvm,
2928 .use_lld = true,
2929 });
2930}
2931
2932fn addSharedLibrary(b: *Build, name: []const u8, opts: Options) *Compile {
2933 return b.addSharedLibrary(.{
2934 .name = name,
147 .target = opts.target,2935 .target = opts.target,
148 .optimize = opts.optimize,2936 .optimize = opts.optimize,
149 .use_llvm = opts.use_llvm,2937 .use_llvm = opts.use_llvm,
...@@ -158,22 +2946,29 @@ fn addRunArtifact(comp: *Compile) *Run {...@@ -158,22 +2946,29 @@ fn addRunArtifact(comp: *Compile) *Run {
158 return run;2946 return run;
159}2947}
1602948
161fn addZigSourceBytes(comp: *Compile, comptime bytes: []const u8) void {2949fn addZigSourceBytes(comp: *Compile, bytes: []const u8) void {
162 const b = comp.step.owner;2950 const b = comp.step.owner;
163 const file = WriteFile.create(b).add("a.zig", bytes);2951 const file = WriteFile.create(b).add("a.zig", bytes);
164 file.addStepDependencies(&comp.step);2952 file.addStepDependencies(&comp.step);
165 comp.root_src = file;2953 comp.root_src = file;
166}2954}
1672955
168fn addCSourceBytes(comp: *Compile, comptime bytes: []const u8) void {2956fn addCSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []const u8) void {
169 const b = comp.step.owner;2957 const b = comp.step.owner;
170 const file = WriteFile.create(b).add("a.c", bytes);2958 const file = WriteFile.create(b).add("a.c", bytes);
171 comp.addCSourceFile(.{ .file = file, .flags = &.{} });2959 comp.addCSourceFile(.{ .file = file, .flags = flags });
2960}
2961
2962fn addCppSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []const u8) void {
2963 const b = comp.step.owner;
2964 const file = WriteFile.create(b).add("a.cpp", bytes);
2965 comp.addCSourceFile(.{ .file = file, .flags = flags });
172}2966}
1732967
174fn addAsmSourceBytes(comp: *Compile, comptime bytes: []const u8) void {2968fn addAsmSourceBytes(comp: *Compile, bytes: []const u8) void {
175 const b = comp.step.owner;2969 const b = comp.step.owner;
176 const file = WriteFile.create(b).add("a.s", bytes ++ "\n");2970 const actual_bytes = std.fmt.allocPrint(b.allocator, "{s}\n", .{bytes}) catch @panic("OOM");
2971 const file = WriteFile.create(b).add("a.s", actual_bytes);
177 comp.addAssemblyFile(file);2972 comp.addAssemblyFile(file);
178}2973}
1792974