authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-09-13 10:07:07+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-09-13 10:07:07+02:00
log4d29b3967873d6ff7e7426b2ab99c00c9e0fa284
tree90bfc83c9ef5aafbdbd626d7e1767db18730dbeb
parent89ea67aee2aeb4c041f55625f22fb9a1c56cf9ea
parent8142349d699140ff71801d31f1d1958599f5adda
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17113 from ziglang/elf-linker

elf: upstream zld/ELF functionality, part 1

20 files changed, 4881 insertions(+), 1232 deletions(-)

CMakeLists.txt+8
...@@ -584,6 +584,14 @@ set(ZIG_STAGE2_SOURCES...@@ -584,6 +584,14 @@ set(ZIG_STAGE2_SOURCES
584 "${CMAKE_SOURCE_DIR}/src/link/Coff/Object.zig"584 "${CMAKE_SOURCE_DIR}/src/link/Coff/Object.zig"
585 "${CMAKE_SOURCE_DIR}/src/link/Coff/lld.zig"585 "${CMAKE_SOURCE_DIR}/src/link/Coff/lld.zig"
586 "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"586 "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"
587 "${CMAKE_SOURCE_DIR}/src/link/Elf/Atom.zig"
588 "${CMAKE_SOURCE_DIR}/src/link/Elf/LinkerDefined.zig"
589 "${CMAKE_SOURCE_DIR}/src/link/Elf/Object.zig"
590 "${CMAKE_SOURCE_DIR}/src/link/Elf/Symbol.zig"
591 "${CMAKE_SOURCE_DIR}/src/link/Elf/ZigModule.zig"
592 "${CMAKE_SOURCE_DIR}/src/link/Elf/eh_frame.zig"
593 "${CMAKE_SOURCE_DIR}/src/link/Elf/file.zig"
594 "${CMAKE_SOURCE_DIR}/src/link/Elf/synthetic_sections.zig"
587 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"595 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
588 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"596 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
589 "${CMAKE_SOURCE_DIR}/src/link/MachO/Atom.zig"597 "${CMAKE_SOURCE_DIR}/src/link/MachO/Atom.zig"
src/arch/aarch64/CodeGen.zig+4-4
...@@ -4314,10 +4314,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4314,10 +4314,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4314 if (try self.air.value(callee, mod)) |func_value| {4314 if (try self.air.value(callee, mod)) |func_value| {
4315 if (func_value.getFunction(mod)) |func| {4315 if (func_value.getFunction(mod)) |func| {
4316 if (self.bin_file.cast(link.File.Elf)) |elf_file| {4316 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4317 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);4317 const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);
4318 const atom = elf_file.getAtom(atom_index);4318 const sym = elf_file.symbol(sym_index);
4319 _ = try atom.getOrCreateOffsetTableEntry(elf_file);4319 _ = try sym.getOrCreateGotEntry(elf_file);
4320 const got_addr = @as(u32, @intCast(atom.getOffsetTableAddress(elf_file)));4320 const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file)));
4321 try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr });4321 try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr });
4322 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {4322 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4323 const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl);4323 const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl);
src/arch/arm/CodeGen.zig+4-4
...@@ -4294,10 +4294,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4294,10 +4294,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4294 if (try self.air.value(callee, mod)) |func_value| {4294 if (try self.air.value(callee, mod)) |func_value| {
4295 if (func_value.getFunction(mod)) |func| {4295 if (func_value.getFunction(mod)) |func| {
4296 if (self.bin_file.cast(link.File.Elf)) |elf_file| {4296 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4297 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);4297 const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);
4298 const atom = elf_file.getAtom(atom_index);4298 const sym = elf_file.symbol(sym_index);
4299 _ = try atom.getOrCreateOffsetTableEntry(elf_file);4299 _ = try sym.getOrCreateGotEntry(elf_file);
4300 const got_addr = @as(u32, @intCast(atom.getOffsetTableAddress(elf_file)));4300 const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file)));
4301 try self.genSetReg(Type.usize, .lr, .{ .memory = got_addr });4301 try self.genSetReg(Type.usize, .lr, .{ .memory = got_addr });
4302 } else if (self.bin_file.cast(link.File.MachO)) |_| {4302 } else if (self.bin_file.cast(link.File.MachO)) |_| {
4303 unreachable; // unsupported architecture for MachO4303 unreachable; // unsupported architecture for MachO
src/arch/riscv64/CodeGen.zig+4-4
...@@ -1747,10 +1747,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -1747,10 +1747,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
1747 if (try self.air.value(callee, mod)) |func_value| {1747 if (try self.air.value(callee, mod)) |func_value| {
1748 switch (mod.intern_pool.indexToKey(func_value.ip_index)) {1748 switch (mod.intern_pool.indexToKey(func_value.ip_index)) {
1749 .func => |func| {1749 .func => |func| {
1750 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);1750 const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);
1751 const atom = elf_file.getAtom(atom_index);1751 const sym = elf_file.symbol(sym_index);
1752 _ = try atom.getOrCreateOffsetTableEntry(elf_file);1752 _ = try sym.getOrCreateGotEntry(elf_file);
1753 const got_addr = @as(u32, @intCast(atom.getOffsetTableAddress(elf_file)));1753 const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file)));
1754 try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr });1754 try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr });
1755 _ = try self.addInst(.{1755 _ = try self.addInst(.{
1756 .tag = .jalr,1756 .tag = .jalr,
src/arch/sparc64/CodeGen.zig+4-4
...@@ -1349,10 +1349,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -1349,10 +1349,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
1349 switch (mod.intern_pool.indexToKey(func_value.ip_index)) {1349 switch (mod.intern_pool.indexToKey(func_value.ip_index)) {
1350 .func => |func| {1350 .func => |func| {
1351 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {1351 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1352 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);1352 const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);
1353 const atom = elf_file.getAtom(atom_index);1353 const sym = elf_file.symbol(sym_index);
1354 _ = try atom.getOrCreateOffsetTableEntry(elf_file);1354 _ = try sym.getOrCreateGotEntry(elf_file);
1355 break :blk @as(u32, @intCast(atom.getOffsetTableAddress(elf_file)));1355 break :blk @as(u32, @intCast(sym.gotAddress(elf_file)));
1356 } else unreachable;1356 } else unreachable;
13571357
1358 try self.genSetReg(Type.usize, .o7, .{ .memory = got_addr });1358 try self.genSetReg(Type.usize, .o7, .{ .memory = got_addr });
src/arch/x86_64/CodeGen.zig+29-11
...@@ -125,7 +125,9 @@ const Owner = union(enum) {...@@ -125,7 +125,9 @@ const Owner = union(enum) {
125 .func_index => |func_index| {125 .func_index => |func_index| {
126 const mod = ctx.bin_file.options.module.?;126 const mod = ctx.bin_file.options.module.?;
127 const decl_index = mod.funcOwnerDeclIndex(func_index);127 const decl_index = mod.funcOwnerDeclIndex(func_index);
128 if (ctx.bin_file.cast(link.File.MachO)) |macho_file| {128 if (ctx.bin_file.cast(link.File.Elf)) |elf_file| {
129 return elf_file.getOrCreateMetadataForDecl(decl_index);
130 } else if (ctx.bin_file.cast(link.File.MachO)) |macho_file| {
129 const atom = try macho_file.getOrCreateAtomForDecl(decl_index);131 const atom = try macho_file.getOrCreateAtomForDecl(decl_index);
130 return macho_file.getAtom(atom).getSymbolIndex().?;132 return macho_file.getAtom(atom).getSymbolIndex().?;
131 } else if (ctx.bin_file.cast(link.File.Coff)) |coff_file| {133 } else if (ctx.bin_file.cast(link.File.Coff)) |coff_file| {
...@@ -136,7 +138,10 @@ const Owner = union(enum) {...@@ -136,7 +138,10 @@ const Owner = union(enum) {
136 } else unreachable;138 } else unreachable;
137 },139 },
138 .lazy_sym => |lazy_sym| {140 .lazy_sym => |lazy_sym| {
139 if (ctx.bin_file.cast(link.File.MachO)) |macho_file| {141 if (ctx.bin_file.cast(link.File.Elf)) |elf_file| {
142 return elf_file.getOrCreateMetadataForLazySymbol(lazy_sym) catch |err|
143 ctx.fail("{s} creating lazy symbol", .{@errorName(err)});
144 } else if (ctx.bin_file.cast(link.File.MachO)) |macho_file| {
140 const atom = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err|145 const atom = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err|
141 return ctx.fail("{s} creating lazy symbol", .{@errorName(err)});146 return ctx.fail("{s} creating lazy symbol", .{@errorName(err)});
142 return macho_file.getAtom(atom).getSymbolIndex().?;147 return macho_file.getAtom(atom).getSymbolIndex().?;
...@@ -8149,10 +8154,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -8149,10 +8154,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
8149 else => null,8154 else => null,
8150 }) |owner_decl| {8155 }) |owner_decl| {
8151 if (self.bin_file.cast(link.File.Elf)) |elf_file| {8156 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
8152 const atom_index = try elf_file.getOrCreateAtomForDecl(owner_decl);8157 const sym_index = try elf_file.getOrCreateMetadataForDecl(owner_decl);
8153 const atom = elf_file.getAtom(atom_index);8158 const sym = elf_file.symbol(sym_index);
8154 _ = try atom.getOrCreateOffsetTableEntry(elf_file);8159 sym.flags.needs_got = true;
8155 const got_addr = atom.getOffsetTableAddress(elf_file);8160 _ = try sym.getOrCreateGotEntry(elf_file);
8161 const got_addr = sym.gotAddress(elf_file);
8156 try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{8162 try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{
8157 .base = .{ .reg = .ds },8163 .base = .{ .reg = .ds },
8158 .disp = @intCast(got_addr),8164 .disp = @intCast(got_addr),
...@@ -8178,7 +8184,18 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -8178,7 +8184,18 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
8178 } else if (func_value.getExternFunc(mod)) |extern_func| {8184 } else if (func_value.getExternFunc(mod)) |extern_func| {
8179 const decl_name = mod.intern_pool.stringToSlice(mod.declPtr(extern_func.decl).name);8185 const decl_name = mod.intern_pool.stringToSlice(mod.declPtr(extern_func.decl).name);
8180 const lib_name = mod.intern_pool.stringToSliceUnwrap(extern_func.lib_name);8186 const lib_name = mod.intern_pool.stringToSliceUnwrap(extern_func.lib_name);
8181 if (self.bin_file.cast(link.File.Coff)) |coff_file| {8187 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
8188 const atom_index = try self.owner.getSymbolIndex(self);
8189 const sym_index = try elf_file.getGlobalSymbol(decl_name, lib_name);
8190 _ = try self.addInst(.{
8191 .tag = .call,
8192 .ops = .extern_fn_reloc,
8193 .data = .{ .reloc = .{
8194 .atom_index = atom_index,
8195 .sym_index = sym_index,
8196 } },
8197 });
8198 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
8182 const atom_index = try self.owner.getSymbolIndex(self);8199 const atom_index = try self.owner.getSymbolIndex(self);
8183 const sym_index = try coff_file.getGlobalSymbol(decl_name, lib_name);8200 const sym_index = try coff_file.getGlobalSymbol(decl_name, lib_name);
8184 _ = try self.addInst(.{8201 _ = try self.addInst(.{
...@@ -10215,11 +10232,12 @@ fn genLazySymbolRef(...@@ -10215,11 +10232,12 @@ fn genLazySymbolRef(
10215 lazy_sym: link.File.LazySymbol,10232 lazy_sym: link.File.LazySymbol,
10216) InnerError!void {10233) InnerError!void {
10217 if (self.bin_file.cast(link.File.Elf)) |elf_file| {10234 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
10218 const atom_index = elf_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err|10235 const sym_index = elf_file.getOrCreateMetadataForLazySymbol(lazy_sym) catch |err|
10219 return self.fail("{s} creating lazy symbol", .{@errorName(err)});10236 return self.fail("{s} creating lazy symbol", .{@errorName(err)});
10220 const atom = elf_file.getAtom(atom_index);10237 const sym = elf_file.symbol(sym_index);
10221 _ = try atom.getOrCreateOffsetTableEntry(elf_file);10238 sym.flags.needs_got = true;
10222 const got_addr = atom.getOffsetTableAddress(elf_file);10239 _ = try sym.getOrCreateGotEntry(elf_file);
10240 const got_addr = sym.gotAddress(elf_file);
10223 const got_mem =10241 const got_mem =
10224 Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(got_addr) });10242 Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(got_addr) });
10225 switch (tag) {10243 switch (tag) {
src/arch/x86_64/Emit.zig+9-1
...@@ -41,7 +41,15 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -41,7 +41,15 @@ pub fn emitMir(emit: *Emit) Error!void {
41 .offset = end_offset - 4,41 .offset = end_offset - 4,
42 .length = @as(u5, @intCast(end_offset - start_offset)),42 .length = @as(u5, @intCast(end_offset - start_offset)),
43 }),43 }),
44 .linker_extern_fn => |symbol| if (emit.bin_file.cast(link.File.MachO)) |macho_file| {44 .linker_extern_fn => |symbol| if (emit.bin_file.cast(link.File.Elf)) |elf_file| {
45 // Add relocation to the decl.
46 const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
47 try atom_ptr.addReloc(elf_file, .{
48 .r_offset = end_offset - 4,
49 .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | std.elf.R_X86_64_PLT32,
50 .r_addend = -4,
51 });
52 } else if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
45 // Add relocation to the decl.53 // Add relocation to the decl.
46 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;54 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
47 const target = macho_file.getGlobalByIndex(symbol.sym_index);55 const target = macho_file.getGlobalByIndex(symbol.sym_index);
src/codegen.zig+6-5
...@@ -858,10 +858,11 @@ fn genDeclRef(...@@ -858,10 +858,11 @@ fn genDeclRef(
858 const is_threadlocal = tv.val.isPtrToThreadLocal(mod) and !bin_file.options.single_threaded;858 const is_threadlocal = tv.val.isPtrToThreadLocal(mod) and !bin_file.options.single_threaded;
859859
860 if (bin_file.cast(link.File.Elf)) |elf_file| {860 if (bin_file.cast(link.File.Elf)) |elf_file| {
861 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);861 const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index);
862 const atom = elf_file.getAtom(atom_index);862 const sym = elf_file.symbol(sym_index);
863 _ = try atom.getOrCreateOffsetTableEntry(elf_file);863 sym.flags.needs_got = true;
864 return GenResult.mcv(.{ .memory = atom.getOffsetTableAddress(elf_file) });864 _ = try sym.getOrCreateGotEntry(elf_file);
865 return GenResult.mcv(.{ .memory = sym.gotAddress(elf_file) });
865 } else if (bin_file.cast(link.File.MachO)) |macho_file| {866 } else if (bin_file.cast(link.File.MachO)) |macho_file| {
866 const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);867 const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
867 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;868 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
...@@ -896,7 +897,7 @@ fn genUnnamedConst(...@@ -896,7 +897,7 @@ fn genUnnamedConst(
896 return GenResult.fail(bin_file.allocator, src_loc, "lowering unnamed constant failed: {s}", .{@errorName(err)});897 return GenResult.fail(bin_file.allocator, src_loc, "lowering unnamed constant failed: {s}", .{@errorName(err)});
897 };898 };
898 if (bin_file.cast(link.File.Elf)) |elf_file| {899 if (bin_file.cast(link.File.Elf)) |elf_file| {
899 return GenResult.mcv(.{ .memory = elf_file.getSymbol(local_sym_index).st_value });900 return GenResult.mcv(.{ .memory = elf_file.symbol(local_sym_index).value });
900 } else if (bin_file.cast(link.File.MachO)) |_| {901 } else if (bin_file.cast(link.File.MachO)) |_| {
901 return GenResult.mcv(.{ .load_direct = local_sym_index });902 return GenResult.mcv(.{ .load_direct = local_sym_index });
902 } else if (bin_file.cast(link.File.Coff)) |_| {903 } else if (bin_file.cast(link.File.Coff)) |_| {
src/link.zig+2-1
...@@ -549,7 +549,7 @@ pub const File = struct {...@@ -549,7 +549,7 @@ pub const File = struct {
549 switch (base.tag) {549 switch (base.tag) {
550 // zig fmt: off550 // zig fmt: off
551 .coff => return @fieldParentPtr(Coff, "base", base).getGlobalSymbol(name, lib_name),551 .coff => return @fieldParentPtr(Coff, "base", base).getGlobalSymbol(name, lib_name),
552 .elf => unreachable,552 .elf => return @fieldParentPtr(Elf, "base", base).getGlobalSymbol(name, lib_name),
553 .macho => return @fieldParentPtr(MachO, "base", base).getGlobalSymbol(name, lib_name),553 .macho => return @fieldParentPtr(MachO, "base", base).getGlobalSymbol(name, lib_name),
554 .plan9 => unreachable,554 .plan9 => unreachable,
555 .spirv => unreachable,555 .spirv => unreachable,
...@@ -849,6 +849,7 @@ pub const File = struct {...@@ -849,6 +849,7 @@ pub const File = struct {
849849
850 pub fn miscErrors(base: *File) []const ErrorMsg {850 pub fn miscErrors(base: *File) []const ErrorMsg {
851 switch (base.tag) {851 switch (base.tag) {
852 .elf => return @fieldParentPtr(Elf, "base", base).misc_errors.items,
852 .macho => return @fieldParentPtr(MachO, "base", base).misc_errors.items,853 .macho => return @fieldParentPtr(MachO, "base", base).misc_errors.items,
853 else => return &.{},854 else => return &.{},
854 }855 }
src/link/Dwarf.zig+12-12
...@@ -1108,7 +1108,7 @@ pub fn commitDeclState(...@@ -1108,7 +1108,7 @@ pub fn commitDeclState(
1108 switch (self.bin_file.tag) {1108 switch (self.bin_file.tag) {
1109 .elf => {1109 .elf => {
1110 const elf_file = self.bin_file.cast(File.Elf).?;1110 const elf_file = self.bin_file.cast(File.Elf).?;
1111 const debug_line_sect = &elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?];1111 const debug_line_sect = &elf_file.shdrs.items[elf_file.debug_line_section_index.?];
1112 const file_pos = debug_line_sect.sh_offset + src_fn.off;1112 const file_pos = debug_line_sect.sh_offset + src_fn.off;
1113 try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len);1113 try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len);
1114 },1114 },
...@@ -1170,7 +1170,7 @@ pub fn commitDeclState(...@@ -1170,7 +1170,7 @@ pub fn commitDeclState(
1170 const elf_file = self.bin_file.cast(File.Elf).?;1170 const elf_file = self.bin_file.cast(File.Elf).?;
1171 const shdr_index = elf_file.debug_line_section_index.?;1171 const shdr_index = elf_file.debug_line_section_index.?;
1172 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true);1172 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true);
1173 const debug_line_sect = elf_file.sections.items(.shdr)[shdr_index];1173 const debug_line_sect = elf_file.shdrs.items[shdr_index];
1174 const file_pos = debug_line_sect.sh_offset + src_fn.off;1174 const file_pos = debug_line_sect.sh_offset + src_fn.off;
1175 try pwriteDbgLineNops(1175 try pwriteDbgLineNops(
1176 elf_file.base.file.?,1176 elf_file.base.file.?,
...@@ -1337,7 +1337,7 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom_index: Atom.Index, len: u32)...@@ -1337,7 +1337,7 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom_index: Atom.Index, len: u32)
1337 switch (self.bin_file.tag) {1337 switch (self.bin_file.tag) {
1338 .elf => {1338 .elf => {
1339 const elf_file = self.bin_file.cast(File.Elf).?;1339 const elf_file = self.bin_file.cast(File.Elf).?;
1340 const debug_info_sect = &elf_file.sections.items(.shdr)[elf_file.debug_info_section_index.?];1340 const debug_info_sect = &elf_file.shdrs.items[elf_file.debug_info_section_index.?];
1341 const file_pos = debug_info_sect.sh_offset + atom.off;1341 const file_pos = debug_info_sect.sh_offset + atom.off;
1342 try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false);1342 try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false);
1343 },1343 },
...@@ -1415,7 +1415,7 @@ fn writeDeclDebugInfo(self: *Dwarf, atom_index: Atom.Index, dbg_info_buf: []cons...@@ -1415,7 +1415,7 @@ fn writeDeclDebugInfo(self: *Dwarf, atom_index: Atom.Index, dbg_info_buf: []cons
1415 const elf_file = self.bin_file.cast(File.Elf).?;1415 const elf_file = self.bin_file.cast(File.Elf).?;
1416 const shdr_index = elf_file.debug_info_section_index.?;1416 const shdr_index = elf_file.debug_info_section_index.?;
1417 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true);1417 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true);
1418 const debug_info_sect = elf_file.sections.items(.shdr)[shdr_index];1418 const debug_info_sect = &elf_file.shdrs.items[shdr_index];
1419 const file_pos = debug_info_sect.sh_offset + atom.off;1419 const file_pos = debug_info_sect.sh_offset + atom.off;
1420 try pwriteDbgInfoNops(1420 try pwriteDbgInfoNops(
1421 elf_file.base.file.?,1421 elf_file.base.file.?,
...@@ -1496,7 +1496,7 @@ pub fn updateDeclLineNumber(self: *Dwarf, mod: *Module, decl_index: Module.Decl....@@ -1496,7 +1496,7 @@ pub fn updateDeclLineNumber(self: *Dwarf, mod: *Module, decl_index: Module.Decl.
1496 switch (self.bin_file.tag) {1496 switch (self.bin_file.tag) {
1497 .elf => {1497 .elf => {
1498 const elf_file = self.bin_file.cast(File.Elf).?;1498 const elf_file = self.bin_file.cast(File.Elf).?;
1499 const shdr = elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?];1499 const shdr = elf_file.shdrs.items[elf_file.debug_line_section_index.?];
1500 const file_pos = shdr.sh_offset + atom.off + self.getRelocDbgLineOff();1500 const file_pos = shdr.sh_offset + atom.off + self.getRelocDbgLineOff();
1501 try elf_file.base.file.?.pwriteAll(&data, file_pos);1501 try elf_file.base.file.?.pwriteAll(&data, file_pos);
1502 },1502 },
...@@ -1713,7 +1713,7 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void {...@@ -1713,7 +1713,7 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void {
1713 const elf_file = self.bin_file.cast(File.Elf).?;1713 const elf_file = self.bin_file.cast(File.Elf).?;
1714 const shdr_index = elf_file.debug_abbrev_section_index.?;1714 const shdr_index = elf_file.debug_abbrev_section_index.?;
1715 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, false);1715 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, false);
1716 const debug_abbrev_sect = elf_file.sections.items(.shdr)[shdr_index];1716 const debug_abbrev_sect = &elf_file.shdrs.items[shdr_index];
1717 const file_pos = debug_abbrev_sect.sh_offset + abbrev_offset;1717 const file_pos = debug_abbrev_sect.sh_offset + abbrev_offset;
1718 try elf_file.base.file.?.pwriteAll(&abbrev_buf, file_pos);1718 try elf_file.base.file.?.pwriteAll(&abbrev_buf, file_pos);
1719 },1719 },
...@@ -1828,7 +1828,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u...@@ -1828,7 +1828,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u
1828 switch (self.bin_file.tag) {1828 switch (self.bin_file.tag) {
1829 .elf => {1829 .elf => {
1830 const elf_file = self.bin_file.cast(File.Elf).?;1830 const elf_file = self.bin_file.cast(File.Elf).?;
1831 const debug_info_sect = elf_file.sections.items(.shdr)[elf_file.debug_info_section_index.?];1831 const debug_info_sect = &elf_file.shdrs.items[elf_file.debug_info_section_index.?];
1832 const file_pos = debug_info_sect.sh_offset;1832 const file_pos = debug_info_sect.sh_offset;
1833 try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false);1833 try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false);
1834 },1834 },
...@@ -2147,7 +2147,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {...@@ -2147,7 +2147,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {
2147 const elf_file = self.bin_file.cast(File.Elf).?;2147 const elf_file = self.bin_file.cast(File.Elf).?;
2148 const shdr_index = elf_file.debug_aranges_section_index.?;2148 const shdr_index = elf_file.debug_aranges_section_index.?;
2149 try elf_file.growNonAllocSection(shdr_index, needed_size, 16, false);2149 try elf_file.growNonAllocSection(shdr_index, needed_size, 16, false);
2150 const debug_aranges_sect = elf_file.sections.items(.shdr)[shdr_index];2150 const debug_aranges_sect = &elf_file.shdrs.items[shdr_index];
2151 const file_pos = debug_aranges_sect.sh_offset;2151 const file_pos = debug_aranges_sect.sh_offset;
2152 try elf_file.base.file.?.pwriteAll(di_buf.items, file_pos);2152 try elf_file.base.file.?.pwriteAll(di_buf.items, file_pos);
2153 },2153 },
...@@ -2312,9 +2312,9 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {...@@ -2312,9 +2312,9 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
2312 .elf => {2312 .elf => {
2313 const elf_file = self.bin_file.cast(File.Elf).?;2313 const elf_file = self.bin_file.cast(File.Elf).?;
2314 const shdr_index = elf_file.debug_line_section_index.?;2314 const shdr_index = elf_file.debug_line_section_index.?;
2315 const needed_size = elf_file.sections.items(.shdr)[shdr_index].sh_size + delta;2315 const needed_size = elf_file.shdrs.items[shdr_index].sh_size + delta;
2316 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true);2316 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true);
2317 const file_pos = elf_file.sections.items(.shdr)[shdr_index].sh_offset + first_fn.off;2317 const file_pos = elf_file.shdrs.items[shdr_index].sh_offset + first_fn.off;
23182318
2319 const amt = try elf_file.base.file.?.preadAll(buffer, file_pos);2319 const amt = try elf_file.base.file.?.preadAll(buffer, file_pos);
2320 if (amt != buffer.len) return error.InputOutput;2320 if (amt != buffer.len) return error.InputOutput;
...@@ -2377,7 +2377,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {...@@ -2377,7 +2377,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
2377 switch (self.bin_file.tag) {2377 switch (self.bin_file.tag) {
2378 .elf => {2378 .elf => {
2379 const elf_file = self.bin_file.cast(File.Elf).?;2379 const elf_file = self.bin_file.cast(File.Elf).?;
2380 const debug_line_sect = elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?];2380 const debug_line_sect = &elf_file.shdrs.items[elf_file.debug_line_section_index.?];
2381 const file_pos = debug_line_sect.sh_offset;2381 const file_pos = debug_line_sect.sh_offset;
2382 try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt);2382 try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt);
2383 },2383 },
...@@ -2500,7 +2500,7 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {...@@ -2500,7 +2500,7 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {
2500 switch (self.bin_file.tag) {2500 switch (self.bin_file.tag) {
2501 .elf => {2501 .elf => {
2502 const elf_file = self.bin_file.cast(File.Elf).?;2502 const elf_file = self.bin_file.cast(File.Elf).?;
2503 const debug_info_sect = &elf_file.sections.items(.shdr)[elf_file.debug_info_section_index.?];2503 const debug_info_sect = &elf_file.shdrs.items[elf_file.debug_info_section_index.?];
2504 break :blk debug_info_sect.sh_offset;2504 break :blk debug_info_sect.sh_offset;
2505 },2505 },
2506 .macho => {2506 .macho => {
src/link/Elf.zig+1599-1110
...@@ -1,100 +1,4 @@...@@ -1,100 +1,4 @@
1const Elf = @This();1base: link.File,
2
3const std = @import("std");
4const build_options = @import("build_options");
5const builtin = @import("builtin");
6const assert = std.debug.assert;
7const elf = std.elf;
8const fs = std.fs;
9const log = std.log.scoped(.link);
10const math = std.math;
11const mem = std.mem;
12
13const codegen = @import("../codegen.zig");
14const glibc = @import("../glibc.zig");
15const link = @import("../link.zig");
16const lldMain = @import("../main.zig").lldMain;
17const musl = @import("../musl.zig");
18const target_util = @import("../target.zig");
19const trace = @import("../tracy.zig").trace;
20
21const Air = @import("../Air.zig");
22const Allocator = std.mem.Allocator;
23pub const Atom = @import("Elf/Atom.zig");
24const Cache = std.Build.Cache;
25const Compilation = @import("../Compilation.zig");
26const Dwarf = @import("Dwarf.zig");
27const File = link.File;
28const Liveness = @import("../Liveness.zig");
29const LlvmObject = @import("../codegen/llvm.zig").Object;
30const Module = @import("../Module.zig");
31const InternPool = @import("../InternPool.zig");
32const Package = @import("../Package.zig");
33const StringTable = @import("strtab.zig").StringTable;
34const TableSection = @import("table_section.zig").TableSection;
35const Type = @import("../type.zig").Type;
36const TypedValue = @import("../TypedValue.zig");
37const Value = @import("../value.zig").Value;
38
39const default_entry_addr = 0x8000000;
40
41pub const base_tag: File.Tag = .elf;
42
43const Section = struct {
44 shdr: elf.Elf64_Shdr,
45 phdr_index: u16,
46
47 /// Index of the last allocated atom in this section.
48 last_atom_index: ?Atom.Index = null,
49
50 /// A list of atoms that have surplus capacity. This list can have false
51 /// positives, as functions grow and shrink over time, only sometimes being added
52 /// or removed from the freelist.
53 ///
54 /// An atom has surplus capacity when its overcapacity value is greater than
55 /// padToIdeal(minimum_atom_size). That is, when it has so
56 /// much extra capacity, that we could fit a small new symbol in it, itself with
57 /// ideal_capacity or more.
58 ///
59 /// Ideal capacity is defined by size + (size / ideal_factor)
60 ///
61 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
62 /// overcapacity can be negative. A simple way to have negative overcapacity is to
63 /// allocate a fresh text block, which will have ideal capacity, and then grow it
64 /// by 1 byte. It will then have -1 overcapacity.
65 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
66};
67
68const LazySymbolMetadata = struct {
69 const State = enum { unused, pending_flush, flushed };
70 text_atom: Atom.Index = undefined,
71 rodata_atom: Atom.Index = undefined,
72 text_state: State = .unused,
73 rodata_state: State = .unused,
74};
75
76const DeclMetadata = struct {
77 atom: Atom.Index,
78 shdr: u16,
79 /// A list of all exports aliases of this Decl.
80 exports: std.ArrayListUnmanaged(u32) = .{},
81
82 fn getExport(m: DeclMetadata, elf_file: *const Elf, name: []const u8) ?u32 {
83 for (m.exports.items) |exp| {
84 if (mem.eql(u8, name, elf_file.getGlobalName(exp))) return exp;
85 }
86 return null;
87 }
88
89 fn getExportPtr(m: *DeclMetadata, elf_file: *Elf, name: []const u8) ?*u32 {
90 for (m.exports.items) |*exp| {
91 if (mem.eql(u8, name, elf_file.getGlobalName(exp.*))) return exp;
92 }
93 return null;
94 }
95};
96
97base: File,
98dwarf: ?Dwarf = null,2dwarf: ?Dwarf = null,
993
100ptr_width: PtrWidth,4ptr_width: PtrWidth,
...@@ -102,14 +6,25 @@ ptr_width: PtrWidth,...@@ -102,14 +6,25 @@ ptr_width: PtrWidth,
102/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.6/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
103llvm_object: ?*LlvmObject = null,7llvm_object: ?*LlvmObject = null,
1048
9/// A list of all input files.
10/// Index of each input file also encodes the priority or precedence of one input file
11/// over another.
12files: std.MultiArrayList(File.Entry) = .{},
13zig_module_index: ?File.Index = null,
14linker_defined_index: ?File.Index = null,
15objects: std.ArrayListUnmanaged(File.Index) = .{},
16
105/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.17/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
106/// Same order as in the file.18/// Same order as in the file.
107sections: std.MultiArrayList(Section) = .{},19shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{},
20/// Given index to a section, pulls index of containing phdr if any.
21phdr_to_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{},
22/// File offset into the shdr table.
108shdr_table_offset: ?u64 = null,23shdr_table_offset: ?u64 = null,
10924
110/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.25/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
111/// Same order as in the file.26/// Same order as in the file.
112program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{},27phdrs: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{},
113/// The index into the program headers of the PT_PHDR program header28/// The index into the program headers of the PT_PHDR program header
114phdr_table_index: ?u16 = null,29phdr_table_index: ?u16 = null,
115/// The index into the program headers of the PT_LOAD program header containing the phdr30/// The index into the program headers of the PT_LOAD program header containing the phdr
...@@ -128,17 +43,26 @@ phdr_load_rw_index: ?u16 = null,...@@ -128,17 +43,26 @@ phdr_load_rw_index: ?u16 = null,
12843
129entry_addr: ?u64 = null,44entry_addr: ?u64 = null,
130page_size: u32,45page_size: u32,
46default_sym_version: elf.Elf64_Versym,
13147
132/// .shstrtab buffer48/// .shstrtab buffer
133shstrtab: StringTable(.strtab) = .{},49shstrtab: StringTable(.strtab) = .{},
134/// .strtab buffer50/// .strtab buffer
135strtab: StringTable(.strtab) = .{},51strtab: StringTable(.strtab) = .{},
13652
137symtab_section_index: ?u16 = null,53/// Representation of the GOT table as committed to the file.
54got: GotSection = .{},
55
138text_section_index: ?u16 = null,56text_section_index: ?u16 = null,
139rodata_section_index: ?u16 = null,57rodata_section_index: ?u16 = null,
140got_section_index: ?u16 = null,
141data_section_index: ?u16 = null,58data_section_index: ?u16 = null,
59eh_frame_section_index: ?u16 = null,
60eh_frame_hdr_section_index: ?u16 = null,
61dynamic_section_index: ?u16 = null,
62got_section_index: ?u16 = null,
63got_plt_section_index: ?u16 = null,
64plt_section_index: ?u16 = null,
65rela_dyn_section_index: ?u16 = null,
142debug_info_section_index: ?u16 = null,66debug_info_section_index: ?u16 = null,
143debug_abbrev_section_index: ?u16 = null,67debug_abbrev_section_index: ?u16 = null,
144debug_str_section_index: ?u16 = null,68debug_str_section_index: ?u16 = null,
...@@ -146,24 +70,36 @@ debug_aranges_section_index: ?u16 = null,...@@ -146,24 +70,36 @@ debug_aranges_section_index: ?u16 = null,
146debug_line_section_index: ?u16 = null,70debug_line_section_index: ?u16 = null,
147shstrtab_section_index: ?u16 = null,71shstrtab_section_index: ?u16 = null,
148strtab_section_index: ?u16 = null,72strtab_section_index: ?u16 = null,
73symtab_section_index: ?u16 = null,
14974
150/// The same order as in the file. ELF requires global symbols to all be after the75// Linker-defined symbols
151/// local symbols, they cannot be mixed. So we must buffer all the global symbols and76dynamic_index: ?Symbol.Index = null,
152/// write them at the end. These are only the local symbols. The length of this array77ehdr_start_index: ?Symbol.Index = null,
153/// is the value used for sh_info in the .symtab section.78init_array_start_index: ?Symbol.Index = null,
154local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},79init_array_end_index: ?Symbol.Index = null,
155global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},80fini_array_start_index: ?Symbol.Index = null,
15681fini_array_end_index: ?Symbol.Index = null,
157local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},82preinit_array_start_index: ?Symbol.Index = null,
158global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},83preinit_array_end_index: ?Symbol.Index = null,
15984got_index: ?Symbol.Index = null,
160got_table: TableSection(u32) = .{},85plt_index: ?Symbol.Index = null,
86end_index: ?Symbol.Index = null,
87gnu_eh_frame_hdr_index: ?Symbol.Index = null,
88dso_handle_index: ?Symbol.Index = null,
89rela_iplt_start_index: ?Symbol.Index = null,
90rela_iplt_end_index: ?Symbol.Index = null,
91start_stop_indexes: std.ArrayListUnmanaged(u32) = .{},
92
93/// An array of symbols parsed across all input files.
94symbols: std.ArrayListUnmanaged(Symbol) = .{},
95symbols_extra: std.ArrayListUnmanaged(u32) = .{},
96resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},
97symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},
16198
162phdr_table_dirty: bool = false,99phdr_table_dirty: bool = false,
163shdr_table_dirty: bool = false,100shdr_table_dirty: bool = false,
164shstrtab_dirty: bool = false,101shstrtab_dirty: bool = false,
165strtab_dirty: bool = false,102strtab_dirty: bool = false,
166got_table_count_dirty: bool = false,
167103
168debug_strtab_dirty: bool = false,104debug_strtab_dirty: bool = false,
169debug_abbrev_section_dirty: bool = false,105debug_abbrev_section_dirty: bool = false,
...@@ -171,7 +107,8 @@ debug_aranges_section_dirty: bool = false,...@@ -171,7 +107,8 @@ debug_aranges_section_dirty: bool = false,
171debug_info_header_dirty: bool = false,107debug_info_header_dirty: bool = false,
172debug_line_header_dirty: bool = false,108debug_line_header_dirty: bool = false,
173109
174error_flags: File.ErrorFlags = File.ErrorFlags{},110error_flags: link.File.ErrorFlags = link.File.ErrorFlags{},
111misc_errors: std.ArrayListUnmanaged(link.File.ErrorMsg) = .{},
175112
176/// Table of tracked LazySymbols.113/// Table of tracked LazySymbols.
177lazy_syms: LazySymbolTable = .{},114lazy_syms: LazySymbolTable = .{},
...@@ -181,9 +118,8 @@ decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},...@@ -181,9 +118,8 @@ decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
181118
182/// List of atoms that are owned directly by the linker.119/// List of atoms that are owned directly by the linker.
183atoms: std.ArrayListUnmanaged(Atom) = .{},120atoms: std.ArrayListUnmanaged(Atom) = .{},
184121/// Table of last atom index in a section and matching atom free list if any.
185/// Table of atoms indexed by the symbol index.122last_atom_and_free_list_table: std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFreeList) = .{},
186atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{},
187123
188/// Table of unnamed constants associated with a parent `Decl`.124/// Table of unnamed constants associated with a parent `Decl`.
189/// We store them here so that we can free the constants whenever the `Decl`125/// We store them here so that we can free the constants whenever the `Decl`
...@@ -204,15 +140,13 @@ atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{},...@@ -204,15 +140,13 @@ atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{},
204///140///
205/// value assigned to label `foo` is an unnamed constant belonging/associated141/// value assigned to label `foo` is an unnamed constant belonging/associated
206/// with `Decl` `main`, and lives as long as that `Decl`.142/// with `Decl` `main`, and lives as long as that `Decl`.
207unnamed_const_atoms: UnnamedConstTable = .{},143unnamed_consts: UnnamedConstTable = .{},
208144
209/// A table of relocations indexed by the owning them `TextBlock`.145comdat_groups: std.ArrayListUnmanaged(ComdatGroup) = .{},
210/// Note that once we refactor `TextBlock`'s lifetime and ownership rules,146comdat_groups_owners: std.ArrayListUnmanaged(ComdatGroupOwner) = .{},
211/// this will be a table indexed by index into the list of Atoms.147comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{},
212relocs: RelocTable = .{},
213148
214const RelocTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Reloc));149const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index));
215const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));
216const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata);150const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata);
217151
218/// When allocating, the ideal_capacity is calculated by152/// When allocating, the ideal_capacity is calculated by
...@@ -237,40 +171,34 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -237,40 +171,34 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
237 const self = try createEmpty(allocator, options);171 const self = try createEmpty(allocator, options);
238 errdefer self.base.destroy();172 errdefer self.base.destroy();
239173
240 const file = try options.emit.?.directory.handle.createFile(sub_path, .{174 self.base.file = try options.emit.?.directory.handle.createFile(sub_path, .{
241 .truncate = false,175 .truncate = false,
242 .read = true,176 .read = true,
243 .mode = link.determineMode(options),177 .mode = link.determineMode(options),
244 });178 });
245179
246 self.base.file = file;
247 self.shdr_table_dirty = true;180 self.shdr_table_dirty = true;
248181
249 // Index 0 is always a null symbol.182 // Index 0 is always a null symbol.
250 try self.local_symbols.append(allocator, .{183 try self.symbols.append(allocator, .{});
251 .st_name = 0,184 // Index 0 is always a null symbol.
252 .st_info = 0,185 try self.symbols_extra.append(allocator, 0);
253 .st_other = 0,186 // Allocate atom index 0 to null atom
254 .st_shndx = 0,187 try self.atoms.append(allocator, .{});
255 .st_value = 0,188 // Append null file at index 0
256 .st_size = 0,189 try self.files.append(allocator, .null);
257 });190 // There must always be a null shdr in index 0
258191 try self.shdrs.append(allocator, .{
259 // There must always be a null section in index 0192 .sh_name = 0,
260 try self.sections.append(allocator, .{193 .sh_type = elf.SHT_NULL,
261 .shdr = .{194 .sh_flags = 0,
262 .sh_name = 0,195 .sh_addr = 0,
263 .sh_type = elf.SHT_NULL,196 .sh_offset = 0,
264 .sh_flags = 0,197 .sh_size = 0,
265 .sh_addr = 0,198 .sh_link = 0,
266 .sh_offset = 0,199 .sh_info = 0,
267 .sh_size = 0,200 .sh_addralign = 0,
268 .sh_link = 0,201 .sh_entsize = 0,
269 .sh_info = 0,
270 .sh_addralign = 0,
271 .sh_entsize = 0,
272 },
273 .phdr_index = undefined,
274 });202 });
275203
276 try self.populateMissingMetadata();204 try self.populateMissingMetadata();
...@@ -292,6 +220,10 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {...@@ -292,6 +220,10 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
292 .sparc64 => 0x2000,220 .sparc64 => 0x2000,
293 else => 0x1000,221 else => 0x1000,
294 };222 };
223 const default_sym_version: elf.Elf64_Versym = if (options.output_mode == .Lib and options.link_mode == .Dynamic)
224 elf.VER_NDX_GLOBAL
225 else
226 elf.VER_NDX_LOCAL;
295227
296 var dwarf: ?Dwarf = if (!options.strip and options.module != null)228 var dwarf: ?Dwarf = if (!options.strip and options.module != null)
297 Dwarf.init(gpa, &self.base, options.target)229 Dwarf.init(gpa, &self.base, options.target)
...@@ -308,11 +240,13 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {...@@ -308,11 +240,13 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
308 .dwarf = dwarf,240 .dwarf = dwarf,
309 .ptr_width = ptr_width,241 .ptr_width = ptr_width,
310 .page_size = page_size,242 .page_size = page_size,
243 .default_sym_version = default_sym_version,
311 };244 };
312 const use_llvm = options.use_llvm;245 const use_llvm = options.use_llvm;
313 if (use_llvm) {246 if (use_llvm) {
314 self.llvm_object = try LlvmObject.create(gpa, options);247 self.llvm_object = try LlvmObject.create(gpa, options);
315 }248 }
249
316 return self;250 return self;
317}251}
318252
...@@ -321,19 +255,27 @@ pub fn deinit(self: *Elf) void {...@@ -321,19 +255,27 @@ pub fn deinit(self: *Elf) void {
321255
322 if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);256 if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);
323257
324 for (self.sections.items(.free_list)) |*free_list| {258 for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) {
325 free_list.deinit(gpa);259 .null => {},
326 }260 .zig_module => data.zig_module.deinit(gpa),
327 self.sections.deinit(gpa);261 .linker_defined => data.linker_defined.deinit(gpa),
262 .object => data.object.deinit(gpa),
263 // .shared_object => data.shared_object.deinit(gpa),
264 };
265 self.files.deinit(gpa);
266 self.objects.deinit(gpa);
328267
329 self.program_headers.deinit(gpa);268 self.shdrs.deinit(gpa);
269 self.phdr_to_shdr_table.deinit(gpa);
270 self.phdrs.deinit(gpa);
330 self.shstrtab.deinit(gpa);271 self.shstrtab.deinit(gpa);
331 self.strtab.deinit(gpa);272 self.strtab.deinit(gpa);
332 self.local_symbols.deinit(gpa);273 self.symbols.deinit(gpa);
333 self.global_symbols.deinit(gpa);274 self.symbols_extra.deinit(gpa);
334 self.global_symbol_free_list.deinit(gpa);275 self.symbols_free_list.deinit(gpa);
335 self.local_symbol_free_list.deinit(gpa);276 self.got.deinit(gpa);
336 self.got_table.deinit(gpa);277 self.resolver.deinit(gpa);
278 self.start_stop_indexes.deinit(gpa);
337279
338 {280 {
339 var it = self.decls.iterator();281 var it = self.decls.iterator();
...@@ -344,43 +286,41 @@ pub fn deinit(self: *Elf) void {...@@ -344,43 +286,41 @@ pub fn deinit(self: *Elf) void {
344 }286 }
345287
346 self.atoms.deinit(gpa);288 self.atoms.deinit(gpa);
347 self.atom_by_index_table.deinit(gpa);289 for (self.last_atom_and_free_list_table.values()) |*value| {
348 self.lazy_syms.deinit(gpa);290 value.free_list.deinit(gpa);
349
350 {
351 var it = self.unnamed_const_atoms.valueIterator();
352 while (it.next()) |atoms| {
353 atoms.deinit(gpa);
354 }
355 self.unnamed_const_atoms.deinit(gpa);
356 }291 }
292 self.last_atom_and_free_list_table.deinit(gpa);
293 self.lazy_syms.deinit(gpa);
357294
358 {295 {
359 var it = self.relocs.valueIterator();296 var it = self.unnamed_consts.valueIterator();
360 while (it.next()) |relocs| {297 while (it.next()) |syms| {
361 relocs.deinit(gpa);298 syms.deinit(gpa);
362 }299 }
363 self.relocs.deinit(gpa);300 self.unnamed_consts.deinit(gpa);
364 }301 }
365302
366 if (self.dwarf) |*dw| {303 if (self.dwarf) |*dw| {
367 dw.deinit();304 dw.deinit();
368 }305 }
306
307 self.misc_errors.deinit(gpa);
308 self.comdat_groups.deinit(gpa);
309 self.comdat_groups_owners.deinit(gpa);
310 self.comdat_groups_table.deinit(gpa);
369}311}
370312
371pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 {313pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 {
372 assert(self.llvm_object == null);314 assert(self.llvm_object == null);
373315
374 const this_atom_index = try self.getOrCreateAtomForDecl(decl_index);316 const this_sym_index = try self.getOrCreateMetadataForDecl(decl_index);
375 const this_atom = self.getAtom(this_atom_index);317 const this_sym = self.symbol(this_sym_index);
376 const target = this_atom.getSymbolIndex().?;318 const vaddr = this_sym.value;
377 const vaddr = this_atom.getSymbol(self).st_value;319 const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(self).?;
378 const atom_index = self.getAtomIndexForSymbol(reloc_info.parent_atom_index).?;320 try parent_atom.addReloc(self, .{
379 try Atom.addRelocation(self, atom_index, .{321 .r_offset = reloc_info.offset,
380 .target = target,322 .r_info = (@as(u64, @intCast(this_sym.esym_index)) << 32) | elf.R_X86_64_64,
381 .offset = reloc_info.offset,323 .r_addend = reloc_info.addend,
382 .addend = reloc_info.addend,
383 .prev_vaddr = vaddr,
384 });324 });
385325
386 return vaddr;326 return vaddr;
...@@ -397,7 +337,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {...@@ -397,7 +337,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
397337
398 if (self.shdr_table_offset) |off| {338 if (self.shdr_table_offset) |off| {
399 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);339 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
400 const tight_size = self.sections.slice().len * shdr_size;340 const tight_size = self.shdrs.items.len * shdr_size;
401 const increased_size = padToIdeal(tight_size);341 const increased_size = padToIdeal(tight_size);
402 const test_end = off + increased_size;342 const test_end = off + increased_size;
403 if (end > off and start < test_end) {343 if (end > off and start < test_end) {
...@@ -405,17 +345,17 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {...@@ -405,17 +345,17 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
405 }345 }
406 }346 }
407347
408 for (self.sections.items(.shdr)) |section| {348 for (self.shdrs.items) |section| {
409 const increased_size = padToIdeal(section.sh_size);349 const increased_size = padToIdeal(section.sh_size);
410 const test_end = section.sh_offset + increased_size;350 const test_end = section.sh_offset + increased_size;
411 if (end > section.sh_offset and start < test_end) {351 if (end > section.sh_offset and start < test_end) {
412 return test_end;352 return test_end;
413 }353 }
414 }354 }
415 for (self.program_headers.items) |program_header| {355 for (self.phdrs.items) |phdr| {
416 const increased_size = padToIdeal(program_header.p_filesz);356 const increased_size = padToIdeal(phdr.p_filesz);
417 const test_end = program_header.p_offset + increased_size;357 const test_end = phdr.p_offset + increased_size;
418 if (end > program_header.p_offset and start < test_end) {358 if (end > phdr.p_offset and start < test_end) {
419 return test_end;359 return test_end;
420 }360 }
421 }361 }
...@@ -429,13 +369,13 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 {...@@ -429,13 +369,13 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 {
429 if (self.shdr_table_offset) |off| {369 if (self.shdr_table_offset) |off| {
430 if (off > start and off < min_pos) min_pos = off;370 if (off > start and off < min_pos) min_pos = off;
431 }371 }
432 for (self.sections.items(.shdr)) |section| {372 for (self.shdrs.items) |section| {
433 if (section.sh_offset <= start) continue;373 if (section.sh_offset <= start) continue;
434 if (section.sh_offset < min_pos) min_pos = section.sh_offset;374 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
435 }375 }
436 for (self.program_headers.items) |program_header| {376 for (self.phdrs.items) |phdr| {
437 if (program_header.p_offset <= start) continue;377 if (phdr.p_offset <= start) continue;
438 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;378 if (phdr.p_offset < min_pos) min_pos = phdr.p_offset;
439 }379 }
440 return min_pos - start;380 return min_pos - start;
441}381}
...@@ -457,19 +397,20 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -457,19 +397,20 @@ pub fn populateMissingMetadata(self: *Elf) !void {
457 .p64 => false,397 .p64 => false,
458 };398 };
459 const ptr_size: u8 = self.ptrWidthBytes();399 const ptr_size: u8 = self.ptrWidthBytes();
400 const image_base = self.calcImageBase();
460401
461 if (self.phdr_table_index == null) {402 if (self.phdr_table_index == null) {
462 self.phdr_table_index = @as(u16, @intCast(self.program_headers.items.len));403 self.phdr_table_index = @as(u16, @intCast(self.phdrs.items.len));
463 const p_align: u16 = switch (self.ptr_width) {404 const p_align: u16 = switch (self.ptr_width) {
464 .p32 => @alignOf(elf.Elf32_Phdr),405 .p32 => @alignOf(elf.Elf32_Phdr),
465 .p64 => @alignOf(elf.Elf64_Phdr),406 .p64 => @alignOf(elf.Elf64_Phdr),
466 };407 };
467 try self.program_headers.append(gpa, .{408 try self.phdrs.append(gpa, .{
468 .p_type = elf.PT_PHDR,409 .p_type = elf.PT_PHDR,
469 .p_offset = 0,410 .p_offset = 0,
470 .p_filesz = 0,411 .p_filesz = 0,
471 .p_vaddr = 0,412 .p_vaddr = image_base,
472 .p_paddr = 0,413 .p_paddr = image_base,
473 .p_memsz = 0,414 .p_memsz = 0,
474 .p_align = p_align,415 .p_align = p_align,
475 .p_flags = elf.PF_R,416 .p_flags = elf.PF_R,
...@@ -478,31 +419,29 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -478,31 +419,29 @@ pub fn populateMissingMetadata(self: *Elf) !void {
478 }419 }
479420
480 if (self.phdr_table_load_index == null) {421 if (self.phdr_table_load_index == null) {
481 self.phdr_table_load_index = @as(u16, @intCast(self.program_headers.items.len));422 self.phdr_table_load_index = @as(u16, @intCast(self.phdrs.items.len));
482 // TODO Same as for GOT423 // TODO Same as for GOT
483 const phdr_addr: u64 = if (self.base.options.target.ptrBitWidth() >= 32) 0x1000000 else 0x1000;424 try self.phdrs.append(gpa, .{
484 const p_align = self.page_size;
485 try self.program_headers.append(gpa, .{
486 .p_type = elf.PT_LOAD,425 .p_type = elf.PT_LOAD,
487 .p_offset = 0,426 .p_offset = 0,
488 .p_filesz = 0,427 .p_filesz = 0,
489 .p_vaddr = phdr_addr,428 .p_vaddr = image_base,
490 .p_paddr = phdr_addr,429 .p_paddr = image_base,
491 .p_memsz = 0,430 .p_memsz = 0,
492 .p_align = p_align,431 .p_align = self.page_size,
493 .p_flags = elf.PF_R,432 .p_flags = elf.PF_R,
494 });433 });
495 self.phdr_table_dirty = true;434 self.phdr_table_dirty = true;
496 }435 }
497436
498 if (self.phdr_load_re_index == null) {437 if (self.phdr_load_re_index == null) {
499 self.phdr_load_re_index = @as(u16, @intCast(self.program_headers.items.len));438 self.phdr_load_re_index = @as(u16, @intCast(self.phdrs.items.len));
500 const file_size = self.base.options.program_code_size_hint;439 const file_size = self.base.options.program_code_size_hint;
501 const p_align = self.page_size;440 const p_align = self.page_size;
502 const off = self.findFreeSpace(file_size, p_align);441 const off = self.findFreeSpace(file_size, p_align);
503 log.debug("found PT_LOAD RE free space 0x{x} to 0x{x}", .{ off, off + file_size });442 log.debug("found PT_LOAD RE free space 0x{x} to 0x{x}", .{ off, off + file_size });
504 const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr;443 const entry_addr = self.defaultEntryAddress();
505 try self.program_headers.append(gpa, .{444 try self.phdrs.append(gpa, .{
506 .p_type = elf.PT_LOAD,445 .p_type = elf.PT_LOAD,
507 .p_offset = off,446 .p_offset = off,
508 .p_filesz = file_size,447 .p_filesz = file_size,
...@@ -517,7 +456,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -517,7 +456,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
517 }456 }
518457
519 if (self.phdr_got_index == null) {458 if (self.phdr_got_index == null) {
520 self.phdr_got_index = @as(u16, @intCast(self.program_headers.items.len));459 self.phdr_got_index = @as(u16, @intCast(self.phdrs.items.len));
521 const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint;460 const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint;
522 // We really only need ptr alignment but since we are using PROGBITS, linux requires461 // We really only need ptr alignment but since we are using PROGBITS, linux requires
523 // page align.462 // page align.
...@@ -528,7 +467,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -528,7 +467,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
528 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something467 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
529 // else in virtual memory.468 // else in virtual memory.
530 const got_addr: u32 = if (self.base.options.target.ptrBitWidth() >= 32) 0x4000000 else 0x8000;469 const got_addr: u32 = if (self.base.options.target.ptrBitWidth() >= 32) 0x4000000 else 0x8000;
531 try self.program_headers.append(gpa, .{470 try self.phdrs.append(gpa, .{
532 .p_type = elf.PT_LOAD,471 .p_type = elf.PT_LOAD,
533 .p_offset = off,472 .p_offset = off,
534 .p_filesz = file_size,473 .p_filesz = file_size,
...@@ -542,7 +481,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -542,7 +481,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
542 }481 }
543482
544 if (self.phdr_load_ro_index == null) {483 if (self.phdr_load_ro_index == null) {
545 self.phdr_load_ro_index = @as(u16, @intCast(self.program_headers.items.len));484 self.phdr_load_ro_index = @as(u16, @intCast(self.phdrs.items.len));
546 // TODO Find a hint about how much data need to be in rodata ?485 // TODO Find a hint about how much data need to be in rodata ?
547 const file_size = 1024;486 const file_size = 1024;
548 // Same reason as for GOT487 // Same reason as for GOT
...@@ -551,7 +490,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -551,7 +490,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
551 log.debug("found PT_LOAD RO free space 0x{x} to 0x{x}", .{ off, off + file_size });490 log.debug("found PT_LOAD RO free space 0x{x} to 0x{x}", .{ off, off + file_size });
552 // TODO Same as for GOT491 // TODO Same as for GOT
553 const rodata_addr: u32 = if (self.base.options.target.ptrBitWidth() >= 32) 0xc000000 else 0xa000;492 const rodata_addr: u32 = if (self.base.options.target.ptrBitWidth() >= 32) 0xc000000 else 0xa000;
554 try self.program_headers.append(gpa, .{493 try self.phdrs.append(gpa, .{
555 .p_type = elf.PT_LOAD,494 .p_type = elf.PT_LOAD,
556 .p_offset = off,495 .p_offset = off,
557 .p_filesz = file_size,496 .p_filesz = file_size,
...@@ -565,7 +504,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -565,7 +504,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
565 }504 }
566505
567 if (self.phdr_load_rw_index == null) {506 if (self.phdr_load_rw_index == null) {
568 self.phdr_load_rw_index = @as(u16, @intCast(self.program_headers.items.len));507 self.phdr_load_rw_index = @as(u16, @intCast(self.phdrs.items.len));
569 // TODO Find a hint about how much data need to be in data ?508 // TODO Find a hint about how much data need to be in data ?
570 const file_size = 1024;509 const file_size = 1024;
571 // Same reason as for GOT510 // Same reason as for GOT
...@@ -574,7 +513,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -574,7 +513,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
574 log.debug("found PT_LOAD RW free space 0x{x} to 0x{x}", .{ off, off + file_size });513 log.debug("found PT_LOAD RW free space 0x{x} to 0x{x}", .{ off, off + file_size });
575 // TODO Same as for GOT514 // TODO Same as for GOT
576 const rwdata_addr: u32 = if (self.base.options.target.ptrBitWidth() >= 32) 0x10000000 else 0xc000;515 const rwdata_addr: u32 = if (self.base.options.target.ptrBitWidth() >= 32) 0x10000000 else 0xc000;
577 try self.program_headers.append(gpa, .{516 try self.phdrs.append(gpa, .{
578 .p_type = elf.PT_LOAD,517 .p_type = elf.PT_LOAD,
579 .p_offset = off,518 .p_offset = off,
580 .p_filesz = file_size,519 .p_filesz = file_size,
...@@ -588,197 +527,174 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -588,197 +527,174 @@ pub fn populateMissingMetadata(self: *Elf) !void {
588 }527 }
589528
590 if (self.shstrtab_section_index == null) {529 if (self.shstrtab_section_index == null) {
591 self.shstrtab_section_index = @as(u16, @intCast(self.sections.slice().len));530 self.shstrtab_section_index = @as(u16, @intCast(self.shdrs.items.len));
592 assert(self.shstrtab.buffer.items.len == 0);531 assert(self.shstrtab.buffer.items.len == 0);
593 try self.shstrtab.buffer.append(gpa, 0); // need a 0 at position 0532 try self.shstrtab.buffer.append(gpa, 0); // need a 0 at position 0
594 const off = self.findFreeSpace(self.shstrtab.buffer.items.len, 1);533 const off = self.findFreeSpace(self.shstrtab.buffer.items.len, 1);
595 log.debug("found .shstrtab free space 0x{x} to 0x{x}", .{ off, off + self.shstrtab.buffer.items.len });534 log.debug("found .shstrtab free space 0x{x} to 0x{x}", .{ off, off + self.shstrtab.buffer.items.len });
596 try self.sections.append(gpa, .{535 try self.shdrs.append(gpa, .{
597 .shdr = .{536 .sh_name = try self.shstrtab.insert(gpa, ".shstrtab"),
598 .sh_name = try self.shstrtab.insert(gpa, ".shstrtab"),537 .sh_type = elf.SHT_STRTAB,
599 .sh_type = elf.SHT_STRTAB,538 .sh_flags = 0,
600 .sh_flags = 0,539 .sh_addr = 0,
601 .sh_addr = 0,540 .sh_offset = off,
602 .sh_offset = off,541 .sh_size = self.shstrtab.buffer.items.len,
603 .sh_size = self.shstrtab.buffer.items.len,542 .sh_link = 0,
604 .sh_link = 0,543 .sh_info = 0,
605 .sh_info = 0,544 .sh_addralign = 1,
606 .sh_addralign = 1,545 .sh_entsize = 0,
607 .sh_entsize = 0,
608 },
609 .phdr_index = undefined,
610 });546 });
611 self.shstrtab_dirty = true;547 self.shstrtab_dirty = true;
612 self.shdr_table_dirty = true;548 self.shdr_table_dirty = true;
613 }549 }
614550
615 if (self.strtab_section_index == null) {551 if (self.strtab_section_index == null) {
616 self.strtab_section_index = @as(u16, @intCast(self.sections.slice().len));552 self.strtab_section_index = @as(u16, @intCast(self.shdrs.items.len));
617 assert(self.strtab.buffer.items.len == 0);553 assert(self.strtab.buffer.items.len == 0);
618 try self.strtab.buffer.append(gpa, 0); // need a 0 at position 0554 try self.strtab.buffer.append(gpa, 0); // need a 0 at position 0
619 const off = self.findFreeSpace(self.strtab.buffer.items.len, 1);555 const off = self.findFreeSpace(self.strtab.buffer.items.len, 1);
620 log.debug("found .strtab free space 0x{x} to 0x{x}", .{ off, off + self.strtab.buffer.items.len });556 log.debug("found .strtab free space 0x{x} to 0x{x}", .{ off, off + self.strtab.buffer.items.len });
621 try self.sections.append(gpa, .{557 try self.shdrs.append(gpa, .{
622 .shdr = .{558 .sh_name = try self.shstrtab.insert(gpa, ".strtab"),
623 .sh_name = try self.shstrtab.insert(gpa, ".strtab"),559 .sh_type = elf.SHT_STRTAB,
624 .sh_type = elf.SHT_STRTAB,560 .sh_flags = 0,
625 .sh_flags = 0,561 .sh_addr = 0,
626 .sh_addr = 0,562 .sh_offset = off,
627 .sh_offset = off,563 .sh_size = self.strtab.buffer.items.len,
628 .sh_size = self.strtab.buffer.items.len,564 .sh_link = 0,
629 .sh_link = 0,565 .sh_info = 0,
630 .sh_info = 0,566 .sh_addralign = 1,
631 .sh_addralign = 1,567 .sh_entsize = 0,
632 .sh_entsize = 0,
633 },
634 .phdr_index = undefined,
635 });568 });
636 self.strtab_dirty = true;569 self.strtab_dirty = true;
637 self.shdr_table_dirty = true;570 self.shdr_table_dirty = true;
638 }571 }
639572
640 if (self.text_section_index == null) {573 if (self.text_section_index == null) {
641 self.text_section_index = @as(u16, @intCast(self.sections.slice().len));574 self.text_section_index = @as(u16, @intCast(self.shdrs.items.len));
642 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];575 const phdr = &self.phdrs.items[self.phdr_load_re_index.?];
643576 try self.shdrs.append(gpa, .{
644 try self.sections.append(gpa, .{577 .sh_name = try self.shstrtab.insert(gpa, ".text"),
645 .shdr = .{578 .sh_type = elf.SHT_PROGBITS,
646 .sh_name = try self.shstrtab.insert(gpa, ".text"),579 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
647 .sh_type = elf.SHT_PROGBITS,580 .sh_addr = phdr.p_vaddr,
648 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,581 .sh_offset = phdr.p_offset,
649 .sh_addr = phdr.p_vaddr,582 .sh_size = phdr.p_filesz,
650 .sh_offset = phdr.p_offset,583 .sh_link = 0,
651 .sh_size = phdr.p_filesz,584 .sh_info = 0,
652 .sh_link = 0,585 .sh_addralign = 1,
653 .sh_info = 0,586 .sh_entsize = 0,
654 .sh_addralign = 1,
655 .sh_entsize = 0,
656 },
657 .phdr_index = self.phdr_load_re_index.?,
658 });587 });
588 try self.phdr_to_shdr_table.putNoClobber(gpa, self.text_section_index.?, self.phdr_load_re_index.?);
589 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.text_section_index.?, .{});
659 self.shdr_table_dirty = true;590 self.shdr_table_dirty = true;
660 }591 }
661592
662 if (self.got_section_index == null) {593 if (self.got_section_index == null) {
663 self.got_section_index = @as(u16, @intCast(self.sections.slice().len));594 self.got_section_index = @as(u16, @intCast(self.shdrs.items.len));
664 const phdr = &self.program_headers.items[self.phdr_got_index.?];595 const phdr = &self.phdrs.items[self.phdr_got_index.?];
665596 try self.shdrs.append(gpa, .{
666 try self.sections.append(gpa, .{597 .sh_name = try self.shstrtab.insert(gpa, ".got"),
667 .shdr = .{598 .sh_type = elf.SHT_PROGBITS,
668 .sh_name = try self.shstrtab.insert(gpa, ".got"),599 .sh_flags = elf.SHF_ALLOC,
669 .sh_type = elf.SHT_PROGBITS,600 .sh_addr = phdr.p_vaddr,
670 .sh_flags = elf.SHF_ALLOC,601 .sh_offset = phdr.p_offset,
671 .sh_addr = phdr.p_vaddr,602 .sh_size = phdr.p_filesz,
672 .sh_offset = phdr.p_offset,603 .sh_link = 0,
673 .sh_size = phdr.p_filesz,604 .sh_info = 0,
674 .sh_link = 0,605 .sh_addralign = @as(u16, ptr_size),
675 .sh_info = 0,606 .sh_entsize = 0,
676 .sh_addralign = @as(u16, ptr_size),
677 .sh_entsize = 0,
678 },
679 .phdr_index = self.phdr_got_index.?,
680 });607 });
608 try self.phdr_to_shdr_table.putNoClobber(gpa, self.got_section_index.?, self.phdr_got_index.?);
681 self.shdr_table_dirty = true;609 self.shdr_table_dirty = true;
682 }610 }
683611
684 if (self.rodata_section_index == null) {612 if (self.rodata_section_index == null) {
685 self.rodata_section_index = @as(u16, @intCast(self.sections.slice().len));613 self.rodata_section_index = @as(u16, @intCast(self.shdrs.items.len));
686 const phdr = &self.program_headers.items[self.phdr_load_ro_index.?];614 const phdr = &self.phdrs.items[self.phdr_load_ro_index.?];
687615 try self.shdrs.append(gpa, .{
688 try self.sections.append(gpa, .{616 .sh_name = try self.shstrtab.insert(gpa, ".rodata"),
689 .shdr = .{617 .sh_type = elf.SHT_PROGBITS,
690 .sh_name = try self.shstrtab.insert(gpa, ".rodata"),618 .sh_flags = elf.SHF_ALLOC,
691 .sh_type = elf.SHT_PROGBITS,619 .sh_addr = phdr.p_vaddr,
692 .sh_flags = elf.SHF_ALLOC,620 .sh_offset = phdr.p_offset,
693 .sh_addr = phdr.p_vaddr,621 .sh_size = phdr.p_filesz,
694 .sh_offset = phdr.p_offset,622 .sh_link = 0,
695 .sh_size = phdr.p_filesz,623 .sh_info = 0,
696 .sh_link = 0,624 .sh_addralign = 1,
697 .sh_info = 0,625 .sh_entsize = 0,
698 .sh_addralign = 1,
699 .sh_entsize = 0,
700 },
701 .phdr_index = self.phdr_load_ro_index.?,
702 });626 });
627 try self.phdr_to_shdr_table.putNoClobber(gpa, self.rodata_section_index.?, self.phdr_load_ro_index.?);
628 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.rodata_section_index.?, .{});
703 self.shdr_table_dirty = true;629 self.shdr_table_dirty = true;
704 }630 }
705631
706 if (self.data_section_index == null) {632 if (self.data_section_index == null) {
707 self.data_section_index = @as(u16, @intCast(self.sections.slice().len));633 self.data_section_index = @as(u16, @intCast(self.shdrs.items.len));
708 const phdr = &self.program_headers.items[self.phdr_load_rw_index.?];634 const phdr = &self.phdrs.items[self.phdr_load_rw_index.?];
709635 try self.shdrs.append(gpa, .{
710 try self.sections.append(gpa, .{636 .sh_name = try self.shstrtab.insert(gpa, ".data"),
711 .shdr = .{637 .sh_type = elf.SHT_PROGBITS,
712 .sh_name = try self.shstrtab.insert(gpa, ".data"),638 .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC,
713 .sh_type = elf.SHT_PROGBITS,639 .sh_addr = phdr.p_vaddr,
714 .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC,640 .sh_offset = phdr.p_offset,
715 .sh_addr = phdr.p_vaddr,641 .sh_size = phdr.p_filesz,
716 .sh_offset = phdr.p_offset,642 .sh_link = 0,
717 .sh_size = phdr.p_filesz,643 .sh_info = 0,
718 .sh_link = 0,644 .sh_addralign = @as(u16, ptr_size),
719 .sh_info = 0,645 .sh_entsize = 0,
720 .sh_addralign = @as(u16, ptr_size),
721 .sh_entsize = 0,
722 },
723 .phdr_index = self.phdr_load_rw_index.?,
724 });646 });
647 try self.phdr_to_shdr_table.putNoClobber(gpa, self.data_section_index.?, self.phdr_load_rw_index.?);
648 try self.last_atom_and_free_list_table.putNoClobber(gpa, self.data_section_index.?, .{});
725 self.shdr_table_dirty = true;649 self.shdr_table_dirty = true;
726 }650 }
727651
728 if (self.symtab_section_index == null) {652 if (self.symtab_section_index == null) {
729 self.symtab_section_index = @as(u16, @intCast(self.sections.slice().len));653 self.symtab_section_index = @as(u16, @intCast(self.shdrs.items.len));
730 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);654 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
731 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);655 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
732 const file_size = self.base.options.symbol_count_hint * each_size;656 const file_size = self.base.options.symbol_count_hint * each_size;
733 const off = self.findFreeSpace(file_size, min_align);657 const off = self.findFreeSpace(file_size, min_align);
734 log.debug("found symtab free space 0x{x} to 0x{x}", .{ off, off + file_size });658 log.debug("found symtab free space 0x{x} to 0x{x}", .{ off, off + file_size });
735659 try self.shdrs.append(gpa, .{
736 try self.sections.append(gpa, .{660 .sh_name = try self.shstrtab.insert(gpa, ".symtab"),
737 .shdr = .{661 .sh_type = elf.SHT_SYMTAB,
738 .sh_name = try self.shstrtab.insert(gpa, ".symtab"),662 .sh_flags = 0,
739 .sh_type = elf.SHT_SYMTAB,663 .sh_addr = 0,
740 .sh_flags = 0,664 .sh_offset = off,
741 .sh_addr = 0,665 .sh_size = file_size,
742 .sh_offset = off,666 // The section header index of the associated string table.
743 .sh_size = file_size,667 .sh_link = self.strtab_section_index.?,
744 // The section header index of the associated string table.668 .sh_info = @as(u32, @intCast(self.symbols.items.len)),
745 .sh_link = self.strtab_section_index.?,669 .sh_addralign = min_align,
746 .sh_info = @as(u32, @intCast(self.local_symbols.items.len)),670 .sh_entsize = each_size,
747 .sh_addralign = min_align,
748 .sh_entsize = each_size,
749 },
750 .phdr_index = undefined,
751 });671 });
752 self.shdr_table_dirty = true;672 self.shdr_table_dirty = true;
753 }673 }
754674
755 if (self.dwarf) |*dw| {675 if (self.dwarf) |*dw| {
756 if (self.debug_str_section_index == null) {676 if (self.debug_str_section_index == null) {
757 self.debug_str_section_index = @as(u16, @intCast(self.sections.slice().len));677 self.debug_str_section_index = @as(u16, @intCast(self.shdrs.items.len));
758 assert(dw.strtab.buffer.items.len == 0);678 assert(dw.strtab.buffer.items.len == 0);
759 try dw.strtab.buffer.append(gpa, 0);679 try dw.strtab.buffer.append(gpa, 0);
760 try self.sections.append(gpa, .{680 try self.shdrs.append(gpa, .{
761 .shdr = .{681 .sh_name = try self.shstrtab.insert(gpa, ".debug_str"),
762 .sh_name = try self.shstrtab.insert(gpa, ".debug_str"),682 .sh_type = elf.SHT_PROGBITS,
763 .sh_type = elf.SHT_PROGBITS,683 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
764 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,684 .sh_addr = 0,
765 .sh_addr = 0,685 .sh_offset = 0,
766 .sh_offset = 0,686 .sh_size = 0,
767 .sh_size = 0,687 .sh_link = 0,
768 .sh_link = 0,688 .sh_info = 0,
769 .sh_info = 0,689 .sh_addralign = 1,
770 .sh_addralign = 1,690 .sh_entsize = 1,
771 .sh_entsize = 1,
772 },
773 .phdr_index = undefined,
774 });691 });
775 self.debug_strtab_dirty = true;692 self.debug_strtab_dirty = true;
776 self.shdr_table_dirty = true;693 self.shdr_table_dirty = true;
777 }694 }
778695
779 if (self.debug_info_section_index == null) {696 if (self.debug_info_section_index == null) {
780 self.debug_info_section_index = @as(u16, @intCast(self.sections.slice().len));697 self.debug_info_section_index = @as(u16, @intCast(self.shdrs.items.len));
781
782 const file_size_hint = 200;698 const file_size_hint = 200;
783 const p_align = 1;699 const p_align = 1;
784 const off = self.findFreeSpace(file_size_hint, p_align);700 const off = self.findFreeSpace(file_size_hint, p_align);
...@@ -786,28 +702,24 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -786,28 +702,24 @@ pub fn populateMissingMetadata(self: *Elf) !void {
786 off,702 off,
787 off + file_size_hint,703 off + file_size_hint,
788 });704 });
789 try self.sections.append(gpa, .{705 try self.shdrs.append(gpa, .{
790 .shdr = .{706 .sh_name = try self.shstrtab.insert(gpa, ".debug_info"),
791 .sh_name = try self.shstrtab.insert(gpa, ".debug_info"),707 .sh_type = elf.SHT_PROGBITS,
792 .sh_type = elf.SHT_PROGBITS,708 .sh_flags = 0,
793 .sh_flags = 0,709 .sh_addr = 0,
794 .sh_addr = 0,710 .sh_offset = off,
795 .sh_offset = off,711 .sh_size = file_size_hint,
796 .sh_size = file_size_hint,712 .sh_link = 0,
797 .sh_link = 0,713 .sh_info = 0,
798 .sh_info = 0,714 .sh_addralign = p_align,
799 .sh_addralign = p_align,715 .sh_entsize = 0,
800 .sh_entsize = 0,
801 },
802 .phdr_index = undefined,
803 });716 });
804 self.shdr_table_dirty = true;717 self.shdr_table_dirty = true;
805 self.debug_info_header_dirty = true;718 self.debug_info_header_dirty = true;
806 }719 }
807720
808 if (self.debug_abbrev_section_index == null) {721 if (self.debug_abbrev_section_index == null) {
809 self.debug_abbrev_section_index = @as(u16, @intCast(self.sections.slice().len));722 self.debug_abbrev_section_index = @as(u16, @intCast(self.shdrs.items.len));
810
811 const file_size_hint = 128;723 const file_size_hint = 128;
812 const p_align = 1;724 const p_align = 1;
813 const off = self.findFreeSpace(file_size_hint, p_align);725 const off = self.findFreeSpace(file_size_hint, p_align);
...@@ -815,28 +727,24 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -815,28 +727,24 @@ pub fn populateMissingMetadata(self: *Elf) !void {
815 off,727 off,
816 off + file_size_hint,728 off + file_size_hint,
817 });729 });
818 try self.sections.append(gpa, .{730 try self.shdrs.append(gpa, .{
819 .shdr = .{731 .sh_name = try self.shstrtab.insert(gpa, ".debug_abbrev"),
820 .sh_name = try self.shstrtab.insert(gpa, ".debug_abbrev"),732 .sh_type = elf.SHT_PROGBITS,
821 .sh_type = elf.SHT_PROGBITS,733 .sh_flags = 0,
822 .sh_flags = 0,734 .sh_addr = 0,
823 .sh_addr = 0,735 .sh_offset = off,
824 .sh_offset = off,736 .sh_size = file_size_hint,
825 .sh_size = file_size_hint,737 .sh_link = 0,
826 .sh_link = 0,738 .sh_info = 0,
827 .sh_info = 0,739 .sh_addralign = p_align,
828 .sh_addralign = p_align,740 .sh_entsize = 0,
829 .sh_entsize = 0,
830 },
831 .phdr_index = undefined,
832 });741 });
833 self.shdr_table_dirty = true;742 self.shdr_table_dirty = true;
834 self.debug_abbrev_section_dirty = true;743 self.debug_abbrev_section_dirty = true;
835 }744 }
836745
837 if (self.debug_aranges_section_index == null) {746 if (self.debug_aranges_section_index == null) {
838 self.debug_aranges_section_index = @as(u16, @intCast(self.sections.slice().len));747 self.debug_aranges_section_index = @as(u16, @intCast(self.shdrs.items.len));
839
840 const file_size_hint = 160;748 const file_size_hint = 160;
841 const p_align = 16;749 const p_align = 16;
842 const off = self.findFreeSpace(file_size_hint, p_align);750 const off = self.findFreeSpace(file_size_hint, p_align);
...@@ -844,28 +752,24 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -844,28 +752,24 @@ pub fn populateMissingMetadata(self: *Elf) !void {
844 off,752 off,
845 off + file_size_hint,753 off + file_size_hint,
846 });754 });
847 try self.sections.append(gpa, .{755 try self.shdrs.append(gpa, .{
848 .shdr = .{756 .sh_name = try self.shstrtab.insert(gpa, ".debug_aranges"),
849 .sh_name = try self.shstrtab.insert(gpa, ".debug_aranges"),757 .sh_type = elf.SHT_PROGBITS,
850 .sh_type = elf.SHT_PROGBITS,758 .sh_flags = 0,
851 .sh_flags = 0,759 .sh_addr = 0,
852 .sh_addr = 0,760 .sh_offset = off,
853 .sh_offset = off,761 .sh_size = file_size_hint,
854 .sh_size = file_size_hint,762 .sh_link = 0,
855 .sh_link = 0,763 .sh_info = 0,
856 .sh_info = 0,764 .sh_addralign = p_align,
857 .sh_addralign = p_align,765 .sh_entsize = 0,
858 .sh_entsize = 0,
859 },
860 .phdr_index = undefined,
861 });766 });
862 self.shdr_table_dirty = true;767 self.shdr_table_dirty = true;
863 self.debug_aranges_section_dirty = true;768 self.debug_aranges_section_dirty = true;
864 }769 }
865770
866 if (self.debug_line_section_index == null) {771 if (self.debug_line_section_index == null) {
867 self.debug_line_section_index = @as(u16, @intCast(self.sections.slice().len));772 self.debug_line_section_index = @as(u16, @intCast(self.shdrs.items.len));
868
869 const file_size_hint = 250;773 const file_size_hint = 250;
870 const p_align = 1;774 const p_align = 1;
871 const off = self.findFreeSpace(file_size_hint, p_align);775 const off = self.findFreeSpace(file_size_hint, p_align);
...@@ -873,20 +777,17 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -873,20 +777,17 @@ pub fn populateMissingMetadata(self: *Elf) !void {
873 off,777 off,
874 off + file_size_hint,778 off + file_size_hint,
875 });779 });
876 try self.sections.append(gpa, .{780 try self.shdrs.append(gpa, .{
877 .shdr = .{781 .sh_name = try self.shstrtab.insert(gpa, ".debug_line"),
878 .sh_name = try self.shstrtab.insert(gpa, ".debug_line"),782 .sh_type = elf.SHT_PROGBITS,
879 .sh_type = elf.SHT_PROGBITS,783 .sh_flags = 0,
880 .sh_flags = 0,784 .sh_addr = 0,
881 .sh_addr = 0,785 .sh_offset = off,
882 .sh_offset = off,786 .sh_size = file_size_hint,
883 .sh_size = file_size_hint,787 .sh_link = 0,
884 .sh_link = 0,788 .sh_info = 0,
885 .sh_info = 0,789 .sh_addralign = p_align,
886 .sh_addralign = p_align,790 .sh_entsize = 0,
887 .sh_entsize = 0,
888 },
889 .phdr_index = undefined,
890 });791 });
891 self.shdr_table_dirty = true;792 self.shdr_table_dirty = true;
892 self.debug_line_header_dirty = true;793 self.debug_line_header_dirty = true;
...@@ -902,13 +803,13 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -902,13 +803,13 @@ pub fn populateMissingMetadata(self: *Elf) !void {
902 .p64 => @alignOf(elf.Elf64_Shdr),803 .p64 => @alignOf(elf.Elf64_Shdr),
903 };804 };
904 if (self.shdr_table_offset == null) {805 if (self.shdr_table_offset == null) {
905 self.shdr_table_offset = self.findFreeSpace(self.sections.slice().len * shsize, shalign);806 self.shdr_table_offset = self.findFreeSpace(self.shdrs.items.len * shsize, shalign);
906 self.shdr_table_dirty = true;807 self.shdr_table_dirty = true;
907 }808 }
908809
909 {810 {
910 // Iterate over symbols, populating free_list and last_text_block.811 // Iterate over symbols, populating free_list and last_text_block.
911 if (self.local_symbols.items.len != 1) {812 if (self.symbols.items.len != 1) {
912 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");813 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");
913 }814 }
914 // We are starting with an empty file. The default values are correct, null and empty list.815 // We are starting with an empty file. The default values are correct, null and empty list.
...@@ -920,7 +821,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -920,7 +821,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
920 // offset + it's filesize.821 // offset + it's filesize.
921 var max_file_offset: u64 = 0;822 var max_file_offset: u64 = 0;
922823
923 for (self.sections.items(.shdr)) |shdr| {824 for (self.shdrs.items) |shdr| {
924 if (shdr.sh_offset + shdr.sh_size > max_file_offset) {825 if (shdr.sh_offset + shdr.sh_size > max_file_offset) {
925 max_file_offset = shdr.sh_offset + shdr.sh_size;826 max_file_offset = shdr.sh_offset + shdr.sh_size;
926 }827 }
...@@ -928,25 +829,47 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -928,25 +829,47 @@ pub fn populateMissingMetadata(self: *Elf) !void {
928829
929 try self.base.file.?.pwriteAll(&[_]u8{0}, max_file_offset);830 try self.base.file.?.pwriteAll(&[_]u8{0}, max_file_offset);
930 }831 }
832
833 if (self.base.options.module) |module| {
834 if (self.zig_module_index == null) {
835 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
836 self.files.set(index, .{ .zig_module = .{
837 .index = index,
838 .path = module.main_pkg.root_src_path,
839 } });
840 self.zig_module_index = index;
841 const zig_module = self.file(index).?.zig_module;
842
843 const name_off = try self.strtab.insert(gpa, std.fs.path.stem(module.main_pkg.root_src_path));
844 const symbol_index = try self.addSymbol();
845 try zig_module.local_symbols.append(gpa, symbol_index);
846 const symbol_ptr = self.symbol(symbol_index);
847 symbol_ptr.file_index = zig_module.index;
848 symbol_ptr.name_offset = name_off;
849
850 const esym_index = try zig_module.addLocalEsym(gpa);
851 const esym = &zig_module.local_esyms.items[esym_index];
852 esym.st_name = name_off;
853 esym.st_info |= elf.STT_FILE;
854 esym.st_shndx = elf.SHN_ABS;
855 symbol_ptr.esym_index = esym_index;
856 }
857 }
931}858}
932859
933fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {860pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {
934 // TODO Also detect virtual address collisions.861 // TODO Also detect virtual address collisions.
935 const shdr = &self.sections.items(.shdr)[shdr_index];862 const shdr = &self.shdrs.items[shdr_index];
936 const phdr_index = self.sections.items(.phdr_index)[shdr_index];863 const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?;
937 const phdr = &self.program_headers.items[phdr_index];864 const phdr = &self.phdrs.items[phdr_index];
938 const maybe_last_atom_index = self.sections.items(.last_atom_index)[shdr_index];
939865
940 if (needed_size > self.allocatedSize(shdr.sh_offset)) {866 if (needed_size > self.allocatedSize(shdr.sh_offset)) {
941 // Must move the entire section.867 // Must move the entire section.
942 const new_offset = self.findFreeSpace(needed_size, self.page_size);868 const new_offset = self.findFreeSpace(needed_size, self.page_size);
943 const existing_size = if (maybe_last_atom_index) |last_atom_index| blk: {869 const existing_size = if (self.last_atom_and_free_list_table.get(shdr_index)) |meta| blk: {
944 const last = self.getAtom(last_atom_index);870 const last = self.atom(meta.last_atom_index) orelse break :blk 0;
945 const sym = last.getSymbol(self);871 break :blk (last.value + last.size) - phdr.p_vaddr;
946 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;872 } else shdr.sh_size;
947 } else if (shdr_index == self.got_section_index.?) blk: {
948 break :blk shdr.sh_size;
949 } else 0;
950 shdr.sh_size = 0;873 shdr.sh_size = 0;
951874
952 log.debug("new '{?s}' file offset 0x{x} to 0x{x}", .{875 log.debug("new '{?s}' file offset 0x{x} to 0x{x}", .{
...@@ -976,7 +899,7 @@ pub fn growNonAllocSection(...@@ -976,7 +899,7 @@ pub fn growNonAllocSection(
976 min_alignment: u32,899 min_alignment: u32,
977 requires_file_copy: bool,900 requires_file_copy: bool,
978) !void {901) !void {
979 const shdr = &self.sections.items(.shdr)[shdr_index];902 const shdr = &self.shdrs.items[shdr_index];
980903
981 if (needed_size > self.allocatedSize(shdr.sh_offset)) {904 if (needed_size > self.allocatedSize(shdr.sh_offset)) {
982 const existing_size = if (self.symtab_section_index.? == shdr_index) blk: {905 const existing_size = if (self.symtab_section_index.? == shdr_index) blk: {
...@@ -989,7 +912,12 @@ pub fn growNonAllocSection(...@@ -989,7 +912,12 @@ pub fn growNonAllocSection(
989 shdr.sh_size = 0;912 shdr.sh_size = 0;
990 // Move all the symbols to a new file location.913 // Move all the symbols to a new file location.
991 const new_offset = self.findFreeSpace(needed_size, min_alignment);914 const new_offset = self.findFreeSpace(needed_size, min_alignment);
992 log.debug("moving '{?s}' from 0x{x} to 0x{x}", .{ self.shstrtab.get(shdr.sh_name), shdr.sh_offset, new_offset });915
916 log.debug("moving '{?s}' from 0x{x} to 0x{x}", .{
917 self.shstrtab.get(shdr.sh_name),
918 shdr.sh_offset,
919 new_offset,
920 });
993921
994 if (requires_file_copy) {922 if (requires_file_copy) {
995 const amt = try self.base.file.?.copyRangeAll(923 const amt = try self.base.file.?.copyRangeAll(
...@@ -1065,21 +993,50 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1065,21 +993,50 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1065 // corresponds to the Zig source code.993 // corresponds to the Zig source code.
1066 const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;994 const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
1067995
996 const compiler_rt_path: ?[]const u8 = blk: {
997 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
998 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
999 break :blk null;
1000 };
1001 _ = compiler_rt_path;
1002
1003 // Here we will parse input positional and library files (if referenced).
1004 // This will roughly match in any linker backend we support.
1005 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
1006 defer positionals.deinit();
1007 try positionals.ensureUnusedCapacity(self.base.options.objects.len);
1008 positionals.appendSliceAssumeCapacity(self.base.options.objects);
1009
1010 // This is a set of object files emitted by clang in a single `build-exe` invocation.
1011 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
1012 // in this set.
1013 for (comp.c_object_table.keys()) |key| {
1014 try positionals.append(.{ .path = key.status.success.object_path });
1015 }
1016
1017 for (positionals.items) |obj| {
1018 const in_file = try std.fs.cwd().openFile(obj.path, .{});
1019 defer in_file.close();
1020
1021 var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
1022 self.parsePositional(in_file, obj.path, obj.must_link, &parse_ctx) catch |err|
1023 try self.handleAndReportParseError(obj.path, err, &parse_ctx);
1024 }
1025
1026 // Handle any lazy symbols that were emitted by incremental compilation.
1068 if (self.lazy_syms.getPtr(.none)) |metadata| {1027 if (self.lazy_syms.getPtr(.none)) |metadata| {
1069 // Most lazy symbols can be updated on first use, but1028 // Most lazy symbols can be updated on first use, but
1070 // anyerror needs to wait for everything to be flushed.1029 // anyerror needs to wait for everything to be flushed.
1071 if (metadata.text_state != .unused) self.updateLazySymbolAtom(1030 if (metadata.text_state != .unused) self.updateLazySymbol(
1072 File.LazySymbol.initDecl(.code, null, module),1031 link.File.LazySymbol.initDecl(.code, null, module),
1073 metadata.text_atom,1032 metadata.text_symbol_index,
1074 self.text_section_index.?,
1075 ) catch |err| return switch (err) {1033 ) catch |err| return switch (err) {
1076 error.CodegenFail => error.FlushFailure,1034 error.CodegenFail => error.FlushFailure,
1077 else => |e| e,1035 else => |e| e,
1078 };1036 };
1079 if (metadata.rodata_state != .unused) self.updateLazySymbolAtom(1037 if (metadata.rodata_state != .unused) self.updateLazySymbol(
1080 File.LazySymbol.initDecl(.const_data, null, module),1038 link.File.LazySymbol.initDecl(.const_data, null, module),
1081 metadata.rodata_atom,1039 metadata.rodata_symbol_index,
1082 self.rodata_section_index.?,
1083 ) catch |err| return switch (err) {1040 ) catch |err| return switch (err) {
1084 error.CodegenFail => error.FlushFailure,1041 error.CodegenFail => error.FlushFailure,
1085 else => |e| e,1042 else => |e| e,
...@@ -1097,54 +1054,67 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1097,54 +1054,67 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1097 try dw.flushModule(module);1054 try dw.flushModule(module);
1098 }1055 }
10991056
1100 {1057 // If we haven't already, create a linker-generated input file comprising of
1101 var it = self.relocs.iterator();1058 // linker-defined synthetic symbols only such as `_DYNAMIC`, etc.
1102 while (it.next()) |entry| {1059 if (self.linker_defined_index == null) {
1103 const atom_index = entry.key_ptr.*;1060 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
1104 const relocs = entry.value_ptr.*;1061 self.files.set(index, .{ .linker_defined = .{ .index = index } });
1105 const atom = self.getAtom(atom_index);1062 self.linker_defined_index = index;
1106 const source_sym = atom.getSymbol(self);1063 }
1107 const source_shdr = self.sections.items(.shdr)[source_sym.st_shndx];1064 try self.addLinkerDefinedSymbols();
11081065
1109 log.debug("relocating '{?s}'", .{self.strtab.get(source_sym.st_name)});1066 // Now, we are ready to resolve the symbols across all input files.
11101067 // We will first resolve the files in the ZigModule, next in the parsed
1111 for (relocs.items) |*reloc| {1068 // input Object files.
1112 const target_sym = self.local_symbols.items[reloc.target];1069 // Any qualifing unresolved symbol will be upgraded to an absolute, weak
1113 const target_vaddr = target_sym.st_value + reloc.addend;1070 // symbol for potential resolution at load-time.
11141071 self.resolveSymbols();
1115 if (target_vaddr == reloc.prev_vaddr) continue;1072 self.markImportsExports();
11161073 self.claimUnresolved();
1117 const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr;1074
1118 const file_offset = source_shdr.sh_offset + section_offset;1075 // Scan and create missing synthetic entries such as GOT indirection.
11191076 try self.scanRelocs();
1120 log.debug(" ({x}: [() => 0x{x}] ({?s}))", .{1077
1121 reloc.offset,1078 // Allocate atoms parsed from input object files, followed by allocating
1122 target_vaddr,1079 // linker-defined synthetic symbols.
1123 self.strtab.get(target_sym.st_name),1080 try self.allocateObjects();
1124 });1081 self.allocateLinkerDefinedSymbols();
11251082
1126 switch (self.ptr_width) {1083 // Beyond this point, everything has been allocated a virtual address and we can resolve
1127 .p32 => try self.base.file.?.pwriteAll(mem.asBytes(&@as(u32, @intCast(target_vaddr))), file_offset),1084 // the relocations, and commit objects to file.
1128 .p64 => try self.base.file.?.pwriteAll(mem.asBytes(&target_vaddr), file_offset),1085 if (self.zig_module_index) |index| {
1129 }1086 for (self.file(index).?.zig_module.atoms.keys()) |atom_index| {
11301087 const atom_ptr = self.atom(atom_index).?;
1131 reloc.prev_vaddr = target_vaddr;1088 if (!atom_ptr.alive) continue;
1132 }1089 const shdr = &self.shdrs.items[atom_ptr.output_section_index];
1133 }1090 const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;
1134 }1091 const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;
11351092 const code = try gpa.alloc(u8, size);
1136 try self.writeSymbols();1093 defer gpa.free(code);
11371094 const amt = try self.base.file.?.preadAll(code, file_offset);
1138 if (build_options.enable_logging) {1095 if (amt != code.len) return error.InputOutput;
1139 self.logSymtab();1096 try atom_ptr.resolveRelocs(self, code);
1140 }1097 try self.base.file.?.pwriteAll(code, file_offset);
1098 }
1099 }
1100 try self.writeObjects();
1101
1102 // Generate and emit the symbol table.
1103 try self.updateSymtabSize();
1104 try self.writeSymtab();
1105
1106 // Dump the state for easy debugging.
1107 // State can be dumped via `--debug-log link_state`.
1108 if (build_options.enable_logging) {
1109 state_log.debug("{}", .{self.dumpState()});
1110 }
11411111
1142 if (self.dwarf) |*dw| {1112 if (self.dwarf) |*dw| {
1143 if (self.debug_abbrev_section_dirty) {1113 if (self.debug_abbrev_section_dirty) {
1144 try dw.writeDbgAbbrev();1114 try dw.writeDbgAbbrev();
1145 if (!self.shdr_table_dirty) {1115 if (!self.shdr_table_dirty) {
1146 // Then it won't get written with the others and we need to do it.1116 // Then it won't get written with the others and we need to do it.
1147 try self.writeSectHeader(self.debug_abbrev_section_index.?);1117 try self.writeShdr(self.debug_abbrev_section_index.?);
1148 }1118 }
1149 self.debug_abbrev_section_dirty = false;1119 self.debug_abbrev_section_dirty = false;
1150 }1120 }
...@@ -1152,7 +1122,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1152,7 +1122,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1152 if (self.debug_info_header_dirty) {1122 if (self.debug_info_header_dirty) {
1153 // Currently only one compilation unit is supported, so the address range is simply1123 // Currently only one compilation unit is supported, so the address range is simply
1154 // identical to the main program header virtual address and memory size.1124 // identical to the main program header virtual address and memory size.
1155 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];1125 const text_phdr = &self.phdrs.items[self.phdr_load_re_index.?];
1156 const low_pc = text_phdr.p_vaddr;1126 const low_pc = text_phdr.p_vaddr;
1157 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;1127 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
1158 try dw.writeDbgInfoHeader(module, low_pc, high_pc);1128 try dw.writeDbgInfoHeader(module, low_pc, high_pc);
...@@ -1162,11 +1132,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1162,11 +1132,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1162 if (self.debug_aranges_section_dirty) {1132 if (self.debug_aranges_section_dirty) {
1163 // Currently only one compilation unit is supported, so the address range is simply1133 // Currently only one compilation unit is supported, so the address range is simply
1164 // identical to the main program header virtual address and memory size.1134 // identical to the main program header virtual address and memory size.
1165 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];1135 const text_phdr = &self.phdrs.items[self.phdr_load_re_index.?];
1166 try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz);1136 try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz);
1167 if (!self.shdr_table_dirty) {1137 if (!self.shdr_table_dirty) {
1168 // Then it won't get written with the others and we need to do it.1138 // Then it won't get written with the others and we need to do it.
1169 try self.writeSectHeader(self.debug_aranges_section_index.?);1139 try self.writeShdr(self.debug_aranges_section_index.?);
1170 }1140 }
1171 self.debug_aranges_section_dirty = false;1141 self.debug_aranges_section_dirty = false;
1172 }1142 }
...@@ -1184,11 +1154,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1184,11 +1154,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1184 };1154 };
11851155
1186 const phdr_table_index = self.phdr_table_index.?;1156 const phdr_table_index = self.phdr_table_index.?;
1187 const phdr_table = &self.program_headers.items[phdr_table_index];1157 const phdr_table = &self.phdrs.items[phdr_table_index];
1188 const phdr_table_load = &self.program_headers.items[self.phdr_table_load_index.?];1158 const phdr_table_load = &self.phdrs.items[self.phdr_table_load_index.?];
11891159
1190 const allocated_size = self.allocatedSize(phdr_table.p_offset);1160 const allocated_size = self.allocatedSize(phdr_table.p_offset);
1191 const needed_size = self.program_headers.items.len * phsize;1161 const needed_size = self.phdrs.items.len * phsize;
11921162
1193 if (needed_size > allocated_size) {1163 if (needed_size > allocated_size) {
1194 phdr_table.p_offset = 0; // free the space1164 phdr_table.p_offset = 0; // free the space
...@@ -1207,11 +1177,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1207,11 +1177,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
12071177
1208 switch (self.ptr_width) {1178 switch (self.ptr_width) {
1209 .p32 => {1179 .p32 => {
1210 const buf = try gpa.alloc(elf.Elf32_Phdr, self.program_headers.items.len);1180 const buf = try gpa.alloc(elf.Elf32_Phdr, self.phdrs.items.len);
1211 defer gpa.free(buf);1181 defer gpa.free(buf);
12121182
1213 for (buf, 0..) |*phdr, i| {1183 for (buf, 0..) |*phdr, i| {
1214 phdr.* = progHeaderTo32(self.program_headers.items[i]);1184 phdr.* = phdrTo32(self.phdrs.items[i]);
1215 if (foreign_endian) {1185 if (foreign_endian) {
1216 mem.byteSwapAllFields(elf.Elf32_Phdr, phdr);1186 mem.byteSwapAllFields(elf.Elf32_Phdr, phdr);
1217 }1187 }
...@@ -1219,11 +1189,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1219,11 +1189,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1219 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);1189 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
1220 },1190 },
1221 .p64 => {1191 .p64 => {
1222 const buf = try gpa.alloc(elf.Elf64_Phdr, self.program_headers.items.len);1192 const buf = try gpa.alloc(elf.Elf64_Phdr, self.phdrs.items.len);
1223 defer gpa.free(buf);1193 defer gpa.free(buf);
12241194
1225 for (buf, 0..) |*phdr, i| {1195 for (buf, 0..) |*phdr, i| {
1226 phdr.* = self.program_headers.items[i];1196 phdr.* = self.phdrs.items[i];
1227 if (foreign_endian) {1197 if (foreign_endian) {
1228 mem.byteSwapAllFields(elf.Elf64_Phdr, phdr);1198 mem.byteSwapAllFields(elf.Elf64_Phdr, phdr);
1229 }1199 }
...@@ -1241,9 +1211,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1241,9 +1211,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
12411211
1242 {1212 {
1243 const shdr_index = self.shstrtab_section_index.?;1213 const shdr_index = self.shstrtab_section_index.?;
1244 if (self.shstrtab_dirty or self.shstrtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) {1214 if (self.shstrtab_dirty or self.shstrtab.buffer.items.len != self.shdrs.items[shdr_index].sh_size) {
1245 try self.growNonAllocSection(shdr_index, self.shstrtab.buffer.items.len, 1, false);1215 try self.growNonAllocSection(shdr_index, self.shstrtab.buffer.items.len, 1, false);
1246 const shstrtab_sect = self.sections.items(.shdr)[shdr_index];1216 const shstrtab_sect = &self.shdrs.items[shdr_index];
1247 try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shstrtab_sect.sh_offset);1217 try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shstrtab_sect.sh_offset);
1248 self.shstrtab_dirty = false;1218 self.shstrtab_dirty = false;
1249 }1219 }
...@@ -1251,9 +1221,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1251,9 +1221,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
12511221
1252 {1222 {
1253 const shdr_index = self.strtab_section_index.?;1223 const shdr_index = self.strtab_section_index.?;
1254 if (self.strtab_dirty or self.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) {1224 if (self.strtab_dirty or self.strtab.buffer.items.len != self.shdrs.items[shdr_index].sh_size) {
1255 try self.growNonAllocSection(shdr_index, self.strtab.buffer.items.len, 1, false);1225 try self.growNonAllocSection(shdr_index, self.strtab.buffer.items.len, 1, false);
1256 const strtab_sect = self.sections.items(.shdr)[shdr_index];1226 const strtab_sect = self.shdrs.items[shdr_index];
1257 try self.base.file.?.pwriteAll(self.strtab.buffer.items, strtab_sect.sh_offset);1227 try self.base.file.?.pwriteAll(self.strtab.buffer.items, strtab_sect.sh_offset);
1258 self.strtab_dirty = false;1228 self.strtab_dirty = false;
1259 }1229 }
...@@ -1261,9 +1231,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1261,9 +1231,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
12611231
1262 if (self.dwarf) |dwarf| {1232 if (self.dwarf) |dwarf| {
1263 const shdr_index = self.debug_str_section_index.?;1233 const shdr_index = self.debug_str_section_index.?;
1264 if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) {1234 if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.shdrs.items[shdr_index].sh_size) {
1265 try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false);1235 try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false);
1266 const debug_strtab_sect = self.sections.items(.shdr)[shdr_index];1236 const debug_strtab_sect = self.shdrs.items[shdr_index];
1267 try self.base.file.?.pwriteAll(dwarf.strtab.buffer.items, debug_strtab_sect.sh_offset);1237 try self.base.file.?.pwriteAll(dwarf.strtab.buffer.items, debug_strtab_sect.sh_offset);
1268 self.debug_strtab_dirty = false;1238 self.debug_strtab_dirty = false;
1269 }1239 }
...@@ -1279,7 +1249,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1279,7 +1249,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1279 .p64 => @alignOf(elf.Elf64_Shdr),1249 .p64 => @alignOf(elf.Elf64_Shdr),
1280 };1250 };
1281 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);1251 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
1282 const needed_size = self.sections.slice().len * shsize;1252 const needed_size = self.shdrs.items.len * shsize;
12831253
1284 if (needed_size > allocated_size) {1254 if (needed_size > allocated_size) {
1285 self.shdr_table_offset = null; // free the space1255 self.shdr_table_offset = null; // free the space
...@@ -1288,12 +1258,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1288,12 +1258,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
12881258
1289 switch (self.ptr_width) {1259 switch (self.ptr_width) {
1290 .p32 => {1260 .p32 => {
1291 const slice = self.sections.slice();1261 const buf = try gpa.alloc(elf.Elf32_Shdr, self.shdrs.items.len);
1292 const buf = try gpa.alloc(elf.Elf32_Shdr, slice.len);
1293 defer gpa.free(buf);1262 defer gpa.free(buf);
12941263
1295 for (buf, 0..) |*shdr, i| {1264 for (buf, 0..) |*shdr, i| {
1296 shdr.* = sectHeaderTo32(slice.items(.shdr)[i]);1265 shdr.* = shdrTo32(self.shdrs.items[i]);
1297 log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* });1266 log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* });
1298 if (foreign_endian) {1267 if (foreign_endian) {
1299 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);1268 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);
...@@ -1302,12 +1271,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1302,12 +1271,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1302 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);1271 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
1303 },1272 },
1304 .p64 => {1273 .p64 => {
1305 const slice = self.sections.slice();1274 const buf = try gpa.alloc(elf.Elf64_Shdr, self.shdrs.items.len);
1306 const buf = try gpa.alloc(elf.Elf64_Shdr, slice.len);
1307 defer gpa.free(buf);1275 defer gpa.free(buf);
13081276
1309 for (buf, 0..) |*shdr, i| {1277 for (buf, 0..) |*shdr, i| {
1310 shdr.* = slice.items(.shdr)[i];1278 shdr.* = self.shdrs.items[i];
1311 log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* });1279 log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* });
1312 if (foreign_endian) {1280 if (foreign_endian) {
1313 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);1281 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);
...@@ -1338,7 +1306,194 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1338,7 +1306,194 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1338 assert(!self.shstrtab_dirty);1306 assert(!self.shstrtab_dirty);
1339 assert(!self.strtab_dirty);1307 assert(!self.strtab_dirty);
1340 assert(!self.debug_strtab_dirty);1308 assert(!self.debug_strtab_dirty);
1341 assert(!self.got_table_count_dirty);1309 assert(!self.got.dirty);
1310}
1311
1312const ParseError = error{
1313 UnknownFileType,
1314 InvalidCpuArch,
1315 OutOfMemory,
1316 Overflow,
1317 InputOutput,
1318 EndOfStream,
1319 FileSystem,
1320 NotSupported,
1321} || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError;
1322
1323fn parsePositional(
1324 self: *Elf,
1325 in_file: std.fs.File,
1326 path: []const u8,
1327 must_link: bool,
1328 ctx: *ParseErrorCtx,
1329) ParseError!void {
1330 const tracy = trace(@src());
1331 defer tracy.end();
1332 _ = must_link;
1333
1334 if (Object.isObject(in_file)) {
1335 try self.parseObject(in_file, path, ctx);
1336 } else return error.UnknownFileType;
1337}
1338
1339fn parseObject(self: *Elf, in_file: std.fs.File, path: []const u8, ctx: *ParseErrorCtx) ParseError!void {
1340 const tracy = trace(@src());
1341 defer tracy.end();
1342
1343 const gpa = self.base.allocator;
1344 const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
1345 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
1346 self.files.set(index, .{ .object = .{
1347 .path = path,
1348 .data = data,
1349 .index = index,
1350 } });
1351 try self.objects.append(gpa, index);
1352
1353 const object = self.file(index).?.object;
1354 try object.parse(self);
1355
1356 ctx.detected_cpu_arch = object.header.?.e_machine.toTargetCpuArch().?;
1357 if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch;
1358}
1359
1360fn resolveSymbols(self: *Elf) void {
1361 if (self.zig_module_index) |index| {
1362 const zig_module = self.file(index).?.zig_module;
1363 zig_module.resolveSymbols(self);
1364 }
1365
1366 for (self.objects.items) |index| {
1367 const object = self.file(index).?.object;
1368 object.resolveSymbols(self);
1369 }
1370}
1371
1372fn markImportsExports(self: *Elf) void {
1373 const mark = struct {
1374 fn mark(elf_file: *Elf, file_index: File.Index) void {
1375 for (elf_file.file(file_index).?.globals()) |global_index| {
1376 const global = elf_file.symbol(global_index);
1377 if (global.version_index == elf.VER_NDX_LOCAL) continue;
1378 const file_ptr = global.file(elf_file) orelse continue;
1379 const vis = @as(elf.STV, @enumFromInt(global.elfSym(elf_file).st_other));
1380 if (vis == .HIDDEN) continue;
1381 // if (file == .shared and !global.isAbs(self)) {
1382 // global.flags.import = true;
1383 // continue;
1384 // }
1385 if (file_ptr.index() == file_index) {
1386 global.flags.@"export" = true;
1387 if (elf_file.isDynLib() and vis != .PROTECTED) {
1388 global.flags.import = true;
1389 }
1390 }
1391 }
1392 }
1393 }.mark;
1394
1395 if (self.zig_module_index) |index| {
1396 mark(self, index);
1397 }
1398
1399 for (self.objects.items) |index| {
1400 mark(self, index);
1401 }
1402}
1403
1404fn claimUnresolved(self: *Elf) void {
1405 if (self.zig_module_index) |index| {
1406 const zig_module = self.file(index).?.zig_module;
1407 zig_module.claimUnresolved(self);
1408 }
1409 for (self.objects.items) |index| {
1410 const object = self.file(index).?.object;
1411 object.claimUnresolved(self);
1412 }
1413}
1414
1415/// In scanRelocs we will go over all live atoms and scan their relocs.
1416/// This will help us work out what synthetics to emit, GOT indirection, etc.
1417/// This is also the point where we will report undefined symbols for any
1418/// alloc sections.
1419fn scanRelocs(self: *Elf) !void {
1420 const gpa = self.base.allocator;
1421
1422 var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Atom.Index)).init(gpa);
1423 defer {
1424 var it = undefs.iterator();
1425 while (it.next()) |entry| {
1426 entry.value_ptr.deinit();
1427 }
1428 undefs.deinit();
1429 }
1430
1431 if (self.zig_module_index) |index| {
1432 const zig_module = self.file(index).?.zig_module;
1433 try zig_module.scanRelocs(self, &undefs);
1434 }
1435 for (self.objects.items) |index| {
1436 const object = self.file(index).?.object;
1437 try object.scanRelocs(self, &undefs);
1438 }
1439
1440 try self.reportUndefined(&undefs);
1441
1442 for (self.symbols.items) |*sym| {
1443 if (sym.flags.needs_got) {
1444 log.debug("'{s}' needs GOT", .{sym.name(self)});
1445 // TODO how can we tell we need to write it again, aka the entry is dirty?
1446 const gop = try sym.getOrCreateGotEntry(self);
1447 try self.got.writeEntry(self, gop.index);
1448 }
1449 }
1450}
1451
1452fn allocateObjects(self: *Elf) !void {
1453 for (self.objects.items) |index| {
1454 const object = self.file(index).?.object;
1455 for (object.atoms.items) |atom_index| {
1456 const atom_ptr = self.atom(atom_index) orelse continue;
1457 if (!atom_ptr.alive) continue;
1458 try atom_ptr.allocate(self);
1459 }
1460
1461 for (object.locals()) |local_index| {
1462 const local = self.symbol(local_index);
1463 const atom_ptr = local.atom(self) orelse continue;
1464 if (!atom_ptr.alive) continue;
1465 local.value = atom_ptr.value;
1466 }
1467
1468 for (object.globals()) |global_index| {
1469 const global = self.symbol(global_index);
1470 const atom_ptr = global.atom(self) orelse continue;
1471 if (!atom_ptr.alive) continue;
1472 if (global.file_index == index) {
1473 global.value = atom_ptr.value;
1474 }
1475 }
1476 }
1477}
1478
1479fn writeObjects(self: *Elf) !void {
1480 const gpa = self.base.allocator;
1481
1482 for (self.objects.items) |index| {
1483 const object = self.file(index).?.object;
1484 for (object.atoms.items) |atom_index| {
1485 const atom_ptr = self.atom(atom_index) orelse continue;
1486 if (!atom_ptr.alive) continue;
1487
1488 const shdr = &self.shdrs.items[atom_ptr.output_section_index];
1489 const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;
1490 const code = try atom_ptr.codeInObjectUncompressAlloc(self);
1491 defer gpa.free(code);
1492
1493 try atom_ptr.resolveRelocs(self, code);
1494 try self.base.file.?.pwriteAll(code, file_offset);
1495 }
1496 }
1342}1497}
13431498
1344fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {1499fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {
...@@ -1563,9 +1718,9 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -1563,9 +1718,9 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
1563 try argv.append(entry);1718 try argv.append(entry);
1564 }1719 }
15651720
1566 for (self.base.options.force_undefined_symbols.keys()) |symbol| {1721 for (self.base.options.force_undefined_symbols.keys()) |sym| {
1567 try argv.append("-u");1722 try argv.append("-u");
1568 try argv.append(symbol);1723 try argv.append(sym);
1569 }1724 }
15701725
1571 switch (self.base.options.hash_style) {1726 switch (self.base.options.hash_style) {
...@@ -2092,7 +2247,7 @@ fn writeElfHeader(self: *Elf) !void {...@@ -2092,7 +2247,7 @@ fn writeElfHeader(self: *Elf) !void {
20922247
2093 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;2248 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
20942249
2095 const phdr_table_offset = self.program_headers.items[self.phdr_table_index.?].p_offset;2250 const phdr_table_offset = self.phdrs.items[self.phdr_table_index.?].p_offset;
2096 switch (self.ptr_width) {2251 switch (self.ptr_width) {
2097 .p32 => {2252 .p32 => {
2098 mem.writeInt(u32, hdr_buf[index..][0..4], @as(u32, @intCast(e_entry)), endian);2253 mem.writeInt(u32, hdr_buf[index..][0..4], @as(u32, @intCast(e_entry)), endian);
...@@ -2139,7 +2294,7 @@ fn writeElfHeader(self: *Elf) !void {...@@ -2139,7 +2294,7 @@ fn writeElfHeader(self: *Elf) !void {
2139 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);2294 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
2140 index += 2;2295 index += 2;
21412296
2142 const e_phnum = @as(u16, @intCast(self.program_headers.items.len));2297 const e_phnum = @as(u16, @intCast(self.phdrs.items.len));
2143 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);2298 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
2144 index += 2;2299 index += 2;
21452300
...@@ -2150,7 +2305,7 @@ fn writeElfHeader(self: *Elf) !void {...@@ -2150,7 +2305,7 @@ fn writeElfHeader(self: *Elf) !void {
2150 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);2305 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
2151 index += 2;2306 index += 2;
21522307
2153 const e_shnum = @as(u16, @intCast(self.sections.slice().len));2308 const e_shnum = @as(u16, @intCast(self.shdrs.items.len));
2154 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);2309 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
2155 index += 2;2310 index += 2;
21562311
...@@ -2162,268 +2317,23 @@ fn writeElfHeader(self: *Elf) !void {...@@ -2162,268 +2317,23 @@ fn writeElfHeader(self: *Elf) !void {
2162 try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);2317 try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);
2163}2318}
21642319
2165fn freeAtom(self: *Elf, atom_index: Atom.Index) void {
2166 const atom = self.getAtom(atom_index);
2167 log.debug("freeAtom {d} ({s})", .{ atom_index, atom.getName(self) });
2168
2169 Atom.freeRelocations(self, atom_index);
2170
2171 const gpa = self.base.allocator;
2172 const shndx = atom.getSymbol(self).st_shndx;
2173 const free_list = &self.sections.items(.free_list)[shndx];
2174 var already_have_free_list_node = false;
2175 {
2176 var i: usize = 0;
2177 // TODO turn free_list into a hash map
2178 while (i < free_list.items.len) {
2179 if (free_list.items[i] == atom_index) {
2180 _ = free_list.swapRemove(i);
2181 continue;
2182 }
2183 if (free_list.items[i] == atom.prev_index) {
2184 already_have_free_list_node = true;
2185 }
2186 i += 1;
2187 }
2188 }
2189
2190 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[shndx];
2191 if (maybe_last_atom_index.*) |last_atom_index| {
2192 if (last_atom_index == atom_index) {
2193 if (atom.prev_index) |prev_index| {
2194 // TODO shrink the section size here
2195 maybe_last_atom_index.* = prev_index;
2196 } else {
2197 maybe_last_atom_index.* = null;
2198 }
2199 }
2200 }
2201
2202 if (atom.prev_index) |prev_index| {
2203 const prev = self.getAtomPtr(prev_index);
2204 prev.next_index = atom.next_index;
2205
2206 if (!already_have_free_list_node and prev.*.freeListEligible(self)) {
2207 // The free list is heuristics, it doesn't have to be perfect, so we can
2208 // ignore the OOM here.
2209 free_list.append(gpa, prev_index) catch {};
2210 }
2211 } else {
2212 self.getAtomPtr(atom_index).prev_index = null;
2213 }
2214
2215 if (atom.next_index) |next_index| {
2216 self.getAtomPtr(next_index).prev_index = atom.prev_index;
2217 } else {
2218 self.getAtomPtr(atom_index).next_index = null;
2219 }
2220
2221 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
2222 const local_sym_index = atom.getSymbolIndex().?;
2223
2224 log.debug("adding %{d} to local symbols free list", .{local_sym_index});
2225 self.local_symbol_free_list.append(gpa, local_sym_index) catch {};
2226 self.local_symbols.items[local_sym_index] = .{
2227 .st_name = 0,
2228 .st_info = 0,
2229 .st_other = 0,
2230 .st_shndx = 0,
2231 .st_value = 0,
2232 .st_size = 0,
2233 };
2234 _ = self.atom_by_index_table.remove(local_sym_index);
2235 self.getAtomPtr(atom_index).local_sym_index = 0;
2236
2237 self.got_table.freeEntry(gpa, local_sym_index);
2238}
2239
2240fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void {
2241 _ = self;
2242 _ = atom_index;
2243 _ = new_block_size;
2244}
2245
2246fn growAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignment: u64) !u64 {
2247 const atom = self.getAtom(atom_index);
2248 const sym = atom.getSymbol(self);
2249 const align_ok = mem.alignBackward(u64, sym.st_value, alignment) == sym.st_value;
2250 const need_realloc = !align_ok or new_block_size > atom.capacity(self);
2251 if (!need_realloc) return sym.st_value;
2252 return self.allocateAtom(atom_index, new_block_size, alignment);
2253}
2254
2255pub fn createAtom(self: *Elf) !Atom.Index {
2256 const gpa = self.base.allocator;
2257 const atom_index = @as(Atom.Index, @intCast(self.atoms.items.len));
2258 const atom = try self.atoms.addOne(gpa);
2259 const local_sym_index = try self.allocateLocalSymbol();
2260 try self.atom_by_index_table.putNoClobber(gpa, local_sym_index, atom_index);
2261 atom.* = .{
2262 .local_sym_index = local_sym_index,
2263 .prev_index = null,
2264 .next_index = null,
2265 };
2266 log.debug("creating ATOM(%{d}) at index {d}", .{ local_sym_index, atom_index });
2267 return atom_index;
2268}
2269
2270fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignment: u64) !u64 {
2271 const atom = self.getAtom(atom_index);
2272 const sym = atom.getSymbol(self);
2273 const phdr_index = self.sections.items(.phdr_index)[sym.st_shndx];
2274 const phdr = &self.program_headers.items[phdr_index];
2275 const shdr = &self.sections.items(.shdr)[sym.st_shndx];
2276 const free_list = &self.sections.items(.free_list)[sym.st_shndx];
2277 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sym.st_shndx];
2278 const new_atom_ideal_capacity = padToIdeal(new_block_size);
2279
2280 // We use these to indicate our intention to update metadata, placing the new atom,
2281 // and possibly removing a free list node.
2282 // It would be simpler to do it inside the for loop below, but that would cause a
2283 // problem if an error was returned later in the function. So this action
2284 // is actually carried out at the end of the function, when errors are no longer possible.
2285 var atom_placement: ?Atom.Index = null;
2286 var free_list_removal: ?usize = null;
2287
2288 // First we look for an appropriately sized free list node.
2289 // The list is unordered. We'll just take the first thing that works.
2290 const vaddr = blk: {
2291 var i: usize = if (self.base.child_pid == null) 0 else free_list.items.len;
2292 while (i < free_list.items.len) {
2293 const big_atom_index = free_list.items[i];
2294 const big_atom = self.getAtom(big_atom_index);
2295 // We now have a pointer to a live atom that has too much capacity.
2296 // Is it enough that we could fit this new atom?
2297 const big_atom_sym = big_atom.getSymbol(self);
2298 const capacity = big_atom.capacity(self);
2299 const ideal_capacity = padToIdeal(capacity);
2300 const ideal_capacity_end_vaddr = std.math.add(u64, big_atom_sym.st_value, ideal_capacity) catch ideal_capacity;
2301 const capacity_end_vaddr = big_atom_sym.st_value + capacity;
2302 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
2303 const new_start_vaddr = mem.alignBackward(u64, new_start_vaddr_unaligned, alignment);
2304 if (new_start_vaddr < ideal_capacity_end_vaddr) {
2305 // Additional bookkeeping here to notice if this free list node
2306 // should be deleted because the block that it points to has grown to take up
2307 // more of the extra capacity.
2308 if (!big_atom.freeListEligible(self)) {
2309 _ = free_list.swapRemove(i);
2310 } else {
2311 i += 1;
2312 }
2313 continue;
2314 }
2315 // At this point we know that we will place the new block here. But the
2316 // remaining question is whether there is still yet enough capacity left
2317 // over for there to still be a free list node.
2318 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
2319 const keep_free_list_node = remaining_capacity >= min_text_capacity;
2320
2321 // Set up the metadata to be updated, after errors are no longer possible.
2322 atom_placement = big_atom_index;
2323 if (!keep_free_list_node) {
2324 free_list_removal = i;
2325 }
2326 break :blk new_start_vaddr;
2327 } else if (maybe_last_atom_index.*) |last_index| {
2328 const last = self.getAtom(last_index);
2329 const last_sym = last.getSymbol(self);
2330 const ideal_capacity = padToIdeal(last_sym.st_size);
2331 const ideal_capacity_end_vaddr = last_sym.st_value + ideal_capacity;
2332 const new_start_vaddr = mem.alignForward(u64, ideal_capacity_end_vaddr, alignment);
2333 // Set up the metadata to be updated, after errors are no longer possible.
2334 atom_placement = last_index;
2335 break :blk new_start_vaddr;
2336 } else {
2337 break :blk phdr.p_vaddr;
2338 }
2339 };
2340
2341 const expand_section = if (atom_placement) |placement_index|
2342 self.getAtom(placement_index).next_index == null
2343 else
2344 true;
2345 if (expand_section) {
2346 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
2347 try self.growAllocSection(sym.st_shndx, needed_size);
2348 maybe_last_atom_index.* = atom_index;
2349
2350 if (self.dwarf) |_| {
2351 // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
2352 // range of the compilation unit. When we expand the text section, this range changes,
2353 // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty.
2354 self.debug_info_header_dirty = true;
2355 // This becomes dirty for the same reason. We could potentially make this more
2356 // fine-grained with the addition of support for more compilation units. It is planned to
2357 // model each package as a different compilation unit.
2358 self.debug_aranges_section_dirty = true;
2359 }
2360 }
2361 shdr.sh_addralign = @max(shdr.sh_addralign, alignment);
2362
2363 // This function can also reallocate an atom.
2364 // In this case we need to "unplug" it from its previous location before
2365 // plugging it in to its new location.
2366 if (atom.prev_index) |prev_index| {
2367 const prev = self.getAtomPtr(prev_index);
2368 prev.next_index = atom.next_index;
2369 }
2370 if (atom.next_index) |next_index| {
2371 const next = self.getAtomPtr(next_index);
2372 next.prev_index = atom.prev_index;
2373 }
2374
2375 if (atom_placement) |big_atom_index| {
2376 const big_atom = self.getAtomPtr(big_atom_index);
2377 const atom_ptr = self.getAtomPtr(atom_index);
2378 atom_ptr.prev_index = big_atom_index;
2379 atom_ptr.next_index = big_atom.next_index;
2380 big_atom.next_index = atom_index;
2381 } else {
2382 const atom_ptr = self.getAtomPtr(atom_index);
2383 atom_ptr.prev_index = null;
2384 atom_ptr.next_index = null;
2385 }
2386 if (free_list_removal) |i| {
2387 _ = free_list.swapRemove(i);
2388 }
2389 return vaddr;
2390}
2391
2392pub fn allocateLocalSymbol(self: *Elf) !u32 {
2393 try self.local_symbols.ensureUnusedCapacity(self.base.allocator, 1);
2394
2395 const index = blk: {
2396 if (self.local_symbol_free_list.popOrNull()) |index| {
2397 log.debug(" (reusing symbol index {d})", .{index});
2398 break :blk index;
2399 } else {
2400 log.debug(" (allocating symbol index {d})", .{self.local_symbols.items.len});
2401 const index = @as(u32, @intCast(self.local_symbols.items.len));
2402 _ = self.local_symbols.addOneAssumeCapacity();
2403 break :blk index;
2404 }
2405 };
2406
2407 self.local_symbols.items[index] = .{
2408 .st_name = 0,
2409 .st_info = 0,
2410 .st_other = 0,
2411 .st_shndx = 0,
2412 .st_value = 0,
2413 .st_size = 0,
2414 };
2415
2416 return index;
2417}
2418
2419fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void {2320fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void {
2420 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;2321 const unnamed_consts = self.unnamed_consts.getPtr(decl_index) orelse return;
2421 for (unnamed_consts.items) |atom| {2322 for (unnamed_consts.items) |sym_index| {
2422 self.freeAtom(atom);2323 self.freeDeclMetadata(sym_index);
2423 }2324 }
2424 unnamed_consts.clearAndFree(self.base.allocator);2325 unnamed_consts.clearAndFree(self.base.allocator);
2425}2326}
24262327
2328fn freeDeclMetadata(self: *Elf, sym_index: Symbol.Index) void {
2329 const sym = self.symbol(sym_index);
2330 sym.atom(self).?.free(self);
2331 log.debug("adding %{d} to local symbols free list", .{sym_index});
2332 self.symbols_free_list.append(self.base.allocator, sym_index) catch {};
2333 self.symbols.items[sym_index] = .{};
2334 // TODO free GOT entry here
2335}
2336
2427pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {2337pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {
2428 if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);2338 if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
24292339
...@@ -2434,7 +2344,8 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {...@@ -2434,7 +2344,8 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {
24342344
2435 if (self.decls.fetchRemove(decl_index)) |const_kv| {2345 if (self.decls.fetchRemove(decl_index)) |const_kv| {
2436 var kv = const_kv;2346 var kv = const_kv;
2437 self.freeAtom(kv.value.atom);2347 const sym_index = kv.value.symbol_index;
2348 self.freeDeclMetadata(sym_index);
2438 self.freeUnnamedConsts(decl_index);2349 self.freeUnnamedConsts(decl_index);
2439 kv.value.exports.deinit(self.base.allocator);2350 kv.value.exports.deinit(self.base.allocator);
2440 }2351 }
...@@ -2444,40 +2355,50 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {...@@ -2444,40 +2355,50 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {
2444 }2355 }
2445}2356}
24462357
2447pub fn getOrCreateAtomForLazySymbol(self: *Elf, sym: File.LazySymbol) !Atom.Index {2358pub fn getOrCreateMetadataForLazySymbol(self: *Elf, sym: link.File.LazySymbol) !Symbol.Index {
2448 const mod = self.base.options.module.?;2359 const mod = self.base.options.module.?;
2449 const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl(mod));2360 const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl(mod));
2450 errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();2361 errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();
2451 if (!gop.found_existing) gop.value_ptr.* = .{};2362 if (!gop.found_existing) gop.value_ptr.* = .{};
2452 const metadata: struct { atom: *Atom.Index, state: *LazySymbolMetadata.State } = switch (sym.kind) {2363 const metadata: struct {
2453 .code => .{ .atom = &gop.value_ptr.text_atom, .state = &gop.value_ptr.text_state },2364 symbol_index: *Symbol.Index,
2454 .const_data => .{ .atom = &gop.value_ptr.rodata_atom, .state = &gop.value_ptr.rodata_state },2365 state: *LazySymbolMetadata.State,
2366 } = switch (sym.kind) {
2367 .code => .{
2368 .symbol_index = &gop.value_ptr.text_symbol_index,
2369 .state = &gop.value_ptr.text_state,
2370 },
2371 .const_data => .{
2372 .symbol_index = &gop.value_ptr.rodata_symbol_index,
2373 .state = &gop.value_ptr.rodata_state,
2374 },
2455 };2375 };
2376 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
2456 switch (metadata.state.*) {2377 switch (metadata.state.*) {
2457 .unused => metadata.atom.* = try self.createAtom(),2378 .unused => metadata.symbol_index.* = try zig_module.addAtom(switch (sym.kind) {
2458 .pending_flush => return metadata.atom.*,2379 .code => self.text_section_index.?,
2380 .const_data => self.rodata_section_index.?,
2381 }, self),
2382 .pending_flush => return metadata.symbol_index.*,
2459 .flushed => {},2383 .flushed => {},
2460 }2384 }
2461 metadata.state.* = .pending_flush;2385 metadata.state.* = .pending_flush;
2462 const atom = metadata.atom.*;2386 const symbol_index = metadata.symbol_index.*;
2463 // anyerror needs to be deferred until flushModule2387 // anyerror needs to be deferred until flushModule
2464 if (sym.getDecl(mod) != .none) try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) {2388 if (sym.getDecl(mod) != .none) try self.updateLazySymbol(sym, symbol_index);
2465 .code => self.text_section_index.?,2389 return symbol_index;
2466 .const_data => self.rodata_section_index.?,
2467 });
2468 return atom;
2469}2390}
24702391
2471pub fn getOrCreateAtomForDecl(self: *Elf, decl_index: Module.Decl.Index) !Atom.Index {2392pub fn getOrCreateMetadataForDecl(self: *Elf, decl_index: Module.Decl.Index) !Symbol.Index {
2472 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);2393 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
2473 if (!gop.found_existing) {2394 if (!gop.found_existing) {
2395 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
2474 gop.value_ptr.* = .{2396 gop.value_ptr.* = .{
2475 .atom = try self.createAtom(),2397 .symbol_index = try zig_module.addAtom(self.getDeclShdrIndex(decl_index), self),
2476 .shdr = self.getDeclShdrIndex(decl_index),
2477 .exports = .{},2398 .exports = .{},
2478 };2399 };
2479 }2400 }
2480 return gop.value_ptr.atom;2401 return gop.value_ptr.symbol_index;
2481}2402}
24822403
2483fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index) u16 {2404fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index) u16 {
...@@ -2506,9 +2427,16 @@ fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index) u16 {...@@ -2506,9 +2427,16 @@ fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index) u16 {
2506 return shdr_index;2427 return shdr_index;
2507}2428}
25082429
2509fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym {2430fn updateDeclCode(
2431 self: *Elf,
2432 decl_index: Module.Decl.Index,
2433 sym_index: Symbol.Index,
2434 code: []const u8,
2435 stt_bits: u8,
2436) !void {
2510 const gpa = self.base.allocator;2437 const gpa = self.base.allocator;
2511 const mod = self.base.options.module.?;2438 const mod = self.base.options.module.?;
2439 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
2512 const decl = mod.declPtr(decl_index);2440 const decl = mod.declPtr(decl_index);
25132441
2514 const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));2442 const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
...@@ -2516,63 +2444,60 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s...@@ -2516,63 +2444,60 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
2516 log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });2444 log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });
2517 const required_alignment = decl.getAlignment(mod);2445 const required_alignment = decl.getAlignment(mod);
25182446
2519 const decl_metadata = self.decls.get(decl_index).?;2447 const sym = self.symbol(sym_index);
2520 const atom_index = decl_metadata.atom;2448 const esym = &zig_module.local_esyms.items[sym.esym_index];
2521 const atom = self.getAtom(atom_index);2449 const atom_ptr = sym.atom(self).?;
2522 const local_sym_index = atom.getSymbolIndex().?;2450 const shdr_index = sym.output_section_index;
25232451
2524 const shdr_index = decl_metadata.shdr;2452 sym.name_offset = try self.strtab.insert(gpa, decl_name);
2525 if (atom.getSymbol(self).st_size != 0 and self.base.child_pid == null) {2453 atom_ptr.alive = true;
2526 const local_sym = atom.getSymbolPtr(self);2454 atom_ptr.name_offset = sym.name_offset;
2527 local_sym.st_name = try self.strtab.insert(gpa, decl_name);2455 esym.st_name = sym.name_offset;
2528 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;2456 esym.st_info |= stt_bits;
2529 local_sym.st_other = 0;2457 esym.st_size = code.len;
2530 local_sym.st_shndx = shdr_index;2458
25312459 const old_size = atom_ptr.size;
2532 const capacity = atom.capacity(self);2460 const old_vaddr = atom_ptr.value;
2533 const need_realloc = code.len > capacity or2461 atom_ptr.alignment = math.log2_int(u64, required_alignment);
2534 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);2462 atom_ptr.size = code.len;
25352463
2464 if (old_size > 0 and self.base.child_pid == null) {
2465 const capacity = atom_ptr.capacity(self);
2466 const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, sym.value, required_alignment);
2536 if (need_realloc) {2467 if (need_realloc) {
2537 const vaddr = try self.growAtom(atom_index, code.len, required_alignment);2468 try atom_ptr.grow(self);
2538 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, local_sym.st_value, vaddr });2469 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, old_vaddr, atom_ptr.value });
2539 if (vaddr != local_sym.st_value) {2470 if (old_vaddr != atom_ptr.value) {
2540 local_sym.st_value = vaddr;2471 sym.value = atom_ptr.value;
2472 esym.st_value = atom_ptr.value;
25412473
2542 log.debug(" (writing new offset table entry)", .{});2474 log.debug(" (writing new offset table entry)", .{});
2543 const got_entry_index = self.got_table.lookup.get(local_sym_index).?;2475 const extra = sym.extra(self).?;
2544 self.got_table.entries.items[got_entry_index] = local_sym_index;2476 try self.got.writeEntry(self, extra.got);
2545 try self.writeOffsetTableEntry(got_entry_index);
2546 }2477 }
2547 } else if (code.len < local_sym.st_size) {2478 } else if (code.len < old_size) {
2548 self.shrinkAtom(atom_index, code.len);2479 atom_ptr.shrink(self);
2549 }2480 }
2550 local_sym.st_size = code.len;
2551 } else {2481 } else {
2552 const local_sym = atom.getSymbolPtr(self);2482 try atom_ptr.allocate(self);
2553 local_sym.* = .{2483 errdefer self.freeDeclMetadata(sym_index);
2554 .st_name = try self.strtab.insert(gpa, decl_name),2484 log.debug("allocated atom for {s} at 0x{x} to 0x{x}", .{
2555 .st_info = (elf.STB_LOCAL << 4) | stt_bits,2485 decl_name,
2556 .st_other = 0,2486 atom_ptr.value,
2557 .st_shndx = shdr_index,2487 atom_ptr.value + atom_ptr.size,
2558 .st_value = 0,2488 });
2559 .st_size = 0,
2560 };
2561 const vaddr = try self.allocateAtom(atom_index, code.len, required_alignment);
2562 errdefer self.freeAtom(atom_index);
2563 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr });
25642489
2565 local_sym.st_value = vaddr;2490 sym.value = atom_ptr.value;
2566 local_sym.st_size = code.len;2491 esym.st_value = atom_ptr.value;
25672492
2568 const got_entry_index = try atom.getOrCreateOffsetTableEntry(self);2493 sym.flags.needs_got = true;
2569 try self.writeOffsetTableEntry(got_entry_index);2494 const gop = try sym.getOrCreateGotEntry(self);
2495 try self.got.writeEntry(self, gop.index);
2570 }2496 }
25712497
2572 const local_sym = atom.getSymbolPtr(self);2498 const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?;
2573 const phdr_index = self.sections.items(.phdr_index)[shdr_index];2499 const section_offset = sym.value - self.phdrs.items[phdr_index].p_vaddr;
2574 const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr;2500 const file_offset = self.shdrs.items[shdr_index].sh_offset + section_offset;
2575 const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset;
25762501
2577 if (self.base.child_pid) |pid| {2502 if (self.base.child_pid) |pid| {
2578 switch (builtin.os.tag) {2503 switch (builtin.os.tag) {
...@@ -2582,7 +2507,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s...@@ -2582,7 +2507,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
2582 .iov_len = code.len,2507 .iov_len = code.len,
2583 }};2508 }};
2584 var remote_vec: [1]std.os.iovec_const = .{.{2509 var remote_vec: [1]std.os.iovec_const = .{.{
2585 .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(local_sym.st_value)))),2510 .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(sym.value)))),
2586 .iov_len = code.len,2511 .iov_len = code.len,
2587 }};2512 }};
2588 const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0);2513 const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0);
...@@ -2596,8 +2521,6 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s...@@ -2596,8 +2521,6 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
2596 }2521 }
25972522
2598 try self.base.file.?.pwriteAll(code, file_offset);2523 try self.base.file.?.pwriteAll(code, file_offset);
2599
2600 return local_sym;
2601}2524}
26022525
2603pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {2526pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
...@@ -2613,9 +2536,9 @@ pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: A...@@ -2613,9 +2536,9 @@ pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: A
2613 const decl_index = func.owner_decl;2536 const decl_index = func.owner_decl;
2614 const decl = mod.declPtr(decl_index);2537 const decl = mod.declPtr(decl_index);
26152538
2616 const atom_index = try self.getOrCreateAtomForDecl(decl_index);2539 const sym_index = try self.getOrCreateMetadataForDecl(decl_index);
2617 self.freeUnnamedConsts(decl_index);2540 self.freeUnnamedConsts(decl_index);
2618 Atom.freeRelocations(self, atom_index);2541 self.symbol(sym_index).atom(self).?.freeRelocs(self);
26192542
2620 var code_buffer = std.ArrayList(u8).init(self.base.allocator);2543 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2621 defer code_buffer.deinit();2544 defer code_buffer.deinit();
...@@ -2638,13 +2561,14 @@ pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: A...@@ -2638,13 +2561,14 @@ pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: A
2638 return;2561 return;
2639 },2562 },
2640 };2563 };
2641 const local_sym = try self.updateDeclCode(decl_index, code, elf.STT_FUNC);2564 try self.updateDeclCode(decl_index, sym_index, code, elf.STT_FUNC);
2642 if (decl_state) |*ds| {2565 if (decl_state) |*ds| {
2566 const sym = self.symbol(sym_index);
2643 try self.dwarf.?.commitDeclState(2567 try self.dwarf.?.commitDeclState(
2644 mod,2568 mod,
2645 decl_index,2569 decl_index,
2646 local_sym.st_value,2570 sym.value,
2647 local_sym.st_size,2571 sym.atom(self).?.size,
2648 ds,2572 ds,
2649 );2573 );
2650 }2574 }
...@@ -2658,7 +2582,7 @@ pub fn updateDecl(...@@ -2658,7 +2582,7 @@ pub fn updateDecl(
2658 self: *Elf,2582 self: *Elf,
2659 mod: *Module,2583 mod: *Module,
2660 decl_index: Module.Decl.Index,2584 decl_index: Module.Decl.Index,
2661) File.UpdateDeclError!void {2585) link.File.UpdateDeclError!void {
2662 if (build_options.skip_non_native and builtin.object_format != .elf) {2586 if (build_options.skip_non_native and builtin.object_format != .elf) {
2663 @panic("Attempted to compile for object format that was disabled by build configuration");2587 @panic("Attempted to compile for object format that was disabled by build configuration");
2664 }2588 }
...@@ -2678,9 +2602,8 @@ pub fn updateDecl(...@@ -2678,9 +2602,8 @@ pub fn updateDecl(
2678 }2602 }
2679 }2603 }
26802604
2681 const atom_index = try self.getOrCreateAtomForDecl(decl_index);2605 const sym_index = try self.getOrCreateMetadataForDecl(decl_index);
2682 Atom.freeRelocations(self, atom_index);2606 self.symbol(sym_index).atom(self).?.freeRelocs(self);
2683 const atom = self.getAtom(atom_index);
26842607
2685 var code_buffer = std.ArrayList(u8).init(self.base.allocator);2608 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2686 defer code_buffer.deinit();2609 defer code_buffer.deinit();
...@@ -2697,14 +2620,14 @@ pub fn updateDecl(...@@ -2697,14 +2620,14 @@ pub fn updateDecl(
2697 }, &code_buffer, .{2620 }, &code_buffer, .{
2698 .dwarf = ds,2621 .dwarf = ds,
2699 }, .{2622 }, .{
2700 .parent_atom_index = atom.getSymbolIndex().?,2623 .parent_atom_index = sym_index,
2701 })2624 })
2702 else2625 else
2703 try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{2626 try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{
2704 .ty = decl.ty,2627 .ty = decl.ty,
2705 .val = decl_val,2628 .val = decl_val,
2706 }, &code_buffer, .none, .{2629 }, &code_buffer, .none, .{
2707 .parent_atom_index = atom.getSymbolIndex().?,2630 .parent_atom_index = sym_index,
2708 });2631 });
27092632
2710 const code = switch (res) {2633 const code = switch (res) {
...@@ -2716,13 +2639,14 @@ pub fn updateDecl(...@@ -2716,13 +2639,14 @@ pub fn updateDecl(
2716 },2639 },
2717 };2640 };
27182641
2719 const local_sym = try self.updateDeclCode(decl_index, code, elf.STT_OBJECT);2642 try self.updateDeclCode(decl_index, sym_index, code, elf.STT_OBJECT);
2720 if (decl_state) |*ds| {2643 if (decl_state) |*ds| {
2644 const sym = self.symbol(sym_index);
2721 try self.dwarf.?.commitDeclState(2645 try self.dwarf.?.commitDeclState(
2722 mod,2646 mod,
2723 decl_index,2647 decl_index,
2724 local_sym.st_value,2648 sym.value,
2725 local_sym.st_size,2649 sym.atom(self).?.size,
2726 ds,2650 ds,
2727 );2651 );
2728 }2652 }
...@@ -2732,14 +2656,10 @@ pub fn updateDecl(...@@ -2732,14 +2656,10 @@ pub fn updateDecl(
2732 return self.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index));2656 return self.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index));
2733}2657}
27342658
2735fn updateLazySymbolAtom(2659fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.Index) !void {
2736 self: *Elf,
2737 sym: File.LazySymbol,
2738 atom_index: Atom.Index,
2739 shdr_index: u16,
2740) !void {
2741 const gpa = self.base.allocator;2660 const gpa = self.base.allocator;
2742 const mod = self.base.options.module.?;2661 const mod = self.base.options.module.?;
2662 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
27432663
2744 var required_alignment: u32 = undefined;2664 var required_alignment: u32 = undefined;
2745 var code_buffer = std.ArrayList(u8).init(gpa);2665 var code_buffer = std.ArrayList(u8).init(gpa);
...@@ -2755,9 +2675,6 @@ fn updateLazySymbolAtom(...@@ -2755,9 +2675,6 @@ fn updateLazySymbolAtom(
2755 };2675 };
2756 const name = self.strtab.get(name_str_index).?;2676 const name = self.strtab.get(name_str_index).?;
27572677
2758 const atom = self.getAtom(atom_index);
2759 const local_sym_index = atom.getSymbolIndex().?;
2760
2761 const src = if (sym.ty.getOwnerDeclOrNull(mod)) |owner_decl|2678 const src = if (sym.ty.getOwnerDeclOrNull(mod)) |owner_decl|
2762 mod.declPtr(owner_decl).srcLoc(mod)2679 mod.declPtr(owner_decl).srcLoc(mod)
2763 else2680 else
...@@ -2773,7 +2690,7 @@ fn updateLazySymbolAtom(...@@ -2773,7 +2690,7 @@ fn updateLazySymbolAtom(
2773 &required_alignment,2690 &required_alignment,
2774 &code_buffer,2691 &code_buffer,
2775 .none,2692 .none,
2776 .{ .parent_atom_index = local_sym_index },2693 .{ .parent_atom_index = symbol_index },
2777 );2694 );
2778 const code = switch (res) {2695 const code = switch (res) {
2779 .ok => code_buffer.items,2696 .ok => code_buffer.items,
...@@ -2783,28 +2700,37 @@ fn updateLazySymbolAtom(...@@ -2783,28 +2700,37 @@ fn updateLazySymbolAtom(
2783 },2700 },
2784 };2701 };
27852702
2786 const phdr_index = self.sections.items(.phdr_index)[shdr_index];2703 const local_sym = self.symbol(symbol_index);
2787 const local_sym = atom.getSymbolPtr(self);2704 const phdr_index = self.phdr_to_shdr_table.get(local_sym.output_section_index).?;
2788 local_sym.* = .{2705 local_sym.name_offset = name_str_index;
2789 .st_name = name_str_index,2706 const local_esym = &zig_module.local_esyms.items[local_sym.esym_index];
2790 .st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT,2707 local_esym.st_name = name_str_index;
2791 .st_other = 0,2708 local_esym.st_info |= elf.STT_OBJECT;
2792 .st_shndx = shdr_index,2709 local_esym.st_size = code.len;
2793 .st_value = 0,2710 const atom_ptr = local_sym.atom(self).?;
2794 .st_size = 0,2711 atom_ptr.alive = true;
2795 };2712 atom_ptr.name_offset = name_str_index;
2796 const vaddr = try self.allocateAtom(atom_index, code.len, required_alignment);2713 atom_ptr.alignment = math.log2_int(u64, required_alignment);
2797 errdefer self.freeAtom(atom_index);2714 atom_ptr.size = code.len;
2798 log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr });2715
2716 try atom_ptr.allocate(self);
2717 errdefer self.freeDeclMetadata(symbol_index);
2718
2719 log.debug("allocated atom for {s} at 0x{x} to 0x{x}", .{
2720 name,
2721 atom_ptr.value,
2722 atom_ptr.value + atom_ptr.size,
2723 });
27992724
2800 local_sym.st_value = vaddr;2725 local_sym.value = atom_ptr.value;
2801 local_sym.st_size = code.len;2726 local_esym.st_value = atom_ptr.value;
28022727
2803 const got_entry_index = try atom.getOrCreateOffsetTableEntry(self);2728 local_sym.flags.needs_got = true;
2804 try self.writeOffsetTableEntry(got_entry_index);2729 const gop = try local_sym.getOrCreateGotEntry(self);
2730 try self.got.writeEntry(self, gop.index);
28052731
2806 const section_offset = vaddr - self.program_headers.items[phdr_index].p_vaddr;2732 const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr;
2807 const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset;2733 const file_offset = self.shdrs.items[local_sym.output_section_index].sh_offset + section_offset;
2808 try self.base.file.?.pwriteAll(code, file_offset);2734 try self.base.file.?.pwriteAll(code, file_offset);
2809}2735}
28102736
...@@ -2815,7 +2741,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module...@@ -2815,7 +2741,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
2815 defer code_buffer.deinit();2741 defer code_buffer.deinit();
28162742
2817 const mod = self.base.options.module.?;2743 const mod = self.base.options.module.?;
2818 const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);2744 const gop = try self.unnamed_consts.getOrPut(gpa, decl_index);
2819 if (!gop.found_existing) {2745 if (!gop.found_existing) {
2820 gop.value_ptr.* = .{};2746 gop.value_ptr.* = .{};
2821 }2747 }
...@@ -2831,12 +2757,13 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module...@@ -2831,12 +2757,13 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
2831 };2757 };
2832 const name = self.strtab.get(name_str_index).?;2758 const name = self.strtab.get(name_str_index).?;
28332759
2834 const atom_index = try self.createAtom();2760 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
2761 const sym_index = try zig_module.addAtom(self.rodata_section_index.?, self);
28352762
2836 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), typed_value, &code_buffer, .{2763 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), typed_value, &code_buffer, .{
2837 .none = {},2764 .none = {},
2838 }, .{2765 }, .{
2839 .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,2766 .parent_atom_index = sym_index,
2840 });2767 });
2841 const code = switch (res) {2768 const code = switch (res) {
2842 .ok => code_buffer.items,2769 .ok => code_buffer.items,
...@@ -2850,25 +2777,34 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module...@@ -2850,25 +2777,34 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
28502777
2851 const required_alignment = typed_value.ty.abiAlignment(mod);2778 const required_alignment = typed_value.ty.abiAlignment(mod);
2852 const shdr_index = self.rodata_section_index.?;2779 const shdr_index = self.rodata_section_index.?;
2853 const phdr_index = self.sections.items(.phdr_index)[shdr_index];2780 const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?;
2854 const local_sym = self.getAtom(atom_index).getSymbolPtr(self);2781 const local_sym = self.symbol(sym_index);
2855 local_sym.st_name = name_str_index;2782 local_sym.name_offset = name_str_index;
2856 local_sym.st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT;2783 const local_esym = &zig_module.local_esyms.items[local_sym.esym_index];
2857 local_sym.st_other = 0;2784 local_esym.st_name = name_str_index;
2858 local_sym.st_shndx = shdr_index;2785 local_esym.st_info |= elf.STT_OBJECT;
2859 local_sym.st_size = code.len;2786 local_esym.st_size = code.len;
2860 local_sym.st_value = try self.allocateAtom(atom_index, code.len, required_alignment);2787 const atom_ptr = local_sym.atom(self).?;
2861 errdefer self.freeAtom(atom_index);2788 atom_ptr.alive = true;
28622789 atom_ptr.name_offset = name_str_index;
2863 log.debug("allocated text block for {s} at 0x{x}", .{ name, local_sym.st_value });2790 atom_ptr.alignment = math.log2_int(u64, required_alignment);
28642791 atom_ptr.size = code.len;
2865 try unnamed_consts.append(gpa, atom_index);2792
28662793 try atom_ptr.allocate(self);
2867 const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr;2794 errdefer self.freeDeclMetadata(sym_index);
2868 const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset;2795
2796 log.debug("allocated atom for {s} at 0x{x} to 0x{x}", .{ name, atom_ptr.value, atom_ptr.value + atom_ptr.size });
2797
2798 local_sym.value = atom_ptr.value;
2799 local_esym.st_value = atom_ptr.value;
2800
2801 try unnamed_consts.append(gpa, atom_ptr.atom_index);
2802
2803 const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr;
2804 const file_offset = self.shdrs.items[shdr_index].sh_offset + section_offset;
2869 try self.base.file.?.pwriteAll(code, file_offset);2805 try self.base.file.?.pwriteAll(code, file_offset);
28702806
2871 return self.getAtom(atom_index).getSymbolIndex().?;2807 return sym_index;
2872}2808}
28732809
2874pub fn updateDeclExports(2810pub fn updateDeclExports(
...@@ -2876,7 +2812,7 @@ pub fn updateDeclExports(...@@ -2876,7 +2812,7 @@ pub fn updateDeclExports(
2876 mod: *Module,2812 mod: *Module,
2877 decl_index: Module.Decl.Index,2813 decl_index: Module.Decl.Index,
2878 exports: []const *Module.Export,2814 exports: []const *Module.Export,
2879) File.UpdateDeclExportsError!void {2815) link.File.UpdateDeclExportsError!void {
2880 if (build_options.skip_non_native and builtin.object_format != .elf) {2816 if (build_options.skip_non_native and builtin.object_format != .elf) {
2881 @panic("Attempted to compile for object format that was disabled by build configuration");2817 @panic("Attempted to compile for object format that was disabled by build configuration");
2882 }2818 }
...@@ -2889,14 +2825,12 @@ pub fn updateDeclExports(...@@ -2889,14 +2825,12 @@ pub fn updateDeclExports(
28892825
2890 const gpa = self.base.allocator;2826 const gpa = self.base.allocator;
28912827
2828 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
2892 const decl = mod.declPtr(decl_index);2829 const decl = mod.declPtr(decl_index);
2893 const atom_index = try self.getOrCreateAtomForDecl(decl_index);2830 const decl_sym_index = try self.getOrCreateMetadataForDecl(decl_index);
2894 const atom = self.getAtom(atom_index);2831 const decl_sym = self.symbol(decl_sym_index);
2895 const decl_sym = atom.getSymbol(self);2832 const decl_esym = zig_module.local_esyms.items[decl_sym.esym_index];
2896 const decl_metadata = self.decls.getPtr(decl_index).?;2833 const decl_metadata = self.decls.getPtr(decl_index).?;
2897 const shdr_index = decl_metadata.shdr;
2898
2899 try self.global_symbols.ensureUnusedCapacity(gpa, exports.len);
29002834
2901 for (exports) |exp| {2835 for (exports) |exp| {
2902 const exp_name = mod.intern_pool.stringToSlice(exp.opts.name);2836 const exp_name = mod.intern_pool.stringToSlice(exp.opts.name);
...@@ -2905,7 +2839,7 @@ pub fn updateDeclExports(...@@ -2905,7 +2839,7 @@ pub fn updateDeclExports(
2905 try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);2839 try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);
2906 mod.failed_exports.putAssumeCapacityNoClobber(2840 mod.failed_exports.putAssumeCapacityNoClobber(
2907 exp,2841 exp,
2908 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(mod), "Unimplemented: ExportOptions.section", .{}),2842 try Module.ErrorMsg.create(gpa, decl.srcLoc(mod), "Unimplemented: ExportOptions.section", .{}),
2909 );2843 );
2910 continue;2844 continue;
2911 }2845 }
...@@ -2915,7 +2849,7 @@ pub fn updateDeclExports(...@@ -2915,7 +2849,7 @@ pub fn updateDeclExports(
2915 .Strong => blk: {2849 .Strong => blk: {
2916 const entry_name = self.base.options.entry orelse "_start";2850 const entry_name = self.base.options.entry orelse "_start";
2917 if (mem.eql(u8, exp_name, entry_name)) {2851 if (mem.eql(u8, exp_name, entry_name)) {
2918 self.entry_addr = decl_sym.st_value;2852 self.entry_addr = decl_sym.value;
2919 }2853 }
2920 break :blk elf.STB_GLOBAL;2854 break :blk elf.STB_GLOBAL;
2921 },2855 },
...@@ -2924,37 +2858,30 @@ pub fn updateDeclExports(...@@ -2924,37 +2858,30 @@ pub fn updateDeclExports(
2924 try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);2858 try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);
2925 mod.failed_exports.putAssumeCapacityNoClobber(2859 mod.failed_exports.putAssumeCapacityNoClobber(
2926 exp,2860 exp,
2927 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(mod), "Unimplemented: GlobalLinkage.LinkOnce", .{}),2861 try Module.ErrorMsg.create(gpa, decl.srcLoc(mod), "Unimplemented: GlobalLinkage.LinkOnce", .{}),
2928 );2862 );
2929 continue;2863 continue;
2930 },2864 },
2931 };2865 };
2932 const stt_bits: u8 = @as(u4, @truncate(decl_sym.st_info));2866 const stt_bits: u8 = @as(u4, @truncate(decl_esym.st_info));
2933 if (decl_metadata.getExport(self, exp_name)) |i| {2867
2934 const sym = &self.global_symbols.items[i];2868 const name_off = try self.strtab.insert(gpa, exp_name);
2935 sym.* = .{2869 const sym_index = if (decl_metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: {
2936 .st_name = try self.strtab.insert(gpa, exp_name),2870 const sym_index = try zig_module.addGlobalEsym(gpa);
2937 .st_info = (stb_bits << 4) | stt_bits,2871 const lookup_gop = try zig_module.globals_lookup.getOrPut(gpa, name_off);
2938 .st_other = 0,2872 const esym = zig_module.elfSym(sym_index);
2939 .st_shndx = shdr_index,2873 esym.st_name = name_off;
2940 .st_value = decl_sym.st_value,2874 lookup_gop.value_ptr.* = sym_index;
2941 .st_size = decl_sym.st_size,2875 try decl_metadata.exports.append(gpa, sym_index);
2942 };2876 const gop = try self.getOrPutGlobal(name_off);
2943 } else {2877 try zig_module.global_symbols.append(gpa, gop.index);
2944 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {2878 break :blk sym_index;
2945 _ = self.global_symbols.addOneAssumeCapacity();2879 };
2946 break :blk self.global_symbols.items.len - 1;2880 const esym = &zig_module.global_esyms.items[sym_index & 0x0fffffff];
2947 };2881 esym.st_value = decl_sym.value;
2948 try decl_metadata.exports.append(gpa, @as(u32, @intCast(i)));2882 esym.st_shndx = decl_sym.atom_index;
2949 self.global_symbols.items[i] = .{2883 esym.st_info = (stb_bits << 4) | stt_bits;
2950 .st_name = try self.strtab.insert(gpa, exp_name),2884 esym.st_name = name_off;
2951 .st_info = (stb_bits << 4) | stt_bits,
2952 .st_other = 0,
2953 .st_shndx = shdr_index,
2954 .st_value = decl_sym.st_value,
2955 .st_size = decl_sym.st_size,
2956 };
2957 }
2958 }2885 }
2959}2886}
29602887
...@@ -2982,124 +2909,224 @@ pub fn deleteDeclExport(...@@ -2982,124 +2909,224 @@ pub fn deleteDeclExport(
2982 if (self.llvm_object) |_| return;2909 if (self.llvm_object) |_| return;
2983 const metadata = self.decls.getPtr(decl_index) orelse return;2910 const metadata = self.decls.getPtr(decl_index) orelse return;
2984 const mod = self.base.options.module.?;2911 const mod = self.base.options.module.?;
2985 const sym_index = metadata.getExportPtr(self, mod.intern_pool.stringToSlice(name)) orelse return;2912 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
2986 self.global_symbol_free_list.append(self.base.allocator, sym_index.*) catch {};2913 const exp_name = mod.intern_pool.stringToSlice(name);
2987 self.global_symbols.items[sym_index.*].st_info = 0;2914 const esym_index = metadata.@"export"(self, exp_name) orelse return;
2988 sym_index.* = 0;2915 log.debug("deleting export '{s}'", .{exp_name});
2916 const esym = &zig_module.global_esyms.items[esym_index.*];
2917 _ = zig_module.globals_lookup.remove(esym.st_name);
2918 const sym_index = self.resolver.get(esym.st_name).?;
2919 const sym = self.symbol(sym_index);
2920 if (sym.file_index == zig_module.index) {
2921 _ = self.resolver.swapRemove(esym.st_name);
2922 sym.* = .{};
2923 }
2924 esym.* = null_sym;
2989}2925}
29902926
2991fn writeProgHeader(self: *Elf, index: usize) !void {2927fn addLinkerDefinedSymbols(self: *Elf) !void {
2992 const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian();2928 const linker_defined_index = self.linker_defined_index orelse return;
2993 const offset = self.program_headers.items[index].p_offset;2929 const linker_defined = self.file(linker_defined_index).?.linker_defined;
2994 switch (self.ptr_width) {2930 self.dynamic_index = try linker_defined.addGlobal("_DYNAMIC", self);
2995 .p32 => {2931 self.ehdr_start_index = try linker_defined.addGlobal("__ehdr_start", self);
2996 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};2932 self.init_array_start_index = try linker_defined.addGlobal("__init_array_start", self);
2997 if (foreign_endian) {2933 self.init_array_end_index = try linker_defined.addGlobal("__init_array_end", self);
2998 mem.byteSwapAllFields(elf.Elf32_Phdr, &phdr[0]);2934 self.fini_array_start_index = try linker_defined.addGlobal("__fini_array_start", self);
2999 }2935 self.fini_array_end_index = try linker_defined.addGlobal("__fini_array_end", self);
3000 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);2936 self.preinit_array_start_index = try linker_defined.addGlobal("__preinit_array_start", self);
3001 },2937 self.preinit_array_end_index = try linker_defined.addGlobal("__preinit_array_end", self);
3002 .p64 => {2938 self.got_index = try linker_defined.addGlobal("_GLOBAL_OFFSET_TABLE_", self);
3003 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};2939 self.plt_index = try linker_defined.addGlobal("_PROCEDURE_LINKAGE_TABLE_", self);
3004 if (foreign_endian) {2940 self.end_index = try linker_defined.addGlobal("_end", self);
3005 mem.byteSwapAllFields(elf.Elf64_Phdr, &phdr[0]);2941
3006 }2942 if (self.base.options.eh_frame_hdr) {
3007 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);2943 self.gnu_eh_frame_hdr_index = try linker_defined.addGlobal("__GNU_EH_FRAME_HDR", self);
3008 },2944 }
3009 }2945
2946 if (self.globalByName("__dso_handle")) |index| {
2947 if (self.symbol(index).file(self) == null)
2948 self.dso_handle_index = try linker_defined.addGlobal("__dso_handle", self);
2949 }
2950
2951 self.rela_iplt_start_index = try linker_defined.addGlobal("__rela_iplt_start", self);
2952 self.rela_iplt_end_index = try linker_defined.addGlobal("__rela_iplt_end", self);
2953
2954 // for (self.objects.items) |index| {
2955 // const object = self.getFile(index).?.object;
2956 // for (object.atoms.items) |atom_index| {
2957 // if (self.getStartStopBasename(atom_index)) |name| {
2958 // const gpa = self.base.allocator;
2959 // try self.start_stop_indexes.ensureUnusedCapacity(gpa, 2);
2960
2961 // const start = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name});
2962 // defer gpa.free(start);
2963 // const stop = try std.fmt.allocPrintZ(gpa, "__stop_{s}", .{name});
2964 // defer gpa.free(stop);
2965
2966 // self.start_stop_indexes.appendAssumeCapacity(try internal.addSyntheticGlobal(start, self));
2967 // self.start_stop_indexes.appendAssumeCapacity(try internal.addSyntheticGlobal(stop, self));
2968 // }
2969 // }
2970 // }
2971
2972 linker_defined.resolveSymbols(self);
3010}2973}
30112974
3012fn writeSectHeader(self: *Elf, index: usize) !void {2975fn allocateLinkerDefinedSymbols(self: *Elf) void {
3013 const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian();2976 // _DYNAMIC
3014 switch (self.ptr_width) {2977 if (self.dynamic_section_index) |shndx| {
3015 .p32 => {2978 const shdr = &self.shdrs.items[shndx];
3016 var shdr: [1]elf.Elf32_Shdr = undefined;2979 const symbol_ptr = self.symbol(self.dynamic_index.?);
3017 shdr[0] = sectHeaderTo32(self.sections.items(.shdr)[index]);2980 symbol_ptr.value = shdr.sh_addr;
3018 if (foreign_endian) {2981 symbol_ptr.output_section_index = shndx;
3019 mem.byteSwapAllFields(elf.Elf32_Shdr, &shdr[0]);2982 }
3020 }2983
3021 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr);2984 // __ehdr_start
3022 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);2985 {
3023 },2986 const symbol_ptr = self.symbol(self.ehdr_start_index.?);
3024 .p64 => {2987 symbol_ptr.value = self.calcImageBase();
3025 var shdr = [1]elf.Elf64_Shdr{self.sections.items(.shdr)[index]};2988 symbol_ptr.output_section_index = 1;
3026 if (foreign_endian) {2989 }
3027 mem.byteSwapAllFields(elf.Elf64_Shdr, &shdr[0]);2990
2991 // __init_array_start, __init_array_end
2992 if (self.sectionByName(".init_array")) |shndx| {
2993 const start_sym = self.symbol(self.init_array_start_index.?);
2994 const end_sym = self.symbol(self.init_array_end_index.?);
2995 const shdr = &self.shdrs.items[shndx];
2996 start_sym.output_section_index = shndx;
2997 start_sym.value = shdr.sh_addr;
2998 end_sym.output_section_index = shndx;
2999 end_sym.value = shdr.sh_addr + shdr.sh_size;
3000 }
3001
3002 // __fini_array_start, __fini_array_end
3003 if (self.sectionByName(".fini_array")) |shndx| {
3004 const start_sym = self.symbol(self.fini_array_start_index.?);
3005 const end_sym = self.symbol(self.fini_array_end_index.?);
3006 const shdr = &self.shdrs.items[shndx];
3007 start_sym.output_section_index = shndx;
3008 start_sym.value = shdr.sh_addr;
3009 end_sym.output_section_index = shndx;
3010 end_sym.value = shdr.sh_addr + shdr.sh_size;
3011 }
3012
3013 // __preinit_array_start, __preinit_array_end
3014 if (self.sectionByName(".preinit_array")) |shndx| {
3015 const start_sym = self.symbol(self.preinit_array_start_index.?);
3016 const end_sym = self.symbol(self.preinit_array_end_index.?);
3017 const shdr = &self.shdrs.items[shndx];
3018 start_sym.output_section_index = shndx;
3019 start_sym.value = shdr.sh_addr;
3020 end_sym.output_section_index = shndx;
3021 end_sym.value = shdr.sh_addr + shdr.sh_size;
3022 }
3023
3024 // _GLOBAL_OFFSET_TABLE_
3025 if (self.got_plt_section_index) |shndx| {
3026 const shdr = &self.shdrs.items[shndx];
3027 const symbol_ptr = self.symbol(self.got_index.?);
3028 symbol_ptr.value = shdr.sh_addr;
3029 symbol_ptr.output_section_index = shndx;
3030 }
3031
3032 // _PROCEDURE_LINKAGE_TABLE_
3033 if (self.plt_section_index) |shndx| {
3034 const shdr = &self.shdrs.items[shndx];
3035 const symbol_ptr = self.symbol(self.plt_index.?);
3036 symbol_ptr.value = shdr.sh_addr;
3037 symbol_ptr.output_section_index = shndx;
3038 }
3039
3040 // __dso_handle
3041 if (self.dso_handle_index) |index| {
3042 const shdr = &self.shdrs.items[1];
3043 const symbol_ptr = self.symbol(index);
3044 symbol_ptr.value = shdr.sh_addr;
3045 symbol_ptr.output_section_index = 0;
3046 }
3047
3048 // __GNU_EH_FRAME_HDR
3049 if (self.eh_frame_hdr_section_index) |shndx| {
3050 const shdr = &self.shdrs.items[shndx];
3051 const symbol_ptr = self.symbol(self.gnu_eh_frame_hdr_index.?);
3052 symbol_ptr.value = shdr.sh_addr;
3053 symbol_ptr.output_section_index = shndx;
3054 }
3055
3056 // __rela_iplt_start, __rela_iplt_end
3057 if (self.rela_dyn_section_index) |shndx| blk: {
3058 if (self.base.options.link_mode != .Static or self.base.options.pie) break :blk;
3059 const shdr = &self.shdrs.items[shndx];
3060 const end_addr = shdr.sh_addr + shdr.sh_size;
3061 const start_addr = end_addr - self.calcNumIRelativeRelocs() * @sizeOf(elf.Elf64_Rela);
3062 const start_sym = self.symbol(self.rela_iplt_start_index.?);
3063 const end_sym = self.symbol(self.rela_iplt_end_index.?);
3064 start_sym.value = start_addr;
3065 start_sym.output_section_index = shndx;
3066 end_sym.value = end_addr;
3067 end_sym.output_section_index = shndx;
3068 }
3069
3070 // _end
3071 {
3072 const end_symbol = self.symbol(self.end_index.?);
3073 for (self.shdrs.items, 0..) |*shdr, shndx| {
3074 if (shdr.sh_flags & elf.SHF_ALLOC != 0) {
3075 end_symbol.value = shdr.sh_addr + shdr.sh_size;
3076 end_symbol.output_section_index = @intCast(shndx);
3028 }3077 }
3029 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr);3078 }
3030 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);3079 }
3031 },3080
3081 // __start_*, __stop_*
3082 {
3083 var index: usize = 0;
3084 while (index < self.start_stop_indexes.items.len) : (index += 2) {
3085 const start = self.symbol(self.start_stop_indexes.items[index]);
3086 const name = start.name(self);
3087 const stop = self.symbol(self.start_stop_indexes.items[index + 1]);
3088 const shndx = self.sectionByName(name["__start_".len..]).?;
3089 const shdr = &self.shdrs.items[shndx];
3090 start.value = shdr.sh_addr;
3091 start.output_section_index = shndx;
3092 stop.value = shdr.sh_addr + shdr.sh_size;
3093 stop.output_section_index = shndx;
3094 }
3032 }3095 }
3033}3096}
30343097
3035fn writeOffsetTableEntry(self: *Elf, index: @TypeOf(self.got_table).Index) !void {3098fn updateSymtabSize(self: *Elf) !void {
3036 const entry_size: u16 = self.archPtrWidthBytes();3099 var sizes = SymtabSize{};
3037 if (self.got_table_count_dirty) {3100
3038 const needed_size = self.got_table.entries.items.len * entry_size;3101 if (self.zig_module_index) |index| {
3039 try self.growAllocSection(self.got_section_index.?, needed_size);3102 const zig_module = self.file(index).?.zig_module;
3040 self.got_table_count_dirty = false;3103 zig_module.updateSymtabSize(self);
3104 sizes.nlocals += zig_module.output_symtab_size.nlocals;
3105 sizes.nglobals += zig_module.output_symtab_size.nglobals;
3041 }3106 }
3042 const endian = self.base.options.target.cpu.arch.endian();3107
3043 const shdr = &self.sections.items(.shdr)[self.got_section_index.?];3108 for (self.objects.items) |index| {
3044 const off = shdr.sh_offset + @as(u64, entry_size) * index;3109 const object = self.file(index).?.object;
3045 const phdr = &self.program_headers.items[self.phdr_got_index.?];3110 object.updateSymtabSize(self);
3046 const vaddr = phdr.p_vaddr + @as(u64, entry_size) * index;3111 sizes.nlocals += object.output_symtab_size.nlocals;
3047 const got_entry = self.got_table.entries.items[index];3112 sizes.nglobals += object.output_symtab_size.nglobals;
3048 const got_value = self.getSymbol(got_entry).st_value;
3049 switch (entry_size) {
3050 2 => {
3051 var buf: [2]u8 = undefined;
3052 mem.writeInt(u16, &buf, @as(u16, @intCast(got_value)), endian);
3053 try self.base.file.?.pwriteAll(&buf, off);
3054 },
3055 4 => {
3056 var buf: [4]u8 = undefined;
3057 mem.writeInt(u32, &buf, @as(u32, @intCast(got_value)), endian);
3058 try self.base.file.?.pwriteAll(&buf, off);
3059 },
3060 8 => {
3061 var buf: [8]u8 = undefined;
3062 mem.writeInt(u64, &buf, got_value, endian);
3063 try self.base.file.?.pwriteAll(&buf, off);
3064
3065 if (self.base.child_pid) |pid| {
3066 switch (builtin.os.tag) {
3067 .linux => {
3068 var local_vec: [1]std.os.iovec_const = .{.{
3069 .iov_base = &buf,
3070 .iov_len = buf.len,
3071 }};
3072 var remote_vec: [1]std.os.iovec_const = .{.{
3073 .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(vaddr)))),
3074 .iov_len = buf.len,
3075 }};
3076 const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0);
3077 switch (std.os.errno(rc)) {
3078 .SUCCESS => assert(rc == buf.len),
3079 else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
3080 }
3081 },
3082 else => return error.HotSwapUnavailableOnHostOperatingSystem,
3083 }
3084 }
3085 },
3086 else => unreachable,
3087 }3113 }
3088}
30893114
3090fn elf32SymFromSym(sym: elf.Elf64_Sym, out: *elf.Elf32_Sym) void {3115 if (self.got_section_index) |_| {
3091 out.* = .{3116 self.got.updateSymtabSize(self);
3092 .st_name = sym.st_name,3117 sizes.nlocals += self.got.output_symtab_size.nlocals;
3093 .st_value = @as(u32, @intCast(sym.st_value)),3118 }
3094 .st_size = @as(u32, @intCast(sym.st_size)),3119
3095 .st_info = sym.st_info,3120 if (self.linker_defined_index) |index| {
3096 .st_other = sym.st_other,3121 const linker_defined = self.file(index).?.linker_defined;
3097 .st_shndx = sym.st_shndx,3122 linker_defined.updateSymtabSize(self);
3098 };3123 sizes.nlocals += linker_defined.output_symtab_size.nlocals;
3099}3124 }
3125
3126 const shdr = &self.shdrs.items[self.symtab_section_index.?];
3127 shdr.sh_info = sizes.nlocals + 1;
3128 self.markDirty(self.symtab_section_index.?, null);
31003129
3101fn writeSymbols(self: *Elf) !void {
3102 const gpa = self.base.allocator;
3103 const sym_size: u64 = switch (self.ptr_width) {3130 const sym_size: u64 = switch (self.ptr_width) {
3104 .p32 => @sizeOf(elf.Elf32_Sym),3131 .p32 => @sizeOf(elf.Elf32_Sym),
3105 .p64 => @sizeOf(elf.Elf64_Sym),3132 .p64 => @sizeOf(elf.Elf64_Sym),
...@@ -3108,54 +3135,80 @@ fn writeSymbols(self: *Elf) !void {...@@ -3108,54 +3135,80 @@ fn writeSymbols(self: *Elf) !void {
3108 .p32 => @alignOf(elf.Elf32_Sym),3135 .p32 => @alignOf(elf.Elf32_Sym),
3109 .p64 => @alignOf(elf.Elf64_Sym),3136 .p64 => @alignOf(elf.Elf64_Sym),
3110 };3137 };
3138 const needed_size = (sizes.nlocals + sizes.nglobals + 1) * sym_size;
3139 try self.growNonAllocSection(self.symtab_section_index.?, needed_size, sym_align, true);
3140}
31113141
3112 const shdr = &self.sections.items(.shdr)[self.symtab_section_index.?];3142fn writeSymtab(self: *Elf) !void {
3113 shdr.sh_info = @intCast(self.local_symbols.items.len);3143 const gpa = self.base.allocator;
3114 self.markDirty(self.symtab_section_index.?, null);3144 const shdr = &self.shdrs.items[self.symtab_section_index.?];
3145 const sym_size: u64 = switch (self.ptr_width) {
3146 .p32 => @sizeOf(elf.Elf32_Sym),
3147 .p64 => @sizeOf(elf.Elf64_Sym),
3148 };
3149 const nsyms = math.cast(usize, @divExact(shdr.sh_size, sym_size)) orelse return error.Overflow;
31153150
3116 const nsyms = self.local_symbols.items.len + self.global_symbols.items.len;3151 log.debug("writing {d} symbols at 0x{x}", .{ nsyms, shdr.sh_offset });
3117 const needed_size = nsyms * sym_size;3152
3118 try self.growNonAllocSection(self.symtab_section_index.?, needed_size, sym_align, true);3153 const symtab = try gpa.alloc(elf.Elf64_Sym, nsyms);
3154 defer gpa.free(symtab);
3155 symtab[0] = null_sym;
3156
3157 var ctx: struct { ilocal: usize, iglobal: usize, symtab: []elf.Elf64_Sym } = .{
3158 .ilocal = 1,
3159 .iglobal = shdr.sh_info,
3160 .symtab = symtab,
3161 };
3162
3163 if (self.zig_module_index) |index| {
3164 const zig_module = self.file(index).?.zig_module;
3165 zig_module.writeSymtab(self, ctx);
3166 ctx.ilocal += zig_module.output_symtab_size.nlocals;
3167 ctx.iglobal += zig_module.output_symtab_size.nglobals;
3168 }
3169
3170 for (self.objects.items) |index| {
3171 const object = self.file(index).?.object;
3172 object.writeSymtab(self, ctx);
3173 ctx.ilocal += object.output_symtab_size.nlocals;
3174 ctx.iglobal += object.output_symtab_size.nglobals;
3175 }
3176
3177 if (self.got_section_index) |_| {
3178 try self.got.writeSymtab(self, ctx);
3179 ctx.ilocal += self.got.output_symtab_size.nlocals;
3180 }
3181
3182 if (self.linker_defined_index) |index| {
3183 const linker_defined = self.file(index).?.linker_defined;
3184 linker_defined.writeSymtab(self, ctx);
3185 ctx.ilocal += linker_defined.output_symtab_size.nlocals;
3186 }
31193187
3120 const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian();3188 const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian();
3121 log.debug("writing {d} symbols at 0x{x}", .{ nsyms, shdr.sh_offset });
3122 switch (self.ptr_width) {3189 switch (self.ptr_width) {
3123 .p32 => {3190 .p32 => {
3124 const buf = try gpa.alloc(elf.Elf32_Sym, nsyms);3191 const buf = try gpa.alloc(elf.Elf32_Sym, symtab.len);
3125 defer gpa.free(buf);3192 defer gpa.free(buf);
31263193
3127 for (buf[0..self.local_symbols.items.len], self.local_symbols.items) |*sym, local| {3194 for (buf, symtab) |*out, sym| {
3128 elf32SymFromSym(local, sym);3195 out.* = .{
3129 if (foreign_endian) {3196 .st_name = sym.st_name,
3130 mem.byteSwapAllFields(elf.Elf32_Sym, sym);3197 .st_info = sym.st_info,
3131 }3198 .st_other = sym.st_other,
3132 }3199 .st_shndx = sym.st_shndx,
31333200 .st_value = @as(u32, @intCast(sym.st_value)),
3134 for (buf[self.local_symbols.items.len..], self.global_symbols.items) |*sym, global| {3201 .st_size = @as(u32, @intCast(sym.st_size)),
3135 elf32SymFromSym(global, sym);3202 };
3136 if (foreign_endian) {3203 if (foreign_endian) mem.byteSwapAllFields(elf.Elf32_Sym, out);
3137 mem.byteSwapAllFields(elf.Elf32_Sym, sym);
3138 }
3139 }3204 }
3140 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), shdr.sh_offset);3205 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), shdr.sh_offset);
3141 },3206 },
3142 .p64 => {3207 .p64 => {
3143 const buf = try gpa.alloc(elf.Elf64_Sym, nsyms);3208 if (foreign_endian) {
3144 defer gpa.free(buf);3209 for (symtab) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym);
3145 for (buf[0..self.local_symbols.items.len], self.local_symbols.items) |*sym, local| {
3146 sym.* = local;
3147 if (foreign_endian) {
3148 mem.byteSwapAllFields(elf.Elf64_Sym, sym);
3149 }
3150 }
3151
3152 for (buf[self.local_symbols.items.len..], self.global_symbols.items) |*sym, global| {
3153 sym.* = global;
3154 if (foreign_endian) {
3155 mem.byteSwapAllFields(elf.Elf64_Sym, sym);
3156 }
3157 }3210 }
3158 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), shdr.sh_offset);3211 try self.base.file.?.pwriteAll(mem.sliceAsBytes(symtab), shdr.sh_offset);
3159 },3212 },
3160 }3213 }
3161}3214}
...@@ -3170,11 +3223,11 @@ fn ptrWidthBytes(self: Elf) u8 {...@@ -3170,11 +3223,11 @@ fn ptrWidthBytes(self: Elf) u8 {
31703223
3171/// Does not necessarily match `ptrWidthBytes` for example can be 2 bytes3224/// Does not necessarily match `ptrWidthBytes` for example can be 2 bytes
3172/// in a 32-bit ELF file.3225/// in a 32-bit ELF file.
3173fn archPtrWidthBytes(self: Elf) u8 {3226pub fn archPtrWidthBytes(self: Elf) u8 {
3174 return @as(u8, @intCast(self.base.options.target.ptrBitWidth() / 8));3227 return @as(u8, @intCast(@divExact(self.base.options.target.ptrBitWidth(), 8)));
3175}3228}
31763229
3177fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr {3230fn phdrTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr {
3178 return .{3231 return .{
3179 .p_type = phdr.p_type,3232 .p_type = phdr.p_type,
3180 .p_flags = phdr.p_flags,3233 .p_flags = phdr.p_flags,
...@@ -3187,7 +3240,30 @@ fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr {...@@ -3187,7 +3240,30 @@ fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr {
3187 };3240 };
3188}3241}
31893242
3190fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {3243fn writeShdr(self: *Elf, index: usize) !void {
3244 const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian();
3245 switch (self.ptr_width) {
3246 .p32 => {
3247 var shdr: [1]elf.Elf32_Shdr = undefined;
3248 shdr[0] = shdrTo32(self.shdrs.items[index]);
3249 if (foreign_endian) {
3250 mem.byteSwapAllFields(elf.Elf32_Shdr, &shdr[0]);
3251 }
3252 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr);
3253 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
3254 },
3255 .p64 => {
3256 var shdr = [1]elf.Elf64_Shdr{self.shdrs.items[index]};
3257 if (foreign_endian) {
3258 mem.byteSwapAllFields(elf.Elf64_Shdr, &shdr[0]);
3259 }
3260 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr);
3261 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
3262 },
3263 }
3264}
3265
3266fn shdrTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
3191 return .{3267 return .{
3192 .sh_name = shdr.sh_name,3268 .sh_name = shdr.sh_name,
3193 .sh_type = shdr.sh_type,3269 .sh_type = shdr.sh_type,
...@@ -3448,61 +3524,474 @@ const CsuObjects = struct {...@@ -3448,61 +3524,474 @@ const CsuObjects = struct {
3448 }3524 }
3449};3525};
34503526
3451fn logSymtab(self: Elf) void {3527pub fn calcImageBase(self: Elf) u64 {
3452 log.debug("locals:", .{});3528 if (self.base.options.pic) return 0; // TODO flag an error if PIC and image_base_override
3453 for (self.local_symbols.items, 0..) |sym, id| {3529 return self.base.options.image_base_override orelse switch (self.ptr_width) {
3454 log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.strtab.get(sym.st_name), sym.st_value, sym.st_shndx });3530 .p32 => 0x1000,
3531 .p64 => 0x1000000,
3532 };
3533}
3534
3535pub fn defaultEntryAddress(self: Elf) u64 {
3536 if (self.entry_addr) |addr| return addr;
3537 return switch (self.base.options.target.cpu.arch) {
3538 .spu_2 => 0,
3539 else => default_entry_addr,
3540 };
3541}
3542
3543pub fn isDynLib(self: Elf) bool {
3544 return self.base.options.output_mode == .Lib and self.base.options.link_mode == .Dynamic;
3545}
3546
3547pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u16 {
3548 for (self.shdrs.items, 0..) |*shdr, i| {
3549 const this_name = self.shstrtab.getAssumeExists(shdr.sh_name);
3550 if (mem.eql(u8, this_name, name)) return @as(u16, @intCast(i));
3551 } else return null;
3552}
3553
3554pub fn calcNumIRelativeRelocs(self: *Elf) u64 {
3555 _ = self;
3556 unreachable; // TODO
3557}
3558
3559pub fn atom(self: *Elf, atom_index: Atom.Index) ?*Atom {
3560 if (atom_index == 0) return null;
3561 assert(atom_index < self.atoms.items.len);
3562 return &self.atoms.items[atom_index];
3563}
3564
3565pub fn addAtom(self: *Elf) !Atom.Index {
3566 const index = @as(Atom.Index, @intCast(self.atoms.items.len));
3567 const atom_ptr = try self.atoms.addOne(self.base.allocator);
3568 atom_ptr.* = .{ .atom_index = index };
3569 return index;
3570}
3571
3572pub fn file(self: *Elf, index: File.Index) ?File {
3573 const tag = self.files.items(.tags)[index];
3574 return switch (tag) {
3575 .null => null,
3576 .linker_defined => .{ .linker_defined = &self.files.items(.data)[index].linker_defined },
3577 .zig_module => .{ .zig_module = &self.files.items(.data)[index].zig_module },
3578 .object => .{ .object = &self.files.items(.data)[index].object },
3579 };
3580}
3581
3582/// Returns pointer-to-symbol described at sym_index.
3583pub fn symbol(self: *Elf, sym_index: Symbol.Index) *Symbol {
3584 return &self.symbols.items[sym_index];
3585}
3586
3587pub fn addSymbol(self: *Elf) !Symbol.Index {
3588 try self.symbols.ensureUnusedCapacity(self.base.allocator, 1);
3589 const index = blk: {
3590 if (self.symbols_free_list.popOrNull()) |index| {
3591 log.debug(" (reusing symbol index {d})", .{index});
3592 break :blk index;
3593 } else {
3594 log.debug(" (allocating symbol index {d})", .{self.symbols.items.len});
3595 const index = @as(Symbol.Index, @intCast(self.symbols.items.len));
3596 _ = self.symbols.addOneAssumeCapacity();
3597 break :blk index;
3598 }
3599 };
3600 self.symbols.items[index] = .{ .index = index };
3601 return index;
3602}
3603
3604pub fn addSymbolExtra(self: *Elf, extra: Symbol.Extra) !u32 {
3605 const fields = @typeInfo(Symbol.Extra).Struct.fields;
3606 try self.symbols_extra.ensureUnusedCapacity(self.base.allocator, fields.len);
3607 return self.addSymbolExtraAssumeCapacity(extra);
3608}
3609
3610pub fn addSymbolExtraAssumeCapacity(self: *Elf, extra: Symbol.Extra) u32 {
3611 const index = @as(u32, @intCast(self.symbols_extra.items.len));
3612 const fields = @typeInfo(Symbol.Extra).Struct.fields;
3613 inline for (fields) |field| {
3614 self.symbols_extra.appendAssumeCapacity(switch (field.type) {
3615 u32 => @field(extra, field.name),
3616 else => @compileError("bad field type"),
3617 });
3618 }
3619 return index;
3620}
3621
3622pub fn symbolExtra(self: *Elf, index: u32) ?Symbol.Extra {
3623 if (index == 0) return null;
3624 const fields = @typeInfo(Symbol.Extra).Struct.fields;
3625 var i: usize = index;
3626 var result: Symbol.Extra = undefined;
3627 inline for (fields) |field| {
3628 @field(result, field.name) = switch (field.type) {
3629 u32 => self.symbols_extra.items[i],
3630 else => @compileError("bad field type"),
3631 };
3632 i += 1;
3455 }3633 }
3456 log.debug("globals:", .{});3634 return result;
3457 for (self.global_symbols.items, 0..) |sym, id| {3635}
3458 log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.strtab.get(sym.st_name), sym.st_value, sym.st_shndx });3636
3637pub fn setSymbolExtra(self: *Elf, index: u32, extra: Symbol.Extra) void {
3638 assert(index > 0);
3639 const fields = @typeInfo(Symbol.Extra).Struct.fields;
3640 inline for (fields, 0..) |field, i| {
3641 self.symbols_extra.items[index + i] = switch (field.type) {
3642 u32 => @field(extra, field.name),
3643 else => @compileError("bad field type"),
3644 };
3459 }3645 }
3460}3646}
34613647
3462pub fn getProgramHeader(self: *const Elf, shdr_index: u16) elf.Elf64_Phdr {3648const GetOrPutGlobalResult = struct {
3463 const index = self.sections.items(.phdr_index)[shdr_index];3649 found_existing: bool,
3464 return self.program_headers.items[index];3650 index: Symbol.Index,
3651};
3652
3653pub fn getOrPutGlobal(self: *Elf, name_off: u32) !GetOrPutGlobalResult {
3654 const gpa = self.base.allocator;
3655 const gop = try self.resolver.getOrPut(gpa, name_off);
3656 if (!gop.found_existing) {
3657 const index = try self.addSymbol();
3658 const global = self.symbol(index);
3659 global.name_offset = name_off;
3660 gop.value_ptr.* = index;
3661 }
3662 return .{
3663 .found_existing = gop.found_existing,
3664 .index = gop.value_ptr.*,
3665 };
3465}3666}
34663667
3467pub fn getProgramHeaderPtr(self: *Elf, shdr_index: u16) *elf.Elf64_Phdr {3668pub fn globalByName(self: *Elf, name: []const u8) ?Symbol.Index {
3468 const index = self.sections.items(.phdr_index)[shdr_index];3669 const name_off = self.strtab.getOffset(name) orelse return null;
3469 return &self.program_headers.items[index];3670 return self.resolver.get(name_off);
3470}3671}
34713672
3472/// Returns pointer-to-symbol described at sym_index.3673pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 {
3473pub fn getSymbolPtr(self: *Elf, sym_index: u32) *elf.Elf64_Sym {3674 _ = lib_name;
3474 return &self.local_symbols.items[sym_index];3675 const gpa = self.base.allocator;
3676 const off = try self.strtab.insert(gpa, name);
3677 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
3678 const lookup_gop = try zig_module.globals_lookup.getOrPut(gpa, off);
3679 if (!lookup_gop.found_existing) {
3680 const esym_index = try zig_module.addGlobalEsym(gpa);
3681 const esym = zig_module.elfSym(esym_index);
3682 esym.st_name = off;
3683 lookup_gop.value_ptr.* = esym_index;
3684 const gop = try self.getOrPutGlobal(off);
3685 try zig_module.global_symbols.append(gpa, gop.index);
3686 }
3687 return lookup_gop.value_ptr.*;
3475}3688}
34763689
3477/// Returns symbol at sym_index.3690const GetOrCreateComdatGroupOwnerResult = struct {
3478pub fn getSymbol(self: *const Elf, sym_index: u32) elf.Elf64_Sym {3691 found_existing: bool,
3479 return self.local_symbols.items[sym_index];3692 index: ComdatGroupOwner.Index,
3693};
3694
3695pub fn getOrCreateComdatGroupOwner(self: *Elf, off: u32) !GetOrCreateComdatGroupOwnerResult {
3696 const gpa = self.base.allocator;
3697 const gop = try self.comdat_groups_table.getOrPut(gpa, off);
3698 if (!gop.found_existing) {
3699 const index = @as(ComdatGroupOwner.Index, @intCast(self.comdat_groups_owners.items.len));
3700 const owner = try self.comdat_groups_owners.addOne(gpa);
3701 owner.* = .{};
3702 gop.value_ptr.* = index;
3703 }
3704 return .{
3705 .found_existing = gop.found_existing,
3706 .index = gop.value_ptr.*,
3707 };
3480}3708}
34813709
3482/// Returns name of the symbol at sym_index.3710pub fn addComdatGroup(self: *Elf) !ComdatGroup.Index {
3483pub fn getSymbolName(self: *const Elf, sym_index: u32) []const u8 {3711 const index = @as(ComdatGroup.Index, @intCast(self.comdat_groups.items.len));
3484 const sym = self.local_symbols.items[sym_index];3712 _ = try self.comdat_groups.addOne(self.base.allocator);
3485 return self.strtab.get(sym.st_name).?;3713 return index;
3486}3714}
34873715
3488/// Returns name of the global symbol at index.3716pub fn comdatGroup(self: *Elf, index: ComdatGroup.Index) *ComdatGroup {
3489pub fn getGlobalName(self: *const Elf, index: u32) []const u8 {3717 assert(index < self.comdat_groups.items.len);
3490 const sym = self.global_symbols.items[index];3718 return &self.comdat_groups.items[index];
3491 return self.strtab.get(sym.st_name).?;
3492}3719}
34933720
3494pub fn getAtom(self: *const Elf, atom_index: Atom.Index) Atom {3721pub fn comdatGroupOwner(self: *Elf, index: ComdatGroupOwner.Index) *ComdatGroupOwner {
3495 assert(atom_index < self.atoms.items.len);3722 assert(index < self.comdat_groups_owners.items.len);
3496 return self.atoms.items[atom_index];3723 return &self.comdat_groups_owners.items[index];
3497}3724}
34983725
3499pub fn getAtomPtr(self: *Elf, atom_index: Atom.Index) *Atom {3726fn reportUndefined(self: *Elf, undefs: anytype) !void {
3500 assert(atom_index < self.atoms.items.len);3727 const gpa = self.base.allocator;
3501 return &self.atoms.items[atom_index];3728 const max_notes = 4;
3729
3730 try self.misc_errors.ensureUnusedCapacity(gpa, undefs.count());
3731
3732 var it = undefs.iterator();
3733 while (it.next()) |entry| {
3734 const undef_index = entry.key_ptr.*;
3735 const atoms = entry.value_ptr.*.items;
3736 const nnotes = @min(atoms.len, max_notes);
3737
3738 var notes = try std.ArrayList(link.File.ErrorMsg).initCapacity(gpa, max_notes + 1);
3739 defer notes.deinit();
3740
3741 for (atoms[0..nnotes]) |atom_index| {
3742 const atom_ptr = self.atom(atom_index).?;
3743 const file_ptr = self.file(atom_ptr.file_index).?;
3744 const note = try std.fmt.allocPrint(gpa, "referenced by {s}:{s}", .{
3745 file_ptr.fmtPath(),
3746 atom_ptr.name(self),
3747 });
3748 notes.appendAssumeCapacity(.{ .msg = note });
3749 }
3750
3751 if (atoms.len > max_notes) {
3752 const remaining = atoms.len - max_notes;
3753 const note = try std.fmt.allocPrint(gpa, "referenced {d} more times", .{remaining});
3754 notes.appendAssumeCapacity(.{ .msg = note });
3755 }
3756
3757 var err_msg = link.File.ErrorMsg{
3758 .msg = try std.fmt.allocPrint(gpa, "undefined symbol: {s}", .{self.symbol(undef_index).name(self)}),
3759 };
3760 err_msg.notes = try notes.toOwnedSlice();
3761
3762 self.misc_errors.appendAssumeCapacity(err_msg);
3763 }
3764}
3765
3766const ParseErrorCtx = struct {
3767 detected_cpu_arch: std.Target.Cpu.Arch,
3768};
3769
3770fn handleAndReportParseError(
3771 self: *Elf,
3772 path: []const u8,
3773 err: ParseError,
3774 ctx: *const ParseErrorCtx,
3775) error{OutOfMemory}!void {
3776 const cpu_arch = self.base.options.target.cpu.arch;
3777 switch (err) {
3778 error.UnknownFileType => try self.reportParseError(path, "unknown file type", .{}),
3779 error.InvalidCpuArch => try self.reportParseError(
3780 path,
3781 "invalid cpu architecture: expected '{s}', but found '{s}'",
3782 .{ @tagName(cpu_arch), @tagName(ctx.detected_cpu_arch) },
3783 ),
3784 else => |e| try self.reportParseError(
3785 path,
3786 "unexpected error: parsing object failed with error {s}",
3787 .{@errorName(e)},
3788 ),
3789 }
3790}
3791
3792fn reportParseError(
3793 self: *Elf,
3794 path: []const u8,
3795 comptime format: []const u8,
3796 args: anytype,
3797) error{OutOfMemory}!void {
3798 const gpa = self.base.allocator;
3799 try self.misc_errors.ensureUnusedCapacity(gpa, 1);
3800 var notes = try gpa.alloc(link.File.ErrorMsg, 1);
3801 errdefer gpa.free(notes);
3802 notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "while parsing {s}", .{path}) };
3803 self.misc_errors.appendAssumeCapacity(.{
3804 .msg = try std.fmt.allocPrint(gpa, format, args),
3805 .notes = notes,
3806 });
3502}3807}
35033808
3504/// Returns atom if there is an atom referenced by the symbol.3809fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) {
3505/// Returns null on failure.3810 return .{ .data = self };
3506pub fn getAtomIndexForSymbol(self: *Elf, sym_index: u32) ?Atom.Index {
3507 return self.atom_by_index_table.get(sym_index);
3508}3811}
3812
3813fn fmtDumpState(
3814 self: *Elf,
3815 comptime unused_fmt_string: []const u8,
3816 options: std.fmt.FormatOptions,
3817 writer: anytype,
3818) !void {
3819 _ = unused_fmt_string;
3820 _ = options;
3821
3822 if (self.zig_module_index) |index| {
3823 const zig_module = self.file(index).?.zig_module;
3824 try writer.print("zig_module({d}) : {s}\n", .{ index, zig_module.path });
3825 try writer.print("{}\n", .{zig_module.fmtSymtab(self)});
3826 }
3827
3828 for (self.objects.items) |index| {
3829 const object = self.file(index).?.object;
3830 try writer.print("object({d}) : {}", .{ index, object.fmtPath() });
3831 if (!object.alive) try writer.writeAll(" : [*]");
3832 try writer.writeByte('\n');
3833 try writer.print("{}{}{}{}{}\n", .{
3834 object.fmtAtoms(self),
3835 object.fmtCies(self),
3836 object.fmtFdes(self),
3837 object.fmtSymtab(self),
3838 object.fmtComdatGroups(self),
3839 });
3840 }
3841
3842 if (self.linker_defined_index) |index| {
3843 const linker_defined = self.file(index).?.linker_defined;
3844 try writer.print("linker_defined({d}) : (linker defined)\n", .{index});
3845 try writer.print("{}\n", .{linker_defined.fmtSymtab(self)});
3846 }
3847 try writer.print("{}\n", .{self.got.fmt(self)});
3848}
3849
3850/// Binary search
3851pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
3852 if (!@hasDecl(@TypeOf(predicate), "predicate"))
3853 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
3854
3855 var min: usize = 0;
3856 var max: usize = haystack.len;
3857 while (min < max) {
3858 const index = (min + max) / 2;
3859 const curr = haystack[index];
3860 if (predicate.predicate(curr)) {
3861 min = index + 1;
3862 } else {
3863 max = index;
3864 }
3865 }
3866 return min;
3867}
3868
3869/// Linear search
3870pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
3871 if (!@hasDecl(@TypeOf(predicate), "predicate"))
3872 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
3873
3874 var i: usize = 0;
3875 while (i < haystack.len) : (i += 1) {
3876 if (predicate.predicate(haystack[i])) break;
3877 }
3878 return i;
3879}
3880
3881const default_entry_addr = 0x8000000;
3882
3883pub const base_tag: link.File.Tag = .elf;
3884
3885const LastAtomAndFreeList = struct {
3886 /// Index of the last allocated atom in this section.
3887 last_atom_index: Atom.Index = 0,
3888
3889 /// A list of atoms that have surplus capacity. This list can have false
3890 /// positives, as functions grow and shrink over time, only sometimes being added
3891 /// or removed from the freelist.
3892 ///
3893 /// An atom has surplus capacity when its overcapacity value is greater than
3894 /// padToIdeal(minimum_atom_size). That is, when it has so
3895 /// much extra capacity, that we could fit a small new symbol in it, itself with
3896 /// ideal_capacity or more.
3897 ///
3898 /// Ideal capacity is defined by size + (size / ideal_factor)
3899 ///
3900 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
3901 /// overcapacity can be negative. A simple way to have negative overcapacity is to
3902 /// allocate a fresh text block, which will have ideal capacity, and then grow it
3903 /// by 1 byte. It will then have -1 overcapacity.
3904 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
3905};
3906
3907const LazySymbolMetadata = struct {
3908 const State = enum { unused, pending_flush, flushed };
3909 text_symbol_index: Symbol.Index = undefined,
3910 rodata_symbol_index: Symbol.Index = undefined,
3911 text_state: State = .unused,
3912 rodata_state: State = .unused,
3913};
3914
3915const DeclMetadata = struct {
3916 symbol_index: Symbol.Index,
3917 /// A list of all exports aliases of this Decl.
3918 exports: std.ArrayListUnmanaged(Symbol.Index) = .{},
3919
3920 fn @"export"(m: DeclMetadata, elf_file: *Elf, name: []const u8) ?*u32 {
3921 const zig_module = elf_file.file(elf_file.zig_module_index.?).?.zig_module;
3922 for (m.exports.items) |*exp| {
3923 const exp_name = elf_file.strtab.getAssumeExists(zig_module.elfSym(exp.*).st_name);
3924 if (mem.eql(u8, name, exp_name)) return exp;
3925 }
3926 return null;
3927 }
3928};
3929
3930const ComdatGroupOwner = struct {
3931 file: File.Index = 0,
3932 const Index = u32;
3933};
3934
3935pub const ComdatGroup = struct {
3936 owner: ComdatGroupOwner.Index,
3937 shndx: u16,
3938 pub const Index = u32;
3939};
3940
3941pub const SymtabSize = struct {
3942 nlocals: u32 = 0,
3943 nglobals: u32 = 0,
3944};
3945
3946pub const null_sym = elf.Elf64_Sym{
3947 .st_name = 0,
3948 .st_info = 0,
3949 .st_other = 0,
3950 .st_shndx = 0,
3951 .st_value = 0,
3952 .st_size = 0,
3953};
3954
3955const std = @import("std");
3956const build_options = @import("build_options");
3957const builtin = @import("builtin");
3958const assert = std.debug.assert;
3959const elf = std.elf;
3960const fs = std.fs;
3961const log = std.log.scoped(.link);
3962const state_log = std.log.scoped(.link_state);
3963const math = std.math;
3964const mem = std.mem;
3965
3966const codegen = @import("../codegen.zig");
3967const glibc = @import("../glibc.zig");
3968const link = @import("../link.zig");
3969const lldMain = @import("../main.zig").lldMain;
3970const musl = @import("../musl.zig");
3971const target_util = @import("../target.zig");
3972const trace = @import("../tracy.zig").trace;
3973const synthetic_sections = @import("Elf/synthetic_sections.zig");
3974
3975const Air = @import("../Air.zig");
3976const Allocator = std.mem.Allocator;
3977pub const Atom = @import("Elf/Atom.zig");
3978const Cache = std.Build.Cache;
3979const Compilation = @import("../Compilation.zig");
3980const Dwarf = @import("Dwarf.zig");
3981const Elf = @This();
3982const File = @import("Elf/file.zig").File;
3983const GotSection = synthetic_sections.GotSection;
3984const LinkerDefined = @import("Elf/LinkerDefined.zig");
3985const Liveness = @import("../Liveness.zig");
3986const LlvmObject = @import("../codegen/llvm.zig").Object;
3987const Module = @import("../Module.zig");
3988const Object = @import("Elf/Object.zig");
3989const InternPool = @import("../InternPool.zig");
3990const Package = @import("../Package.zig");
3991const Symbol = @import("Elf/Symbol.zig");
3992const StringTable = @import("strtab.zig").StringTable;
3993const TableSection = @import("table_section.zig").TableSection;
3994const Type = @import("../type.zig").Type;
3995const TypedValue = @import("../TypedValue.zig");
3996const Value = @import("../value.zig").Value;
3997const ZigModule = @import("Elf/ZigModule.zig");
src/link/Elf/Atom.zig+570-74
...@@ -1,108 +1,604 @@...@@ -1,108 +1,604 @@
1const Atom = @This();1/// Address allocated for this Atom.
2value: u64 = 0,
23
3const std = @import("std");4/// Name of this Atom.
4const assert = std.debug.assert;5name_offset: u32 = 0,
5const elf = std.elf;
66
7const Elf = @import("../Elf.zig");7/// Index into linker's input file table.
8file_index: File.Index = 0,
89
9/// Each decl always gets a local symbol with the fully qualified name.10/// Size of this atom
10/// The vaddr and size are found here directly.11size: u64 = 0,
11/// The file offset is found by computing the vaddr offset from the section vaddr
12/// the symbol references, and adding that to the file offset of the section.
13/// If this field is 0, it means the codegen size = 0 and there is no symbol or
14/// offset table entry.
15local_sym_index: u32,
1612
17/// Points to the previous and next neighbors, based on the `text_offset`.13/// Alignment of this atom as a power of two.
18/// This can be used to find, for example, the capacity of this `TextBlock`.14alignment: u8 = 0,
19prev_index: ?Index,
20next_index: ?Index,
2115
22pub const Index = u32;16/// Index of the input section.
17input_section_index: Index = 0,
2318
24pub const Reloc = struct {19/// Index of the output section.
25 target: u32,20output_section_index: Index = 0,
26 offset: u64,
27 addend: u32,
28 prev_vaddr: u64,
29};
3021
31pub fn getSymbolIndex(self: Atom) ?u32 {22/// Index of the input section containing this atom's relocs.
32 if (self.local_sym_index == 0) return null;23relocs_section_index: Index = 0,
33 return self.local_sym_index;24
34}25/// Index of this atom in the linker's atoms table.
26atom_index: Index = 0,
27
28/// Specifies whether this atom is alive or has been garbage collected.
29alive: bool = false,
30
31/// Specifies if the atom has been visited during garbage collection.
32visited: bool = false,
33
34/// Start index of FDEs referencing this atom.
35fde_start: u32 = 0,
3536
36pub fn getSymbol(self: Atom, elf_file: *const Elf) elf.Elf64_Sym {37/// End index of FDEs referencing this atom.
37 return elf_file.getSymbol(self.getSymbolIndex().?);38fde_end: u32 = 0,
39
40/// Points to the previous and next neighbors, based on the `text_offset`.
41/// This can be used to find, for example, the capacity of this `TextBlock`.
42prev_index: Index = 0,
43next_index: Index = 0,
44
45pub fn name(self: Atom, elf_file: *Elf) []const u8 {
46 return elf_file.strtab.getAssumeExists(self.name_offset);
38}47}
3948
40pub fn getSymbolPtr(self: Atom, elf_file: *Elf) *elf.Elf64_Sym {49pub fn inputShdr(self: Atom, elf_file: *Elf) elf.Elf64_Shdr {
41 return elf_file.getSymbolPtr(self.getSymbolIndex().?);50 const object = elf_file.file(self.file_index).?.object;
51 return object.shdrs.items[self.input_section_index];
42}52}
4353
44pub fn getName(self: Atom, elf_file: *const Elf) []const u8 {54pub fn codeInObject(self: Atom, elf_file: *Elf) error{Overflow}![]const u8 {
45 return elf_file.getSymbolName(self.getSymbolIndex().?);55 const object = elf_file.file(self.file_index).?.object;
56 return object.shdrContents(self.input_section_index);
46}57}
4758
48/// If entry already exists, returns index to it.59/// Returns atom's code and optionally uncompresses data if required (for compressed sections).
49/// Otherwise, creates a new entry in the Global Offset Table for this Atom.60/// Caller owns the memory.
50pub fn getOrCreateOffsetTableEntry(self: Atom, elf_file: *Elf) !u32 {61pub fn codeInObjectUncompressAlloc(self: Atom, elf_file: *Elf) ![]u8 {
51 const sym_index = self.getSymbolIndex().?;62 const gpa = elf_file.base.allocator;
52 if (elf_file.got_table.lookup.get(sym_index)) |index| return index;63 const data = try self.codeInObject(elf_file);
53 const index = try elf_file.got_table.allocateEntry(elf_file.base.allocator, sym_index);64 const shdr = self.inputShdr(elf_file);
54 elf_file.got_table_count_dirty = true;65 if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {
55 return index;66 const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
67 switch (chdr.ch_type) {
68 .ZLIB => {
69 var stream = std.io.fixedBufferStream(data[@sizeOf(elf.Elf64_Chdr)..]);
70 var zlib_stream = std.compress.zlib.decompressStream(gpa, stream.reader()) catch
71 return error.InputOutput;
72 defer zlib_stream.deinit();
73 const size = std.math.cast(usize, chdr.ch_size) orelse return error.Overflow;
74 const decomp = try gpa.alloc(u8, size);
75 const nread = zlib_stream.reader().readAll(decomp) catch return error.InputOutput;
76 if (nread != decomp.len) {
77 return error.InputOutput;
78 }
79 return decomp;
80 },
81 else => @panic("TODO unhandled compression scheme"),
82 }
83 } else return gpa.dupe(u8, data);
56}84}
5785
58pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 {86pub fn priority(self: Atom, elf_file: *Elf) u64 {
59 const sym_index = self.getSymbolIndex().?;87 const index = elf_file.file(self.file_index).?.index();
60 const got_entry_index = elf_file.got_table.lookup.get(sym_index).?;88 return (@as(u64, @intCast(index)) << 32) | @as(u64, @intCast(self.input_section_index));
61 const target = elf_file.base.options.target;
62 const ptr_bits = target.ptrBitWidth();
63 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
64 const got = elf_file.program_headers.items[elf_file.phdr_got_index.?];
65 return got.p_vaddr + got_entry_index * ptr_bytes;
66}89}
6790
68/// Returns how much room there is to grow in virtual address space.91/// Returns how much room there is to grow in virtual address space.
69/// File offset relocation happens transparently, so it is not included in92/// File offset relocation happens transparently, so it is not included in
70/// this calculation.93/// this calculation.
71pub fn capacity(self: Atom, elf_file: *const Elf) u64 {94pub fn capacity(self: Atom, elf_file: *Elf) u64 {
72 const self_sym = self.getSymbol(elf_file);95 const next_value = if (elf_file.atom(self.next_index)) |next| next.value else std.math.maxInt(u32);
73 if (self.next_index) |next_index| {96 return next_value - self.value;
74 const next = elf_file.getAtom(next_index);
75 const next_sym = next.getSymbol(elf_file);
76 return next_sym.st_value - self_sym.st_value;
77 } else {
78 // We are the last block. The capacity is limited only by virtual address space.
79 return std.math.maxInt(u32) - self_sym.st_value;
80 }
81}97}
8298
83pub fn freeListEligible(self: Atom, elf_file: *const Elf) bool {99pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {
84 // No need to keep a free list node for the last block.100 // No need to keep a free list node for the last block.
85 const next_index = self.next_index orelse return false;101 const next = elf_file.atom(self.next_index) orelse return false;
86 const next = elf_file.getAtom(next_index);102 const cap = next.value - self.value;
87 const self_sym = self.getSymbol(elf_file);103 const ideal_cap = Elf.padToIdeal(self.size);
88 const next_sym = next.getSymbol(elf_file);
89 const cap = next_sym.st_value - self_sym.st_value;
90 const ideal_cap = Elf.padToIdeal(self_sym.st_size);
91 if (cap <= ideal_cap) return false;104 if (cap <= ideal_cap) return false;
92 const surplus = cap - ideal_cap;105 const surplus = cap - ideal_cap;
93 return surplus >= Elf.min_text_capacity;106 return surplus >= Elf.min_text_capacity;
94}107}
95108
96pub fn addRelocation(elf_file: *Elf, atom_index: Index, reloc: Reloc) !void {109pub fn allocate(self: *Atom, elf_file: *Elf) !void {
110 const shdr = &elf_file.shdrs.items[self.output_section_index];
111 const meta = elf_file.last_atom_and_free_list_table.getPtr(self.output_section_index).?;
112 const free_list = &meta.free_list;
113 const last_atom_index = &meta.last_atom_index;
114 const new_atom_ideal_capacity = Elf.padToIdeal(self.size);
115 const alignment = try std.math.powi(u64, 2, self.alignment);
116
117 // We use these to indicate our intention to update metadata, placing the new atom,
118 // and possibly removing a free list node.
119 // It would be simpler to do it inside the for loop below, but that would cause a
120 // problem if an error was returned later in the function. So this action
121 // is actually carried out at the end of the function, when errors are no longer possible.
122 var atom_placement: ?Atom.Index = null;
123 var free_list_removal: ?usize = null;
124
125 // First we look for an appropriately sized free list node.
126 // The list is unordered. We'll just take the first thing that works.
127 self.value = blk: {
128 var i: usize = if (elf_file.base.child_pid == null) 0 else free_list.items.len;
129 while (i < free_list.items.len) {
130 const big_atom_index = free_list.items[i];
131 const big_atom = elf_file.atom(big_atom_index).?;
132 // We now have a pointer to a live atom that has too much capacity.
133 // Is it enough that we could fit this new atom?
134 const cap = big_atom.capacity(elf_file);
135 const ideal_capacity = Elf.padToIdeal(cap);
136 const ideal_capacity_end_vaddr = std.math.add(u64, big_atom.value, ideal_capacity) catch ideal_capacity;
137 const capacity_end_vaddr = big_atom.value + cap;
138 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
139 const new_start_vaddr = std.mem.alignBackward(u64, new_start_vaddr_unaligned, alignment);
140 if (new_start_vaddr < ideal_capacity_end_vaddr) {
141 // Additional bookkeeping here to notice if this free list node
142 // should be deleted because the block that it points to has grown to take up
143 // more of the extra capacity.
144 if (!big_atom.freeListEligible(elf_file)) {
145 _ = free_list.swapRemove(i);
146 } else {
147 i += 1;
148 }
149 continue;
150 }
151 // At this point we know that we will place the new block here. But the
152 // remaining question is whether there is still yet enough capacity left
153 // over for there to still be a free list node.
154 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
155 const keep_free_list_node = remaining_capacity >= Elf.min_text_capacity;
156
157 // Set up the metadata to be updated, after errors are no longer possible.
158 atom_placement = big_atom_index;
159 if (!keep_free_list_node) {
160 free_list_removal = i;
161 }
162 break :blk new_start_vaddr;
163 } else if (elf_file.atom(last_atom_index.*)) |last| {
164 const ideal_capacity = Elf.padToIdeal(last.size);
165 const ideal_capacity_end_vaddr = last.value + ideal_capacity;
166 const new_start_vaddr = std.mem.alignForward(u64, ideal_capacity_end_vaddr, alignment);
167 // Set up the metadata to be updated, after errors are no longer possible.
168 atom_placement = last.atom_index;
169 break :blk new_start_vaddr;
170 } else {
171 break :blk shdr.sh_addr;
172 }
173 };
174
175 const expand_section = if (atom_placement) |placement_index|
176 elf_file.atom(placement_index).?.next_index == 0
177 else
178 true;
179 if (expand_section) {
180 const needed_size = (self.value + self.size) - shdr.sh_addr;
181 try elf_file.growAllocSection(self.output_section_index, needed_size);
182 last_atom_index.* = self.atom_index;
183
184 if (elf_file.dwarf) |_| {
185 // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
186 // range of the compilation unit. When we expand the text section, this range changes,
187 // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty.
188 elf_file.debug_info_header_dirty = true;
189 // This becomes dirty for the same reason. We could potentially make this more
190 // fine-grained with the addition of support for more compilation units. It is planned to
191 // model each package as a different compilation unit.
192 elf_file.debug_aranges_section_dirty = true;
193 }
194 }
195 shdr.sh_addralign = @max(shdr.sh_addralign, alignment);
196
197 // This function can also reallocate an atom.
198 // In this case we need to "unplug" it from its previous location before
199 // plugging it in to its new location.
200 if (elf_file.atom(self.prev_index)) |prev| {
201 prev.next_index = self.next_index;
202 }
203 if (elf_file.atom(self.next_index)) |next| {
204 next.prev_index = self.prev_index;
205 }
206
207 if (atom_placement) |big_atom_index| {
208 const big_atom = elf_file.atom(big_atom_index).?;
209 self.prev_index = big_atom_index;
210 self.next_index = big_atom.next_index;
211 big_atom.next_index = self.atom_index;
212 } else {
213 self.prev_index = 0;
214 self.next_index = 0;
215 }
216 if (free_list_removal) |i| {
217 _ = free_list.swapRemove(i);
218 }
219}
220
221pub fn shrink(self: *Atom, elf_file: *Elf) void {
222 _ = self;
223 _ = elf_file;
224}
225
226pub fn grow(self: *Atom, elf_file: *Elf) !void {
227 const alignment = try std.math.powi(u64, 2, self.alignment);
228 const align_ok = std.mem.alignBackward(u64, self.value, alignment) == self.value;
229 const need_realloc = !align_ok or self.size > self.capacity(elf_file);
230 if (need_realloc) try self.allocate(elf_file);
231}
232
233pub fn free(self: *Atom, elf_file: *Elf) void {
234 log.debug("freeAtom {d} ({s})", .{ self.atom_index, self.name(elf_file) });
235
236 const gpa = elf_file.base.allocator;
237 const zig_module = elf_file.file(self.file_index).?.zig_module;
238 const shndx = self.output_section_index;
239 const meta = elf_file.last_atom_and_free_list_table.getPtr(shndx).?;
240 const free_list = &meta.free_list;
241 const last_atom_index = &meta.last_atom_index;
242 var already_have_free_list_node = false;
243 {
244 var i: usize = 0;
245 // TODO turn free_list into a hash map
246 while (i < free_list.items.len) {
247 if (free_list.items[i] == self.atom_index) {
248 _ = free_list.swapRemove(i);
249 continue;
250 }
251 if (free_list.items[i] == self.prev_index) {
252 already_have_free_list_node = true;
253 }
254 i += 1;
255 }
256 }
257
258 if (elf_file.atom(last_atom_index.*)) |last_atom| {
259 if (last_atom.atom_index == self.atom_index) {
260 if (elf_file.atom(self.prev_index)) |_| {
261 // TODO shrink the section size here
262 last_atom_index.* = self.prev_index;
263 } else {
264 last_atom_index.* = 0;
265 }
266 }
267 }
268
269 if (elf_file.atom(self.prev_index)) |prev| {
270 prev.next_index = self.next_index;
271 if (!already_have_free_list_node and prev.*.freeListEligible(elf_file)) {
272 // The free list is heuristics, it doesn't have to be perfect, so we can
273 // ignore the OOM here.
274 free_list.append(gpa, prev.atom_index) catch {};
275 }
276 } else {
277 self.prev_index = 0;
278 }
279
280 if (elf_file.atom(self.next_index)) |next| {
281 next.prev_index = self.prev_index;
282 } else {
283 self.next_index = 0;
284 }
285
286 // TODO create relocs free list
287 self.freeRelocs(elf_file);
288 assert(zig_module.atoms.swapRemove(self.atom_index));
289 self.* = .{};
290}
291
292pub fn relocs(self: Atom, elf_file: *Elf) error{Overflow}![]align(1) const elf.Elf64_Rela {
293 return switch (elf_file.file(self.file_index).?) {
294 .zig_module => |x| x.relocs.items[self.relocs_section_index].items,
295 .object => |x| x.getRelocs(self.relocs_section_index),
296 else => unreachable,
297 };
298}
299
300pub fn addReloc(self: Atom, elf_file: *Elf, reloc: elf.Elf64_Rela) !void {
97 const gpa = elf_file.base.allocator;301 const gpa = elf_file.base.allocator;
98 const gop = try elf_file.relocs.getOrPut(gpa, atom_index);302 const file_ptr = elf_file.file(self.file_index).?;
99 if (!gop.found_existing) {303 assert(file_ptr == .zig_module);
100 gop.value_ptr.* = .{};304 const zig_module = file_ptr.zig_module;
305 const rels = &zig_module.relocs.items[self.relocs_section_index];
306 try rels.append(gpa, reloc);
307}
308
309pub fn freeRelocs(self: Atom, elf_file: *Elf) void {
310 const file_ptr = elf_file.file(self.file_index).?;
311 assert(file_ptr == .zig_module);
312 const zig_module = file_ptr.zig_module;
313 zig_module.relocs.items[self.relocs_section_index].clearRetainingCapacity();
314}
315
316pub fn scanRelocs(self: Atom, elf_file: *Elf, undefs: anytype) !void {
317 const file_ptr = elf_file.file(self.file_index).?;
318 const rels = try self.relocs(elf_file);
319 var i: usize = 0;
320 while (i < rels.len) : (i += 1) {
321 const rel = rels[i];
322
323 if (rel.r_type() == elf.R_X86_64_NONE) continue;
324
325 const symbol = switch (file_ptr) {
326 .zig_module => |x| elf_file.symbol(x.symbol(rel.r_sym())),
327 .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]),
328 else => unreachable,
329 };
330
331 // Check for violation of One Definition Rule for COMDATs.
332 if (symbol.file(elf_file) == null) {
333 // TODO convert into an error
334 log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{
335 file_ptr.fmtPath(),
336 self.name(elf_file),
337 symbol.name(elf_file),
338 });
339 continue;
340 }
341
342 // Report an undefined symbol.
343 try self.reportUndefined(elf_file, symbol, rel, undefs);
344
345 // While traversing relocations, mark symbols that require special handling such as
346 // pointer indirection via GOT, or a stub trampoline via PLT.
347 switch (rel.r_type()) {
348 elf.R_X86_64_64 => {},
349
350 elf.R_X86_64_32,
351 elf.R_X86_64_32S,
352 => {},
353
354 elf.R_X86_64_GOT32,
355 elf.R_X86_64_GOT64,
356 elf.R_X86_64_GOTPC32,
357 elf.R_X86_64_GOTPC64,
358 elf.R_X86_64_GOTPCREL,
359 elf.R_X86_64_GOTPCREL64,
360 elf.R_X86_64_GOTPCRELX,
361 elf.R_X86_64_REX_GOTPCRELX,
362 => {
363 symbol.flags.needs_got = true;
364 },
365
366 elf.R_X86_64_PLT32,
367 elf.R_X86_64_PLTOFF64,
368 => {
369 if (symbol.flags.import) {
370 symbol.flags.needs_plt = true;
371 }
372 },
373
374 elf.R_X86_64_PC32 => {},
375
376 else => @panic("TODO"),
377 }
378 }
379}
380
381// This function will report any undefined non-weak symbols that are not imports.
382fn reportUndefined(self: Atom, elf_file: *Elf, sym: *const Symbol, rel: elf.Elf64_Rela, undefs: anytype) !void {
383 const rel_esym = switch (elf_file.file(self.file_index).?) {
384 .zig_module => |x| x.elfSym(rel.r_sym()).*,
385 .object => |x| x.symtab[rel.r_sym()],
386 else => unreachable,
387 };
388 const esym = sym.elfSym(elf_file);
389 if (rel_esym.st_shndx == elf.SHN_UNDEF and
390 rel_esym.st_bind() == elf.STB_GLOBAL and
391 sym.esym_index > 0 and
392 !sym.flags.import and
393 esym.st_shndx == elf.SHN_UNDEF)
394 {
395 const gop = try undefs.getOrPut(sym.index);
396 if (!gop.found_existing) {
397 gop.value_ptr.* = std.ArrayList(Atom.Index).init(elf_file.base.allocator);
398 }
399 try gop.value_ptr.append(self.atom_index);
400 }
401}
402
403/// TODO mark relocs dirty
404pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {
405 relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) });
406
407 const file_ptr = elf_file.file(self.file_index).?;
408 var stream = std.io.fixedBufferStream(code);
409 const cwriter = stream.writer();
410
411 for (try self.relocs(elf_file)) |rel| {
412 const r_type = rel.r_type();
413 if (r_type == elf.R_X86_64_NONE) continue;
414
415 const target = switch (file_ptr) {
416 .zig_module => |x| elf_file.symbol(x.symbol(rel.r_sym())),
417 .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]),
418 else => unreachable,
419 };
420
421 // We will use equation format to resolve relocations:
422 // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/
423 //
424 // Address of the source atom.
425 const P = @as(i64, @intCast(self.value + rel.r_offset));
426 // Addend from the relocation.
427 const A = rel.r_addend;
428 // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub.
429 const S = @as(i64, @intCast(target.address(.{}, elf_file)));
430 // Address of the global offset table.
431 const GOT = blk: {
432 const shndx = if (elf_file.got_plt_section_index) |shndx|
433 shndx
434 else if (elf_file.got_section_index) |shndx|
435 shndx
436 else
437 null;
438 break :blk if (shndx) |index| @as(i64, @intCast(elf_file.shdrs.items[index].sh_addr)) else 0;
439 };
440 // Relative offset to the start of the global offset table.
441 const G = @as(i64, @intCast(target.gotAddress(elf_file))) - GOT;
442 // // Address of the thread pointer.
443 // const TP = @as(i64, @intCast(elf_file.getTpAddress()));
444 // // Address of the dynamic thread pointer.
445 // const DTP = @as(i64, @intCast(elf_file.getDtpAddress()));
446
447 relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ({s})", .{
448 fmtRelocType(r_type),
449 rel.r_offset,
450 P,
451 S + A,
452 G + GOT + A,
453 target.name(elf_file),
454 });
455
456 try stream.seekTo(rel.r_offset);
457
458 switch (rel.r_type()) {
459 elf.R_X86_64_NONE => unreachable,
460
461 elf.R_X86_64_64 => try cwriter.writeIntLittle(i64, S + A),
462
463 elf.R_X86_64_PLT32,
464 elf.R_X86_64_PC32,
465 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - P))),
466
467 else => {
468 log.err("TODO: unhandled relocation type {}", .{fmtRelocType(rel.r_type())});
469 @panic("TODO unhandled relocation type");
470 },
471 }
101 }472 }
102 try gop.value_ptr.append(gpa, reloc);
103}473}
104474
105pub fn freeRelocations(elf_file: *Elf, atom_index: Index) void {475pub fn fmtRelocType(r_type: u32) std.fmt.Formatter(formatRelocType) {
106 var removed_relocs = elf_file.relocs.fetchRemove(atom_index);476 return .{ .data = r_type };
107 if (removed_relocs) |*relocs| relocs.value.deinit(elf_file.base.allocator);477}
478
479fn formatRelocType(
480 r_type: u32,
481 comptime unused_fmt_string: []const u8,
482 options: std.fmt.FormatOptions,
483 writer: anytype,
484) !void {
485 _ = unused_fmt_string;
486 _ = options;
487 const str = switch (r_type) {
488 elf.R_X86_64_NONE => "R_X86_64_NONE",
489 elf.R_X86_64_64 => "R_X86_64_64",
490 elf.R_X86_64_PC32 => "R_X86_64_PC32",
491 elf.R_X86_64_GOT32 => "R_X86_64_GOT32",
492 elf.R_X86_64_PLT32 => "R_X86_64_PLT32",
493 elf.R_X86_64_COPY => "R_X86_64_COPY",
494 elf.R_X86_64_GLOB_DAT => "R_X86_64_GLOB_DAT",
495 elf.R_X86_64_JUMP_SLOT => "R_X86_64_JUMP_SLOT",
496 elf.R_X86_64_RELATIVE => "R_X86_64_RELATIVE",
497 elf.R_X86_64_GOTPCREL => "R_X86_64_GOTPCREL",
498 elf.R_X86_64_32 => "R_X86_64_32",
499 elf.R_X86_64_32S => "R_X86_64_32S",
500 elf.R_X86_64_16 => "R_X86_64_16",
501 elf.R_X86_64_PC16 => "R_X86_64_PC16",
502 elf.R_X86_64_8 => "R_X86_64_8",
503 elf.R_X86_64_PC8 => "R_X86_64_PC8",
504 elf.R_X86_64_DTPMOD64 => "R_X86_64_DTPMOD64",
505 elf.R_X86_64_DTPOFF64 => "R_X86_64_DTPOFF64",
506 elf.R_X86_64_TPOFF64 => "R_X86_64_TPOFF64",
507 elf.R_X86_64_TLSGD => "R_X86_64_TLSGD",
508 elf.R_X86_64_TLSLD => "R_X86_64_TLSLD",
509 elf.R_X86_64_DTPOFF32 => "R_X86_64_DTPOFF32",
510 elf.R_X86_64_GOTTPOFF => "R_X86_64_GOTTPOFF",
511 elf.R_X86_64_TPOFF32 => "R_X86_64_TPOFF32",
512 elf.R_X86_64_PC64 => "R_X86_64_PC64",
513 elf.R_X86_64_GOTOFF64 => "R_X86_64_GOTOFF64",
514 elf.R_X86_64_GOTPC32 => "R_X86_64_GOTPC32",
515 elf.R_X86_64_GOT64 => "R_X86_64_GOT64",
516 elf.R_X86_64_GOTPCREL64 => "R_X86_64_GOTPCREL64",
517 elf.R_X86_64_GOTPC64 => "R_X86_64_GOTPC64",
518 elf.R_X86_64_GOTPLT64 => "R_X86_64_GOTPLT64",
519 elf.R_X86_64_PLTOFF64 => "R_X86_64_PLTOFF64",
520 elf.R_X86_64_SIZE32 => "R_X86_64_SIZE32",
521 elf.R_X86_64_SIZE64 => "R_X86_64_SIZE64",
522 elf.R_X86_64_GOTPC32_TLSDESC => "R_X86_64_GOTPC32_TLSDESC",
523 elf.R_X86_64_TLSDESC_CALL => "R_X86_64_TLSDESC_CALL",
524 elf.R_X86_64_TLSDESC => "R_X86_64_TLSDESC",
525 elf.R_X86_64_IRELATIVE => "R_X86_64_IRELATIVE",
526 elf.R_X86_64_RELATIVE64 => "R_X86_64_RELATIVE64",
527 elf.R_X86_64_GOTPCRELX => "R_X86_64_GOTPCRELX",
528 elf.R_X86_64_REX_GOTPCRELX => "R_X86_64_REX_GOTPCRELX",
529 elf.R_X86_64_NUM => "R_X86_64_NUM",
530 else => "R_X86_64_UNKNOWN",
531 };
532 try writer.print("{s}", .{str});
533}
534
535pub fn format(
536 atom: Atom,
537 comptime unused_fmt_string: []const u8,
538 options: std.fmt.FormatOptions,
539 writer: anytype,
540) !void {
541 _ = atom;
542 _ = unused_fmt_string;
543 _ = options;
544 _ = writer;
545 @compileError("do not format symbols directly");
546}
547
548pub fn fmt(atom: Atom, elf_file: *Elf) std.fmt.Formatter(format2) {
549 return .{ .data = .{
550 .atom = atom,
551 .elf_file = elf_file,
552 } };
108}553}
554
555const FormatContext = struct {
556 atom: Atom,
557 elf_file: *Elf,
558};
559
560fn format2(
561 ctx: FormatContext,
562 comptime unused_fmt_string: []const u8,
563 options: std.fmt.FormatOptions,
564 writer: anytype,
565) !void {
566 _ = options;
567 _ = unused_fmt_string;
568 const atom = ctx.atom;
569 const elf_file = ctx.elf_file;
570 try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x})", .{
571 atom.atom_index, atom.name(elf_file), atom.value,
572 atom.output_section_index, atom.alignment, atom.size,
573 });
574 // if (atom.fde_start != atom.fde_end) {
575 // try writer.writeAll(" : fdes{ ");
576 // for (atom.getFdes(elf_file), atom.fde_start..) |fde, i| {
577 // try writer.print("{d}", .{i});
578 // if (!fde.alive) try writer.writeAll("([*])");
579 // if (i < atom.fde_end - 1) try writer.writeAll(", ");
580 // }
581 // try writer.writeAll(" }");
582 // }
583 const gc_sections = if (elf_file.base.options.gc_sections) |gc_sections| gc_sections else false;
584 if (gc_sections and !atom.alive) {
585 try writer.writeAll(" : [*]");
586 }
587}
588
589// TODO this has to be u32 but for now, to avoid redesigning elfSym machinery for
590// ZigModule, keep it at u16 with the intention of bumping it to u32 in the near
591// future.
592pub const Index = u16;
593
594const std = @import("std");
595const assert = std.debug.assert;
596const elf = std.elf;
597const log = std.log.scoped(.link);
598const relocs_log = std.log.scoped(.link_relocs);
599
600const Allocator = std.mem.Allocator;
601const Atom = @This();
602const Elf = @import("../Elf.zig");
603const File = @import("file.zig").File;
604const Symbol = @import("Symbol.zig");
src/link/Elf/LinkerDefined.zig created+112
...@@ -0,0 +1,112 @@
1index: File.Index,
2symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
3symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
4
5output_symtab_size: Elf.SymtabSize = .{},
6
7pub fn deinit(self: *LinkerDefined, allocator: Allocator) void {
8 self.symtab.deinit(allocator);
9 self.symbols.deinit(allocator);
10}
11
12pub fn addGlobal(self: *LinkerDefined, name: [:0]const u8, elf_file: *Elf) !u32 {
13 const gpa = elf_file.base.allocator;
14 try self.symtab.ensureUnusedCapacity(gpa, 1);
15 try self.symbols.ensureUnusedCapacity(gpa, 1);
16 self.symtab.appendAssumeCapacity(.{
17 .st_name = try elf_file.strtab.insert(gpa, name),
18 .st_info = elf.STB_GLOBAL << 4,
19 .st_other = @intFromEnum(elf.STV.HIDDEN),
20 .st_shndx = elf.SHN_ABS,
21 .st_value = 0,
22 .st_size = 0,
23 });
24 const off = try elf_file.strtab.insert(gpa, name);
25 const gop = try elf_file.getOrPutGlobal(off);
26 self.symbols.addOneAssumeCapacity().* = gop.index;
27 return gop.index;
28}
29
30pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void {
31 for (self.symbols.items, 0..) |index, i| {
32 const sym_idx = @as(Symbol.Index, @intCast(i));
33 const this_sym = self.symtab.items[sym_idx];
34
35 if (this_sym.st_shndx == elf.SHN_UNDEF) continue;
36
37 const global = elf_file.symbol(index);
38 if (self.asFile().symbolRank(this_sym, false) < global.symbolRank(elf_file)) {
39 global.value = 0;
40 global.name_offset = global.name_offset;
41 global.atom_index = 0;
42 global.file_index = self.index;
43 global.esym_index = sym_idx;
44 global.version_index = elf_file.default_sym_version;
45 }
46 }
47}
48
49pub fn updateSymtabSize(self: *LinkerDefined, elf_file: *Elf) void {
50 for (self.globals()) |global_index| {
51 const global = elf_file.symbol(global_index);
52 if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
53 global.flags.output_symtab = true;
54 self.output_symtab_size.nlocals += 1;
55 }
56}
57
58pub fn writeSymtab(self: *LinkerDefined, elf_file: *Elf, ctx: anytype) void {
59 var ilocal = ctx.ilocal;
60 for (self.globals()) |global_index| {
61 const global = elf_file.symbol(global_index);
62 if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
63 if (!global.flags.output_symtab) continue;
64 global.setOutputSym(elf_file, &ctx.symtab[ilocal]);
65 ilocal += 1;
66 }
67}
68
69pub fn globals(self: *LinkerDefined) []const Symbol.Index {
70 return self.symbols.items;
71}
72
73pub fn asFile(self: *LinkerDefined) File {
74 return .{ .linker_defined = self };
75}
76
77pub fn fmtSymtab(self: *LinkerDefined, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
78 return .{ .data = .{
79 .self = self,
80 .elf_file = elf_file,
81 } };
82}
83
84const FormatContext = struct {
85 self: *LinkerDefined,
86 elf_file: *Elf,
87};
88
89fn formatSymtab(
90 ctx: FormatContext,
91 comptime unused_fmt_string: []const u8,
92 options: std.fmt.FormatOptions,
93 writer: anytype,
94) !void {
95 _ = unused_fmt_string;
96 _ = options;
97 try writer.writeAll(" globals\n");
98 for (ctx.self.globals()) |index| {
99 const global = ctx.elf_file.symbol(index);
100 try writer.print(" {}\n", .{global.fmt(ctx.elf_file)});
101 }
102}
103
104const std = @import("std");
105const elf = std.elf;
106
107const Allocator = std.mem.Allocator;
108const Elf = @import("../Elf.zig");
109const File = @import("file.zig").File;
110const LinkerDefined = @This();
111// const Object = @import("Object.zig");
112const Symbol = @import("Symbol.zig");
src/link/Elf/Object.zig created+872
...@@ -0,0 +1,872 @@
1archive: ?[]const u8 = null,
2path: []const u8,
3data: []const u8,
4index: File.Index,
5
6header: ?elf.Elf64_Ehdr = null,
7shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{},
8strings: StringTable(.object_strings) = .{},
9symtab: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{},
10strtab: []const u8 = &[0]u8{},
11first_global: ?Symbol.Index = null,
12
13symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
14atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
15comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{},
16
17fdes: std.ArrayListUnmanaged(Fde) = .{},
18cies: std.ArrayListUnmanaged(Cie) = .{},
19
20alive: bool = true,
21num_dynrelocs: u32 = 0,
22
23output_symtab_size: Elf.SymtabSize = .{},
24
25pub fn isObject(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.REL) return false;
32 if (header.e_version != 1) return false;
33 return true;
34}
35
36pub fn deinit(self: *Object, allocator: Allocator) void {
37 allocator.free(self.data);
38 self.shdrs.deinit(allocator);
39 self.strings.deinit(allocator);
40 self.symbols.deinit(allocator);
41 self.atoms.deinit(allocator);
42 self.comdat_groups.deinit(allocator);
43 self.fdes.deinit(allocator);
44 self.cies.deinit(allocator);
45}
46
47pub fn parse(self: *Object, elf_file: *Elf) !void {
48 var stream = std.io.fixedBufferStream(self.data);
49 const reader = stream.reader();
50
51 self.header = try reader.readStruct(elf.Elf64_Ehdr);
52
53 if (self.header.?.e_shnum == 0) return;
54
55 const gpa = elf_file.base.allocator;
56
57 const shoff = math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;
58 const shdrs = @as(
59 [*]align(1) const elf.Elf64_Shdr,
60 @ptrCast(self.data.ptr + shoff),
61 )[0..self.header.?.e_shnum];
62 try self.shdrs.appendUnalignedSlice(gpa, shdrs);
63 try self.strings.buffer.appendSlice(gpa, try self.shdrContents(self.header.?.e_shstrndx));
64
65 const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
66 elf.SHT_SYMTAB => break @as(u16, @intCast(i)),
67 else => {},
68 } else null;
69
70 if (symtab_index) |index| {
71 const shdr = shdrs[index];
72 self.first_global = shdr.sh_info;
73
74 const symtab = try self.shdrContents(index);
75 const nsyms = @divExact(symtab.len, @sizeOf(elf.Elf64_Sym));
76 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)));
78 }
79
80 try self.initAtoms(elf_file);
81 try self.initSymtab(elf_file);
82
83 // for (self.shdrs.items, 0..) |shdr, i| {
84 // const atom = elf_file.atom(self.atoms.items[i]) orelse continue;
85 // if (!atom.alive) continue;
86 // 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);
88 // }
89}
90
91fn initAtoms(self: *Object, elf_file: *Elf) !void {
92 const shdrs = self.shdrs.items;
93 try self.atoms.resize(elf_file.base.allocator, shdrs.len);
94 @memset(self.atoms.items, 0);
95
96 for (shdrs, 0..) |shdr, i| {
97 if (shdr.sh_flags & elf.SHF_EXCLUDE != 0 and
98 shdr.sh_flags & elf.SHF_ALLOC == 0 and
99 shdr.sh_type != elf.SHT_LLVM_ADDRSIG) continue;
100
101 switch (shdr.sh_type) {
102 elf.SHT_GROUP => {
103 if (shdr.sh_info >= self.symtab.len) {
104 // TODO convert into an error
105 log.debug("{}: invalid symbol index in sh_info", .{self.fmtPath()});
106 continue;
107 }
108 const group_info_sym = self.symtab[shdr.sh_info];
109 const group_signature = blk: {
110 if (group_info_sym.st_name == 0 and group_info_sym.st_type() == elf.STT_SECTION) {
111 const sym_shdr = shdrs[group_info_sym.st_shndx];
112 break :blk self.strings.getAssumeExists(sym_shdr.sh_name);
113 }
114 break :blk self.getString(group_info_sym.st_name);
115 };
116
117 const shndx = @as(u16, @intCast(i));
118 const group_raw_data = try self.shdrContents(shndx);
119 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];
121
122 if (group_members[0] != 0x1) { // GRP_COMDAT
123 // TODO convert into an error
124 log.debug("{}: unknown SHT_GROUP format", .{self.fmtPath()});
125 continue;
126 }
127
128 const group_signature_off = try self.strings.insert(elf_file.base.allocator, group_signature);
129 const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature_off);
130 const comdat_group_index = try elf_file.addComdatGroup();
131 const comdat_group = elf_file.comdatGroup(comdat_group_index);
132 comdat_group.* = .{
133 .owner = gop.index,
134 .shndx = shndx,
135 };
136 try self.comdat_groups.append(elf_file.base.allocator, comdat_group_index);
137 },
138
139 elf.SHT_SYMTAB_SHNDX => @panic("TODO"),
140
141 elf.SHT_NULL,
142 elf.SHT_REL,
143 elf.SHT_RELA,
144 elf.SHT_SYMTAB,
145 elf.SHT_STRTAB,
146 => {},
147
148 else => {
149 const name = self.strings.getAssumeExists(shdr.sh_name);
150 const shndx = @as(u16, @intCast(i));
151 if (self.skipShdr(shndx, elf_file)) continue;
152 try self.addAtom(shdr, shndx, name, elf_file);
153 },
154 }
155 }
156
157 // Parse relocs sections if any.
158 for (shdrs, 0..) |shdr, i| switch (shdr.sh_type) {
159 elf.SHT_REL, elf.SHT_RELA => {
160 const atom_index = self.atoms.items[shdr.sh_info];
161 if (elf_file.atom(atom_index)) |atom| {
162 atom.relocs_section_index = @as(u16, @intCast(i));
163 }
164 },
165 else => {},
166 };
167}
168
169fn addAtom(self: *Object, shdr: elf.Elf64_Shdr, shndx: u16, name: [:0]const u8, elf_file: *Elf) !void {
170 const atom_index = try elf_file.addAtom();
171 const atom = elf_file.atom(atom_index).?;
172 atom.atom_index = atom_index;
173 atom.name_offset = try elf_file.strtab.insert(elf_file.base.allocator, name);
174 atom.file_index = self.index;
175 atom.input_section_index = shndx;
176 atom.output_section_index = self.getOutputSectionIndex(elf_file, shdr);
177 atom.alive = true;
178 self.atoms.items[shndx] = atom_index;
179
180 if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {
181 const data = try self.shdrContents(shndx);
182 const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
183 atom.size = chdr.ch_size;
184 atom.alignment = math.log2_int(u64, chdr.ch_addralign);
185 } else {
186 atom.size = shdr.sh_size;
187 atom.alignment = math.log2_int(u64, shdr.sh_addralign);
188 }
189}
190
191fn getOutputSectionIndex(self: *Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) u16 {
192 const name = blk: {
193 const name = self.strings.getAssumeExists(shdr.sh_name);
194 // if (shdr.sh_flags & elf.SHF_MERGE != 0) break :blk name;
195 const sh_name_prefixes: []const [:0]const u8 = &.{
196 ".text", ".data.rel.ro", ".data", ".rodata", ".bss.rel.ro", ".bss",
197 ".init_array", ".fini_array", ".tbss", ".tdata", ".gcc_except_table", ".ctors",
198 ".dtors", ".gnu.warning",
199 };
200 inline for (sh_name_prefixes) |prefix| {
201 if (std.mem.eql(u8, name, prefix) or std.mem.startsWith(u8, name, prefix ++ ".")) {
202 break :blk prefix;
203 }
204 }
205 break :blk name;
206 };
207 const @"type" = switch (shdr.sh_type) {
208 elf.SHT_NULL => unreachable,
209 elf.SHT_PROGBITS => blk: {
210 if (std.mem.eql(u8, name, ".init_array") or std.mem.startsWith(u8, name, ".init_array."))
211 break :blk elf.SHT_INIT_ARRAY;
212 if (std.mem.eql(u8, name, ".fini_array") or std.mem.startsWith(u8, name, ".fini_array."))
213 break :blk elf.SHT_FINI_ARRAY;
214 break :blk shdr.sh_type;
215 },
216 elf.SHT_X86_64_UNWIND => elf.SHT_PROGBITS,
217 else => shdr.sh_type,
218 };
219 const flags = blk: {
220 const flags = shdr.sh_flags & ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP | elf.SHF_GNU_RETAIN);
221 break :blk switch (@"type") {
222 elf.SHT_INIT_ARRAY, elf.SHT_FINI_ARRAY => flags | elf.SHF_WRITE,
223 else => flags,
224 };
225 };
226 _ = flags;
227 const out_shndx = elf_file.sectionByName(name) orelse {
228 log.err("{}: output section {s} not found", .{ self.fmtPath(), name });
229 @panic("TODO: missing output section!");
230 };
231 return out_shndx;
232}
233
234fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool {
235 _ = elf_file;
236 const shdr = self.shdrs.items[index];
237 const name = self.strings.getAssumeExists(shdr.sh_name);
238 const ignore = blk: {
239 if (mem.startsWith(u8, name, ".note")) break :blk true;
240 if (mem.startsWith(u8, name, ".comment")) break :blk true;
241 if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true;
242 if (mem.startsWith(u8, name, ".eh_frame")) break :blk true;
243 // if (elf_file.base.options.strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and
244 // mem.startsWith(u8, name, ".debug")) break :blk true;
245 if (shdr.sh_flags & elf.SHF_ALLOC == 0 and mem.startsWith(u8, name, ".debug")) break :blk true;
246 break :blk false;
247 };
248 return ignore;
249}
250
251fn initSymtab(self: *Object, elf_file: *Elf) !void {
252 const gpa = elf_file.base.allocator;
253 const first_global = self.first_global orelse self.symtab.len;
254 const shdrs = self.shdrs.items;
255
256 try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.len);
257
258 for (self.symtab[0..first_global], 0..) |sym, i| {
259 const index = try elf_file.addSymbol();
260 self.symbols.appendAssumeCapacity(index);
261 const sym_ptr = elf_file.symbol(index);
262 const name = blk: {
263 if (sym.st_name == 0 and sym.st_type() == elf.STT_SECTION) {
264 const shdr = shdrs[sym.st_shndx];
265 break :blk self.strings.getAssumeExists(shdr.sh_name);
266 }
267 break :blk self.getString(sym.st_name);
268 };
269 sym_ptr.value = sym.st_value;
270 sym_ptr.name_offset = try elf_file.strtab.insert(gpa, name);
271 sym_ptr.esym_index = @as(u32, @intCast(i));
272 sym_ptr.atom_index = if (sym.st_shndx == elf.SHN_ABS) 0 else self.atoms.items[sym.st_shndx];
273 sym_ptr.file_index = self.index;
274 sym_ptr.output_section_index = if (sym_ptr.atom(elf_file)) |atom_ptr|
275 atom_ptr.output_section_index
276 else
277 0;
278 }
279
280 for (self.symtab[first_global..]) |sym| {
281 const name = self.getString(sym.st_name);
282 const off = try elf_file.strtab.insert(gpa, name);
283 const gop = try elf_file.getOrPutGlobal(off);
284 self.symbols.addOneAssumeCapacity().* = gop.index;
285 }
286}
287
288fn parseEhFrame(self: *Object, shndx: u16, elf_file: *Elf) !void {
289 const relocs_shndx = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
290 elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u16, @intCast(i)),
291 else => {},
292 } else {
293 log.debug("{s}: missing reloc section for unwind info section", .{self.fmtPath()});
294 return;
295 };
296
297 const gpa = elf_file.base.allocator;
298 const raw = try self.shdrContents(shndx);
299 const relocs = try self.getRelocs(relocs_shndx);
300 const fdes_start = self.fdes.items.len;
301 const cies_start = self.cies.items.len;
302
303 var it = eh_frame.Iterator{ .data = raw };
304 while (try it.next()) |rec| {
305 const rel_range = filterRelocs(relocs, rec.offset, rec.size + 4);
306 switch (rec.tag) {
307 .cie => try self.cies.append(gpa, .{
308 .offset = rec.offset,
309 .size = rec.size,
310 .rel_index = @as(u32, @intCast(rel_range.start)),
311 .rel_num = @as(u32, @intCast(rel_range.len)),
312 .rel_section_index = relocs_shndx,
313 .input_section_index = shndx,
314 .file_index = self.index,
315 }),
316 .fde => try self.fdes.append(gpa, .{
317 .offset = rec.offset,
318 .size = rec.size,
319 .cie_index = undefined,
320 .rel_index = @as(u32, @intCast(rel_range.start)),
321 .rel_num = @as(u32, @intCast(rel_range.len)),
322 .rel_section_index = relocs_shndx,
323 .input_section_index = shndx,
324 .file_index = self.index,
325 }),
326 }
327 }
328
329 // Tie each FDE to its CIE
330 for (self.fdes.items[fdes_start..]) |*fde| {
331 const cie_ptr = fde.offset + 4 - fde.ciePointer(elf_file);
332 const cie_index = for (self.cies.items[cies_start..], cies_start..) |cie, cie_index| {
333 if (cie.offset == cie_ptr) break @as(u32, @intCast(cie_index));
334 } else {
335 // TODO convert into an error
336 log.debug("{s}: no matching CIE found for FDE at offset {x}", .{
337 self.fmtPath(),
338 fde.offset,
339 });
340 continue;
341 };
342 fde.cie_index = cie_index;
343 }
344
345 // Tie each FDE record to its matching atom
346 const SortFdes = struct {
347 pub fn lessThan(ctx: *Elf, lhs: Fde, rhs: Fde) bool {
348 const lhs_atom = lhs.atom(ctx);
349 const rhs_atom = rhs.atom(ctx);
350 return lhs_atom.priority(ctx) < rhs_atom.priority(ctx);
351 }
352 };
353 mem.sort(Fde, self.fdes.items[fdes_start..], elf_file, SortFdes.lessThan);
354
355 // Create a back-link from atom to FDEs
356 var i: u32 = @as(u32, @intCast(fdes_start));
357 while (i < self.fdes.items.len) {
358 const fde = self.fdes.items[i];
359 const atom = fde.atom(elf_file);
360 atom.fde_start = i;
361 i += 1;
362 while (i < self.fdes.items.len) : (i += 1) {
363 const next_fde = self.fdes.items[i];
364 if (atom.atom_index != next_fde.atom(elf_file).atom_index) break;
365 }
366 atom.fde_end = i;
367 }
368}
369
370fn filterRelocs(
371 relocs: []align(1) const elf.Elf64_Rela,
372 start: u64,
373 len: u64,
374) struct { start: u64, len: u64 } {
375 const Predicate = struct {
376 value: u64,
377
378 pub fn predicate(self: @This(), rel: elf.Elf64_Rela) bool {
379 return rel.r_offset < self.value;
380 }
381 };
382 const LPredicate = struct {
383 value: u64,
384
385 pub fn predicate(self: @This(), rel: elf.Elf64_Rela) bool {
386 return rel.r_offset >= self.value;
387 }
388 };
389
390 const f_start = Elf.bsearch(elf.Elf64_Rela, relocs, Predicate{ .value = start });
391 const f_len = Elf.lsearch(elf.Elf64_Rela, relocs[f_start..], LPredicate{ .value = start + len });
392
393 return .{ .start = f_start, .len = f_len };
394}
395
396pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void {
397 for (self.atoms.items) |atom_index| {
398 const atom = elf_file.atom(atom_index) orelse continue;
399 if (!atom.alive) continue;
400 const shdr = atom.inputShdr(elf_file);
401 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
402 if (shdr.sh_type == elf.SHT_NOBITS) continue;
403 try atom.scanRelocs(elf_file, undefs);
404 }
405
406 for (self.cies.items) |cie| {
407 for (try cie.relocs(elf_file)) |rel| {
408 const sym = elf_file.symbol(self.symbols.items[rel.r_sym()]);
409 if (sym.flags.import) {
410 if (sym.type(elf_file) != elf.STT_FUNC)
411 // TODO convert into an error
412 log.debug("{s}: {s}: CIE referencing external data reference", .{
413 self.fmtPath(),
414 sym.name(elf_file),
415 });
416 sym.flags.needs_plt = true;
417 }
418 }
419 }
420}
421
422pub fn resolveSymbols(self: *Object, elf_file: *Elf) void {
423 const first_global = self.first_global orelse return;
424 for (self.globals(), 0..) |index, i| {
425 const esym_index = @as(Symbol.Index, @intCast(first_global + i));
426 const esym = self.symtab[esym_index];
427
428 if (esym.st_shndx == elf.SHN_UNDEF) continue;
429
430 if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) {
431 const atom_index = self.atoms.items[esym.st_shndx];
432 const atom = elf_file.atom(atom_index) orelse continue;
433 if (!atom.alive) continue;
434 }
435
436 const global = elf_file.symbol(index);
437 if (self.asFile().symbolRank(esym, !self.alive) < global.symbolRank(elf_file)) {
438 const atom_index = switch (esym.st_shndx) {
439 elf.SHN_ABS, elf.SHN_COMMON => 0,
440 else => self.atoms.items[esym.st_shndx],
441 };
442 const output_section_index = if (elf_file.atom(atom_index)) |atom|
443 atom.output_section_index
444 else
445 0;
446 global.value = esym.st_value;
447 global.atom_index = atom_index;
448 global.esym_index = esym_index;
449 global.file_index = self.index;
450 global.output_section_index = output_section_index;
451 global.version_index = elf_file.default_sym_version;
452 if (esym.st_bind() == elf.STB_WEAK) global.flags.weak = true;
453 }
454 }
455}
456
457pub fn claimUnresolved(self: *Object, elf_file: *Elf) void {
458 const first_global = self.first_global orelse return;
459 for (self.globals(), 0..) |index, i| {
460 const esym_index = @as(u32, @intCast(first_global + i));
461 const esym = self.symtab[esym_index];
462 if (esym.st_shndx != elf.SHN_UNDEF) continue;
463
464 const global = elf_file.symbol(index);
465 if (global.file(elf_file)) |_| {
466 if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF) continue;
467 }
468
469 const is_import = blk: {
470 if (!elf_file.isDynLib()) break :blk false;
471 const vis = @as(elf.STV, @enumFromInt(esym.st_other));
472 if (vis == .HIDDEN) break :blk false;
473 break :blk true;
474 };
475
476 global.value = 0;
477 global.atom_index = 0;
478 global.esym_index = esym_index;
479 global.file_index = self.index;
480 global.version_index = if (is_import) elf.VER_NDX_LOCAL else elf_file.default_sym_version;
481 global.flags.import = is_import;
482 }
483}
484
485pub fn resetGlobals(self: *Object, elf_file: *Elf) void {
486 for (self.globals()) |index| {
487 const global = elf_file.symbol(index);
488 const name = global.name;
489 global.* = .{};
490 global.name = name;
491 }
492}
493
494pub fn markLive(self: *Object, elf_file: *Elf) void {
495 const first_global = self.first_global orelse return;
496 for (self.globals(), 0..) |index, i| {
497 const sym_idx = first_global + i;
498 const sym = self.symtab[sym_idx];
499 if (sym.st_bind() == elf.STB_WEAK) continue;
500
501 const global = elf_file.symbol(index);
502 const file = global.getFile(elf_file) orelse continue;
503 const should_keep = sym.st_shndx == elf.SHN_UNDEF or
504 (sym.st_shndx == elf.SHN_COMMON and global.elfSym(elf_file).st_shndx != elf.SHN_COMMON);
505 if (should_keep and !file.isAlive()) {
506 file.setAlive();
507 file.markLive(elf_file);
508 }
509 }
510}
511
512pub fn checkDuplicates(self: *Object, elf_file: *Elf) void {
513 const first_global = self.first_global orelse return;
514 for (self.globals(), 0..) |index, i| {
515 const sym_idx = @as(u32, @intCast(first_global + i));
516 const this_sym = self.symtab[sym_idx];
517 const global = elf_file.symbol(index);
518 const global_file = global.getFile(elf_file) orelse continue;
519
520 if (self.index == global_file.getIndex() or
521 this_sym.st_shndx == elf.SHN_UNDEF or
522 this_sym.st_bind() == elf.STB_WEAK or
523 this_sym.st_shndx == elf.SHN_COMMON) continue;
524
525 if (this_sym.st_shndx != elf.SHN_ABS) {
526 const atom_index = self.atoms.items[this_sym.st_shndx];
527 const atom = elf_file.atom(atom_index) orelse continue;
528 if (!atom.alive) continue;
529 }
530
531 elf_file.base.fatal("multiple definition: {}: {}: {s}", .{
532 self.fmtPath(),
533 global_file.fmtPath(),
534 global.getName(elf_file),
535 });
536 }
537}
538
539/// We will create dummy shdrs per each resolved common symbols to make it
540/// play nicely with the rest of the system.
541pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
542 const first_global = self.first_global orelse return;
543 for (self.globals(), 0..) |index, i| {
544 const sym_idx = @as(u32, @intCast(first_global + i));
545 const this_sym = self.symtab[sym_idx];
546 if (this_sym.st_shndx != elf.SHN_COMMON) continue;
547
548 const global = elf_file.symbol(index);
549 const global_file = global.getFile(elf_file).?;
550 if (global_file.getIndex() != self.index) {
551 if (elf_file.options.warn_common) {
552 elf_file.base.warn("{}: multiple common symbols: {s}", .{
553 self.fmtPath(),
554 global.getName(elf_file),
555 });
556 }
557 continue;
558 }
559
560 const gpa = elf_file.base.allocator;
561
562 const atom_index = try elf_file.addAtom();
563 try self.atoms.append(gpa, atom_index);
564
565 const is_tls = global.getType(elf_file) == elf.STT_TLS;
566 const name = if (is_tls) ".tls_common" else ".common";
567
568 const atom = elf_file.atom(atom_index).?;
569 atom.atom_index = atom_index;
570 atom.name = try elf_file.strtab.insert(gpa, name);
571 atom.file = self.index;
572 atom.size = this_sym.st_size;
573 const alignment = this_sym.st_value;
574 atom.alignment = math.log2_int(u64, alignment);
575
576 var sh_flags: u32 = elf.SHF_ALLOC | elf.SHF_WRITE;
577 if (is_tls) sh_flags |= elf.SHF_TLS;
578 const shndx = @as(u16, @intCast(self.shdrs.items.len));
579 const shdr = try self.shdrs.addOne(gpa);
580 shdr.* = .{
581 .sh_name = try self.strings.insert(gpa, name),
582 .sh_type = elf.SHT_NOBITS,
583 .sh_flags = sh_flags,
584 .sh_addr = 0,
585 .sh_offset = 0,
586 .sh_size = this_sym.st_size,
587 .sh_link = 0,
588 .sh_info = 0,
589 .sh_addralign = alignment,
590 .sh_entsize = 0,
591 };
592 atom.shndx = shndx;
593
594 global.value = 0;
595 global.atom = atom_index;
596 global.flags.weak = false;
597 }
598}
599
600pub fn updateSymtabSize(self: *Object, elf_file: *Elf) void {
601 for (self.locals()) |local_index| {
602 const local = elf_file.symbol(local_index);
603 if (local.atom(elf_file)) |atom| if (!atom.alive) continue;
604 const esym = local.elfSym(elf_file);
605 switch (esym.st_type()) {
606 elf.STT_SECTION, elf.STT_NOTYPE => continue,
607 else => {},
608 }
609 local.flags.output_symtab = true;
610 self.output_symtab_size.nlocals += 1;
611 }
612
613 for (self.globals()) |global_index| {
614 const global = elf_file.symbol(global_index);
615 if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
616 if (global.atom(elf_file)) |atom| if (!atom.alive) continue;
617 global.flags.output_symtab = true;
618 if (global.isLocal()) {
619 self.output_symtab_size.nlocals += 1;
620 } else {
621 self.output_symtab_size.nglobals += 1;
622 }
623 }
624}
625
626pub fn writeSymtab(self: *Object, elf_file: *Elf, ctx: anytype) void {
627 var ilocal = ctx.ilocal;
628 for (self.locals()) |local_index| {
629 const local = elf_file.symbol(local_index);
630 if (!local.flags.output_symtab) continue;
631 local.setOutputSym(elf_file, &ctx.symtab[ilocal]);
632 ilocal += 1;
633 }
634
635 var iglobal = ctx.iglobal;
636 for (self.globals()) |global_index| {
637 const global = elf_file.symbol(global_index);
638 if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
639 if (!global.flags.output_symtab) continue;
640 if (global.isLocal()) {
641 global.setOutputSym(elf_file, &ctx.symtab[ilocal]);
642 ilocal += 1;
643 } else {
644 global.setOutputSym(elf_file, &ctx.symtab[iglobal]);
645 iglobal += 1;
646 }
647 }
648}
649
650pub fn locals(self: *Object) []const Symbol.Index {
651 const end = self.first_global orelse self.symbols.items.len;
652 return self.symbols.items[0..end];
653}
654
655pub fn globals(self: *Object) []const Symbol.Index {
656 const start = self.first_global orelse self.symbols.items.len;
657 return self.symbols.items[start..];
658}
659
660pub fn shdrContents(self: *Object, index: u32) error{Overflow}![]const u8 {
661 assert(index < self.shdrs.items.len);
662 const shdr = self.shdrs.items[index];
663 const offset = math.cast(usize, shdr.sh_offset) orelse return error.Overflow;
664 const size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
665 return self.data[offset..][0..size];
666}
667
668fn getString(self: *Object, off: u32) [:0]const u8 {
669 assert(off < self.strtab.len);
670 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0);
671}
672
673pub fn comdatGroupMembers(self: *Object, index: u16) error{Overflow}![]align(1) const u32 {
674 const raw = try self.shdrContents(index);
675 const nmembers = @divExact(raw.len, @sizeOf(u32));
676 const members = @as([*]align(1) const u32, @ptrCast(raw.ptr))[1..nmembers];
677 return members;
678}
679
680pub fn asFile(self: *Object) File {
681 return .{ .object = self };
682}
683
684pub fn getRelocs(self: *Object, shndx: u32) error{Overflow}![]align(1) const elf.Elf64_Rela {
685 const raw = try self.shdrContents(shndx);
686 const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela));
687 return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num];
688}
689
690pub fn format(
691 self: *Object,
692 comptime unused_fmt_string: []const u8,
693 options: std.fmt.FormatOptions,
694 writer: anytype,
695) !void {
696 _ = self;
697 _ = unused_fmt_string;
698 _ = options;
699 _ = writer;
700 @compileError("do not format objects directly");
701}
702
703pub fn fmtSymtab(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
704 return .{ .data = .{
705 .object = self,
706 .elf_file = elf_file,
707 } };
708}
709
710const FormatContext = struct {
711 object: *Object,
712 elf_file: *Elf,
713};
714
715fn formatSymtab(
716 ctx: FormatContext,
717 comptime unused_fmt_string: []const u8,
718 options: std.fmt.FormatOptions,
719 writer: anytype,
720) !void {
721 _ = unused_fmt_string;
722 _ = options;
723 const object = ctx.object;
724 try writer.writeAll(" locals\n");
725 for (object.locals()) |index| {
726 const local = ctx.elf_file.symbol(index);
727 try writer.print(" {}\n", .{local.fmt(ctx.elf_file)});
728 }
729 try writer.writeAll(" globals\n");
730 for (object.globals()) |index| {
731 const global = ctx.elf_file.symbol(index);
732 try writer.print(" {}\n", .{global.fmt(ctx.elf_file)});
733 }
734}
735
736pub fn fmtAtoms(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatAtoms) {
737 return .{ .data = .{
738 .object = self,
739 .elf_file = elf_file,
740 } };
741}
742
743fn formatAtoms(
744 ctx: FormatContext,
745 comptime unused_fmt_string: []const u8,
746 options: std.fmt.FormatOptions,
747 writer: anytype,
748) !void {
749 _ = unused_fmt_string;
750 _ = options;
751 const object = ctx.object;
752 try writer.writeAll(" atoms\n");
753 for (object.atoms.items) |atom_index| {
754 const atom = ctx.elf_file.atom(atom_index) orelse continue;
755 try writer.print(" {}\n", .{atom.fmt(ctx.elf_file)});
756 }
757}
758
759pub fn fmtCies(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatCies) {
760 return .{ .data = .{
761 .object = self,
762 .elf_file = elf_file,
763 } };
764}
765
766fn formatCies(
767 ctx: FormatContext,
768 comptime unused_fmt_string: []const u8,
769 options: std.fmt.FormatOptions,
770 writer: anytype,
771) !void {
772 _ = unused_fmt_string;
773 _ = options;
774 const object = ctx.object;
775 try writer.writeAll(" cies\n");
776 for (object.cies.items, 0..) |cie, i| {
777 try writer.print(" cie({d}) : {}\n", .{ i, cie.fmt(ctx.elf_file) });
778 }
779}
780
781pub fn fmtFdes(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatFdes) {
782 return .{ .data = .{
783 .object = self,
784 .elf_file = elf_file,
785 } };
786}
787
788fn formatFdes(
789 ctx: FormatContext,
790 comptime unused_fmt_string: []const u8,
791 options: std.fmt.FormatOptions,
792 writer: anytype,
793) !void {
794 _ = unused_fmt_string;
795 _ = options;
796 const object = ctx.object;
797 try writer.writeAll(" fdes\n");
798 for (object.fdes.items, 0..) |fde, i| {
799 try writer.print(" fde({d}) : {}\n", .{ i, fde.fmt(ctx.elf_file) });
800 }
801}
802
803pub fn fmtComdatGroups(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatComdatGroups) {
804 return .{ .data = .{
805 .object = self,
806 .elf_file = elf_file,
807 } };
808}
809
810fn formatComdatGroups(
811 ctx: FormatContext,
812 comptime unused_fmt_string: []const u8,
813 options: std.fmt.FormatOptions,
814 writer: anytype,
815) !void {
816 _ = unused_fmt_string;
817 _ = options;
818 const object = ctx.object;
819 const elf_file = ctx.elf_file;
820 try writer.writeAll(" comdat groups\n");
821 for (object.comdat_groups.items) |cg_index| {
822 const cg = elf_file.comdatGroup(cg_index);
823 const cg_owner = elf_file.comdatGroupOwner(cg.owner);
824 if (cg_owner.file != object.index) continue;
825 const cg_members = object.comdatGroupMembers(cg.shndx) catch continue;
826 for (cg_members) |shndx| {
827 const atom_index = object.atoms.items[shndx];
828 const atom = elf_file.atom(atom_index) orelse continue;
829 try writer.print(" atom({d}) : {s}\n", .{ atom_index, atom.name(elf_file) });
830 }
831 }
832}
833
834pub fn fmtPath(self: *Object) std.fmt.Formatter(formatPath) {
835 return .{ .data = self };
836}
837
838fn formatPath(
839 object: *Object,
840 comptime unused_fmt_string: []const u8,
841 options: std.fmt.FormatOptions,
842 writer: anytype,
843) !void {
844 _ = unused_fmt_string;
845 _ = options;
846 if (object.archive) |path| {
847 try writer.writeAll(path);
848 try writer.writeByte('(');
849 try writer.writeAll(object.path);
850 try writer.writeByte(')');
851 } else try writer.writeAll(object.path);
852}
853
854const Object = @This();
855
856const std = @import("std");
857const assert = std.debug.assert;
858const eh_frame = @import("eh_frame.zig");
859const elf = std.elf;
860const fs = std.fs;
861const log = std.log.scoped(.link);
862const math = std.math;
863const mem = std.mem;
864
865const Allocator = mem.Allocator;
866const Atom = @import("Atom.zig");
867const Cie = eh_frame.Cie;
868const Elf = @import("../Elf.zig");
869const Fde = eh_frame.Fde;
870const File = @import("file.zig").File;
871const StringTable = @import("../strtab.zig").StringTable;
872const Symbol = @import("Symbol.zig");
src/link/Elf/Symbol.zig created+362
...@@ -0,0 +1,362 @@
1//! Represents a defined symbol.
2
3index: Index = 0,
4
5/// Allocated address value of this symbol.
6value: u64 = 0,
7
8/// Offset into the linker's string table.
9name_offset: u32 = 0,
10
11/// Index of file where this symbol is defined.
12file_index: File.Index = 0,
13
14/// Index of atom containing this symbol.
15/// Index of 0 means there is no associated atom with this symbol.
16/// Use `atom` to get the pointer to the atom.
17atom_index: Atom.Index = 0,
18
19/// Assigned output section index for this atom.
20output_section_index: u16 = 0,
21
22/// Index of the source symbol this symbol references.
23/// Use `elfSym` to pull the source symbol from the relevant file.
24esym_index: Index = 0,
25
26/// Index of the source version symbol this symbol references if any.
27/// If the symbol is unversioned it will have either VER_NDX_LOCAL or VER_NDX_GLOBAL.
28version_index: elf.Elf64_Versym = elf.VER_NDX_LOCAL,
29
30/// Misc flags for the symbol packaged as packed struct for compression.
31flags: Flags = .{},
32
33extra_index: u32 = 0,
34
35pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool {
36 const file_ptr = symbol.file(elf_file).?;
37 // if (file_ptr == .shared) return symbol.sourceSymbol(elf_file).st_shndx == elf.SHN_ABS;
38 return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.output_section_index == 0 and
39 file_ptr != .linker_defined;
40}
41
42pub fn isLocal(symbol: Symbol) bool {
43 return !(symbol.flags.import or symbol.flags.@"export");
44}
45
46pub fn isIFunc(symbol: Symbol, elf_file: *Elf) bool {
47 return symbol.type(elf_file) == elf.STT_GNU_IFUNC;
48}
49
50pub fn @"type"(symbol: Symbol, elf_file: *Elf) u4 {
51 const s_sym = symbol.elfSym(elf_file);
52 // const file_ptr = symbol.file(elf_file).?;
53 // if (s_sym.st_type() == elf.STT_GNU_IFUNC and file_ptr == .shared) return elf.STT_FUNC;
54 return s_sym.st_type();
55}
56
57pub fn name(symbol: Symbol, elf_file: *Elf) [:0]const u8 {
58 return elf_file.strtab.getAssumeExists(symbol.name_offset);
59}
60
61pub fn atom(symbol: Symbol, elf_file: *Elf) ?*Atom {
62 return elf_file.atom(symbol.atom_index);
63}
64
65pub fn file(symbol: Symbol, elf_file: *Elf) ?File {
66 return elf_file.file(symbol.file_index);
67}
68
69pub fn elfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym {
70 const file_ptr = symbol.file(elf_file).?;
71 switch (file_ptr) {
72 .zig_module => |x| return x.elfSym(symbol.esym_index).*,
73 .linker_defined => |x| return x.symtab.items[symbol.esym_index],
74 .object => |x| return x.symtab[symbol.esym_index],
75 }
76}
77
78pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 {
79 const file_ptr = symbol.file(elf_file) orelse return std.math.maxInt(u32);
80 const sym = symbol.elfSym(elf_file);
81 const in_archive = switch (file_ptr) {
82 .object => |x| !x.alive,
83 else => false,
84 };
85 return file_ptr.symbolRank(sym, in_archive);
86}
87
88pub fn address(symbol: Symbol, opts: struct {
89 plt: bool = true,
90}, elf_file: *Elf) u64 {
91 _ = elf_file;
92 _ = opts;
93 // if (symbol.flags.copy_rel) {
94 // return elf_file.sectionAddress(elf_file.copy_rel_sect_index.?) + symbol.value;
95 // }
96 // if (symbol.flags.plt and opts.plt) {
97 // const extra = symbol.getExtra(elf_file).?;
98 // if (!symbol.flags.is_canonical and symbol.flags.got) {
99 // // We have a non-lazy bound function pointer, use that!
100 // return elf_file.getPltGotEntryAddress(extra.plt_got);
101 // }
102 // // Lazy-bound function it is!
103 // return elf_file.getPltEntryAddress(extra.plt);
104 // }
105 return symbol.value;
106}
107
108pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 {
109 if (!symbol.flags.has_got) return 0;
110 const extras = symbol.extra(elf_file).?;
111 const entry = elf_file.got.entries.items[extras.got];
112 return entry.address(elf_file);
113}
114
115const GetOrCreateGotEntryResult = struct {
116 found_existing: bool,
117 index: GotSection.Index,
118};
119
120pub fn getOrCreateGotEntry(symbol: *Symbol, elf_file: *Elf) !GetOrCreateGotEntryResult {
121 assert(symbol.flags.needs_got);
122 if (symbol.flags.has_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).?.got };
123 const index = try elf_file.got.addGotSymbol(symbol.index, elf_file);
124 symbol.flags.has_got = true;
125 return .{ .found_existing = false, .index = index };
126}
127
128// pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 {
129// if (!symbol.flags.tlsgd) return 0;
130// const extra = symbol.getExtra(elf_file).?;
131// return elf_file.getGotEntryAddress(extra.tlsgd);
132// }
133
134// pub fn gotTpAddress(symbol: Symbol, elf_file: *Elf) u64 {
135// if (!symbol.flags.gottp) return 0;
136// const extra = symbol.getExtra(elf_file).?;
137// return elf_file.getGotEntryAddress(extra.gottp);
138// }
139
140// pub fn tlsDescAddress(symbol: Symbol, elf_file: *Elf) u64 {
141// if (!symbol.flags.tlsdesc) return 0;
142// const extra = symbol.getExtra(elf_file).?;
143// return elf_file.getGotEntryAddress(extra.tlsdesc);
144// }
145
146// pub fn alignment(symbol: Symbol, elf_file: *Elf) !u64 {
147// const file = symbol.getFile(elf_file) orelse return 0;
148// const shared = file.shared;
149// const s_sym = symbol.getSourceSymbol(elf_file);
150// const shdr = shared.getShdrs()[s_sym.st_shndx];
151// const alignment = @max(1, shdr.sh_addralign);
152// return if (s_sym.st_value == 0)
153// alignment
154// else
155// @min(alignment, try std.math.powi(u64, 2, @ctz(s_sym.st_value)));
156// }
157
158pub fn addExtra(symbol: *Symbol, extras: Extra, elf_file: *Elf) !void {
159 symbol.extra_index = try elf_file.addSymbolExtra(extras);
160}
161
162pub fn extra(symbol: Symbol, elf_file: *Elf) ?Extra {
163 return elf_file.symbolExtra(symbol.extra_index);
164}
165
166pub fn setExtra(symbol: Symbol, extras: Extra, elf_file: *Elf) void {
167 elf_file.setSymbolExtra(symbol.extra_index, extras);
168}
169
170pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
171 const file_ptr = symbol.file(elf_file) orelse {
172 out.* = Elf.null_sym;
173 return;
174 };
175 const esym = symbol.elfSym(elf_file);
176 const st_type = symbol.type(elf_file);
177 const st_bind: u8 = blk: {
178 if (symbol.isLocal()) break :blk 0;
179 if (symbol.flags.weak) break :blk elf.STB_WEAK;
180 // if (file_ptr == .shared) break :blk elf.STB_GLOBAL;
181 break :blk esym.st_bind();
182 };
183 const st_shndx = blk: {
184 // if (symbol.flags.copy_rel) break :blk elf_file.copy_rel_sect_index.?;
185 // if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF;
186 if (symbol.atom(elf_file) == null and file_ptr != .linker_defined)
187 break :blk elf.SHN_ABS;
188 break :blk symbol.output_section_index;
189 };
190 const st_value = blk: {
191 // if (symbol.flags.copy_rel) break :blk symbol.address(.{}, elf_file);
192 // if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) {
193 // if (symbol.flags.is_canonical) break :blk symbol.address(.{}, elf_file);
194 // break :blk 0;
195 // }
196 // if (st_shndx == elf.SHN_ABS) break :blk symbol.value;
197 // const shdr = &elf_file.sections.items(.shdr)[st_shndx];
198 // if (Elf.shdrIsTls(shdr)) break :blk symbol.value - elf_file.getTlsAddress();
199 break :blk symbol.value;
200 };
201 out.* = .{
202 .st_name = symbol.name_offset,
203 .st_info = (st_bind << 4) | st_type,
204 .st_other = esym.st_other,
205 .st_shndx = st_shndx,
206 .st_value = st_value,
207 .st_size = esym.st_size,
208 };
209}
210
211pub fn format(
212 symbol: Symbol,
213 comptime unused_fmt_string: []const u8,
214 options: std.fmt.FormatOptions,
215 writer: anytype,
216) !void {
217 _ = symbol;
218 _ = unused_fmt_string;
219 _ = options;
220 _ = writer;
221 @compileError("do not format symbols directly");
222}
223
224const FormatContext = struct {
225 symbol: Symbol,
226 elf_file: *Elf,
227};
228
229pub fn fmtName(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(formatName) {
230 return .{ .data = .{
231 .symbol = symbol,
232 .elf_file = elf_file,
233 } };
234}
235
236fn formatName(
237 ctx: FormatContext,
238 comptime unused_fmt_string: []const u8,
239 options: std.fmt.FormatOptions,
240 writer: anytype,
241) !void {
242 _ = options;
243 _ = unused_fmt_string;
244 const elf_file = ctx.elf_file;
245 const symbol = ctx.symbol;
246 try writer.writeAll(symbol.name(elf_file));
247 switch (symbol.version_index & elf.VERSYM_VERSION) {
248 elf.VER_NDX_LOCAL, elf.VER_NDX_GLOBAL => {},
249 else => {
250 unreachable;
251 // const shared = symbol.getFile(elf_file).?.shared;
252 // try writer.print("@{s}", .{shared.getVersionString(symbol.version_index)});
253 },
254 }
255}
256
257pub fn fmt(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(format2) {
258 return .{ .data = .{
259 .symbol = symbol,
260 .elf_file = elf_file,
261 } };
262}
263
264fn format2(
265 ctx: FormatContext,
266 comptime unused_fmt_string: []const u8,
267 options: std.fmt.FormatOptions,
268 writer: anytype,
269) !void {
270 _ = options;
271 _ = unused_fmt_string;
272 const symbol = ctx.symbol;
273 try writer.print("%{d} : {s} : @{x}", .{ symbol.index, symbol.fmtName(ctx.elf_file), symbol.value });
274 if (symbol.file(ctx.elf_file)) |file_ptr| {
275 if (symbol.isAbs(ctx.elf_file)) {
276 if (symbol.elfSym(ctx.elf_file).st_shndx == elf.SHN_UNDEF) {
277 try writer.writeAll(" : undef");
278 } else {
279 try writer.writeAll(" : absolute");
280 }
281 } else if (symbol.output_section_index != 0) {
282 try writer.print(" : sect({d})", .{symbol.output_section_index});
283 }
284 if (symbol.atom(ctx.elf_file)) |atom_ptr| {
285 try writer.print(" : atom({d})", .{atom_ptr.atom_index});
286 }
287 var buf: [2]u8 = .{'_'} ** 2;
288 if (symbol.flags.@"export") buf[0] = 'E';
289 if (symbol.flags.import) buf[1] = 'I';
290 try writer.print(" : {s}", .{&buf});
291 if (symbol.flags.weak) try writer.writeAll(" : weak");
292 switch (file_ptr) {
293 inline else => |x| try writer.print(" : {s}({d})", .{ @tagName(file_ptr), x.index }),
294 }
295 } else try writer.writeAll(" : unresolved");
296}
297
298pub const Flags = packed struct {
299 /// Whether the symbol is imported at runtime.
300 import: bool = false,
301
302 /// Whether the symbol is exported at runtime.
303 @"export": bool = false,
304
305 /// Whether this symbol is weak.
306 weak: bool = false,
307
308 /// Whether the symbol makes into the output symtab or not.
309 output_symtab: bool = false,
310
311 /// Whether the symbol contains GOT indirection.
312 needs_got: bool = false,
313 has_got: bool = false,
314
315 /// Whether the symbol contains PLT indirection.
316 needs_plt: bool = false,
317 plt: bool = false,
318 /// Whether the PLT entry is canonical.
319 is_canonical: bool = false,
320
321 /// Whether the symbol contains COPYREL directive.
322 copy_rel: bool = false,
323 has_copy_rel: bool = false,
324 has_dynamic: bool = false,
325
326 /// Whether the symbol contains TLSGD indirection.
327 tlsgd: bool = false,
328
329 /// Whether the symbol contains GOTTP indirection.
330 gottp: bool = false,
331
332 /// Whether the symbol contains TLSDESC indirection.
333 tlsdesc: bool = false,
334};
335
336pub const Extra = struct {
337 got: u32 = 0,
338 plt: u32 = 0,
339 plt_got: u32 = 0,
340 dynamic: u32 = 0,
341 copy_rel: u32 = 0,
342 tlsgd: u32 = 0,
343 gottp: u32 = 0,
344 tlsdesc: u32 = 0,
345};
346
347pub const Index = u32;
348
349const assert = std.debug.assert;
350const elf = std.elf;
351const std = @import("std");
352const synthetic_sections = @import("synthetic_sections.zig");
353
354const Atom = @import("Atom.zig");
355const Elf = @import("../Elf.zig");
356const File = @import("file.zig").File;
357const GotSection = synthetic_sections.GotSection;
358const LinkerDefined = @import("LinkerDefined.zig");
359// const Object = @import("Object.zig");
360// const SharedObject = @import("SharedObject.zig");
361const Symbol = @This();
362const ZigModule = @import("ZigModule.zig");
src/link/Elf/ZigModule.zig created+295
...@@ -0,0 +1,295 @@
1//! ZigModule encapsulates the state of the incrementally compiled Zig module.
2//! It stores the associated input local and global symbols, allocated atoms,
3//! and any relocations that may have been emitted.
4//! Think about this as fake in-memory Object file for the Zig module.
5
6/// Path is owned by Module and lives as long as *Module.
7path: []const u8,
8index: File.Index,
9
10local_esyms: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
11global_esyms: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
12local_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
13global_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
14globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{},
15
16atoms: std.AutoArrayHashMapUnmanaged(Atom.Index, void) = .{},
17relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{},
18
19output_symtab_size: Elf.SymtabSize = .{},
20
21pub fn deinit(self: *ZigModule, allocator: Allocator) void {
22 self.local_esyms.deinit(allocator);
23 self.global_esyms.deinit(allocator);
24 self.local_symbols.deinit(allocator);
25 self.global_symbols.deinit(allocator);
26 self.globals_lookup.deinit(allocator);
27 self.atoms.deinit(allocator);
28 for (self.relocs.items) |*list| {
29 list.deinit(allocator);
30 }
31 self.relocs.deinit(allocator);
32}
33
34pub fn addLocalEsym(self: *ZigModule, allocator: Allocator) !Symbol.Index {
35 try self.local_esyms.ensureUnusedCapacity(allocator, 1);
36 const index = @as(Symbol.Index, @intCast(self.local_esyms.items.len));
37 const esym = self.local_esyms.addOneAssumeCapacity();
38 esym.* = Elf.null_sym;
39 esym.st_info = elf.STB_LOCAL << 4;
40 return index;
41}
42
43pub fn addGlobalEsym(self: *ZigModule, allocator: Allocator) !Symbol.Index {
44 try self.global_esyms.ensureUnusedCapacity(allocator, 1);
45 const index = @as(Symbol.Index, @intCast(self.global_esyms.items.len));
46 const esym = self.global_esyms.addOneAssumeCapacity();
47 esym.* = Elf.null_sym;
48 esym.st_info = elf.STB_GLOBAL << 4;
49 return index | 0x10000000;
50}
51
52pub fn addAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !Symbol.Index {
53 const gpa = elf_file.base.allocator;
54
55 const atom_index = try elf_file.addAtom();
56 try self.atoms.putNoClobber(gpa, atom_index, {});
57 const atom_ptr = elf_file.atom(atom_index).?;
58 atom_ptr.file_index = self.index;
59 atom_ptr.output_section_index = output_section_index;
60
61 const symbol_index = try elf_file.addSymbol();
62 try self.local_symbols.append(gpa, symbol_index);
63 const symbol_ptr = elf_file.symbol(symbol_index);
64 symbol_ptr.file_index = self.index;
65 symbol_ptr.atom_index = atom_index;
66 symbol_ptr.output_section_index = output_section_index;
67
68 const esym_index = try self.addLocalEsym(gpa);
69 const esym = &self.local_esyms.items[esym_index];
70 esym.st_shndx = atom_index;
71 symbol_ptr.esym_index = esym_index;
72
73 const relocs_index = @as(Atom.Index, @intCast(self.relocs.items.len));
74 const relocs = try self.relocs.addOne(gpa);
75 relocs.* = .{};
76 atom_ptr.relocs_section_index = relocs_index;
77
78 return symbol_index;
79}
80
81pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void {
82 for (self.globals(), 0..) |index, i| {
83 const esym_index = @as(Symbol.Index, @intCast(i)) | 0x10000000;
84 const esym = self.global_esyms.items[i];
85
86 if (esym.st_shndx == elf.SHN_UNDEF) continue;
87
88 if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) {
89 const atom_index = esym.st_shndx;
90 const atom = elf_file.atom(atom_index) orelse continue;
91 if (!atom.alive) continue;
92 }
93
94 const global = elf_file.symbol(index);
95 if (self.asFile().symbolRank(esym, false) < global.symbolRank(elf_file)) {
96 const atom_index = switch (esym.st_shndx) {
97 elf.SHN_ABS, elf.SHN_COMMON => 0,
98 else => esym.st_shndx,
99 };
100 const output_section_index = if (elf_file.atom(atom_index)) |atom|
101 atom.output_section_index
102 else
103 0;
104 global.value = esym.st_value;
105 global.atom_index = atom_index;
106 global.esym_index = esym_index;
107 global.file_index = self.index;
108 global.output_section_index = output_section_index;
109 global.version_index = elf_file.default_sym_version;
110 if (esym.st_bind() == elf.STB_WEAK) global.flags.weak = true;
111 }
112 }
113}
114
115pub fn claimUnresolved(self: *ZigModule, elf_file: *Elf) void {
116 for (self.globals(), 0..) |index, i| {
117 const esym_index = @as(Symbol.Index, @intCast(i)) | 0x10000000;
118 const esym = self.global_esyms.items[i];
119
120 if (esym.st_shndx != elf.SHN_UNDEF) continue;
121
122 const global = elf_file.symbol(index);
123 if (global.file(elf_file)) |_| {
124 if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF) continue;
125 }
126
127 const is_import = blk: {
128 if (!elf_file.isDynLib()) break :blk false;
129 const vis = @as(elf.STV, @enumFromInt(esym.st_other));
130 if (vis == .HIDDEN) break :blk false;
131 break :blk true;
132 };
133
134 global.value = 0;
135 global.atom_index = 0;
136 global.esym_index = esym_index;
137 global.file_index = self.index;
138 global.version_index = if (is_import) elf.VER_NDX_LOCAL else elf_file.default_sym_version;
139 global.flags.import = is_import;
140 }
141}
142
143pub fn scanRelocs(self: *ZigModule, elf_file: *Elf, undefs: anytype) !void {
144 for (self.atoms.keys()) |atom_index| {
145 const atom = elf_file.atom(atom_index) orelse continue;
146 if (!atom.alive) continue;
147 try atom.scanRelocs(elf_file, undefs);
148 }
149}
150
151pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void {
152 for (self.locals()) |local_index| {
153 const local = elf_file.symbol(local_index);
154 const esym = local.elfSym(elf_file);
155 switch (esym.st_type()) {
156 elf.STT_SECTION, elf.STT_NOTYPE => {
157 local.flags.output_symtab = false;
158 continue;
159 },
160 else => {},
161 }
162 local.flags.output_symtab = true;
163 self.output_symtab_size.nlocals += 1;
164 }
165
166 for (self.globals()) |global_index| {
167 const global = elf_file.symbol(global_index);
168 if (global.file(elf_file)) |file| if (file.index() != self.index) {
169 global.flags.output_symtab = false;
170 continue;
171 };
172 global.flags.output_symtab = true;
173 if (global.isLocal()) {
174 self.output_symtab_size.nlocals += 1;
175 } else {
176 self.output_symtab_size.nglobals += 1;
177 }
178 }
179}
180
181pub fn writeSymtab(self: *ZigModule, elf_file: *Elf, ctx: anytype) void {
182 var ilocal = ctx.ilocal;
183 for (self.locals()) |local_index| {
184 const local = elf_file.symbol(local_index);
185 if (!local.flags.output_symtab) continue;
186 local.setOutputSym(elf_file, &ctx.symtab[ilocal]);
187 ilocal += 1;
188 }
189
190 var iglobal = ctx.iglobal;
191 for (self.globals()) |global_index| {
192 const global = elf_file.symbol(global_index);
193 if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
194 if (!global.flags.output_symtab) continue;
195 if (global.isLocal()) {
196 global.setOutputSym(elf_file, &ctx.symtab[ilocal]);
197 ilocal += 1;
198 } else {
199 global.setOutputSym(elf_file, &ctx.symtab[iglobal]);
200 iglobal += 1;
201 }
202 }
203}
204
205pub fn symbol(self: *ZigModule, index: Symbol.Index) Symbol.Index {
206 const is_global = index & 0x10000000 != 0;
207 const actual_index = index & 0x0fffffff;
208 if (is_global) return self.global_symbols.items[actual_index];
209 return self.local_symbols.items[actual_index];
210}
211
212pub fn elfSym(self: *ZigModule, index: Symbol.Index) *elf.Elf64_Sym {
213 const is_global = index & 0x10000000 != 0;
214 const actual_index = index & 0x0fffffff;
215 if (is_global) return &self.global_esyms.items[actual_index];
216 return &self.local_esyms.items[actual_index];
217}
218
219pub fn locals(self: *ZigModule) []const Symbol.Index {
220 return self.local_symbols.items;
221}
222
223pub fn globals(self: *ZigModule) []const Symbol.Index {
224 return self.global_symbols.items;
225}
226
227pub fn asFile(self: *ZigModule) File {
228 return .{ .zig_module = self };
229}
230
231pub fn fmtSymtab(self: *ZigModule, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
232 return .{ .data = .{
233 .self = self,
234 .elf_file = elf_file,
235 } };
236}
237
238const FormatContext = struct {
239 self: *ZigModule,
240 elf_file: *Elf,
241};
242
243fn formatSymtab(
244 ctx: FormatContext,
245 comptime unused_fmt_string: []const u8,
246 options: std.fmt.FormatOptions,
247 writer: anytype,
248) !void {
249 _ = unused_fmt_string;
250 _ = options;
251 try writer.writeAll(" locals\n");
252 for (ctx.self.locals()) |index| {
253 const local = ctx.elf_file.symbol(index);
254 try writer.print(" {}\n", .{local.fmt(ctx.elf_file)});
255 }
256 try writer.writeAll(" globals\n");
257 for (ctx.self.globals()) |index| {
258 const global = ctx.elf_file.symbol(index);
259 try writer.print(" {}\n", .{global.fmt(ctx.elf_file)});
260 }
261}
262
263pub fn fmtAtoms(self: *ZigModule, elf_file: *Elf) std.fmt.Formatter(formatAtoms) {
264 return .{ .data = .{
265 .self = self,
266 .elf_file = elf_file,
267 } };
268}
269
270fn formatAtoms(
271 ctx: FormatContext,
272 comptime unused_fmt_string: []const u8,
273 options: std.fmt.FormatOptions,
274 writer: anytype,
275) !void {
276 _ = unused_fmt_string;
277 _ = options;
278 try writer.writeAll(" atoms\n");
279 for (ctx.self.atoms.keys()) |atom_index| {
280 const atom = ctx.elf_file.atom(atom_index) orelse continue;
281 try writer.print(" {}\n", .{atom.fmt(ctx.elf_file)});
282 }
283}
284
285const assert = std.debug.assert;
286const std = @import("std");
287const elf = std.elf;
288
289const Allocator = std.mem.Allocator;
290const Atom = @import("Atom.zig");
291const Elf = @import("../Elf.zig");
292const File = @import("file.zig").File;
293const Module = @import("../../Module.zig");
294const Symbol = @import("Symbol.zig");
295const ZigModule = @This();
src/link/Elf/eh_frame.zig created+449
...@@ -0,0 +1,449 @@
1pub const Fde = struct {
2 /// Includes 4byte size cell.
3 offset: u64,
4 size: u64,
5 cie_index: u32,
6 rel_index: u32 = 0,
7 rel_num: u32 = 0,
8 rel_section_index: u32 = 0,
9 input_section_index: u32 = 0,
10 file_index: u32 = 0,
11 alive: bool = true,
12 /// Includes 4byte size cell.
13 out_offset: u64 = 0,
14
15 pub fn address(fde: Fde, elf_file: *Elf) u64 {
16 const base: u64 = if (elf_file.eh_frame_section_index) |shndx|
17 elf_file.shdrs.items[shndx].sh_addr
18 else
19 0;
20 return base + fde.out_offset;
21 }
22
23 pub fn data(fde: Fde, elf_file: *Elf) error{Overflow}![]const u8 {
24 const object = elf_file.file(fde.file_index).?.object;
25 const contents = try object.shdrContents(fde.input_section_index);
26 return contents[fde.offset..][0..fde.calcSize()];
27 }
28
29 pub fn cie(fde: Fde, elf_file: *Elf) Cie {
30 const object = elf_file.file(fde.file_index).?.object;
31 return object.cies.items[fde.cie_index];
32 }
33
34 pub fn ciePointer(fde: Fde, elf_file: *Elf) u32 {
35 return std.mem.readIntLittle(u32, fde.data(elf_file)[4..8]);
36 }
37
38 pub fn calcSize(fde: Fde) u64 {
39 return fde.size + 4;
40 }
41
42 pub fn atom(fde: Fde, elf_file: *Elf) error{Overflow}!*Atom {
43 const object = elf_file.file(fde.file_index).?.object;
44 const rel = (try fde.relocs(elf_file))[0];
45 const sym = object.symtab[rel.r_sym()];
46 const atom_index = object.atoms.items[sym.st_shndx];
47 return elf_file.atom(atom_index).?;
48 }
49
50 pub fn relocs(fde: Fde, elf_file: *Elf) error{Overflow}![]align(1) const elf.Elf64_Rela {
51 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 }
54
55 pub fn format(
56 fde: Fde,
57 comptime unused_fmt_string: []const u8,
58 options: std.fmt.FormatOptions,
59 writer: anytype,
60 ) !void {
61 _ = fde;
62 _ = unused_fmt_string;
63 _ = options;
64 _ = writer;
65 @compileError("do not format FDEs directly");
66 }
67
68 pub fn fmt(fde: Fde, elf_file: *Elf) std.fmt.Formatter(format2) {
69 return .{ .data = .{
70 .fde = fde,
71 .elf_file = elf_file,
72 } };
73 }
74
75 const FdeFormatContext = struct {
76 fde: Fde,
77 elf_file: *Elf,
78 };
79
80 fn format2(
81 ctx: FdeFormatContext,
82 comptime unused_fmt_string: []const u8,
83 options: std.fmt.FormatOptions,
84 writer: anytype,
85 ) !void {
86 _ = unused_fmt_string;
87 _ = options;
88 const fde = ctx.fde;
89 const elf_file = ctx.elf_file;
90 const base_addr = fde.address(elf_file);
91 const atom_name = if (fde.atom(elf_file)) |atom_ptr|
92 atom_ptr.name(elf_file)
93 else |_|
94 "";
95 try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{
96 base_addr + fde.out_offset,
97 fde.calcSize(),
98 fde.cie_index,
99 atom_name,
100 });
101 if (!fde.alive) try writer.writeAll(" : [*]");
102 }
103};
104
105pub const Cie = struct {
106 /// Includes 4byte size cell.
107 offset: u64,
108 size: u64,
109 rel_index: u32 = 0,
110 rel_num: u32 = 0,
111 rel_section_index: u32 = 0,
112 input_section_index: u32 = 0,
113 file_index: u32 = 0,
114 /// Includes 4byte size cell.
115 out_offset: u64 = 0,
116 alive: bool = false,
117
118 pub fn address(cie: Cie, elf_file: *Elf) u64 {
119 const base: u64 = if (elf_file.eh_frame_section_index) |shndx|
120 elf_file.shdrs.items[shndx].sh_addr
121 else
122 0;
123 return base + cie.out_offset;
124 }
125
126 pub fn data(cie: Cie, elf_file: *Elf) error{Overflow}![]const u8 {
127 const object = elf_file.file(cie.file_index).?.object;
128 const contents = try object.shdrContents(cie.input_section_index);
129 return contents[cie.offset..][0..cie.calcSize()];
130 }
131
132 pub fn calcSize(cie: Cie) u64 {
133 return cie.size + 4;
134 }
135
136 pub fn relocs(cie: Cie, elf_file: *Elf) error{Overflow}![]align(1) const elf.Elf64_Rela {
137 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];
139 }
140
141 pub fn eql(cie: Cie, other: Cie, elf_file: *Elf) error{Overflow}!bool {
142 if (!std.mem.eql(u8, try cie.data(elf_file), try other.data(elf_file))) return false;
143
144 const cie_relocs = try cie.relocs(elf_file);
145 const other_relocs = try other.relocs(elf_file);
146 if (cie_relocs.len != other_relocs.len) return false;
147
148 for (cie_relocs, other_relocs) |cie_rel, other_rel| {
149 if (cie_rel.r_offset - cie.offset != other_rel.r_offset - other.offset) return false;
150 if (cie_rel.r_type() != other_rel.r_type()) return false;
151 if (cie_rel.r_addend != other_rel.r_addend) return false;
152
153 const cie_object = elf_file.file(cie.file_index).?.object;
154 const other_object = elf_file.file(other.file_index).?.object;
155 const cie_sym = cie_object.symbol(cie_rel.r_sym(), elf_file);
156 const other_sym = other_object.symbol(other_rel.r_sym(), elf_file);
157 if (!std.mem.eql(u8, std.mem.asBytes(&cie_sym), std.mem.asBytes(&other_sym))) return false;
158 }
159 return true;
160 }
161
162 pub fn format(
163 cie: Cie,
164 comptime unused_fmt_string: []const u8,
165 options: std.fmt.FormatOptions,
166 writer: anytype,
167 ) !void {
168 _ = cie;
169 _ = unused_fmt_string;
170 _ = options;
171 _ = writer;
172 @compileError("do not format CIEs directly");
173 }
174
175 pub fn fmt(cie: Cie, elf_file: *Elf) std.fmt.Formatter(format2) {
176 return .{ .data = .{
177 .cie = cie,
178 .elf_file = elf_file,
179 } };
180 }
181
182 const CieFormatContext = struct {
183 cie: Cie,
184 elf_file: *Elf,
185 };
186
187 fn format2(
188 ctx: CieFormatContext,
189 comptime unused_fmt_string: []const u8,
190 options: std.fmt.FormatOptions,
191 writer: anytype,
192 ) !void {
193 _ = unused_fmt_string;
194 _ = options;
195 const cie = ctx.cie;
196 const elf_file = ctx.elf_file;
197 const base_addr = cie.address(elf_file);
198 try writer.print("@{x} : size({x})", .{
199 base_addr + cie.out_offset,
200 cie.calcSize(),
201 });
202 if (!cie.alive) try writer.writeAll(" : [*]");
203 }
204};
205
206pub const Iterator = struct {
207 data: []const u8,
208 pos: u64 = 0,
209
210 pub const Record = struct {
211 tag: enum { fde, cie },
212 offset: u64,
213 size: u64,
214 };
215
216 pub fn next(it: *Iterator) !?Record {
217 if (it.pos >= it.data.len) return null;
218
219 var stream = std.io.fixedBufferStream(it.data[it.pos..]);
220 const reader = stream.reader();
221
222 var size = try reader.readIntLittle(u32);
223 if (size == 0xFFFFFFFF) @panic("TODO");
224
225 const id = try reader.readIntLittle(u32);
226 const record = Record{
227 .tag = if (id == 0) .cie else .fde,
228 .offset = it.pos,
229 .size = size,
230 };
231 it.pos += size + 4;
232
233 return record;
234 }
235};
236
237pub fn calcEhFrameSize(elf_file: *Elf) !usize {
238 var offset: u64 = 0;
239
240 var cies = std.ArrayList(Cie).init(elf_file.base.allocator);
241 defer cies.deinit();
242
243 for (elf_file.objects.items) |index| {
244 const object = elf_file.file(index).?.object;
245
246 outer: for (object.cies.items) |*cie| {
247 for (cies.items) |other| {
248 if (other.eql(cie.*, elf_file)) {
249 // We already have a CIE record that has the exact same contents, so instead of
250 // duplicating them, we mark this one dead and set its output offset to be
251 // equal to that of the alive record. This way, we won't have to rewrite
252 // Fde.cie_index field when committing the records to file.
253 cie.out_offset = other.out_offset;
254 continue :outer;
255 }
256 }
257 cie.alive = true;
258 cie.out_offset = offset;
259 offset += cie.calcSize();
260 try cies.append(cie.*);
261 }
262 }
263
264 for (elf_file.objects.items) |index| {
265 const object = elf_file.file(index).?.object;
266 for (object.fdes.items) |*fde| {
267 if (!fde.alive) continue;
268 fde.out_offset = offset;
269 offset += fde.calcSize();
270 }
271 }
272
273 return offset + 4; // NULL terminator
274}
275
276pub fn calcEhFrameHdrSize(elf_file: *Elf) usize {
277 var count: usize = 0;
278 for (elf_file.objects.items) |index| {
279 for (elf_file.file(index).?.object.fdes.items) |fde| {
280 if (!fde.alive) continue;
281 count += 1;
282 }
283 }
284 return eh_frame_hdr_header_size + count * 8;
285}
286
287fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file: *Elf, contents: []u8) !void {
288 const offset = rel.r_offset - rec.offset;
289 const P = @as(i64, @intCast(rec.address(elf_file) + offset));
290 const S = @as(i64, @intCast(sym.address(.{}, elf_file)));
291 const A = rel.r_addend;
292
293 relocs_log.debug(" {s}: {x}: [{x} => {x}] ({s})", .{
294 Atom.fmtRelocType(rel.r_type()),
295 offset,
296 P,
297 S + A,
298 sym.name(elf_file),
299 });
300
301 var where = contents[offset..];
302 switch (rel.r_type()) {
303 elf.R_X86_64_32 => std.mem.writeIntLittle(i32, where[0..4], @as(i32, @truncate(S + A))),
304 elf.R_X86_64_64 => std.mem.writeIntLittle(i64, where[0..8], S + A),
305 elf.R_X86_64_PC32 => std.mem.writeIntLittle(i32, where[0..4], @as(i32, @intCast(S - P + A))),
306 elf.R_X86_64_PC64 => std.mem.writeIntLittle(i64, where[0..8], S - P + A),
307 else => unreachable,
308 }
309}
310
311pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
312 const gpa = elf_file.base.allocator;
313
314 relocs_log.debug("{x}: .eh_frame", .{elf_file.shdrs.items[elf_file.eh_frame_section_index.?].sh_addr});
315
316 for (elf_file.objects.items) |index| {
317 const object = elf_file.file(index).?.object;
318
319 for (object.cies.items) |cie| {
320 if (!cie.alive) continue;
321
322 const contents = try gpa.dupe(u8, try cie.data(elf_file));
323 defer gpa.free(contents);
324
325 for (try cie.relocs(elf_file)) |rel| {
326 const sym = object.symbol(rel.r_sym(), elf_file);
327 try resolveReloc(cie, sym, rel, elf_file, contents);
328 }
329
330 try writer.writeAll(contents);
331 }
332 }
333
334 for (elf_file.objects.items) |index| {
335 const object = elf_file.file(index).?.object;
336
337 for (object.fdes.items) |fde| {
338 if (!fde.alive) continue;
339
340 const contents = try gpa.dupe(u8, try fde.data(elf_file));
341 defer gpa.free(contents);
342
343 std.mem.writeIntLittle(
344 i32,
345 contents[4..8],
346 @as(i32, @truncate(@as(i64, @intCast(fde.out_offset + 4)) - @as(i64, @intCast(fde.cie(elf_file).out_offset)))),
347 );
348
349 for (try fde.relocs(elf_file)) |rel| {
350 const sym = object.symbol(rel.r_sym(), elf_file);
351 try resolveReloc(fde, sym, rel, elf_file, contents);
352 }
353
354 try writer.writeAll(contents);
355 }
356 }
357
358 try writer.writeIntLittle(u32, 0);
359}
360
361pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void {
362 try writer.writeByte(1); // version
363 try writer.writeByte(EH_PE.pcrel | EH_PE.sdata4);
364 try writer.writeByte(EH_PE.udata4);
365 try writer.writeByte(EH_PE.datarel | EH_PE.sdata4);
366
367 const eh_frame_shdr = elf_file.shdrs.items[elf_file.eh_frame_section_index.?];
368 const eh_frame_hdr_shdr = elf_file.shdrs.items[elf_file.eh_frame_hdr_section_index.?];
369 const num_fdes = @as(u32, @intCast(@divExact(eh_frame_hdr_shdr.sh_size - eh_frame_hdr_header_size, 8)));
370 try writer.writeIntLittle(
371 u32,
372 @as(u32, @bitCast(@as(
373 i32,
374 @truncate(@as(i64, @intCast(eh_frame_shdr.sh_addr)) - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr)) - 4),
375 ))),
376 );
377 try writer.writeIntLittle(u32, num_fdes);
378
379 const Entry = struct {
380 init_addr: u32,
381 fde_addr: u32,
382
383 pub fn lessThan(ctx: void, lhs: @This(), rhs: @This()) bool {
384 _ = ctx;
385 return lhs.init_addr < rhs.init_addr;
386 }
387 };
388
389 var entries = std.ArrayList(Entry).init(elf_file.base.allocator);
390 defer entries.deinit();
391 try entries.ensureTotalCapacityPrecise(num_fdes);
392
393 for (elf_file.objects.items) |index| {
394 const object = elf_file.file(index).?.object;
395 for (object.fdes.items) |fde| {
396 if (!fde.alive) continue;
397
398 const relocs = try fde.relocs(elf_file);
399 assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips...
400 const rel = relocs[0];
401 const sym = object.symbol(rel.r_sym(), elf_file);
402 const P = @as(i64, @intCast(fde.address(elf_file)));
403 const S = @as(i64, @intCast(sym.address(.{}, elf_file)));
404 const A = rel.r_addend;
405 entries.appendAssumeCapacity(.{
406 .init_addr = @as(u32, @bitCast(@as(i32, @truncate(S + A - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr)))))),
407 .fde_addr = @as(
408 u32,
409 @bitCast(@as(i32, @truncate(P - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr))))),
410 ),
411 });
412 }
413 }
414
415 std.mem.sort(Entry, entries.items, {}, Entry.lessThan);
416 try writer.writeAll(std.mem.sliceAsBytes(entries.items));
417}
418
419const eh_frame_hdr_header_size: u64 = 12;
420
421const EH_PE = struct {
422 pub const absptr = 0x00;
423 pub const uleb128 = 0x01;
424 pub const udata2 = 0x02;
425 pub const udata4 = 0x03;
426 pub const udata8 = 0x04;
427 pub const sleb128 = 0x09;
428 pub const sdata2 = 0x0A;
429 pub const sdata4 = 0x0B;
430 pub const sdata8 = 0x0C;
431 pub const pcrel = 0x10;
432 pub const textrel = 0x20;
433 pub const datarel = 0x30;
434 pub const funcrel = 0x40;
435 pub const aligned = 0x50;
436 pub const indirect = 0x80;
437 pub const omit = 0xFF;
438};
439
440const std = @import("std");
441const assert = std.debug.assert;
442const elf = std.elf;
443const relocs_log = std.log.scoped(.link_relocs);
444
445const Allocator = std.mem.Allocator;
446const Atom = @import("Atom.zig");
447const Elf = @import("../Elf.zig");
448const Object = @import("Object.zig");
449const Symbol = @import("Symbol.zig");
src/link/Elf/file.zig created+105
...@@ -0,0 +1,105 @@
1pub const File = union(enum) {
2 zig_module: *ZigModule,
3 linker_defined: *LinkerDefined,
4 object: *Object,
5 // shared_object: *SharedObject,
6
7 pub fn index(file: File) Index {
8 return switch (file) {
9 inline else => |x| x.index,
10 };
11 }
12
13 pub fn fmtPath(file: File) std.fmt.Formatter(formatPath) {
14 return .{ .data = file };
15 }
16
17 fn formatPath(
18 file: File,
19 comptime unused_fmt_string: []const u8,
20 options: std.fmt.FormatOptions,
21 writer: anytype,
22 ) !void {
23 _ = unused_fmt_string;
24 _ = options;
25 switch (file) {
26 .zig_module => |x| try writer.print("{s}", .{x.path}),
27 .linker_defined => try writer.writeAll("(linker defined)"),
28 .object => |x| try writer.print("{}", .{x.fmtPath()}),
29 // .shared_object => |x| try writer.writeAll(x.path),
30 }
31 }
32
33 pub fn isAlive(file: File) bool {
34 return switch (file) {
35 .zig_module => true,
36 .linker_defined => true,
37 inline else => |x| x.alive,
38 };
39 }
40
41 /// Encodes symbol rank so that the following ordering applies:
42 /// * strong defined
43 /// * weak defined
44 /// * strong in lib (dso/archive)
45 /// * weak in lib (dso/archive)
46 /// * common
47 /// * common in lib (archive)
48 /// * unclaimed
49 pub fn symbolRank(file: File, sym: elf.Elf64_Sym, in_archive: bool) u32 {
50 const base: u3 = blk: {
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()) {
53 if (in_archive) break :blk switch (sym.st_bind()) {
54 elf.STB_GLOBAL => 3,
55 else => 4,
56 };
57 break :blk switch (sym.st_bind()) {
58 elf.STB_GLOBAL => 1,
59 else => 2,
60 };
61 };
62 return (@as(u32, base) << 24) + file.index();
63 }
64
65 pub fn setAlive(file: File) void {
66 switch (file) {
67 .zig_module, .linker_defined => {},
68 inline else => |x| x.alive = true,
69 }
70 }
71
72 pub fn markLive(file: File, elf_file: *Elf) void {
73 switch (file) {
74 .zig_module, .linker_defined => {},
75 inline else => |x| x.markLive(elf_file),
76 }
77 }
78
79 pub fn globals(file: File) []const Symbol.Index {
80 return switch (file) {
81 inline else => |x| x.globals(),
82 };
83 }
84
85 pub const Index = u32;
86
87 pub const Entry = union(enum) {
88 null: void,
89 zig_module: ZigModule,
90 linker_defined: LinkerDefined,
91 object: Object,
92 // shared_object: SharedObject,
93 };
94};
95
96const std = @import("std");
97const elf = std.elf;
98
99const Allocator = std.mem.Allocator;
100const Elf = @import("../Elf.zig");
101const LinkerDefined = @import("LinkerDefined.zig");
102const Object = @import("Object.zig");
103// const SharedObject = @import("SharedObject.zig");
104const Symbol = @import("Symbol.zig");
105const ZigModule = @import("ZigModule.zig");
src/link/Elf/synthetic_sections.zig created+433
...@@ -0,0 +1,433 @@
1pub const GotSection = struct {
2 entries: std.ArrayListUnmanaged(Entry) = .{},
3 needs_rela: bool = false,
4 dirty: bool = false,
5 output_symtab_size: Elf.SymtabSize = .{},
6
7 pub const Index = u32;
8
9 const Tag = enum {
10 got,
11 tlsld,
12 tlsgd,
13 gottp,
14 tlsdesc,
15 };
16
17 const Entry = struct {
18 tag: Tag,
19 symbol_index: Symbol.Index,
20 cell_index: Index,
21
22 /// Returns how many indexes in the GOT this entry uses.
23 pub inline fn len(entry: Entry) usize {
24 return switch (entry.tag) {
25 .got, .gottp => 1,
26 .tlsld, .tlsgd, .tlsdesc => 2,
27 };
28 }
29
30 pub fn address(entry: Entry, elf_file: *Elf) u64 {
31 const ptr_bytes = @as(u64, elf_file.archPtrWidthBytes());
32 const shdr = &elf_file.shdrs.items[elf_file.got_section_index.?];
33 return shdr.sh_addr + @as(u64, entry.cell_index) * ptr_bytes;
34 }
35 };
36
37 pub fn deinit(got: *GotSection, allocator: Allocator) void {
38 got.entries.deinit(allocator);
39 }
40
41 fn allocateEntry(got: *GotSection, allocator: Allocator) !Index {
42 try got.entries.ensureUnusedCapacity(allocator, 1);
43 // TODO add free list
44 const index = @as(Index, @intCast(got.entries.items.len));
45 const entry = got.entries.addOneAssumeCapacity();
46 const cell_index: Index = if (index > 0) blk: {
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;
53 }
54
55 pub fn addGotSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index {
56 const index = try got.allocateEntry(elf_file.base.allocator);
57 const entry = &got.entries.items[index];
58 entry.tag = .got;
59 entry.symbol_index = sym_index;
60 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)))
62 got.needs_rela = true;
63 if (symbol.extra(elf_file)) |extra| {
64 var new_extra = extra;
65 new_extra.got = index;
66 symbol.setExtra(new_extra, elf_file);
67 } else try symbol.addExtra(.{ .got = index }, elf_file);
68 return index;
69 }
70
71 // pub fn addTlsGdSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void {
72 // const index = got.next_index;
73 // const symbol = elf_file.getSymbol(sym_index);
74 // if (symbol.flags.import or elf_file.options.output_mode == .lib) got.needs_rela = true;
75 // if (symbol.getExtra(elf_file)) |extra| {
76 // var new_extra = extra;
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 // }
109
110 pub fn size(got: GotSection, elf_file: *Elf) usize {
111 var s: usize = 0;
112 for (got.entries.items) |entry| {
113 s += elf_file.archPtrWidthBytes() * entry.len();
114 }
115 return s;
116 }
117
118 pub fn writeEntry(got: *GotSection, elf_file: *Elf, index: Index) !void {
119 const entry_size: u16 = elf_file.archPtrWidthBytes();
120 if (got.dirty) {
121 const needed_size = got.size(elf_file);
122 try elf_file.growAllocSection(elf_file.got_section_index.?, needed_size);
123 got.dirty = false;
124 }
125 const endian = elf_file.base.options.target.cpu.arch.endian();
126 const entry = got.entries.items[index];
127 const shdr = &elf_file.shdrs.items[elf_file.got_section_index.?];
128 const off = shdr.sh_offset + @as(u64, entry_size) * entry.cell_index;
129 const vaddr = shdr.sh_addr + @as(u64, entry_size) * entry.cell_index;
130 const value = elf_file.symbol(entry.symbol_index).value;
131 switch (entry_size) {
132 2 => {
133 var buf: [2]u8 = undefined;
134 std.mem.writeInt(u16, &buf, @as(u16, @intCast(value)), endian);
135 try elf_file.base.file.?.pwriteAll(&buf, off);
136 },
137 4 => {
138 var buf: [4]u8 = undefined;
139 std.mem.writeInt(u32, &buf, @as(u32, @intCast(value)), endian);
140 try elf_file.base.file.?.pwriteAll(&buf, off);
141 },
142 8 => {
143 var buf: [8]u8 = undefined;
144 std.mem.writeInt(u64, &buf, value, endian);
145 try elf_file.base.file.?.pwriteAll(&buf, off);
146
147 if (elf_file.base.child_pid) |pid| {
148 switch (builtin.os.tag) {
149 .linux => {
150 var local_vec: [1]std.os.iovec_const = .{.{
151 .iov_base = &buf,
152 .iov_len = buf.len,
153 }};
154 var remote_vec: [1]std.os.iovec_const = .{.{
155 .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(vaddr)))),
156 .iov_len = buf.len,
157 }};
158 const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0);
159 switch (std.os.errno(rc)) {
160 .SUCCESS => assert(rc == buf.len),
161 else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
162 }
163 },
164 else => return error.HotSwapUnavailableOnHostOperatingSystem,
165 }
166 }
167 },
168 else => unreachable,
169 }
170 }
171
172 // pub fn write(got: GotSection, elf_file: *Elf, writer: anytype) !void {
173 // const is_shared = elf_file.options.output_mode == .lib;
174 // const apply_relocs = elf_file.options.apply_dynamic_relocs;
175
176 // for (got.symbols.items) |sym| {
177 // const symbol = elf_file.getSymbol(sym.getIndex());
178 // switch (sym) {
179 // .got => {
180 // const value: u64 = blk: {
181 // const value = symbol.getAddress(.{ .plt = false }, elf_file);
182 // if (symbol.flags.import) break :blk 0;
183 // if (symbol.isIFunc(elf_file))
184 // break :blk if (apply_relocs) value else 0;
185 // if (elf_file.options.pic and !symbol.isAbs(elf_file))
186 // break :blk if (apply_relocs) value else 0;
187 // break :blk value;
188 // };
189 // try writer.writeIntLittle(u64, value);
190 // },
191
192 // .tlsgd => {
193 // if (symbol.flags.import) {
194 // try writer.writeIntLittle(u64, 0);
195 // try writer.writeIntLittle(u64, 0);
196 // } else {
197 // try writer.writeIntLittle(u64, if (is_shared) @as(u64, 0) else 1);
198 // const offset = symbol.getAddress(.{}, elf_file) - elf_file.getDtpAddress();
199 // try writer.writeIntLittle(u64, offset);
200 // }
201 // },
202
203 // .gottp => {
204 // if (symbol.flags.import) {
205 // try writer.writeIntLittle(u64, 0);
206 // } else if (is_shared) {
207 // const offset = if (apply_relocs)
208 // symbol.getAddress(.{}, elf_file) - elf_file.getTlsAddress()
209 // else
210 // 0;
211 // try writer.writeIntLittle(u64, offset);
212 // } else {
213 // const offset = @as(i64, @intCast(symbol.getAddress(.{}, elf_file))) -
214 // @as(i64, @intCast(elf_file.getTpAddress()));
215 // try writer.writeIntLittle(u64, @as(u64, @bitCast(offset)));
216 // }
217 // },
218
219 // .tlsdesc => {
220 // try writer.writeIntLittle(u64, 0);
221 // try writer.writeIntLittle(u64, 0);
222 // },
223 // }
224 // }
225
226 // if (got.emit_tlsld) {
227 // try writer.writeIntLittle(u64, if (is_shared) @as(u64, 0) else 1);
228 // try writer.writeIntLittle(u64, 0);
229 // }
230 // }
231
232 // pub fn addRela(got: GotSection, elf_file: *Elf) !void {
233 // const is_shared = elf_file.options.output_mode == .lib;
234 // try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, got.numRela(elf_file));
235
236 // for (got.symbols.items) |sym| {
237 // const symbol = elf_file.getSymbol(sym.getIndex());
238 // const extra = symbol.getExtra(elf_file).?;
239
240 // switch (sym) {
241 // .got => {
242 // const offset = symbol.gotAddress(elf_file);
243
244 // if (symbol.flags.import) {
245 // elf_file.addRelaDynAssumeCapacity(.{
246 // .offset = offset,
247 // .sym = extra.dynamic,
248 // .type = elf.R_X86_64_GLOB_DAT,
249 // });
250 // continue;
251 // }
252
253 // if (symbol.isIFunc(elf_file)) {
254 // elf_file.addRelaDynAssumeCapacity(.{
255 // .offset = offset,
256 // .type = elf.R_X86_64_IRELATIVE,
257 // .addend = @intCast(symbol.getAddress(.{ .plt = false }, elf_file)),
258 // });
259 // continue;
260 // }
261
262 // if (elf_file.options.pic and !symbol.isAbs(elf_file)) {
263 // elf_file.addRelaDynAssumeCapacity(.{
264 // .offset = offset,
265 // .type = elf.R_X86_64_RELATIVE,
266 // .addend = @intCast(symbol.getAddress(.{ .plt = false }, elf_file)),
267 // });
268 // }
269 // },
270
271 // .tlsgd => {
272 // const offset = symbol.getTlsGdAddress(elf_file);
273 // if (symbol.flags.import) {
274 // elf_file.addRelaDynAssumeCapacity(.{
275 // .offset = offset,
276 // .sym = extra.dynamic,
277 // .type = elf.R_X86_64_DTPMOD64,
278 // });
279 // elf_file.addRelaDynAssumeCapacity(.{
280 // .offset = offset + 8,
281 // .sym = extra.dynamic,
282 // .type = elf.R_X86_64_DTPOFF64,
283 // });
284 // } else if (is_shared) {
285 // elf_file.addRelaDynAssumeCapacity(.{
286 // .offset = offset,
287 // .sym = extra.dynamic,
288 // .type = elf.R_X86_64_DTPMOD64,
289 // });
290 // }
291 // },
292
293 // .gottp => {
294 // const offset = symbol.getGotTpAddress(elf_file);
295 // if (symbol.flags.import) {
296 // elf_file.addRelaDynAssumeCapacity(.{
297 // .offset = offset,
298 // .sym = extra.dynamic,
299 // .type = elf.R_X86_64_TPOFF64,
300 // });
301 // } else if (is_shared) {
302 // elf_file.addRelaDynAssumeCapacity(.{
303 // .offset = offset,
304 // .type = elf.R_X86_64_TPOFF64,
305 // .addend = @intCast(symbol.getAddress(.{}, elf_file) - elf_file.getTlsAddress()),
306 // });
307 // }
308 // },
309
310 // .tlsdesc => {
311 // const offset = symbol.getTlsDescAddress(elf_file);
312 // elf_file.addRelaDynAssumeCapacity(.{
313 // .offset = offset,
314 // .sym = extra.dynamic,
315 // .type = elf.R_X86_64_TLSDESC,
316 // });
317 // },
318 // }
319 // }
320
321 // if (is_shared and got.emit_tlsld) {
322 // const offset = elf_file.getTlsLdAddress();
323 // elf_file.addRelaDynAssumeCapacity(.{
324 // .offset = offset,
325 // .type = elf.R_X86_64_DTPMOD64,
326 // });
327 // }
328 // }
329
330 // pub fn numRela(got: GotSection, elf_file: *Elf) usize {
331 // const is_shared = elf_file.options.output_mode == .lib;
332 // var num: usize = 0;
333 // for (got.symbols.items) |sym| {
334 // const symbol = elf_file.symbol(sym.index());
335 // switch (sym) {
336 // .got => if (symbol.flags.import or
337 // symbol.isIFunc(elf_file) or (elf_file.options.pic and !symbol.isAbs(elf_file)))
338 // {
339 // num += 1;
340 // },
341
342 // .tlsgd => if (symbol.flags.import) {
343 // num += 2;
344 // } else if (is_shared) {
345 // num += 1;
346 // },
347
348 // .gottp => if (symbol.flags.import or is_shared) {
349 // num += 1;
350 // },
351
352 // .tlsdesc => num += 1,
353 // }
354 // }
355 // if (is_shared and got.emit_tlsld) num += 1;
356 // return num;
357 // }
358
359 pub fn updateSymtabSize(got: *GotSection, elf_file: *Elf) void {
360 _ = elf_file;
361 got.output_symtab_size.nlocals = @as(u32, @intCast(got.entries.items.len));
362 }
363
364 pub fn writeSymtab(got: GotSection, elf_file: *Elf, ctx: anytype) !void {
365 const gpa = elf_file.base.allocator;
366 for (got.entries.items, ctx.ilocal..) |entry, ilocal| {
367 const suffix = switch (entry.tag) {
368 .tlsld => "$tlsld",
369 .tlsgd => "$tlsgd",
370 .got => "$got",
371 .gottp => "$gottp",
372 .tlsdesc => "$tlsdesc",
373 };
374 const symbol = elf_file.symbol(entry.symbol_index);
375 const name = try std.fmt.allocPrint(gpa, "{s}{s}", .{ symbol.name(elf_file), suffix });
376 defer gpa.free(name);
377 const st_name = try elf_file.strtab.insert(gpa, name);
378 const st_value = switch (entry.tag) {
379 .got => symbol.gotAddress(elf_file),
380 else => unreachable,
381 };
382 const st_size: u64 = entry.len() * elf_file.archPtrWidthBytes();
383 ctx.symtab[ilocal] = .{
384 .st_name = st_name,
385 .st_info = elf.STT_OBJECT,
386 .st_other = 0,
387 .st_shndx = elf_file.got_section_index.?,
388 .st_value = st_value,
389 .st_size = st_size,
390 };
391 }
392 }
393
394 const FormatCtx = struct {
395 got: GotSection,
396 elf_file: *Elf,
397 };
398
399 pub fn fmt(got: GotSection, elf_file: *Elf) std.fmt.Formatter(format2) {
400 return .{ .data = .{ .got = got, .elf_file = elf_file } };
401 }
402
403 pub fn format2(
404 ctx: FormatCtx,
405 comptime unused_fmt_string: []const u8,
406 options: std.fmt.FormatOptions,
407 writer: anytype,
408 ) !void {
409 _ = options;
410 _ = unused_fmt_string;
411 try writer.writeAll("GOT\n");
412 for (ctx.got.entries.items) |entry| {
413 const symbol = ctx.elf_file.symbol(entry.symbol_index);
414 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
415 entry.cell_index,
416 entry.address(ctx.elf_file),
417 entry.symbol_index,
418 symbol.address(.{}, ctx.elf_file),
419 symbol.name(ctx.elf_file),
420 });
421 }
422 }
423};
424
425const assert = std.debug.assert;
426const builtin = @import("builtin");
427const elf = std.elf;
428const log = std.log.scoped(.link);
429const std = @import("std");
430
431const Allocator = std.mem.Allocator;
432const Elf = @import("../Elf.zig");
433const Symbol = @import("Symbol.zig");
src/link/strtab.zig+2-2
...@@ -100,13 +100,13 @@ pub fn StringTable(comptime log_scope: @Type(.EnumLiteral)) type {...@@ -100,13 +100,13 @@ pub fn StringTable(comptime log_scope: @Type(.EnumLiteral)) type {
100 });100 });
101 }101 }
102102
103 pub fn get(self: Self, off: u32) ?[]const u8 {103 pub fn get(self: Self, off: u32) ?[:0]const u8 {
104 log.debug("getting string at 0x{x}", .{off});104 log.debug("getting string at 0x{x}", .{off});
105 if (off >= self.buffer.items.len) return null;105 if (off >= self.buffer.items.len) return null;
106 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.buffer.items.ptr + off)), 0);106 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.buffer.items.ptr + off)), 0);
107 }107 }
108108
109 pub fn getAssumeExists(self: Self, off: u32) []const u8 {109 pub fn getAssumeExists(self: Self, off: u32) [:0]const u8 {
110 return self.get(off) orelse unreachable;110 return self.get(off) orelse unreachable;
111 }111 }
112112