aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2023-11-04 20:58:15 +0100
committerGitHub <noreply@github.com>2023-11-04 20:58:15 +0100
commitf24ceec35a6fd1e5e6a671461b78919b5f588a32 (patch)
tree00e4242cf5dcdae789e0d8f1de77303f4a2e6e23
parent98dc28bbe223cb7183aabe7ed7a847c67c1a4df9 (diff)
parent7a186d9eb6a84fb22bdb53b9c81a70169e9fa65f (diff)
downloadzig-f24ceec35a6fd1e5e6a671461b78919b5f588a32.tar.gz
zig-f24ceec35a6fd1e5e6a671461b78919b5f588a32.zip
Merge pull request #17844 from ziglang/elf-object
elf: handle emitting relocatables and static libraries - humble beginnings
-rw-r--r--CMakeLists.txt2
-rw-r--r--src/Compilation.zig70
-rw-r--r--src/arch/x86_64/CodeGen.zig5
-rw-r--r--src/arch/x86_64/Emit.zig43
-rw-r--r--src/arch/x86_64/Lower.zig34
-rw-r--r--src/codegen.zig2
-rw-r--r--src/link/Coff.zig16
-rw-r--r--src/link/Dwarf.zig4
-rw-r--r--src/link/Elf.zig993
-rw-r--r--src/link/Elf/Archive.zig335
-rw-r--r--src/link/Elf/Atom.zig10
-rw-r--r--src/link/Elf/LinkerDefined.zig42
-rw-r--r--src/link/Elf/Object.zig167
-rw-r--r--src/link/Elf/SharedObject.zig112
-rw-r--r--src/link/Elf/Symbol.zig49
-rw-r--r--src/link/Elf/ZigObject.zig410
-rw-r--r--src/link/Elf/eh_frame.zig2
-rw-r--r--src/link/Elf/file.zig111
-rw-r--r--src/link/Elf/gc.zig43
-rw-r--r--src/link/Elf/synthetic_sections.zig106
-rw-r--r--src/link/MachO.zig4
-rw-r--r--src/link/MachO/DebugSymbols.zig4
-rw-r--r--src/link/MachO/zld.zig1
-rw-r--r--src/link/StringTable.zig49
-rw-r--r--src/link/strtab.zig121
-rw-r--r--test/link/elf.zig188
26 files changed, 1913 insertions, 1010 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4b27fd555e..536d36e245 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -624,7 +624,7 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig"
"${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
"${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"
- "${CMAKE_SOURCE_DIR}/src/link/strtab.zig"
+ "${CMAKE_SOURCE_DIR}/src/link/StringTable.zig"
"${CMAKE_SOURCE_DIR}/src/link/tapi.zig"
"${CMAKE_SOURCE_DIR}/src/link/tapi/Tokenizer.zig"
"${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig"
diff --git a/src/Compilation.zig b/src/Compilation.zig
index a041a4b188..f2a0fb5e63 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -810,16 +810,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
return error.ExportTableAndImportTableConflict;
}
- // The `have_llvm` condition is here only because native backends cannot yet build compiler-rt.
- // Once they are capable this condition could be removed. When removing this condition,
- // also test the use case of `build-obj -fcompiler-rt` with the native backends
- // and make sure the compiler-rt symbols are emitted.
- const is_p9 = options.target.os.tag == .plan9;
- const is_spv = options.target.cpu.arch.isSpirV();
- const capable_of_building_compiler_rt = build_options.have_llvm and !is_p9 and !is_spv;
- const capable_of_building_zig_libc = build_options.have_llvm and !is_p9 and !is_spv;
- const capable_of_building_ssp = build_options.have_llvm and !is_p9 and !is_spv;
-
const comp: *Compilation = comp: {
// For allocations that have the same lifetime as Compilation. This arena is used only during this
// initialization and then is freed in deinit().
@@ -1094,6 +1084,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
if (stack_check and !target_util.supportsStackProbing(options.target))
return error.StackCheckUnsupportedByTarget;
+ const capable_of_building_ssp = canBuildLibSsp(options.target, use_llvm);
+
const stack_protector: u32 = options.want_stack_protector orelse b: {
if (!target_util.supportsStackProtector(options.target)) break :b @as(u32, 0);
@@ -1754,6 +1746,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
const target = comp.getTarget();
+ const capable_of_building_compiler_rt = canBuildLibCompilerRt(target, comp.bin_file.options.use_llvm);
+ const capable_of_building_zig_libc = canBuildZigLibC(target, comp.bin_file.options.use_llvm);
+
// Add a `CObject` for each `c_source_files`.
try comp.c_object_table.ensureTotalCapacity(gpa, options.c_source_files.len);
for (options.c_source_files) |c_source_file| {
@@ -6240,9 +6235,62 @@ pub fn dump_argv(argv: []const []const u8) void {
nosuspend stderr.print("{s}\n", .{argv[argv.len - 1]}) catch {};
}
+fn canBuildLibCompilerRt(target: std.Target, use_llvm: bool) bool {
+ switch (target.os.tag) {
+ .plan9 => return false,
+ else => {},
+ }
+ switch (target.cpu.arch) {
+ .spirv32, .spirv64 => return false,
+ else => {},
+ }
+ return switch (zigBackend(target, use_llvm)) {
+ .stage2_llvm => true,
+ .stage2_x86_64 => if (target.ofmt == .elf) true else build_options.have_llvm,
+ else => build_options.have_llvm,
+ };
+}
+
+fn canBuildLibSsp(target: std.Target, use_llvm: bool) bool {
+ switch (target.os.tag) {
+ .plan9 => return false,
+ else => {},
+ }
+ switch (target.cpu.arch) {
+ .spirv32, .spirv64 => return false,
+ else => {},
+ }
+ return switch (zigBackend(target, use_llvm)) {
+ .stage2_llvm => true,
+ else => build_options.have_llvm,
+ };
+}
+
+/// Not to be confused with canBuildLibC, which builds musl, glibc, and similar.
+/// This one builds lib/c.zig.
+fn canBuildZigLibC(target: std.Target, use_llvm: bool) bool {
+ switch (target.os.tag) {
+ .plan9 => return false,
+ else => {},
+ }
+ switch (target.cpu.arch) {
+ .spirv32, .spirv64 => return false,
+ else => {},
+ }
+ return switch (zigBackend(target, use_llvm)) {
+ .stage2_llvm => true,
+ .stage2_x86_64 => if (target.ofmt == .elf) true else build_options.have_llvm,
+ else => build_options.have_llvm,
+ };
+}
+
pub fn getZigBackend(comp: Compilation) std.builtin.CompilerBackend {
- if (comp.bin_file.options.use_llvm) return .stage2_llvm;
const target = comp.bin_file.options.target;
+ return zigBackend(target, comp.bin_file.options.use_llvm);
+}
+
+fn zigBackend(target: std.Target, use_llvm: bool) std.builtin.CompilerBackend {
+ if (use_llvm) return .stage2_llvm;
if (target.ofmt == .c) return .stage2_c;
return switch (target.cpu.arch) {
.wasm32, .wasm64 => std.builtin.CompilerBackend.stage2_wasm,
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
index 756a3cc32b..1065d968b5 100644
--- a/src/arch/x86_64/CodeGen.zig
+++ b/src/arch/x86_64/CodeGen.zig
@@ -10796,7 +10796,7 @@ fn genCall(self: *Self, info: union(enum) {
if (self.bin_file.cast(link.File.Elf)) |elf_file| {
const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl);
const sym = elf_file.symbol(sym_index);
- _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
+ sym.flags.needs_zig_got = true;
if (self.bin_file.options.pic) {
const callee_reg: Register = switch (resolved_cc) {
.SysV => callee: {
@@ -13682,8 +13682,7 @@ fn genLazySymbolRef(
const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, lazy_sym) catch |err|
return self.fail("{s} creating lazy symbol", .{@errorName(err)});
const sym = elf_file.symbol(sym_index);
- _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
-
+ sym.flags.needs_zig_got = true;
if (self.bin_file.options.pic) {
switch (tag) {
.lea, .call => try self.genSetReg(reg, Type.usize, .{
diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig
index 36f4b05a6e..db951a71a2 100644
--- a/src/arch/x86_64/Emit.zig
+++ b/src/arch/x86_64/Emit.zig
@@ -85,10 +85,19 @@ pub fn emitMir(emit: *Emit) Error!void {
@tagName(emit.lower.bin_file.tag),
}),
.linker_reloc => |data| if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| {
+ const is_obj_or_static_lib = switch (emit.lower.bin_file.options.output_mode) {
+ .Exe => false,
+ .Obj => true,
+ .Lib => emit.lower.bin_file.options.link_mode == .Static,
+ };
const atom = elf_file.symbol(data.atom_index).atom(elf_file).?;
- const sym = elf_file.symbol(elf_file.zigObjectPtr().?.symbol(data.sym_index));
+ const sym_index = elf_file.zigObjectPtr().?.symbol(data.sym_index);
+ const sym = elf_file.symbol(sym_index);
+ if (sym.flags.needs_zig_got and !is_obj_or_static_lib) {
+ _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
+ }
if (emit.lower.bin_file.options.pic) {
- const r_type: u32 = if (sym.flags.has_zig_got)
+ const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib)
link.File.Elf.R_X86_64_ZIG_GOTPCREL
else if (sym.flags.needs_got)
std.elf.R_X86_64_GOTPCREL
@@ -100,17 +109,25 @@ pub fn emitMir(emit: *Emit) Error!void {
.r_addend = -4,
});
} else {
- const r_type: u32 = if (sym.flags.has_zig_got)
- link.File.Elf.R_X86_64_ZIG_GOT32
- else if (sym.flags.needs_got)
- std.elf.R_X86_64_GOT32
- else
- std.elf.R_X86_64_32;
- try atom.addReloc(elf_file, .{
- .r_offset = end_offset - 4,
- .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type,
- .r_addend = 0,
- });
+ if (lowered_inst.encoding.mnemonic == .call and sym.flags.needs_zig_got and is_obj_or_static_lib) {
+ try atom.addReloc(elf_file, .{
+ .r_offset = end_offset - 4,
+ .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | std.elf.R_X86_64_PC32,
+ .r_addend = -4,
+ });
+ } else {
+ const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib)
+ link.File.Elf.R_X86_64_ZIG_GOT32
+ else if (sym.flags.needs_got)
+ std.elf.R_X86_64_GOT32
+ else
+ std.elf.R_X86_64_32;
+ try atom.addReloc(elf_file, .{
+ .r_offset = end_offset - 4,
+ .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type,
+ .r_addend = 0,
+ });
+ }
}
} else unreachable,
.linker_got,
diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig
index fe735a3700..621d61d7f8 100644
--- a/src/arch/x86_64/Lower.zig
+++ b/src/arch/x86_64/Lower.zig
@@ -319,6 +319,19 @@ fn reloc(lower: *Lower, target: Reloc.Target) Immediate {
}
fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void {
+ const needsZigGot = struct {
+ fn needsZigGot(sym: bits.Symbol, ctx: *link.File) bool {
+ const elf_file = ctx.cast(link.File.Elf).?;
+ const sym_index = elf_file.zigObjectPtr().?.symbol(sym.sym_index);
+ return elf_file.symbol(sym_index).flags.needs_zig_got;
+ }
+ }.needsZigGot;
+
+ const is_obj_or_static_lib = switch (lower.bin_file.options.output_mode) {
+ .Exe => false,
+ .Obj => true,
+ .Lib => lower.bin_file.options.link_mode == .Static,
+ };
var emit_prefix = prefix;
var emit_mnemonic = mnemonic;
var emit_ops_storage: [4]Operand = undefined;
@@ -334,19 +347,30 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
assert(mem_op.sib.scale_index.scale == 0);
_ = lower.reloc(.{ .linker_reloc = sym });
break :op if (lower.bin_file.options.pic) switch (mnemonic) {
- .mov, .lea => .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) },
+ .lea => {
+ break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
+ },
+ .mov => {
+ if (is_obj_or_static_lib and needsZigGot(sym, lower.bin_file)) emit_mnemonic = .lea;
+ break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
+ },
else => unreachable,
} else switch (mnemonic) {
- .call => .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
+ .call => break :op if (is_obj_or_static_lib and needsZigGot(sym, lower.bin_file)) .{
+ .imm = Immediate.s(0),
+ } else .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
.base = .{ .reg = .ds },
}) },
.lea => {
emit_mnemonic = .mov;
break :op .{ .imm = Immediate.s(0) };
},
- .mov => .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
- .base = .{ .reg = .ds },
- }) },
+ .mov => {
+ if (is_obj_or_static_lib and needsZigGot(sym, lower.bin_file)) emit_mnemonic = .lea;
+ break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
+ .base = .{ .reg = .ds },
+ }) };
+ },
else => unreachable,
};
},
diff --git a/src/codegen.zig b/src/codegen.zig
index d347aff7bc..1df993f78b 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -912,7 +912,7 @@ fn genDeclRef(
}
const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index);
const sym = elf_file.symbol(sym_index);
- _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
+ sym.flags.needs_zig_got = true;
return GenResult.mcv(.{ .load_symbol = sym.esym_index });
} else if (bin_file.cast(link.File.MachO)) |macho_file| {
if (is_extern) {
diff --git a/src/link/Coff.zig b/src/link/Coff.zig
index 075fb60861..e9cd5f8609 100644
--- a/src/link/Coff.zig
+++ b/src/link/Coff.zig
@@ -33,10 +33,10 @@ need_got_table: std.AutoHashMapUnmanaged(u32, void) = .{},
locals_free_list: std.ArrayListUnmanaged(u32) = .{},
globals_free_list: std.ArrayListUnmanaged(u32) = .{},
-strtab: StringTable(.strtab) = .{},
+strtab: StringTable = .{},
strtab_offset: ?u32 = null,
-temp_strtab: StringTable(.temp_strtab) = .{},
+temp_strtab: StringTable = .{},
got_table: TableSection(SymbolWithLoc) = .{},
@@ -419,7 +419,7 @@ fn populateMissingMetadata(self: *Coff) !void {
}
if (self.strtab_offset == null) {
- const file_size = @as(u32, @intCast(self.strtab.len()));
+ const file_size = @as(u32, @intCast(self.strtab.buffer.items.len));
self.strtab_offset = self.findFreeSpace(file_size, @alignOf(u32)); // 4bytes aligned seems like a good idea here
log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + file_size });
}
@@ -2143,7 +2143,7 @@ fn writeStrtab(self: *Coff) !void {
if (self.strtab_offset == null) return;
const allocated_size = self.allocatedSize(self.strtab_offset.?);
- const needed_size = @as(u32, @intCast(self.strtab.len()));
+ const needed_size = @as(u32, @intCast(self.strtab.buffer.items.len));
if (needed_size > allocated_size) {
self.strtab_offset = null;
@@ -2155,10 +2155,10 @@ fn writeStrtab(self: *Coff) !void {
var buffer = std.ArrayList(u8).init(self.base.allocator);
defer buffer.deinit();
try buffer.ensureTotalCapacityPrecise(needed_size);
- buffer.appendSliceAssumeCapacity(self.strtab.items());
+ buffer.appendSliceAssumeCapacity(self.strtab.buffer.items);
// Here, we do a trick in that we do not commit the size of the strtab to strtab buffer, instead
// we write the length of the strtab to a temporary buffer that goes to file.
- mem.writeInt(u32, buffer.items[0..4], @as(u32, @intCast(self.strtab.len())), .little);
+ mem.writeInt(u32, buffer.items[0..4], @as(u32, @intCast(self.strtab.buffer.items.len)), .little);
try self.base.file.?.pwriteAll(buffer.items, self.strtab_offset.?);
}
@@ -2326,7 +2326,7 @@ fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 {
const end = start + padToIdeal(size);
if (self.strtab_offset) |off| {
- const tight_size = @as(u32, @intCast(self.strtab.len()));
+ const tight_size = @as(u32, @intCast(self.strtab.buffer.items.len));
const increased_size = padToIdeal(tight_size);
const test_end = off + increased_size;
if (end > off and start < test_end) {
@@ -2667,7 +2667,7 @@ const InternPool = @import("../InternPool.zig");
const Object = @import("Coff/Object.zig");
const Relocation = @import("Coff/Relocation.zig");
const TableSection = @import("table_section.zig").TableSection;
-const StringTable = @import("strtab.zig").StringTable;
+const StringTable = @import("StringTable.zig");
const Type = @import("../type.zig").Type;
const TypedValue = @import("../TypedValue.zig");
diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig
index 82cd6153de..3bfe744443 100644
--- a/src/link/Dwarf.zig
+++ b/src/link/Dwarf.zig
@@ -23,7 +23,7 @@ abbrev_table_offset: ?u64 = null,
/// TODO replace with InternPool
/// Table of debug symbol names.
-strtab: StringTable(.strtab) = .{},
+strtab: StringTable = .{},
/// Quick lookup array of all defined source files referenced by at least one Decl.
/// They will end up in the DWARF debug_line header as two lists:
@@ -2760,6 +2760,6 @@ const LinkFn = File.LinkFn;
const LinkerLoad = @import("../codegen.zig").LinkerLoad;
const Module = @import("../Module.zig");
const InternPool = @import("../InternPool.zig");
-const StringTable = @import("strtab.zig").StringTable;
+const StringTable = @import("StringTable.zig");
const Type = @import("../type.zig").Type;
const Value = @import("../value.zig").Value;
diff --git a/src/link/Elf.zig b/src/link/Elf.zig
index 63642d4c6a..ab986aeaf9 100644
--- a/src/link/Elf.zig
+++ b/src/link/Elf.zig
@@ -66,13 +66,15 @@ page_size: u32,
default_sym_version: elf.Elf64_Versym,
/// .shstrtab buffer
-shstrtab: StringTable(.strtab) = .{},
+shstrtab: std.ArrayListUnmanaged(u8) = .{},
+/// .symtab buffer
+symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
/// .strtab buffer
-strtab: StringTable(.strtab) = .{},
+strtab: std.ArrayListUnmanaged(u8) = .{},
/// Dynamic symbol table. Only populated and emitted when linking dynamically.
dynsym: DynsymSection = .{},
/// .dynstrtab buffer
-dynstrtab: StringTable(.dynstrtab) = .{},
+dynstrtab: std.ArrayListUnmanaged(u8) = .{},
/// Version symbol table. Only populated and emitted when linking dynamically.
versym: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{},
/// .verneed section
@@ -97,13 +99,17 @@ plt_got: PltGotSection = .{},
copy_rel: CopyRelSection = .{},
/// .rela.plt section
rela_plt: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{},
-/// .zig.got section
+/// .got.zig section
zig_got: ZigGotSection = .{},
-/// Tracked section headers with incremental updates to Zig object
+/// Tracked section headers with incremental updates to Zig object.
+/// .rela.* sections are only used when emitting a relocatable object file.
zig_text_section_index: ?u16 = null,
-zig_rodata_section_index: ?u16 = null,
+zig_text_rela_section_index: ?u16 = null,
+zig_data_rel_ro_section_index: ?u16 = null,
+zig_data_rel_ro_rela_section_index: ?u16 = null,
zig_data_section_index: ?u16 = null,
+zig_data_rela_section_index: ?u16 = null,
zig_bss_section_index: ?u16 = null,
zig_got_section_index: ?u16 = null,
@@ -156,9 +162,10 @@ start_stop_indexes: std.ArrayListUnmanaged(u32) = .{},
/// An array of symbols parsed across all input files.
symbols: std.ArrayListUnmanaged(Symbol) = .{},
symbols_extra: std.ArrayListUnmanaged(u32) = .{},
-resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},
symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},
+resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},
+
has_text_reloc: bool = false,
num_ifunc_dynrelocs: usize = 0,
@@ -175,6 +182,10 @@ comdat_groups: std.ArrayListUnmanaged(ComdatGroup) = .{},
comdat_groups_owners: std.ArrayListUnmanaged(ComdatGroupOwner) = .{},
comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{},
+/// Global string table used to provide quick access to global symbol resolvers
+/// such as `resolver` and `comdat_groups_table`.
+strings: StringTable = .{},
+
/// When allocating, the ideal_capacity is calculated by
/// actual_capacity + (actual_capacity / ideal_factor)
const ideal_factor = 3;
@@ -227,13 +238,15 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
// Append null file at index 0
try self.files.append(allocator, .null);
// Append null byte to string tables
- try self.shstrtab.buffer.append(allocator, 0);
- try self.strtab.buffer.append(allocator, 0);
+ try self.shstrtab.append(allocator, 0);
+ try self.strtab.append(allocator, 0);
// There must always be a null shdr in index 0
_ = try self.addSection(.{ .name = "" });
+ // Append null symbol in output symtab
+ try self.symtab.append(allocator, null_sym);
if (!is_obj_or_ar) {
- try self.dynstrtab.buffer.append(allocator, 0);
+ try self.dynstrtab.append(allocator, 0);
// Initialize PT_PHDR program header
const p_align: u16 = switch (self.ptr_width) {
@@ -347,6 +360,7 @@ pub fn deinit(self: *Elf) void {
}
self.output_sections.deinit(gpa);
self.shstrtab.deinit(gpa);
+ self.symtab.deinit(gpa);
self.strtab.deinit(gpa);
self.symbols.deinit(gpa);
self.symbols_extra.deinit(gpa);
@@ -364,6 +378,7 @@ pub fn deinit(self: *Elf) void {
self.comdat_groups.deinit(gpa);
self.comdat_groups_owners.deinit(gpa);
self.comdat_groups_table.deinit(gpa);
+ self.strings.deinit(gpa);
self.got.deinit(gpa);
self.plt.deinit(gpa);
@@ -473,262 +488,302 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 {
return start;
}
-const AllocateSegmentOpts = struct {
- addr: u64,
- memsz: u64,
- filesz: u64,
- alignment: u64,
- flags: u32 = elf.PF_R,
-};
-
-pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}!u16 {
- const off = self.findFreeSpace(opts.filesz, opts.alignment);
- const index = try self.addPhdr(.{
- .type = elf.PT_LOAD,
- .offset = off,
- .filesz = opts.filesz,
- .addr = opts.addr,
- .memsz = opts.memsz,
- .@"align" = opts.alignment,
- .flags = opts.flags,
- });
- log.debug("allocating phdr({d})({c}{c}{c}) from 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
- index,
- if (opts.flags & elf.PF_R != 0) @as(u8, 'R') else '_',
- if (opts.flags & elf.PF_W != 0) @as(u8, 'W') else '_',
- if (opts.flags & elf.PF_X != 0) @as(u8, 'X') else '_',
- off,
- off + opts.filesz,
- opts.addr,
- opts.addr + opts.memsz,
- });
- return index;
-}
-
-const AllocateAllocSectionOpts = struct {
- name: [:0]const u8,
- phdr_index: u16,
- alignment: u64 = 1,
- flags: u64 = elf.SHF_ALLOC,
- type: u32 = elf.SHT_PROGBITS,
-};
-
-pub fn allocateAllocSection(self: *Elf, opts: AllocateAllocSectionOpts) error{OutOfMemory}!u16 {
- const gpa = self.base.allocator;
- const phdr = &self.phdrs.items[opts.phdr_index];
- const index = try self.addSection(.{
- .name = opts.name,
- .type = opts.type,
- .flags = opts.flags,
- .addralign = opts.alignment,
- .offset = std.math.maxInt(u64),
- });
- const shdr = &self.shdrs.items[index];
- try self.phdr_to_shdr_table.putNoClobber(gpa, index, opts.phdr_index);
- log.debug("allocating '{s}' in phdr({d}) from 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
- opts.name,
- opts.phdr_index,
- phdr.p_offset,
- phdr.p_offset + phdr.p_filesz,
- phdr.p_vaddr,
- phdr.p_vaddr + phdr.p_memsz,
- });
- shdr.sh_addr = phdr.p_vaddr;
- shdr.sh_offset = phdr.p_offset;
- shdr.sh_size = phdr.p_memsz;
- return index;
-}
-
-const AllocateNonAllocSectionOpts = struct {
- name: [:0]const u8,
- size: u64,
- alignment: u16 = 1,
- flags: u32 = 0,
- type: u32 = elf.SHT_PROGBITS,
- link: u32 = 0,
- info: u32 = 0,
- entsize: u64 = 0,
-};
-
-fn allocateNonAllocSection(self: *Elf, opts: AllocateNonAllocSectionOpts) error{OutOfMemory}!u16 {
- const index = try self.addSection(.{
- .name = opts.name,
- .type = opts.type,
- .flags = opts.flags,
- .link = opts.link,
- .info = opts.info,
- .addralign = opts.alignment,
- .entsize = opts.entsize,
- .offset = std.math.maxInt(u64),
- });
- const shdr = &self.shdrs.items[index];
- const off = self.findFreeSpace(opts.size, opts.alignment);
- log.debug("allocating '{s}' from 0x{x} to 0x{x} ", .{ opts.name, off, off + opts.size });
- shdr.sh_offset = off;
- shdr.sh_size = opts.size;
- return index;
-}
-
/// TODO move to ZigObject
pub fn initMetadata(self: *Elf) !void {
const gpa = self.base.allocator;
const ptr_size = self.ptrWidthBytes();
const ptr_bit_width = self.base.options.target.ptrBitWidth();
const is_linux = self.base.options.target.os.tag == .linux;
+ const zig_object = self.zigObjectPtr().?;
+
+ const fillSection = struct {
+ fn fillSection(elf_file: *Elf, shdr: *elf.Elf64_Shdr, size: u64, phndx: ?u16) void {
+ if (elf_file.isRelocatable()) {
+ const off = elf_file.findFreeSpace(size, shdr.sh_addralign);
+ shdr.sh_offset = off;
+ shdr.sh_size = size;
+ } else {
+ const phdr = elf_file.phdrs.items[phndx.?];
+ shdr.sh_addr = phdr.p_vaddr;
+ shdr.sh_offset = phdr.p_offset;
+ shdr.sh_size = phdr.p_memsz;
+ }
+ }
+ }.fillSection;
comptime assert(number_of_zig_segments == 5);
- if (self.phdr_zig_load_re_index == null) {
- self.phdr_zig_load_re_index = try self.allocateSegment(.{
- .addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000,
- .memsz = self.base.options.program_code_size_hint,
- .filesz = self.base.options.program_code_size_hint,
- .alignment = self.page_size,
- .flags = elf.PF_X | elf.PF_R | elf.PF_W,
- });
- }
+ if (!self.isRelocatable()) {
+ if (self.phdr_zig_load_re_index == null) {
+ const filesz = self.base.options.program_code_size_hint;
+ const off = self.findFreeSpace(filesz, self.page_size);
+ self.phdr_zig_load_re_index = try self.addPhdr(.{
+ .type = elf.PT_LOAD,
+ .offset = off,
+ .filesz = filesz,
+ .addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000,
+ .memsz = filesz,
+ .@"align" = self.page_size,
+ .flags = elf.PF_X | elf.PF_R | elf.PF_W,
+ });
+ }
- if (self.phdr_zig_got_index == null) {
- // We really only need ptr alignment but since we are using PROGBITS, linux requires
- // page align.
- const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
- self.phdr_zig_got_index = try self.allocateSegment(.{
- .addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000,
- .memsz = @as(u64, ptr_size) * self.base.options.symbol_count_hint,
- .filesz = @as(u64, ptr_size) * self.base.options.symbol_count_hint,
- .alignment = alignment,
- .flags = elf.PF_R | elf.PF_W,
- });
- }
+ if (self.phdr_zig_got_index == null) {
+ // We really only need ptr alignment but since we are using PROGBITS, linux requires
+ // page align.
+ const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
+ const filesz = @as(u64, ptr_size) * self.base.options.symbol_count_hint;
+ const off = self.findFreeSpace(filesz, alignment);
+ self.phdr_zig_got_index = try self.addPhdr(.{
+ .type = elf.PT_LOAD,
+ .offset = off,
+ .filesz = filesz,
+ .addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000,
+ .memsz = filesz,
+ .@"align" = alignment,
+ .flags = elf.PF_R | elf.PF_W,
+ });
+ }
- if (self.phdr_zig_load_ro_index == null) {
- const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
- self.phdr_zig_load_ro_index = try self.allocateSegment(.{
- .addr = if (ptr_bit_width >= 32) 0xc000000 else 0xa000,
- .memsz = 1024,
- .filesz = 1024,
- .alignment = alignment,
- .flags = elf.PF_R | elf.PF_W,
- });
- }
+ if (self.phdr_zig_load_ro_index == null) {
+ const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
+ const filesz: u64 = 1024;
+ const off = self.findFreeSpace(filesz, alignment);
+ self.phdr_zig_load_ro_index = try self.addPhdr(.{
+ .type = elf.PT_LOAD,
+ .offset = off,
+ .filesz = filesz,
+ .addr = if (ptr_bit_width >= 32) 0xc000000 else 0xa000,
+ .memsz = filesz,
+ .@"align" = alignment,
+ .flags = elf.PF_R | elf.PF_W,
+ });
+ }
- if (self.phdr_zig_load_rw_index == null) {
- const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
- self.phdr_zig_load_rw_index = try self.allocateSegment(.{
- .addr = if (ptr_bit_width >= 32) 0x10000000 else 0xc000,
- .memsz = 1024,
- .filesz = 1024,
- .alignment = alignment,
- .flags = elf.PF_R | elf.PF_W,
- });
- }
+ if (self.phdr_zig_load_rw_index == null) {
+ const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
+ const filesz: u64 = 1024;
+ const off = self.findFreeSpace(filesz, alignment);
+ self.phdr_zig_load_rw_index = try self.addPhdr(.{
+ .type = elf.PT_LOAD,
+ .offset = off,
+ .filesz = filesz,
+ .addr = if (ptr_bit_width >= 32) 0x10000000 else 0xc000,
+ .memsz = filesz,
+ .@"align" = alignment,
+ .flags = elf.PF_R | elf.PF_W,
+ });
+ }
- if (self.phdr_zig_load_zerofill_index == null) {
- const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
- self.phdr_zig_load_zerofill_index = try self.addPhdr(.{
- .type = elf.PT_LOAD,
- .addr = if (ptr_bit_width >= 32) 0x14000000 else 0xf000,
- .memsz = 1024,
- .@"align" = alignment,
- .flags = elf.PF_R | elf.PF_W,
- });
+ if (self.phdr_zig_load_zerofill_index == null) {
+ const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
+ self.phdr_zig_load_zerofill_index = try self.addPhdr(.{
+ .type = elf.PT_LOAD,
+ .addr = if (ptr_bit_width >= 32) 0x14000000 else 0xf000,
+ .memsz = 1024,
+ .@"align" = alignment,
+ .flags = elf.PF_R | elf.PF_W,
+ });
+ }
}
if (self.zig_text_section_index == null) {
- self.zig_text_section_index = try self.allocateAllocSection(.{
- .name = ".zig.text",
- .phdr_index = self.phdr_zig_load_re_index.?,
+ self.zig_text_section_index = try self.addSection(.{
+ .name = ".text.zig",
+ .type = elf.SHT_PROGBITS,
.flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
+ .addralign = 1,
+ .offset = std.math.maxInt(u64),
});
+ const shdr = &self.shdrs.items[self.zig_text_section_index.?];
+ fillSection(self, shdr, self.base.options.program_code_size_hint, self.phdr_zig_load_re_index);
+ if (self.isRelocatable()) {
+ try zig_object.addSectionSymbol(self.zig_text_section_index.?, self);
+ self.zig_text_rela_section_index = try self.addRelaShdr(
+ ".rela.text.zig",
+ self.zig_text_section_index.?,
+ );
+ } else {
+ try self.phdr_to_shdr_table.putNoClobber(
+ gpa,
+ self.zig_text_section_index.?,
+ self.phdr_zig_load_re_index.?,
+ );
+ }
try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_text_section_index.?, .{});
}
- if (self.zig_got_section_index == null) {
- self.zig_got_section_index = try self.allocateAllocSection(.{
- .name = ".zig.got",
- .phdr_index = self.phdr_zig_got_index.?,
- .alignment = ptr_size,
+ if (self.zig_got_section_index == null and !self.isRelocatable()) {
+ self.zig_got_section_index = try self.addSection(.{
+ .name = ".got.zig",
+ .type = elf.SHT_PROGBITS,
+ .addralign = ptr_size,
.flags = elf.SHF_ALLOC | elf.SHF_WRITE,
+ .offset = std.math.maxInt(u64),
});
- }
-
- if (self.zig_rodata_section_index == null) {
- self.zig_rodata_section_index = try self.allocateAllocSection(.{
- .name = ".zig.rodata",
- .phdr_index = self.phdr_zig_load_ro_index.?,
+ const shdr = &self.shdrs.items[self.zig_got_section_index.?];
+ const phndx = self.phdr_zig_got_index.?;
+ const phdr = self.phdrs.items[phndx];
+ shdr.sh_addr = phdr.p_vaddr;
+ shdr.sh_offset = phdr.p_offset;
+ shdr.sh_size = phdr.p_memsz;
+ try self.phdr_to_shdr_table.putNoClobber(
+ gpa,
+ self.zig_got_section_index.?,
+ self.phdr_zig_got_index.?,
+ );
+ }
+
+ if (self.zig_data_rel_ro_section_index == null) {
+ self.zig_data_rel_ro_section_index = try self.addSection(.{
+ .name = ".data.rel.ro.zig",
+ .type = elf.SHT_PROGBITS,
+ .addralign = 1,
.flags = elf.SHF_ALLOC | elf.SHF_WRITE, // TODO rename this section to .data.rel.ro
+ .offset = std.math.maxInt(u64),
});
- try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_rodata_section_index.?, .{});
+ const shdr = &self.shdrs.items[self.zig_data_rel_ro_section_index.?];
+ fillSection(self, shdr, 1024, self.phdr_zig_load_ro_index);
+ if (self.isRelocatable()) {
+ try zig_object.addSectionSymbol(self.zig_data_rel_ro_section_index.?, self);
+ self.zig_data_rel_ro_rela_section_index = try self.addRelaShdr(
+ ".rela.data.rel.ro.zig",
+ self.zig_data_rel_ro_section_index.?,
+ );
+ } else {
+ try self.phdr_to_shdr_table.putNoClobber(
+ gpa,
+ self.zig_data_rel_ro_section_index.?,
+ self.phdr_zig_load_ro_index.?,
+ );
+ }
+ try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_data_rel_ro_section_index.?, .{});
}
if (self.zig_data_section_index == null) {
- self.zig_data_section_index = try self.allocateAllocSection(.{
- .name = ".zig.data",
- .phdr_index = self.phdr_zig_load_rw_index.?,
- .alignment = ptr_size,
+ self.zig_data_section_index = try self.addSection(.{
+ .name = ".data.zig",
+ .type = elf.SHT_PROGBITS,
+ .addralign = ptr_size,
.flags = elf.SHF_ALLOC | elf.SHF_WRITE,
+ .offset = std.math.maxInt(u64),
});
+ const shdr = &self.shdrs.items[self.zig_data_section_index.?];
+ fillSection(self, shdr, 1024, self.phdr_zig_load_rw_index);
+ if (self.isRelocatable()) {
+ try zig_object.addSectionSymbol(self.zig_data_section_index.?, self);
+ self.zig_data_rela_section_index = try self.addRelaShdr(
+ ".rela.data.zig",
+ self.zig_data_section_index.?,
+ );
+ } else {
+ try self.phdr_to_shdr_table.putNoClobber(
+ gpa,
+ self.zig_data_section_index.?,
+ self.phdr_zig_load_rw_index.?,
+ );
+ }
try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_data_section_index.?, .{});
}
if (self.zig_bss_section_index == null) {
- self.zig_bss_section_index = try self.allocateAllocSection(.{
- .name = ".zig.bss",
- .phdr_index = self.phdr_zig_load_zerofill_index.?,
- .alignment = ptr_size,
- .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
+ self.zig_bss_section_index = try self.addSection(.{
+ .name = ".bss.zig",
.type = elf.SHT_NOBITS,
+ .addralign = ptr_size,
+ .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
+ .offset = 0,
});
+ const shdr = &self.shdrs.items[self.zig_bss_section_index.?];
+ if (self.phdr_zig_load_zerofill_index) |phndx| {
+ const phdr = self.phdrs.items[phndx];
+ shdr.sh_addr = phdr.p_vaddr;
+ shdr.sh_size = phdr.p_memsz;
+ try self.phdr_to_shdr_table.putNoClobber(gpa, self.zig_bss_section_index.?, phndx);
+ } else {
+ try zig_object.addSectionSymbol(self.zig_bss_section_index.?, self);
+ shdr.sh_size = 1024;
+ }
try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_bss_section_index.?, .{});
}
- const zig_object = self.zigObjectPtr().?;
if (zig_object.dwarf) |*dw| {
if (self.debug_str_section_index == null) {
assert(dw.strtab.buffer.items.len == 0);
try dw.strtab.buffer.append(gpa, 0);
- self.debug_str_section_index = try self.allocateNonAllocSection(.{
+ self.debug_str_section_index = try self.addSection(.{
.name = ".debug_str",
- .size = @intCast(dw.strtab.buffer.items.len),
.flags = elf.SHF_MERGE | elf.SHF_STRINGS,
.entsize = 1,
+ .type = elf.SHT_PROGBITS,
+ .addralign = 1,
+ .offset = std.math.maxInt(u64),
});
+ const shdr = &self.shdrs.items[self.debug_str_section_index.?];
+ const size = @as(u64, @intCast(dw.strtab.buffer.items.len));
+ const off = self.findFreeSpace(size, 1);
+ shdr.sh_offset = off;
+ shdr.sh_size = size;
zig_object.debug_strtab_dirty = true;
}
if (self.debug_info_section_index == null) {
- self.debug_info_section_index = try self.allocateNonAllocSection(.{
+ self.debug_info_section_index = try self.addSection(.{
.name = ".debug_info",
- .size = 200,
- .alignment = 1,
+ .type = elf.SHT_PROGBITS,
+ .addralign = 1,
+ .offset = std.math.maxInt(u64),
});
+ const shdr = &self.shdrs.items[self.debug_info_section_index.?];
+ const size: u64 = 200;
+ const off = self.findFreeSpace(size, 1);
+ shdr.sh_offset = off;
+ shdr.sh_size = size;
zig_object.debug_info_header_dirty = true;
}
if (self.debug_abbrev_section_index == null) {
- self.debug_abbrev_section_index = try self.allocateNonAllocSection(.{
+ self.debug_abbrev_section_index = try self.addSection(.{
.name = ".debug_abbrev",
- .size = 128,
- .alignment = 1,
+ .type = elf.SHT_PROGBITS,
+ .addralign = 1,
+ .offset = std.math.maxInt(u64),
});
+ const shdr = &self.shdrs.items[self.debug_abbrev_section_index.?];
+ const size: u64 = 128;
+ const off = self.findFreeSpace(size, 1);
+ shdr.sh_offset = off;
+ shdr.sh_size = size;
zig_object.debug_abbrev_section_dirty = true;
}
if (self.debug_aranges_section_index == null) {
- self.debug_aranges_section_index = try self.allocateNonAllocSection(.{
+ self.debug_aranges_section_index = try self.addSection(.{
.name = ".debug_aranges",
- .size = 160,
- .alignment = 16,
+ .type = elf.SHT_PROGBITS,
+ .addralign = 16,
+ .offset = std.math.maxInt(u64),
});
+ const shdr = &self.shdrs.items[self.debug_aranges_section_index.?];
+ const size: u64 = 160;
+ const off = self.findFreeSpace(size, 16);
+ shdr.sh_offset = off;
+ shdr.sh_size = size;
zig_object.debug_aranges_section_dirty = true;
}
if (self.debug_line_section_index == null) {
- self.debug_line_section_index = try self.allocateNonAllocSection(.{
+ self.debug_line_section_index = try self.addSection(.{
.name = ".debug_line",
- .size = 250,
- .alignment = 1,
+ .type = elf.SHT_PROGBITS,
+ .addralign = 1,
+ .offset = std.math.maxInt(u64),
});
+ const shdr = &self.shdrs.items[self.debug_line_section_index.?];
+ const size: u64 = 250;
+ const off = self.findFreeSpace(size, 1);
+ shdr.sh_offset = off;
+ shdr.sh_size = size;
zig_object.debug_line_header_dirty = true;
}
}
@@ -736,18 +791,18 @@ pub fn initMetadata(self: *Elf) !void {
pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {
const shdr = &self.shdrs.items[shdr_index];
- const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?;
- const phdr = &self.phdrs.items[phdr_index];
+ const maybe_phdr = if (self.phdr_to_shdr_table.get(shdr_index)) |phndx| &self.phdrs.items[phndx] else null;
const is_zerofill = shdr.sh_type == elf.SHT_NOBITS;
if (needed_size > self.allocatedSize(shdr.sh_offset) and !is_zerofill) {
const existing_size = shdr.sh_size;
shdr.sh_size = 0;
// Must move the entire section.
- const new_offset = self.findFreeSpace(needed_size, self.page_size);
+ const alignment = if (maybe_phdr) |phdr| phdr.p_align else shdr.sh_addralign;
+ const new_offset = self.findFreeSpace(needed_size, alignment);
log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{
- self.shstrtab.getAssumeExists(shdr.sh_name),
+ self.getShString(shdr.sh_name),
new_offset,
new_offset + existing_size,
});
@@ -757,25 +812,27 @@ pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {
if (amt != existing_size) return error.InputOutput;
shdr.sh_offset = new_offset;
- phdr.p_offset = new_offset;
+ if (maybe_phdr) |phdr| phdr.p_offset = new_offset;
}
shdr.sh_size = needed_size;
if (!is_zerofill) {
- phdr.p_filesz = needed_size;
+ if (maybe_phdr) |phdr| phdr.p_filesz = needed_size;
}
- const mem_capacity = self.allocatedVirtualSize(phdr.p_vaddr);
- if (needed_size > mem_capacity) {
- var err = try self.addErrorWithNotes(2);
- try err.addMsg(self, "fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{
- phdr_index,
- });
- try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});
- try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
- }
+ if (maybe_phdr) |phdr| {
+ const mem_capacity = self.allocatedVirtualSize(phdr.p_vaddr);
+ if (needed_size > mem_capacity) {
+ var err = try self.addErrorWithNotes(2);
+ try err.addMsg(self, "fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{
+ self.phdr_to_shdr_table.get(shdr_index).?,
+ });
+ try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});
+ try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
+ }
- phdr.p_memsz = needed_size;
+ phdr.p_memsz = needed_size;
+ }
self.markDirty(shdr_index);
}
@@ -796,7 +853,7 @@ pub fn growNonAllocSection(
const new_offset = self.findFreeSpace(needed_size, min_alignment);
log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{
- self.shstrtab.getAssumeExists(shdr.sh_name),
+ self.getShString(shdr.sh_name),
new_offset,
new_offset + existing_size,
});
@@ -847,10 +904,6 @@ pub fn flush(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link
if (use_lld) {
return self.linkWithLLD(comp, prog_node);
}
- if (self.base.options.output_mode == .Lib and self.isStatic()) {
- // TODO writing static library files
- return error.TODOImplementWritingLibFiles;
- }
try self.flushModule(comp, prog_node);
}
@@ -886,7 +939,12 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
} else null;
const gc_sections = self.base.options.gc_sections orelse false;
- if (self.base.options.output_mode == .Obj and self.zig_object_index == null) {
+ if (self.isRelocatable() and self.zig_object_index == null) {
+ if (self.isStaticLib()) {
+ var err = try self.addErrorWithNotes(0);
+ try err.addMsg(self, "fatal linker error: emitting static libs unimplemented", .{});
+ return;
+ }
// TODO this will become -r route I guess. For now, just copy the object file.
assert(self.base.file == null); // TODO uncomment once we implement -r
const the_object_path = blk: {
@@ -1159,6 +1217,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
Compilation.dump_argv(argv.items);
}
+ if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self);
+
// Here we will parse input positional and library files (if referenced).
// This will roughly match in any linker backend we support.
var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
@@ -1226,6 +1286,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
try positionals.append(.{ .path = ssp.full_object_path });
}
+ if (self.isStaticLib()) return self.flushStaticLib(comp, positionals.items);
+
for (positionals.items) |obj| {
var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err|
@@ -1331,8 +1393,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
try self.handleAndReportParseError(obj.path, err, &parse_ctx);
}
- if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self);
-
// Dedup shared objects
{
var seen_dsos = std.StringHashMap(void).init(gpa);
@@ -1353,7 +1413,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
// If we haven't already, create a linker-generated input file comprising of
// linker-defined synthetic symbols only such as `_DYNAMIC`, etc.
- if (self.linker_defined_index == null) {
+ if (self.linker_defined_index == null and !self.isRelocatable()) {
const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
self.files.set(index, .{ .linker_defined = .{ .index = index } });
self.linker_defined_index = index;
@@ -1366,6 +1426,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
// symbol for potential resolution at load-time.
self.resolveSymbols();
self.markEhFrameAtomsDead();
+
+ if (self.isObject()) return self.flushObject(comp);
+
try self.convertCommonSymbols();
self.markImportsExports();
@@ -1449,14 +1512,150 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
try self.writeAtoms();
try self.writeSyntheticSections();
- if (self.entry_index == null and self.base.options.effectiveOutputMode() == .Exe) {
+ if (self.entry_index == null and self.isExe()) {
log.debug("flushing. no_entry_point_found = true", .{});
self.error_flags.no_entry_point_found = true;
} else {
log.debug("flushing. no_entry_point_found = false", .{});
self.error_flags.no_entry_point_found = false;
- try self.writeHeader();
+ try self.writeElfHeader();
+ }
+}
+
+pub fn flushStaticLib(
+ self: *Elf,
+ comp: *Compilation,
+ positionals: []const Compilation.LinkObject,
+) link.File.FlushError!void {
+ _ = comp;
+ if (positionals.len > 0) {
+ var err = try self.addErrorWithNotes(1);
+ try err.addMsg(self, "fatal linker error: too many input positionals", .{});
+ try err.addNote(self, "TODO implement linking objects into an static library", .{});
+ return;
+ }
+ const gpa = self.base.allocator;
+
+ // First, we flush relocatable object file generated with our backends.
+ if (self.zigObjectPtr()) |zig_object| {
+ zig_object.resolveSymbols(self);
+ zig_object.claimUnresolvedObject(self);
+
+ try self.initSymtab();
+ try self.initShStrtab();
+ try self.sortShdrs();
+ zig_object.updateRelaSectionSizes(self);
+ try self.updateSymtabSize();
+ self.updateShStrtabSize();
+
+ try self.allocateNonAllocSections();
+
+ try self.writeShdrTable();
+ try zig_object.writeRelaSections(self);
+ try self.writeSymtab();
+ try self.writeShStrtab();
+ try self.writeElfHeader();
+ }
+
+ // TODO parse positionals that we want to make part of the archive
+
+ // TODO update ar symtab from parsed positionals
+
+ var ar_symtab: Archive.ArSymtab = .{};
+ defer ar_symtab.deinit(gpa);
+
+ if (self.zigObjectPtr()) |zig_object| {
+ try zig_object.updateArSymtab(&ar_symtab, self);
+ }
+
+ ar_symtab.sort();
+
+ // Save object paths in filenames strtab.
+ var ar_strtab: Archive.ArStrtab = .{};
+ defer ar_strtab.deinit(gpa);
+
+ if (self.zigObjectPtr()) |zig_object| {
+ try zig_object.updateArStrtab(gpa, &ar_strtab);
+ zig_object.updateArSize(self);
+ }
+
+ // Update file offsets of contributing objects.
+ const total_size: usize = blk: {
+ var pos: usize = Archive.SARMAG;
+ pos += @sizeOf(Archive.ar_hdr) + ar_symtab.size(.p64);
+
+ if (ar_strtab.size() > 0) {
+ pos = mem.alignForward(usize, pos, 2);
+ pos += @sizeOf(Archive.ar_hdr) + ar_strtab.size();
+ }
+
+ if (self.zigObjectPtr()) |zig_object| {
+ pos = mem.alignForward(usize, pos, 2);
+ zig_object.output_ar_state.file_off = pos;
+ pos += @sizeOf(Archive.ar_hdr) + (math.cast(usize, zig_object.output_ar_state.size) orelse return error.Overflow);
+ }
+
+ break :blk pos;
+ };
+
+ if (build_options.enable_logging) {
+ state_log.debug("ar_symtab\n{}\n", .{ar_symtab.fmt(self)});
+ state_log.debug("ar_strtab\n{}\n", .{ar_strtab});
+ }
+
+ var buffer = std.ArrayList(u8).init(gpa);
+ defer buffer.deinit();
+ try buffer.ensureTotalCapacityPrecise(total_size);
+
+ // Write magic
+ try buffer.writer().writeAll(Archive.ARMAG);
+
+ // Write symtab
+ try ar_symtab.write(.p64, self, buffer.writer());
+
+ // Write strtab
+ if (ar_strtab.size() > 0) {
+ if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
+ try ar_strtab.write(buffer.writer());
+ }
+
+ // Write object files
+ if (self.zigObjectPtr()) |zig_object| {
+ if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
+ try zig_object.writeAr(self, buffer.writer());
+ }
+
+ assert(buffer.items.len == total_size);
+
+ try self.base.file.?.setEndPos(total_size);
+ try self.base.file.?.pwriteAll(buffer.items, 0);
+}
+
+pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void {
+ _ = comp;
+
+ if (self.objects.items.len > 0) {
+ var err = try self.addErrorWithNotes(1);
+ try err.addMsg(self, "fatal linker error: too many input positionals", .{});
+ try err.addNote(self, "TODO implement '-r' option", .{});
+ return;
+ }
+
+ self.claimUnresolvedObject();
+
+ try self.initSections();
+ try self.sortShdrs();
+ try self.updateSectionSizes();
+
+ try self.allocateNonAllocSections();
+
+ if (build_options.enable_logging) {
+ state_log.debug("{}", .{self.dumpState()});
}
+
+ try self.writeShdrTable();
+ try self.writeSyntheticSections();
+ try self.writeElfHeader();
}
const ParseError = error{
@@ -1696,7 +1895,7 @@ fn accessLibPath(
/// 6. Re-run symbol resolution on pruned objects and shared objects sets.
fn resolveSymbols(self: *Elf) void {
// Resolve symbols in the ZigObject. For now, we assume that it's always live.
- if (self.zigObjectPtr()) |zig_object| zig_object.resolveSymbols(self);
+ if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resolveSymbols(self);
// Resolve symbols on the set of all objects and shared objects (even if some are unneeded).
for (self.objects.items) |index| self.file(index).?.resolveSymbols(self);
for (self.shared_objects.items) |index| self.file(index).?.resolveSymbols(self);
@@ -1705,7 +1904,7 @@ fn resolveSymbols(self: *Elf) void {
self.markLive();
// Reset state of all globals after marking live objects.
- if (self.zigObjectPtr()) |zig_object| zig_object.resetGlobals(self);
+ if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resetGlobals(self);
for (self.objects.items) |index| self.file(index).?.resetGlobals(self);
for (self.shared_objects.items) |index| self.file(index).?.resetGlobals(self);
@@ -1767,7 +1966,7 @@ fn resolveSymbols(self: *Elf) void {
/// This routine will prune unneeded objects extracted from archives and
/// unneeded shared objects.
fn markLive(self: *Elf) void {
- if (self.zigObjectPtr()) |zig_object| zig_object.markLive(self);
+ if (self.zigObjectPtr()) |zig_object| zig_object.asFile().markLive(self);
for (self.objects.items) |index| {
const file_ptr = self.file(index).?;
if (file_ptr.isAlive()) file_ptr.markLive(self);
@@ -1845,6 +2044,12 @@ fn claimUnresolved(self: *Elf) void {
}
}
+fn claimUnresolvedObject(self: *Elf) void {
+ if (self.zigObjectPtr()) |zig_object| {
+ zig_object.claimUnresolvedObject(self);
+ }
+}
+
/// In scanRelocs we will go over all live atoms and scan their relocs.
/// This will help us work out what synthetics to emit, GOT indirection, etc.
/// This is also the point where we will report undefined symbols for any
@@ -1873,7 +2078,7 @@ fn scanRelocs(self: *Elf) !void {
for (self.symbols.items, 0..) |*sym, i| {
const index = @as(u32, @intCast(i));
- if (!sym.isLocal() and !sym.flags.has_dynamic) {
+ if (!sym.isLocal(self) and !sym.flags.has_dynamic) {
log.debug("'{s}' is non-local", .{sym.name(self)});
try self.dynsym.addSymbol(index, self);
}
@@ -2704,7 +2909,7 @@ fn writePhdrTable(self: *Elf) !void {
}
}
-fn writeHeader(self: *Elf) !void {
+fn writeElfHeader(self: *Elf) !void {
var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
var index: usize = 0;
@@ -2735,7 +2940,7 @@ fn writeHeader(self: *Elf) !void {
assert(index == 16);
- const elf_type: elf.ET = switch (self.base.options.effectiveOutputMode()) {
+ const elf_type: elf.ET = switch (self.base.options.output_mode) {
.Exe => if (self.base.options.pie) .DYN else .EXEC,
.Obj => .REL,
.Lib => switch (self.base.options.link_mode) {
@@ -2755,7 +2960,7 @@ fn writeHeader(self: *Elf) !void {
index += 4;
const e_entry = if (self.entry_index) |entry_index| self.symbol(entry_index).value else 0;
- const phdr_table_offset = self.phdrs.items[self.phdr_table_index.?].p_offset;
+ const phdr_table_offset = if (self.phdr_table_index) |phndx| self.phdrs.items[phndx].p_offset else 0;
switch (self.ptr_width) {
.p32 => {
mem.writeInt(u32, hdr_buf[index..][0..4], @as(u32, @intCast(e_entry)), endian);
@@ -3054,10 +3259,6 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void {
}
fn initSections(self: *Elf) !void {
- const small_ptr = switch (self.ptr_width) {
- .p32 => true,
- .p64 => false,
- };
const ptr_size = self.ptrWidthBytes();
for (self.objects.items) |index| {
@@ -3247,6 +3448,15 @@ fn initSections(self: *Elf) !void {
}
}
+ try self.initSymtab();
+ try self.initShStrtab();
+}
+
+fn initSymtab(self: *Elf) !void {
+ const small_ptr = switch (self.ptr_width) {
+ .p32 => true,
+ .p64 => false,
+ };
if (self.symtab_section_index == null) {
self.symtab_section_index = try self.addSection(.{
.name = ".symtab",
@@ -3265,6 +3475,9 @@ fn initSections(self: *Elf) !void {
.offset = std.math.maxInt(u64),
});
}
+}
+
+fn initShStrtab(self: *Elf) !void {
if (self.shstrtab_section_index == null) {
self.shstrtab_section_index = try self.addSection(.{
.name = ".shstrtab",
@@ -3358,7 +3571,7 @@ fn sortInitFini(self: *Elf) !void {
elf.SHT_FINI_ARRAY,
=> is_init_fini = true,
else => {
- const name = self.shstrtab.getAssumeExists(shdr.sh_name);
+ const name = self.getShString(shdr.sh_name);
is_ctor_dtor = mem.indexOf(u8, name, ".ctors") != null or mem.indexOf(u8, name, ".dtors") != null;
},
}
@@ -3520,7 +3733,7 @@ fn sortPhdrs(self: *Elf) error{OutOfMemory}!void {
fn shdrRank(self: *Elf, shndx: u16) u8 {
const shdr = self.shdrs.items[shndx];
- const name = self.shstrtab.getAssumeExists(shdr.sh_name);
+ const name = self.getShString(shdr.sh_name);
const flags = shdr.sh_flags;
switch (shdr.sh_type) {
@@ -3620,9 +3833,12 @@ fn sortShdrs(self: *Elf) !void {
&self.versym_section_index,
&self.verneed_section_index,
&self.zig_text_section_index,
+ &self.zig_text_rela_section_index,
&self.zig_got_section_index,
- &self.zig_rodata_section_index,
+ &self.zig_data_rel_ro_section_index,
+ &self.zig_data_rel_ro_rela_section_index,
&self.zig_data_section_index,
+ &self.zig_data_rela_section_index,
&self.zig_bss_section_index,
&self.debug_str_section_index,
&self.debug_info_section_index,
@@ -3681,6 +3897,31 @@ fn sortShdrs(self: *Elf) !void {
shdr.sh_info = self.plt_section_index.?;
}
+ for (&[_]?u16{
+ self.zig_text_rela_section_index,
+ self.zig_data_rel_ro_rela_section_index,
+ self.zig_data_rela_section_index,
+ }) |maybe_index| {
+ const index = maybe_index orelse continue;
+ const shdr = &self.shdrs.items[index];
+ shdr.sh_link = self.symtab_section_index.?;
+ shdr.sh_info = backlinks[shdr.sh_info];
+ }
+
+ {
+ var last_atom_and_free_list_table = try self.last_atom_and_free_list_table.clone(gpa);
+ defer last_atom_and_free_list_table.deinit(gpa);
+
+ self.last_atom_and_free_list_table.clearRetainingCapacity();
+
+ var it = last_atom_and_free_list_table.iterator();
+ while (it.next()) |entry| {
+ const shndx = entry.key_ptr.*;
+ const meta = entry.value_ptr.*;
+ self.last_atom_and_free_list_table.putAssumeCapacityNoClobber(backlinks[shndx], meta);
+ }
+ }
+
{
var phdr_to_shdr_table = try self.phdr_to_shdr_table.clone(gpa);
defer phdr_to_shdr_table.deinit(gpa);
@@ -3698,23 +3939,19 @@ fn sortShdrs(self: *Elf) !void {
if (self.zigObjectPtr()) |zig_object| {
for (zig_object.atoms.items) |atom_index| {
const atom_ptr = self.atom(atom_index) orelse continue;
- if (!atom_ptr.flags.alive) continue;
- const out_shndx = atom_ptr.outputShndx() orelse continue;
- atom_ptr.output_section_index = backlinks[out_shndx];
+ atom_ptr.output_section_index = backlinks[atom_ptr.output_section_index];
}
for (zig_object.locals()) |local_index| {
const local = self.symbol(local_index);
- const atom_ptr = local.atom(self) orelse continue;
- if (!atom_ptr.flags.alive) continue;
- const out_shndx = local.outputShndx() orelse continue;
- local.output_section_index = backlinks[out_shndx];
+ local.output_section_index = backlinks[local.output_section_index];
}
for (zig_object.globals()) |global_index| {
const global = self.symbol(global_index);
const atom_ptr = global.atom(self) orelse continue;
if (!atom_ptr.flags.alive) continue;
+ // TODO claim unresolved for objects
if (global.file(self).?.index() != zig_object.index) continue;
const out_shndx = global.outputShndx() orelse continue;
global.output_section_index = backlinks[out_shndx];
@@ -3737,6 +3974,10 @@ fn updateSectionSizes(self: *Elf) !void {
}
}
+ if (self.zigObjectPtr()) |zig_object| {
+ zig_object.updateRelaSectionSizes(self);
+ }
+
if (self.eh_frame_section_index) |index| {
self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self);
}
@@ -3801,7 +4042,7 @@ fn updateSectionSizes(self: *Elf) !void {
}
if (self.dynstrtab_section_index) |index| {
- self.shdrs.items[index].sh_size = self.dynstrtab.buffer.items.len;
+ self.shdrs.items[index].sh_size = self.dynstrtab.items.len;
}
if (self.versym_section_index) |index| {
@@ -3812,30 +4053,13 @@ fn updateSectionSizes(self: *Elf) !void {
self.shdrs.items[index].sh_size = self.verneed.size();
}
- if (self.symtab_section_index != null) {
- try self.updateSymtabSize();
- }
-
- if (self.strtab_section_index) |index| {
- // TODO I don't really this here but we need it to add symbol names from GOT and other synthetic
- // sections into .strtab for easier debugging.
- if (self.zig_got_section_index) |_| {
- try self.zig_got.updateStrtab(self);
- }
- if (self.got_section_index) |_| {
- try self.got.updateStrtab(self);
- }
- if (self.plt_section_index) |_| {
- try self.plt.updateStrtab(self);
- }
- if (self.plt_got_section_index) |_| {
- try self.plt_got.updateStrtab(self);
- }
- self.shdrs.items[index].sh_size = self.strtab.buffer.items.len;
- }
+ try self.updateSymtabSize();
+ self.updateShStrtabSize();
+}
+fn updateShStrtabSize(self: *Elf) void {
if (self.shstrtab_section_index) |index| {
- self.shdrs.items[index].sh_size = self.shstrtab.buffer.items.len;
+ self.shdrs.items[index].sh_size = self.shstrtab.items.len;
}
}
@@ -4074,7 +4298,7 @@ fn allocateNonAllocSections(self: *Elf) !void {
if (self.isDebugSection(@intCast(shndx))) {
log.debug("moving {s} from 0x{x} to 0x{x}", .{
- self.shstrtab.getAssumeExists(shdr.sh_name),
+ self.getShString(shdr.sh_name),
shdr.sh_offset,
new_offset,
});
@@ -4187,7 +4411,7 @@ fn writeAtoms(self: *Elf) !void {
const atom_list = self.output_sections.get(@intCast(shndx)) orelse continue;
- log.debug("writing atoms in '{s}' section", .{self.shstrtab.getAssumeExists(shdr.sh_name)});
+ log.debug("writing atoms in '{s}' section", .{self.getShString(shdr.sh_name)});
// TODO really, really handle debug section separately
const base_offset = if (self.isDebugSection(@intCast(shndx))) blk: {
@@ -4256,65 +4480,70 @@ fn updateSymtabSize(self: *Elf) !void {
var sizes = SymtabSize{};
if (self.zigObjectPtr()) |zig_object| {
- zig_object.updateSymtabSize(self);
- sizes.nlocals += zig_object.output_symtab_size.nlocals;
- sizes.nglobals += zig_object.output_symtab_size.nglobals;
+ zig_object.asFile().updateSymtabSize(self);
+ sizes.add(zig_object.output_symtab_size);
}
for (self.objects.items) |index| {
- const object = self.file(index).?.object;
- object.updateSymtabSize(self);
- sizes.nlocals += object.output_symtab_size.nlocals;
- sizes.nglobals += object.output_symtab_size.nglobals;
+ const file_ptr = self.file(index).?;
+ file_ptr.updateSymtabSize(self);
+ sizes.add(file_ptr.object.output_symtab_size);
}
for (self.shared_objects.items) |index| {
- const shared_object = self.file(index).?.shared_object;
- shared_object.updateSymtabSize(self);
- sizes.nglobals += shared_object.output_symtab_size.nglobals;
+ const file_ptr = self.file(index).?;
+ file_ptr.updateSymtabSize(self);
+ sizes.add(file_ptr.shared_object.output_symtab_size);
}
if (self.zig_got_section_index) |_| {
self.zig_got.updateSymtabSize(self);
- sizes.nlocals += self.zig_got.output_symtab_size.nlocals;
+ sizes.add(self.zig_got.output_symtab_size);
}
if (self.got_section_index) |_| {
self.got.updateSymtabSize(self);
- sizes.nlocals += self.got.output_symtab_size.nlocals;
+ sizes.add(self.got.output_symtab_size);
}
if (self.plt_section_index) |_| {
self.plt.updateSymtabSize(self);
- sizes.nlocals += self.plt.output_symtab_size.nlocals;
+ sizes.add(self.plt.output_symtab_size);
}
if (self.plt_got_section_index) |_| {
self.plt_got.updateSymtabSize(self);
- sizes.nlocals += self.plt_got.output_symtab_size.nlocals;
+ sizes.add(self.plt_got.output_symtab_size);
}
if (self.linker_defined_index) |index| {
- const linker_defined = self.file(index).?.linker_defined;
- linker_defined.updateSymtabSize(self);
- sizes.nlocals += linker_defined.output_symtab_size.nlocals;
+ const file_ptr = self.file(index).?;
+ file_ptr.updateSymtabSize(self);
+ sizes.add(file_ptr.linker_defined.output_symtab_size);
}
- const shdr = &self.shdrs.items[self.symtab_section_index.?];
- shdr.sh_info = sizes.nlocals + 1;
- shdr.sh_link = self.strtab_section_index.?;
+ const symtab_shdr = &self.shdrs.items[self.symtab_section_index.?];
+ symtab_shdr.sh_info = sizes.nlocals + 1;
+ symtab_shdr.sh_link = self.strtab_section_index.?;
const sym_size: u64 = switch (self.ptr_width) {
.p32 => @sizeOf(elf.Elf32_Sym),
.p64 => @sizeOf(elf.Elf64_Sym),
};
const needed_size = (sizes.nlocals + sizes.nglobals + 1) * sym_size;
- shdr.sh_size = needed_size;
+ symtab_shdr.sh_size = needed_size;
+
+ const strtab = &self.shdrs.items[self.strtab_section_index.?];
+ strtab.sh_size = sizes.strsize + 1;
}
fn writeSyntheticSections(self: *Elf) !void {
const gpa = self.base.allocator;
+ if (self.zigObjectPtr()) |zig_object| {
+ try zig_object.writeRelaSections(self);
+ }
+
if (self.interp_section_index) |shndx| {
const shdr = self.shdrs.items[shndx];
const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
@@ -4370,7 +4599,7 @@ fn writeSyntheticSections(self: *Elf) !void {
if (self.dynstrtab_section_index) |shndx| {
const shdr = self.shdrs.items[shndx];
- try self.base.file.?.pwriteAll(self.dynstrtab.buffer.items, shdr.sh_offset);
+ try self.base.file.?.pwriteAll(self.dynstrtab.items, shdr.sh_offset);
}
if (self.eh_frame_section_index) |shndx| {
@@ -4438,94 +4667,97 @@ fn writeSyntheticSections(self: *Elf) !void {
try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_plt.items), shdr.sh_offset);
}
- if (self.shstrtab_section_index) |index| {
- const shdr = self.shdrs.items[index];
- try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shdr.sh_offset);
- }
+ try self.writeSymtab();
+ try self.writeShStrtab();
+}
- if (self.strtab_section_index) |index| {
+fn writeShStrtab(self: *Elf) !void {
+ if (self.shstrtab_section_index) |index| {
const shdr = self.shdrs.items[index];
- try self.base.file.?.pwriteAll(self.strtab.buffer.items, shdr.sh_offset);
- }
-
- if (self.symtab_section_index) |_| {
- try self.writeSymtab();
+ try self.base.file.?.pwriteAll(self.shstrtab.items, shdr.sh_offset);
}
}
fn writeSymtab(self: *Elf) !void {
const gpa = self.base.allocator;
- const shdr = &self.shdrs.items[self.symtab_section_index.?];
+ const symtab_shdr = self.shdrs.items[self.symtab_section_index.?];
+ const strtab_shdr = self.shdrs.items[self.strtab_section_index.?];
const sym_size: u64 = switch (self.ptr_width) {
.p32 => @sizeOf(elf.Elf32_Sym),
.p64 => @sizeOf(elf.Elf64_Sym),
};
- const nsyms = math.cast(usize, @divExact(shdr.sh_size, sym_size)) orelse return error.Overflow;
+ const nsyms = math.cast(usize, @divExact(symtab_shdr.sh_size, sym_size)) orelse return error.Overflow;
- log.debug("writing {d} symbols at 0x{x}", .{ nsyms, shdr.sh_offset });
+ log.debug("writing {d} symbols at 0x{x}", .{ nsyms, symtab_shdr.sh_offset });
- const symtab = try gpa.alloc(elf.Elf64_Sym, nsyms);
- defer gpa.free(symtab);
- symtab[0] = null_sym;
+ try self.symtab.resize(gpa, nsyms);
+ const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow;
+ try self.strtab.ensureUnusedCapacity(gpa, needed_strtab_size);
- var ctx: struct { ilocal: usize, iglobal: usize, symtab: []elf.Elf64_Sym } = .{
+ const Ctx = struct {
+ ilocal: usize,
+ iglobal: usize,
+
+ fn incr(this: *@This(), ss: SymtabSize) void {
+ this.ilocal += ss.nlocals;
+ this.iglobal += ss.nglobals;
+ }
+ };
+ var ctx: Ctx = .{
.ilocal = 1,
- .iglobal = shdr.sh_info,
- .symtab = symtab,
+ .iglobal = symtab_shdr.sh_info,
};
if (self.zigObjectPtr()) |zig_object| {
- zig_object.writeSymtab(self, ctx);
- ctx.ilocal += zig_object.output_symtab_size.nlocals;
- ctx.iglobal += zig_object.output_symtab_size.nglobals;
+ zig_object.asFile().writeSymtab(self, ctx);
+ ctx.incr(zig_object.output_symtab_size);
}
for (self.objects.items) |index| {
- const object = self.file(index).?.object;
- object.writeSymtab(self, ctx);
- ctx.ilocal += object.output_symtab_size.nlocals;
- ctx.iglobal += object.output_symtab_size.nglobals;
+ const file_ptr = self.file(index).?;
+ file_ptr.writeSymtab(self, ctx);
+ ctx.incr(file_ptr.object.output_symtab_size);
}
for (self.shared_objects.items) |index| {
- const shared_object = self.file(index).?.shared_object;
- shared_object.writeSymtab(self, ctx);
- ctx.iglobal += shared_object.output_symtab_size.nglobals;
+ const file_ptr = self.file(index).?;
+ file_ptr.writeSymtab(self, ctx);
+ ctx.incr(file_ptr.shared_object.output_symtab_size);
}
if (self.zig_got_section_index) |_| {
- try self.zig_got.writeSymtab(self, ctx);
- ctx.ilocal += self.zig_got.output_symtab_size.nlocals;
+ self.zig_got.writeSymtab(self, ctx);
+ ctx.incr(self.zig_got.output_symtab_size);
}
if (self.got_section_index) |_| {
- try self.got.writeSymtab(self, ctx);
- ctx.ilocal += self.got.output_symtab_size.nlocals;
+ self.got.writeSymtab(self, ctx);
+ ctx.incr(self.got.output_symtab_size);
}
if (self.plt_section_index) |_| {
- try self.plt.writeSymtab(self, ctx);
- ctx.ilocal += self.plt.output_symtab_size.nlocals;
+ self.plt.writeSymtab(self, ctx);
+ ctx.incr(self.plt.output_symtab_size);
}
if (self.plt_got_section_index) |_| {
- try self.plt_got.writeSymtab(self, ctx);
- ctx.ilocal += self.plt_got.output_symtab_size.nlocals;
+ self.plt_got.writeSymtab(self, ctx);
+ ctx.incr(self.plt_got.output_symtab_size);
}
if (self.linker_defined_index) |index| {
- const linker_defined = self.file(index).?.linker_defined;
- linker_defined.writeSymtab(self, ctx);
- ctx.ilocal += linker_defined.output_symtab_size.nlocals;
+ const file_ptr = self.file(index).?;
+ file_ptr.writeSymtab(self, ctx);
+ ctx.incr(file_ptr.linker_defined.output_symtab_size);
}
const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian();
switch (self.ptr_width) {
.p32 => {
- const buf = try gpa.alloc(elf.Elf32_Sym, symtab.len);
+ const buf = try gpa.alloc(elf.Elf32_Sym, self.symtab.items.len);
defer gpa.free(buf);
- for (buf, symtab) |*out, sym| {
+ for (buf, self.symtab.items) |*out, sym| {
out.* = .{
.st_name = sym.st_name,
.st_info = sym.st_info,
@@ -4536,15 +4768,17 @@ fn writeSymtab(self: *Elf) !void {
};
if (foreign_endian) mem.byteSwapAllFields(elf.Elf32_Sym, out);
}
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), shdr.sh_offset);
+ try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), symtab_shdr.sh_offset);
},
.p64 => {
if (foreign_endian) {
- for (symtab) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym);
+ for (self.symtab.items) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym);
}
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(symtab), shdr.sh_offset);
+ try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), symtab_shdr.sh_offset);
},
}
+
+ try self.base.file.?.pwriteAll(self.strtab.items, strtab_shdr.sh_offset);
}
/// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF.
@@ -4848,18 +5082,30 @@ pub fn isStatic(self: Elf) bool {
return self.base.options.link_mode == .Static;
}
+pub fn isObject(self: Elf) bool {
+ return self.base.options.output_mode == .Obj;
+}
+
pub fn isExe(self: Elf) bool {
- return self.base.options.effectiveOutputMode() == .Exe;
+ return self.base.options.output_mode == .Exe;
+}
+
+pub fn isStaticLib(self: Elf) bool {
+ return self.base.options.output_mode == .Lib and self.isStatic();
+}
+
+pub fn isRelocatable(self: Elf) bool {
+ return self.isObject() or self.isStaticLib();
}
pub fn isDynLib(self: Elf) bool {
- return self.base.options.effectiveOutputMode() == .Lib and self.base.options.link_mode == .Dynamic;
+ return self.base.options.output_mode == .Lib and !self.isStatic();
}
pub fn isZigSection(self: Elf, shndx: u16) bool {
inline for (&[_]?u16{
self.zig_text_section_index,
- self.zig_rodata_section_index,
+ self.zig_data_rel_ro_section_index,
self.zig_data_section_index,
self.zig_bss_section_index,
self.zig_got_section_index,
@@ -4909,6 +5155,26 @@ fn addPhdr(self: *Elf, opts: struct {
return index;
}
+fn addRelaShdr(self: *Elf, name: [:0]const u8, shndx: u16) !u16 {
+ const entsize: u64 = switch (self.ptr_width) {
+ .p32 => @sizeOf(elf.Elf32_Rela),
+ .p64 => @sizeOf(elf.Elf64_Rela),
+ };
+ const addralign: u64 = switch (self.ptr_width) {
+ .p32 => @alignOf(elf.Elf32_Rela),
+ .p64 => @alignOf(elf.Elf64_Rela),
+ };
+ return self.addSection(.{
+ .name = name,
+ .type = elf.SHT_RELA,
+ .flags = elf.SHF_INFO_LINK,
+ .entsize = entsize,
+ .info = shndx,
+ .addralign = addralign,
+ .offset = std.math.maxInt(u64),
+ });
+}
+
pub const AddSectionOpts = struct {
name: [:0]const u8,
type: u32 = elf.SHT_NULL,
@@ -4925,7 +5191,7 @@ pub fn addSection(self: *Elf, opts: AddSectionOpts) !u16 {
const index = @as(u16, @intCast(self.shdrs.items.len));
const shdr = try self.shdrs.addOne(gpa);
shdr.* = .{
- .sh_name = try self.shstrtab.insert(gpa, opts.name),
+ .sh_name = try self.insertShString(opts.name),
.sh_type = opts.type,
.sh_flags = opts.flags,
.sh_addr = 0,
@@ -4941,7 +5207,7 @@ pub fn addSection(self: *Elf, opts: AddSectionOpts) !u16 {
pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u16 {
for (self.shdrs.items, 0..) |*shdr, i| {
- const this_name = self.shstrtab.getAssumeExists(shdr.sh_name);
+ const this_name = self.getShString(shdr.sh_name);
if (mem.eql(u8, this_name, name)) return @as(u16, @intCast(i));
} else return null;
}
@@ -5114,13 +5380,15 @@ const GetOrPutGlobalResult = struct {
index: Symbol.Index,
};
-pub fn getOrPutGlobal(self: *Elf, name_off: u32) !GetOrPutGlobalResult {
+pub fn getOrPutGlobal(self: *Elf, name: []const u8) !GetOrPutGlobalResult {
const gpa = self.base.allocator;
+ const name_off = try self.strings.insert(gpa, name);
const gop = try self.resolver.getOrPut(gpa, name_off);
if (!gop.found_existing) {
const index = try self.addSymbol();
const global = self.symbol(index);
global.name_offset = name_off;
+ global.flags.global = true;
gop.value_ptr.* = index;
}
return .{
@@ -5130,7 +5398,7 @@ pub fn getOrPutGlobal(self: *Elf, name_off: u32) !GetOrPutGlobalResult {
}
pub fn globalByName(self: *Elf, name: []const u8) ?Symbol.Index {
- const name_off = self.strtab.getOffset(name) orelse return null;
+ const name_off = self.strings.getOffset(name) orelse return null;
return self.resolver.get(name_off);
}
@@ -5148,8 +5416,9 @@ const GetOrCreateComdatGroupOwnerResult = struct {
index: ComdatGroupOwner.Index,
};
-pub fn getOrCreateComdatGroupOwner(self: *Elf, off: u32) !GetOrCreateComdatGroupOwnerResult {
+pub fn getOrCreateComdatGroupOwner(self: *Elf, name: [:0]const u8) !GetOrCreateComdatGroupOwnerResult {
const gpa = self.base.allocator;
+ const off = try self.strings.insert(gpa, name);
const gop = try self.comdat_groups_table.getOrPut(gpa, off);
if (!gop.found_existing) {
const index = @as(ComdatGroupOwner.Index, @intCast(self.comdat_groups_owners.items.len));
@@ -5239,6 +5508,30 @@ fn addErrorWithNotesAssumeCapacity(self: *Elf, note_count: usize) error{OutOfMem
return .{ .index = index };
}
+pub fn getShString(self: Elf, off: u32) [:0]const u8 {
+ assert(off < self.shstrtab.items.len);
+ return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.shstrtab.items.ptr + off)), 0);
+}
+
+pub fn insertShString(self: *Elf, name: [:0]const u8) error{OutOfMemory}!u32 {
+ const off = @as(u32, @intCast(self.shstrtab.items.len));
+ try self.shstrtab.ensureUnusedCapacity(self.base.allocator, name.len + 1);
+ self.shstrtab.writer(self.base.allocator).print("{s}\x00", .{name}) catch unreachable;
+ return off;
+}
+
+pub fn getDynString(self: Elf, off: u32) [:0]const u8 {
+ assert(off < self.dynstrtab.items.len);
+ return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.dynstrtab.items.ptr + off)), 0);
+}
+
+pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 {
+ const off = @as(u32, @intCast(self.dynstrtab.items.len));
+ try self.dynstrtab.ensureUnusedCapacity(self.base.allocator, name.len + 1);
+ self.dynstrtab.writer(self.base.allocator).print("{s}\x00", .{name}) catch unreachable;
+ return off;
+}
+
fn reportUndefined(self: *Elf, undefs: anytype) !void {
const gpa = self.base.allocator;
const max_notes = 4;
@@ -5340,8 +5633,8 @@ fn formatShdr(
_ = unused_fmt_string;
const shdr = ctx.shdr;
try writer.print("{s} : @{x} ({x}) : align({x}) : size({x})", .{
- ctx.elf_file.shstrtab.getAssumeExists(shdr.sh_name), shdr.sh_offset,
- shdr.sh_addr, shdr.sh_addralign,
+ ctx.elf_file.getShString(shdr.sh_name), shdr.sh_offset,
+ shdr.sh_addr, shdr.sh_addralign,
shdr.sh_size,
});
}
@@ -5444,6 +5737,7 @@ fn fmtDumpState(
}
try writer.print("{}\n", .{self.got.fmt(self)});
try writer.print("{}\n", .{self.zig_got.fmt(self)});
+
try writer.writeAll("Output shdrs\n");
for (self.shdrs.items, 0..) |shdr, shndx| {
try writer.print("shdr({d}) : phdr({?d}) : {}\n", .{
@@ -5516,6 +5810,13 @@ pub const ComdatGroup = struct {
pub const SymtabSize = struct {
nlocals: u32 = 0,
nglobals: u32 = 0,
+ strsize: u32 = 0,
+
+ fn add(ss: *SymtabSize, other: SymtabSize) void {
+ ss.nlocals += other.nlocals;
+ ss.nglobals += other.nglobals;
+ ss.strsize += other.strsize;
+ }
};
pub const null_sym = elf.Elf64_Sym{
@@ -5621,7 +5922,7 @@ const PltSection = synthetic_sections.PltSection;
const PltGotSection = synthetic_sections.PltGotSection;
const SharedObject = @import("Elf/SharedObject.zig");
const Symbol = @import("Elf/Symbol.zig");
-const StringTable = @import("strtab.zig").StringTable;
+const StringTable = @import("StringTable.zig");
const TypedValue = @import("../TypedValue.zig");
const VerneedSection = synthetic_sections.VerneedSection;
const ZigGotSection = synthetic_sections.ZigGotSection;
diff --git a/src/link/Elf/Archive.zig b/src/link/Elf/Archive.zig
index 0eb2f2d404..58a9e541a7 100644
--- a/src/link/Elf/Archive.zig
+++ b/src/link/Elf/Archive.zig
@@ -4,20 +4,150 @@ data: []const u8,
objects: std.ArrayListUnmanaged(Object) = .{},
strtab: []const u8 = &[0]u8{},
+pub fn isArchive(path: []const u8) !bool {
+ const file = try std.fs.cwd().openFile(path, .{});
+ defer file.close();
+ const reader = file.reader();
+ const magic = reader.readBytesNoEof(SARMAG) catch return false;
+ if (!mem.eql(u8, &magic, ARMAG)) return false;
+ return true;
+}
+
+pub fn deinit(self: *Archive, allocator: Allocator) void {
+ allocator.free(self.path);
+ allocator.free(self.data);
+ self.objects.deinit(allocator);
+}
+
+pub fn parse(self: *Archive, elf_file: *Elf) !void {
+ const gpa = elf_file.base.allocator;
+
+ var stream = std.io.fixedBufferStream(self.data);
+ const reader = stream.reader();
+ _ = try reader.readBytesNoEof(SARMAG);
+
+ while (true) {
+ if (stream.pos >= self.data.len) break;
+
+ if (stream.pos % 2 != 0) {
+ stream.pos += 1;
+ }
+ const hdr = try reader.readStruct(ar_hdr);
+
+ if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) {
+ // TODO convert into an error
+ log.debug(
+ "{s}: invalid header delimiter: expected '{s}', found '{s}'",
+ .{ self.path, std.fmt.fmtSliceEscapeLower(ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag) },
+ );
+ return;
+ }
+
+ const size = try hdr.size();
+ defer {
+ _ = stream.seekBy(size) catch {};
+ }
+
+ if (hdr.isSymtab()) continue;
+ if (hdr.isStrtab()) {
+ self.strtab = self.data[stream.pos..][0..size];
+ continue;
+ }
+
+ const name = ar_hdr.getValue(&hdr.ar_name);
+
+ if (mem.eql(u8, name, "__.SYMDEF") or mem.eql(u8, name, "__.SYMDEF SORTED")) continue;
+
+ const object_name = blk: {
+ if (name[0] == '/') {
+ const off = try std.fmt.parseInt(u32, name[1..], 10);
+ const object_name = self.getString(off);
+ break :blk try gpa.dupe(u8, object_name[0 .. object_name.len - 1]); // To account for trailing '/'
+ }
+ break :blk try gpa.dupe(u8, name);
+ };
+
+ const object = Object{
+ .archive = try gpa.dupe(u8, self.path),
+ .path = object_name,
+ .data = try gpa.dupe(u8, self.data[stream.pos..][0..size]),
+ .index = undefined,
+ .alive = false,
+ };
+
+ log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, self.path });
+
+ try self.objects.append(gpa, object);
+ }
+}
+
+fn getString(self: Archive, off: u32) []const u8 {
+ assert(off < self.strtab.len);
+ return mem.sliceTo(@as([*:strtab_delimiter]const u8, @ptrCast(self.strtab.ptr + off)), 0);
+}
+
+pub fn setArHdr(opts: struct {
+ name: union(enum) {
+ symtab: void,
+ strtab: void,
+ name: []const u8,
+ name_off: u32,
+ },
+ size: u32,
+}) ar_hdr {
+ var hdr: ar_hdr = .{
+ .ar_name = undefined,
+ .ar_date = undefined,
+ .ar_uid = undefined,
+ .ar_gid = undefined,
+ .ar_mode = undefined,
+ .ar_size = undefined,
+ .ar_fmag = undefined,
+ };
+ @memset(mem.asBytes(&hdr), 0x20);
+ @memcpy(&hdr.ar_fmag, Archive.ARFMAG);
+
+ {
+ var stream = std.io.fixedBufferStream(&hdr.ar_name);
+ const writer = stream.writer();
+ switch (opts.name) {
+ .symtab => writer.print("{s}", .{Archive.SYM64NAME}) catch unreachable,
+ .strtab => writer.print("//", .{}) catch unreachable,
+ .name => |x| writer.print("{s}", .{x}) catch unreachable,
+ .name_off => |x| writer.print("/{d}", .{x}) catch unreachable,
+ }
+ }
+ {
+ var stream = std.io.fixedBufferStream(&hdr.ar_size);
+ stream.writer().print("{d}", .{opts.size}) catch unreachable;
+ }
+
+ return hdr;
+}
+
// Archive files start with the ARMAG identifying string. Then follows a
// `struct ar_hdr', and as many bytes of member file data as its `ar_size'
// member indicates, for each member file.
/// String that begins an archive file.
pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";
/// Size of that string.
-pub const SARMAG: u4 = 8;
+pub const SARMAG = 8;
/// String in ar_fmag at the end of each header.
const ARFMAG: *const [2:0]u8 = "`\n";
+/// Strtab identifier
+const STRNAME: *const [2:0]u8 = "//";
+
+/// 32-bit symtab identifier
+const SYMNAME: *const [1:0]u8 = "/";
+
+/// 64-bit symtab identifier
const SYM64NAME: *const [7:0]u8 = "/SYM64/";
-const ar_hdr = extern struct {
+const strtab_delimiter = '\n';
+
+pub const ar_hdr = extern struct {
/// Member file name, sometimes / terminated.
ar_name: [16]u8,
@@ -54,93 +184,170 @@ const ar_hdr = extern struct {
}
fn isStrtab(self: ar_hdr) bool {
- return mem.eql(u8, getValue(&self.ar_name), "//");
+ return mem.eql(u8, getValue(&self.ar_name), STRNAME);
}
fn isSymtab(self: ar_hdr) bool {
- return mem.eql(u8, getValue(&self.ar_name), "/");
+ return mem.eql(u8, getValue(&self.ar_name), SYMNAME) or mem.eql(u8, getValue(&self.ar_name), SYM64NAME);
}
};
-pub fn isArchive(path: []const u8) !bool {
- const file = try std.fs.cwd().openFile(path, .{});
- defer file.close();
- const reader = file.reader();
- const magic = reader.readBytesNoEof(Archive.SARMAG) catch return false;
- if (!mem.eql(u8, &magic, ARMAG)) return false;
- return true;
-}
+pub const ArSymtab = struct {
+ symtab: std.ArrayListUnmanaged(Entry) = .{},
+ strtab: StringTable = .{},
-pub fn deinit(self: *Archive, allocator: Allocator) void {
- allocator.free(self.path);
- allocator.free(self.data);
- self.objects.deinit(allocator);
-}
+ pub fn deinit(ar: *ArSymtab, allocator: Allocator) void {
+ ar.symtab.deinit(allocator);
+ ar.strtab.deinit(allocator);
+ }
-pub fn parse(self: *Archive, elf_file: *Elf) !void {
- const gpa = elf_file.base.allocator;
+ pub fn sort(ar: *ArSymtab) void {
+ mem.sort(Entry, ar.symtab.items, {}, Entry.lessThan);
+ }
- var stream = std.io.fixedBufferStream(self.data);
- const reader = stream.reader();
- _ = try reader.readBytesNoEof(SARMAG);
+ pub fn size(ar: ArSymtab, kind: enum { p32, p64 }) usize {
+ const ptr_size: usize = switch (kind) {
+ .p32 => 4,
+ .p64 => 8,
+ };
+ var ss: usize = ptr_size + ar.symtab.items.len * ptr_size;
+ for (ar.symtab.items) |entry| {
+ ss += ar.strtab.getAssumeExists(entry.off).len + 1;
+ }
+ return ss;
+ }
- while (true) {
- if (stream.pos % 2 != 0) {
- stream.pos += 1;
+ pub fn write(ar: ArSymtab, kind: enum { p32, p64 }, elf_file: *Elf, writer: anytype) !void {
+ assert(kind == .p64); // TODO p32
+ const hdr = setArHdr(.{ .name = .symtab, .size = @intCast(ar.size(.p64)) });
+ try writer.writeAll(mem.asBytes(&hdr));
+
+ const gpa = elf_file.base.allocator;
+ var offsets = std.AutoHashMap(File.Index, u64).init(gpa);
+ defer offsets.deinit();
+ try offsets.ensureUnusedCapacity(@intCast(elf_file.objects.items.len + 1));
+
+ if (elf_file.zigObjectPtr()) |zig_object| {
+ offsets.putAssumeCapacityNoClobber(zig_object.index, zig_object.output_ar_state.file_off);
}
- const hdr = reader.readStruct(ar_hdr) catch break;
+ // Number of symbols
+ try writer.writeInt(u64, @as(u64, @intCast(ar.symtab.items.len)), .big);
- if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) {
- // TODO convert into an error
- log.debug(
- "{s}: invalid header delimiter: expected '{s}', found '{s}'",
- .{ self.path, std.fmt.fmtSliceEscapeLower(ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag) },
- );
- return;
+ // Offsets to files
+ for (ar.symtab.items) |entry| {
+ const off = offsets.get(entry.file_index).?;
+ try writer.writeInt(u64, off, .big);
}
- const size = try hdr.size();
- defer {
- _ = stream.seekBy(size) catch {};
+ // Strings
+ for (ar.symtab.items) |entry| {
+ try writer.print("{s}\x00", .{ar.strtab.getAssumeExists(entry.off)});
}
+ }
- if (hdr.isSymtab()) continue;
- if (hdr.isStrtab()) {
- self.strtab = self.data[stream.pos..][0..size];
- continue;
+ pub fn format(
+ ar: ArSymtab,
+ comptime unused_fmt_string: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+ ) !void {
+ _ = ar;
+ _ = unused_fmt_string;
+ _ = options;
+ _ = writer;
+ @compileError("do not format ar symtab directly; use fmt instead");
+ }
+
+ const FormatContext = struct {
+ ar: ArSymtab,
+ elf_file: *Elf,
+ };
+
+ pub fn fmt(ar: ArSymtab, elf_file: *Elf) std.fmt.Formatter(format2) {
+ return .{ .data = .{
+ .ar = ar,
+ .elf_file = elf_file,
+ } };
+ }
+
+ fn format2(
+ ctx: FormatContext,
+ comptime unused_fmt_string: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+ ) !void {
+ _ = unused_fmt_string;
+ _ = options;
+ const ar = ctx.ar;
+ const elf_file = ctx.elf_file;
+ for (ar.symtab.items, 0..) |entry, i| {
+ const name = ar.strtab.getAssumeExists(entry.off);
+ const file = elf_file.file(entry.file_index).?;
+ try writer.print(" {d}: {s} in file({d})({})\n", .{ i, name, entry.file_index, file.fmtPath() });
}
+ }
- const name = ar_hdr.getValue(&hdr.ar_name);
+ const Entry = struct {
+ /// Offset into the string table.
+ off: u32,
+ /// Index of the file defining the global.
+ file_index: File.Index,
- if (mem.eql(u8, name, "__.SYMDEF") or mem.eql(u8, name, "__.SYMDEF SORTED")) continue;
+ pub fn lessThan(ctx: void, lhs: Entry, rhs: Entry) bool {
+ _ = ctx;
+ if (lhs.off == rhs.off) return lhs.file_index < rhs.file_index;
+ return lhs.off < rhs.off;
+ }
+ };
+};
- const object_name = blk: {
- if (name[0] == '/') {
- const off = try std.fmt.parseInt(u32, name[1..], 10);
- break :blk self.getString(off);
- }
- break :blk name;
- };
+pub const ArStrtab = struct {
+ buffer: std.ArrayListUnmanaged(u8) = .{},
- const object = Object{
- .archive = try gpa.dupe(u8, self.path),
- .path = try gpa.dupe(u8, object_name[0 .. object_name.len - 1]), // To account for trailing '/'
- .data = try gpa.dupe(u8, self.data[stream.pos..][0..size]),
- .index = undefined,
- .alive = false,
- };
+ pub fn deinit(ar: *ArStrtab, allocator: Allocator) void {
+ ar.buffer.deinit(allocator);
+ }
- log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, self.path });
+ pub fn insert(ar: *ArStrtab, allocator: Allocator, name: []const u8) error{OutOfMemory}!u32 {
+ const off = @as(u32, @intCast(ar.buffer.items.len));
+ try ar.buffer.writer(allocator).print("{s}/{c}", .{ name, strtab_delimiter });
+ return off;
+ }
- try self.objects.append(gpa, object);
+ pub fn size(ar: ArStrtab) usize {
+ return ar.buffer.items.len;
}
-}
-fn getString(self: Archive, off: u32) []const u8 {
- assert(off < self.strtab.len);
- return mem.sliceTo(@as([*:'\n']const u8, @ptrCast(self.strtab.ptr + off)), 0);
-}
+ pub fn write(ar: ArStrtab, writer: anytype) !void {
+ const hdr = setArHdr(.{ .name = .strtab, .size = @intCast(ar.size()) });
+ try writer.writeAll(mem.asBytes(&hdr));
+ try writer.writeAll(ar.buffer.items);
+ }
+
+ pub fn format(
+ ar: ArStrtab,
+ comptime unused_fmt_string: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+ ) !void {
+ _ = unused_fmt_string;
+ _ = options;
+ try writer.print("{s}", .{std.fmt.fmtSliceEscapeLower(ar.buffer.items)});
+ }
+};
+
+pub const ArState = struct {
+ /// Name offset in the string table.
+ name_off: u32 = 0,
+
+ /// File offset of the ar_hdr describing the contributing
+ /// object in the archive.
+ file_off: u64 = 0,
+
+ /// Total size of the contributing object (excludes ar_hdr).
+ size: u64 = 0,
+};
const std = @import("std");
const assert = std.debug.assert;
@@ -152,4 +359,6 @@ const mem = std.mem;
const Allocator = mem.Allocator;
const Archive = @This();
const Elf = @import("../Elf.zig");
+const File = @import("file.zig").File;
const Object = @import("Object.zig");
+const StringTable = @import("../StringTable.zig");
diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig
index 7527b61988..1f6f77cc4b 100644
--- a/src/link/Elf/Atom.zig
+++ b/src/link/Elf/Atom.zig
@@ -42,7 +42,10 @@ next_index: Index = 0,
pub const Alignment = @import("../../InternPool.zig").Alignment;
pub fn name(self: Atom, elf_file: *Elf) []const u8 {
- return elf_file.strtab.getAssumeExists(self.name_offset);
+ const file_ptr = self.file(elf_file).?;
+ return switch (file_ptr) {
+ inline else => |x| x.getString(self.name_offset),
+ };
}
pub fn file(self: Atom, elf_file: *Elf) ?File {
@@ -602,7 +605,8 @@ fn dynAbsRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction {
}
fn outputType(elf_file: *Elf) u2 {
- return switch (elf_file.base.options.effectiveOutputMode()) {
+ assert(!elf_file.isRelocatable());
+ return switch (elf_file.base.options.output_mode) {
.Obj => unreachable,
.Lib => 0,
.Exe => if (elf_file.base.options.pie) 1 else 2,
@@ -692,7 +696,7 @@ fn reportUndefined(
) !void {
const rel_esym = switch (self.file(elf_file).?) {
.zig_object => |x| x.elfSym(rel.r_sym()).*,
- .object => |x| x.symtab[rel.r_sym()],
+ .object => |x| x.symtab.items[rel.r_sym()],
else => unreachable,
};
const esym = sym.elfSym(elf_file);
diff --git a/src/link/Elf/LinkerDefined.zig b/src/link/Elf/LinkerDefined.zig
index 0c6666e8bb..49e0e8f71d 100644
--- a/src/link/Elf/LinkerDefined.zig
+++ b/src/link/Elf/LinkerDefined.zig
@@ -1,11 +1,13 @@
index: File.Index,
symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
+strtab: std.ArrayListUnmanaged(u8) = .{},
symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
output_symtab_size: Elf.SymtabSize = .{},
pub fn deinit(self: *LinkerDefined, allocator: Allocator) void {
self.symtab.deinit(allocator);
+ self.strtab.deinit(allocator);
self.symbols.deinit(allocator);
}
@@ -13,16 +15,17 @@ pub fn addGlobal(self: *LinkerDefined, name: [:0]const u8, elf_file: *Elf) !u32
const gpa = elf_file.base.allocator;
try self.symtab.ensureUnusedCapacity(gpa, 1);
try self.symbols.ensureUnusedCapacity(gpa, 1);
+ const name_off = @as(u32, @intCast(self.strtab.items.len));
+ try self.strtab.writer(gpa).print("{s}\x00", .{name});
self.symtab.appendAssumeCapacity(.{
- .st_name = try elf_file.strtab.insert(gpa, name),
+ .st_name = name_off,
.st_info = elf.STB_GLOBAL << 4,
.st_other = @intFromEnum(elf.STV.HIDDEN),
.st_shndx = elf.SHN_ABS,
.st_value = 0,
.st_size = 0,
});
- const off = try elf_file.strtab.insert(gpa, name);
- const gop = try elf_file.getOrPutGlobal(off);
+ const gop = try elf_file.getOrPutGlobal(name);
self.symbols.addOneAssumeCapacity().* = gop.index;
return gop.index;
}
@@ -37,7 +40,6 @@ pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void {
const global = elf_file.symbol(index);
if (self.asFile().symbolRank(this_sym, false) < global.symbolRank(elf_file)) {
global.value = 0;
- global.name_offset = global.name_offset;
global.atom_index = 0;
global.file_index = self.index;
global.esym_index = sym_idx;
@@ -46,26 +48,6 @@ pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void {
}
}
-pub fn updateSymtabSize(self: *LinkerDefined, elf_file: *Elf) void {
- for (self.globals()) |global_index| {
- const global = elf_file.symbol(global_index);
- if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
- global.flags.output_symtab = true;
- self.output_symtab_size.nlocals += 1;
- }
-}
-
-pub fn writeSymtab(self: *LinkerDefined, elf_file: *Elf, ctx: anytype) void {
- var ilocal = ctx.ilocal;
- for (self.globals()) |global_index| {
- const global = elf_file.symbol(global_index);
- if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
- if (!global.flags.output_symtab) continue;
- global.setOutputSym(elf_file, &ctx.symtab[ilocal]);
- ilocal += 1;
- }
-}
-
pub fn globals(self: *LinkerDefined) []const Symbol.Index {
return self.symbols.items;
}
@@ -74,6 +56,11 @@ pub fn asFile(self: *LinkerDefined) File {
return .{ .linker_defined = self };
}
+pub fn getString(self: LinkerDefined, off: u32) [:0]const u8 {
+ assert(off < self.strtab.items.len);
+ return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
+}
+
pub fn fmtSymtab(self: *LinkerDefined, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
return .{ .data = .{
.self = self,
@@ -101,12 +88,13 @@ fn formatSymtab(
}
}
-const std = @import("std");
+const assert = std.debug.assert;
const elf = std.elf;
+const mem = std.mem;
+const std = @import("std");
-const Allocator = std.mem.Allocator;
+const Allocator = mem.Allocator;
const Elf = @import("../Elf.zig");
const File = @import("file.zig").File;
const LinkerDefined = @This();
-// const Object = @import("Object.zig");
const Symbol = @import("Symbol.zig");
diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig
index e21d6f161c..968ff84853 100644
--- a/src/link/Elf/Object.zig
+++ b/src/link/Elf/Object.zig
@@ -5,11 +5,10 @@ index: File.Index,
header: ?elf.Elf64_Ehdr = null,
shdrs: std.ArrayListUnmanaged(ElfShdr) = .{},
-strings: StringTable(.object_strings) = .{},
-symtab: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{},
-strtab: []const u8 = &[0]u8{},
-first_global: ?Symbol.Index = null,
+symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
+strtab: std.ArrayListUnmanaged(u8) = .{},
+first_global: ?Symbol.Index = null,
symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{},
@@ -39,7 +38,8 @@ pub fn deinit(self: *Object, allocator: Allocator) void {
allocator.free(self.path);
allocator.free(self.data);
self.shdrs.deinit(allocator);
- self.strings.deinit(allocator);
+ self.symtab.deinit(allocator);
+ self.strtab.deinit(allocator);
self.symbols.deinit(allocator);
self.atoms.deinit(allocator);
self.comdat_groups.deinit(allocator);
@@ -68,7 +68,7 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr));
}
- try self.strings.buffer.appendSlice(gpa, self.shdrContents(self.header.?.e_shstrndx));
+ try self.strtab.appendSlice(gpa, self.shdrContents(self.header.?.e_shstrndx));
const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
elf.SHT_SYMTAB => break @as(u16, @intCast(i)),
@@ -79,10 +79,22 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
const shdr = shdrs[index];
self.first_global = shdr.sh_info;
- const symtab = self.shdrContents(index);
- const nsyms = @divExact(symtab.len, @sizeOf(elf.Elf64_Sym));
- self.symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(symtab.ptr))[0..nsyms];
- self.strtab = self.shdrContents(@as(u16, @intCast(shdr.sh_link)));
+ const raw_symtab = self.shdrContents(index);
+ const nsyms = @divExact(raw_symtab.len, @sizeOf(elf.Elf64_Sym));
+ const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];
+
+ const strtab_bias = @as(u32, @intCast(self.strtab.items.len));
+ try self.strtab.appendSlice(gpa, self.shdrContents(@as(u16, @intCast(shdr.sh_link))));
+
+ try self.symtab.ensureUnusedCapacity(gpa, symtab.len);
+ for (symtab) |sym| {
+ const out_sym = self.symtab.addOneAssumeCapacity();
+ out_sym.* = sym;
+ out_sym.st_name = if (sym.st_name == 0 and sym.st_type() == elf.STT_SECTION)
+ shdrs[sym.st_shndx].sh_name
+ else
+ sym.st_name + strtab_bias;
+ }
}
try self.initAtoms(elf_file);
@@ -108,16 +120,16 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
switch (shdr.sh_type) {
elf.SHT_GROUP => {
- if (shdr.sh_info >= self.symtab.len) {
+ if (shdr.sh_info >= self.symtab.items.len) {
// TODO convert into an error
log.debug("{}: invalid symbol index in sh_info", .{self.fmtPath()});
continue;
}
- const group_info_sym = self.symtab[shdr.sh_info];
+ const group_info_sym = self.symtab.items[shdr.sh_info];
const group_signature = blk: {
if (group_info_sym.st_name == 0 and group_info_sym.st_type() == elf.STT_SECTION) {
const sym_shdr = shdrs[group_info_sym.st_shndx];
- break :blk self.strings.getAssumeExists(sym_shdr.sh_name);
+ break :blk self.getString(sym_shdr.sh_name);
}
break :blk self.getString(group_info_sym.st_name);
};
@@ -133,11 +145,8 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
continue;
}
- // Note the assumption about a global strtab used here to disambiguate common
- // COMDAT owners.
const gpa = elf_file.base.allocator;
- const group_signature_off = try elf_file.strtab.insert(gpa, group_signature);
- const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature_off);
+ const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature);
const comdat_group_index = try elf_file.addComdatGroup();
const comdat_group = elf_file.comdatGroup(comdat_group_index);
comdat_group.* = .{
@@ -157,10 +166,9 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
=> {},
else => {
- const name = self.strings.getAssumeExists(shdr.sh_name);
const shndx = @as(u16, @intCast(i));
if (self.skipShdr(shndx, elf_file)) continue;
- try self.addAtom(shdr, shndx, name, elf_file);
+ try self.addAtom(shdr, shndx, elf_file);
},
}
}
@@ -177,17 +185,11 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void {
};
}
-fn addAtom(
- self: *Object,
- shdr: ElfShdr,
- shndx: u16,
- name: [:0]const u8,
- elf_file: *Elf,
-) error{OutOfMemory}!void {
+fn addAtom(self: *Object, shdr: ElfShdr, shndx: u16, elf_file: *Elf) error{OutOfMemory}!void {
const atom_index = try elf_file.addAtom();
const atom = elf_file.atom(atom_index).?;
atom.atom_index = atom_index;
- atom.name_offset = try elf_file.strtab.insert(elf_file.base.allocator, name);
+ atom.name_offset = shdr.sh_name;
atom.file_index = self.index;
atom.input_section_index = shndx;
self.atoms.items[shndx] = atom_index;
@@ -205,7 +207,7 @@ fn addAtom(
fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMemory}!u16 {
const name = blk: {
- const name = self.strings.getAssumeExists(shdr.sh_name);
+ const name = self.getString(shdr.sh_name);
if (shdr.sh_flags & elf.SHF_MERGE != 0) break :blk name;
const sh_name_prefixes: []const [:0]const u8 = &.{
".text", ".data.rel.ro", ".data", ".rodata", ".bss.rel.ro", ".bss",
@@ -248,7 +250,7 @@ fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMem
fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool {
const shdr = self.shdrs.items[index];
- const name = self.strings.getAssumeExists(shdr.sh_name);
+ const name = self.getString(shdr.sh_name);
const ignore = blk: {
if (mem.startsWith(u8, name, ".note")) break :blk true;
if (mem.startsWith(u8, name, ".comment")) break :blk true;
@@ -262,33 +264,24 @@ fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool {
fn initSymtab(self: *Object, elf_file: *Elf) !void {
const gpa = elf_file.base.allocator;
- const first_global = self.first_global orelse self.symtab.len;
- const shdrs = self.shdrs.items;
+ const first_global = self.first_global orelse self.symtab.items.len;
- try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.len);
+ try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.items.len);
- for (self.symtab[0..first_global], 0..) |sym, i| {
+ for (self.symtab.items[0..first_global], 0..) |sym, i| {
const index = try elf_file.addSymbol();
self.symbols.appendAssumeCapacity(index);
const sym_ptr = elf_file.symbol(index);
- const name = blk: {
- if (sym.st_name == 0 and sym.st_type() == elf.STT_SECTION) {
- const shdr = shdrs[sym.st_shndx];
- break :blk self.strings.getAssumeExists(shdr.sh_name);
- }
- break :blk self.getString(sym.st_name);
- };
sym_ptr.value = sym.st_value;
- sym_ptr.name_offset = try elf_file.strtab.insert(gpa, name);
+ sym_ptr.name_offset = sym.st_name;
sym_ptr.esym_index = @as(u32, @intCast(i));
sym_ptr.atom_index = if (sym.st_shndx == elf.SHN_ABS) 0 else self.atoms.items[sym.st_shndx];
sym_ptr.file_index = self.index;
}
- for (self.symtab[first_global..]) |sym| {
+ for (self.symtab.items[first_global..]) |sym| {
const name = self.getString(sym.st_name);
- const off = try elf_file.strtab.insert(gpa, name);
- const gop = try elf_file.getOrPutGlobal(off);
+ const gop = try elf_file.getOrPutGlobal(name);
self.symbols.addOneAssumeCapacity().* = gop.index;
}
}
@@ -437,7 +430,7 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) void {
const first_global = self.first_global orelse return;
for (self.globals(), 0..) |index, i| {
const esym_index = @as(Symbol.Index, @intCast(first_global + i));
- const esym = self.symtab[esym_index];
+ const esym = self.symtab.items[esym_index];
if (esym.st_shndx == elf.SHN_UNDEF) continue;
@@ -467,7 +460,7 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void {
const first_global = self.first_global orelse return;
for (self.globals(), 0..) |index, i| {
const esym_index = @as(u32, @intCast(first_global + i));
- const esym = self.symtab[esym_index];
+ const esym = self.symtab.items[esym_index];
if (esym.st_shndx != elf.SHN_UNDEF) continue;
const global = elf_file.symbol(index);
@@ -491,20 +484,11 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void {
}
}
-pub fn resetGlobals(self: *Object, elf_file: *Elf) void {
- for (self.globals()) |index| {
- const global = elf_file.symbol(index);
- const off = global.name_offset;
- global.* = .{};
- global.name_offset = off;
- }
-}
-
pub fn markLive(self: *Object, elf_file: *Elf) void {
const first_global = self.first_global orelse return;
for (self.globals(), 0..) |index, i| {
const sym_idx = first_global + i;
- const sym = self.symtab[sym_idx];
+ const sym = self.symtab.items[sym_idx];
if (sym.st_bind() == elf.STB_WEAK) continue;
const global = elf_file.symbol(index);
@@ -531,7 +515,7 @@ pub fn checkDuplicates(self: *Object, elf_file: *Elf) void {
const first_global = self.first_global orelse return;
for (self.globals(), 0..) |index, i| {
const sym_idx = @as(u32, @intCast(first_global + i));
- const this_sym = self.symtab[sym_idx];
+ const this_sym = self.symtab.items[sym_idx];
const global = elf_file.symbol(index);
const global_file = global.getFile(elf_file) orelse continue;
@@ -560,7 +544,7 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
const first_global = self.first_global orelse return;
for (self.globals(), 0..) |index, i| {
const sym_idx = @as(u32, @intCast(first_global + i));
- const this_sym = self.symtab[sym_idx];
+ const this_sym = self.symtab.items[sym_idx];
if (this_sym.st_shndx != elf.SHN_COMMON) continue;
const global = elf_file.symbol(index);
@@ -584,8 +568,10 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
const name = if (is_tls) ".tls_common" else ".common";
const atom = elf_file.atom(atom_index).?;
+ const name_offset = @as(u32, @intCast(self.strtab.items.len));
+ try self.strtab.writer(gpa).print("{s}\x00", .{name});
atom.atom_index = atom_index;
- atom.name_offset = try elf_file.strtab.insert(gpa, name);
+ atom.name_offset = name_offset;
atom.file_index = self.index;
atom.size = this_sym.st_size;
const alignment = this_sym.st_value;
@@ -597,7 +583,7 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
const shdr = try self.shdrs.addOne(gpa);
const sh_size = math.cast(usize, this_sym.st_size) orelse return error.Overflow;
shdr.* = .{
- .sh_name = try self.strings.insert(gpa, name),
+ .sh_name = name_offset,
.sh_type = elf.SHT_NOBITS,
.sh_flags = sh_flags,
.sh_addr = 0,
@@ -665,56 +651,6 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void {
}
}
-pub fn updateSymtabSize(self: *Object, elf_file: *Elf) void {
- for (self.locals()) |local_index| {
- const local = elf_file.symbol(local_index);
- if (local.atom(elf_file)) |atom| if (!atom.flags.alive) continue;
- const esym = local.elfSym(elf_file);
- switch (esym.st_type()) {
- elf.STT_SECTION, elf.STT_NOTYPE => continue,
- else => {},
- }
- local.flags.output_symtab = true;
- self.output_symtab_size.nlocals += 1;
- }
-
- for (self.globals()) |global_index| {
- const global = elf_file.symbol(global_index);
- if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
- if (global.atom(elf_file)) |atom| if (!atom.flags.alive) continue;
- global.flags.output_symtab = true;
- if (global.isLocal()) {
- self.output_symtab_size.nlocals += 1;
- } else {
- self.output_symtab_size.nglobals += 1;
- }
- }
-}
-
-pub fn writeSymtab(self: *Object, elf_file: *Elf, ctx: anytype) void {
- var ilocal = ctx.ilocal;
- for (self.locals()) |local_index| {
- const local = elf_file.symbol(local_index);
- if (!local.flags.output_symtab) continue;
- local.setOutputSym(elf_file, &ctx.symtab[ilocal]);
- ilocal += 1;
- }
-
- var iglobal = ctx.iglobal;
- for (self.globals()) |global_index| {
- const global = elf_file.symbol(global_index);
- if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
- if (!global.flags.output_symtab) continue;
- if (global.isLocal()) {
- global.setOutputSym(elf_file, &ctx.symtab[ilocal]);
- ilocal += 1;
- } else {
- global.setOutputSym(elf_file, &ctx.symtab[iglobal]);
- iglobal += 1;
- }
- }
-}
-
pub fn locals(self: Object) []const Symbol.Index {
const end = self.first_global orelse self.symbols.items.len;
return self.symbols.items[0..end];
@@ -760,11 +696,6 @@ pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index)
} else return gpa.dupe(u8, data);
}
-fn getString(self: *Object, off: u32) [:0]const u8 {
- assert(off < self.strtab.len);
- return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0);
-}
-
pub fn comdatGroupMembers(self: *Object, index: u16) []align(1) const u32 {
const raw = self.shdrContents(index);
const nmembers = @divExact(raw.len, @sizeOf(u32));
@@ -782,6 +713,11 @@ pub fn getRelocs(self: *Object, shndx: u32) []align(1) const elf.Elf64_Rela {
return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num];
}
+pub fn getString(self: Object, off: u32) [:0]const u8 {
+ assert(off < self.strtab.items.len);
+ return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
+}
+
pub fn format(
self: *Object,
comptime unused_fmt_string: []const u8,
@@ -991,6 +927,5 @@ const Cie = eh_frame.Cie;
const Elf = @import("../Elf.zig");
const Fde = eh_frame.Fde;
const File = @import("file.zig").File;
-const StringTable = @import("../strtab.zig").StringTable;
const Symbol = @import("Symbol.zig");
const Alignment = Atom.Alignment;
diff --git a/src/link/Elf/SharedObject.zig b/src/link/Elf/SharedObject.zig
index 710c025f34..0924d3c761 100644
--- a/src/link/Elf/SharedObject.zig
+++ b/src/link/Elf/SharedObject.zig
@@ -4,19 +4,20 @@ index: File.Index,
header: ?elf.Elf64_Ehdr = null,
shdrs: std.ArrayListUnmanaged(ElfShdr) = .{},
-symtab: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{},
-strtab: []const u8 = &[0]u8{},
+
+symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
+strtab: std.ArrayListUnmanaged(u8) = .{},
/// Version symtab contains version strings of the symbols if present.
versyms: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{},
verstrings: std.ArrayListUnmanaged(u32) = .{},
+symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
+aliases: ?std.ArrayListUnmanaged(u32) = null,
+dynsym_sect_index: ?u16 = null,
dynamic_sect_index: ?u16 = null,
versym_sect_index: ?u16 = null,
verdef_sect_index: ?u16 = null,
-symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
-aliases: ?std.ArrayListUnmanaged(u32) = null,
-
needed: bool,
alive: bool,
@@ -36,6 +37,8 @@ pub fn isSharedObject(path: []const u8) !bool {
pub fn deinit(self: *SharedObject, allocator: Allocator) void {
allocator.free(self.path);
allocator.free(self.data);
+ self.symtab.deinit(allocator);
+ self.strtab.deinit(allocator);
self.versyms.deinit(allocator);
self.verstrings.deinit(allocator);
self.symbols.deinit(allocator);
@@ -51,7 +54,6 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void {
self.header = try reader.readStruct(elf.Elf64_Ehdr);
const shoff = std.math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;
- var dynsym_index: ?u16 = null;
const shdrs = @as(
[*]align(1) const elf.Elf64_Shdr,
@ptrCast(self.data.ptr + shoff),
@@ -61,7 +63,7 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void {
for (shdrs, 0..) |shdr, i| {
self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr));
switch (shdr.sh_type) {
- elf.SHT_DYNSYM => dynsym_index = @as(u16, @intCast(i)),
+ elf.SHT_DYNSYM => self.dynsym_sect_index = @as(u16, @intCast(i)),
elf.SHT_DYNAMIC => self.dynamic_sect_index = @as(u16, @intCast(i)),
elf.SHT_GNU_VERSYM => self.versym_sect_index = @as(u16, @intCast(i)),
elf.SHT_GNU_VERDEF => self.verdef_sect_index = @as(u16, @intCast(i)),
@@ -69,20 +71,13 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void {
}
}
- if (dynsym_index) |index| {
- const shdr = self.shdrs.items[index];
- const symtab = self.shdrContents(index);
- const nsyms = @divExact(symtab.len, @sizeOf(elf.Elf64_Sym));
- self.symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(symtab.ptr))[0..nsyms];
- self.strtab = self.shdrContents(@as(u16, @intCast(shdr.sh_link)));
- }
-
try self.parseVersions(elf_file);
try self.initSymtab(elf_file);
}
fn parseVersions(self: *SharedObject, elf_file: *Elf) !void {
const gpa = elf_file.base.allocator;
+ const symtab = self.getSymtabRaw();
try self.verstrings.resize(gpa, 2);
self.verstrings.items[elf.VER_NDX_LOCAL] = 0;
@@ -107,7 +102,7 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf) !void {
}
}
- try self.versyms.ensureTotalCapacityPrecise(gpa, self.symtab.len);
+ try self.versyms.ensureTotalCapacityPrecise(gpa, symtab.len);
if (self.versym_sect_index) |shndx| {
const versyms_raw = self.shdrContents(shndx);
@@ -120,30 +115,39 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf) !void {
ver;
self.versyms.appendAssumeCapacity(normalized_ver);
}
- } else for (0..self.symtab.len) |_| {
+ } else for (0..symtab.len) |_| {
self.versyms.appendAssumeCapacity(elf.VER_NDX_GLOBAL);
}
}
fn initSymtab(self: *SharedObject, elf_file: *Elf) !void {
const gpa = elf_file.base.allocator;
+ const symtab = self.getSymtabRaw();
+ const strtab = self.getStrtabRaw();
- try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.len);
+ try self.strtab.appendSlice(gpa, strtab);
+ try self.symtab.ensureTotalCapacityPrecise(gpa, symtab.len);
+ try self.symbols.ensureTotalCapacityPrecise(gpa, symtab.len);
- for (self.symtab, 0..) |sym, i| {
+ for (symtab, 0..) |sym, i| {
const hidden = self.versyms.items[i] & elf.VERSYM_HIDDEN != 0;
const name = self.getString(sym.st_name);
// We need to garble up the name so that we don't pick this symbol
// during symbol resolution. Thank you GNU!
- const off = if (hidden) blk: {
- const full_name = try std.fmt.allocPrint(gpa, "{s}@{s}", .{
+ const name_off = if (hidden) blk: {
+ const mangled = try std.fmt.allocPrint(gpa, "{s}@{s}", .{
name,
self.versionString(self.versyms.items[i]),
});
- defer gpa.free(full_name);
- break :blk try elf_file.strtab.insert(gpa, full_name);
- } else try elf_file.strtab.insert(gpa, name);
- const gop = try elf_file.getOrPutGlobal(off);
+ defer gpa.free(mangled);
+ const name_off = @as(u32, @intCast(self.strtab.items.len));
+ try self.strtab.writer(gpa).print("{s}\x00", .{mangled});
+ break :blk name_off;
+ } else sym.st_name;
+ const out_sym = self.symtab.addOneAssumeCapacity();
+ out_sym.* = sym;
+ out_sym.st_name = name_off;
+ const gop = try elf_file.getOrPutGlobal(self.getString(name_off));
self.symbols.addOneAssumeCapacity().* = gop.index;
}
}
@@ -151,7 +155,7 @@ fn initSymtab(self: *SharedObject, elf_file: *Elf) !void {
pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) void {
for (self.globals(), 0..) |index, i| {
const esym_index = @as(u32, @intCast(i));
- const this_sym = self.symtab[esym_index];
+ const this_sym = self.symtab.items[esym_index];
if (this_sym.st_shndx == elf.SHN_UNDEF) continue;
@@ -166,18 +170,9 @@ pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) void {
}
}
-pub fn resetGlobals(self: *SharedObject, elf_file: *Elf) void {
- for (self.globals()) |index| {
- const global = elf_file.symbol(index);
- const off = global.name_offset;
- global.* = .{};
- global.name_offset = off;
- }
-}
-
pub fn markLive(self: *SharedObject, elf_file: *Elf) void {
for (self.globals(), 0..) |index, i| {
- const sym = self.symtab[i];
+ const sym = self.symtab.items[i];
if (sym.st_shndx != elf.SHN_UNDEF) continue;
const global = elf_file.symbol(index);
@@ -193,27 +188,6 @@ pub fn markLive(self: *SharedObject, elf_file: *Elf) void {
}
}
-pub fn updateSymtabSize(self: *SharedObject, elf_file: *Elf) void {
- for (self.globals()) |global_index| {
- const global = elf_file.symbol(global_index);
- if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
- if (global.isLocal()) continue;
- global.flags.output_symtab = true;
- self.output_symtab_size.nglobals += 1;
- }
-}
-
-pub fn writeSymtab(self: *SharedObject, elf_file: *Elf, ctx: anytype) void {
- var iglobal = ctx.iglobal;
- for (self.globals()) |global_index| {
- const global = elf_file.symbol(global_index);
- if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
- if (!global.flags.output_symtab) continue;
- global.setOutputSym(elf_file, &ctx.symtab[iglobal]);
- iglobal += 1;
- }
-}
-
pub fn globals(self: SharedObject) []const Symbol.Index {
return self.symbols.items;
}
@@ -223,11 +197,6 @@ pub fn shdrContents(self: SharedObject, index: u16) []const u8 {
return self.data[shdr.sh_offset..][0..shdr.sh_size];
}
-pub fn getString(self: SharedObject, off: u32) [:0]const u8 {
- assert(off < self.strtab.len);
- return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0);
-}
-
pub fn versionString(self: SharedObject, index: elf.Elf64_Versym) [:0]const u8 {
const off = self.verstrings.items[index & elf.VERSYM_VERSION];
return self.getString(off);
@@ -309,6 +278,25 @@ pub fn symbolAliases(self: *SharedObject, index: u32, elf_file: *Elf) []const u3
return aliases.items[start..end];
}
+pub fn getString(self: SharedObject, off: u32) [:0]const u8 {
+ assert(off < self.strtab.items.len);
+ return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
+}
+
+pub fn getSymtabRaw(self: SharedObject) []align(1) const elf.Elf64_Sym {
+ const index = self.dynsym_sect_index orelse return &[0]elf.Elf64_Sym{};
+ const raw_symtab = self.shdrContents(index);
+ const nsyms = @divExact(raw_symtab.len, @sizeOf(elf.Elf64_Sym));
+ const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];
+ return symtab;
+}
+
+pub fn getStrtabRaw(self: SharedObject) []const u8 {
+ const index = self.dynsym_sect_index orelse return &[0]u8{};
+ const shdr = self.shdrs.items[index];
+ return self.shdrContents(@as(u16, @intCast(shdr.sh_link)));
+}
+
pub fn format(
self: SharedObject,
comptime unused_fmt_string: []const u8,
diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig
index f6a6e91898..13d9041813 100644
--- a/src/link/Elf/Symbol.zig
+++ b/src/link/Elf/Symbol.zig
@@ -42,7 +42,8 @@ pub fn outputShndx(symbol: Symbol) ?u16 {
return symbol.output_section_index;
}
-pub fn isLocal(symbol: Symbol) bool {
+pub fn isLocal(symbol: Symbol, elf_file: *Elf) bool {
+ if (elf_file.isRelocatable()) return symbol.elfSym(elf_file).st_bind() == elf.STB_LOCAL;
return !(symbol.flags.import or symbol.flags.@"export");
}
@@ -58,7 +59,11 @@ pub fn @"type"(symbol: Symbol, elf_file: *Elf) u4 {
}
pub fn name(symbol: Symbol, elf_file: *Elf) [:0]const u8 {
- return elf_file.strtab.getAssumeExists(symbol.name_offset);
+ if (symbol.flags.global) return elf_file.strings.getAssumeExists(symbol.name_offset);
+ const file_ptr = symbol.file(elf_file).?;
+ return switch (file_ptr) {
+ inline else => |x| x.getString(symbol.name_offset),
+ };
}
pub fn atom(symbol: Symbol, elf_file: *Elf) ?*Atom {
@@ -71,11 +76,10 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File {
pub fn elfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym {
const file_ptr = symbol.file(elf_file).?;
- switch (file_ptr) {
- .zig_object => |x| return x.elfSym(symbol.esym_index).*,
- .linker_defined => |x| return x.symtab.items[symbol.esym_index],
- inline else => |x| return x.symtab[symbol.esym_index],
- }
+ return switch (file_ptr) {
+ .zig_object => |x| x.elfSym(symbol.esym_index).*,
+ inline else => |x| x.symtab.items[symbol.esym_index],
+ };
}
pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 {
@@ -164,6 +168,8 @@ const GetOrCreateZigGotEntryResult = struct {
};
pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf) !GetOrCreateZigGotEntryResult {
+ assert(!elf_file.isRelocatable());
+ assert(symbol.flags.needs_zig_got);
if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).?.zig_got };
const index = try elf_file.zig_got.addSymbol(symbol_index, elf_file);
return .{ .found_existing = false, .index = index };
@@ -201,14 +207,11 @@ pub fn setExtra(symbol: Symbol, extras: Extra, elf_file: *Elf) void {
}
pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
- const file_ptr = symbol.file(elf_file) orelse {
- out.* = Elf.null_sym;
- return;
- };
+ const file_ptr = symbol.file(elf_file).?;
const esym = symbol.elfSym(elf_file);
const st_type = symbol.type(elf_file);
const st_bind: u8 = blk: {
- if (symbol.isLocal()) break :blk 0;
+ if (symbol.isLocal(elf_file)) break :blk 0;
if (symbol.flags.weak) break :blk elf.STB_WEAK;
if (file_ptr == .shared_object) break :blk elf.STB_GLOBAL;
break :blk esym.st_bind();
@@ -216,6 +219,8 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
const st_shndx = blk: {
if (symbol.flags.has_copy_rel) break :blk elf_file.copy_rel_section_index.?;
if (file_ptr == .shared_object or esym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF;
+ // TODO I think this is wrong and obsolete
+ if (elf_file.isRelocatable() and st_type == elf.STT_SECTION) break :blk symbol.outputShndx().?;
if (symbol.atom(elf_file) == null and file_ptr != .linker_defined)
break :blk elf.SHN_ABS;
break :blk symbol.outputShndx() orelse elf.SHN_UNDEF;
@@ -232,14 +237,11 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
break :blk symbol.value - elf_file.tlsAddress();
break :blk symbol.value;
};
- out.* = .{
- .st_name = symbol.name_offset,
- .st_info = (st_bind << 4) | st_type,
- .st_other = esym.st_other,
- .st_shndx = st_shndx,
- .st_value = st_value,
- .st_size = esym.st_size,
- };
+ out.st_info = (st_bind << 4) | st_type;
+ out.st_other = esym.st_other;
+ out.st_shndx = st_shndx;
+ out.st_value = st_value;
+ out.st_size = esym.st_size;
}
pub fn format(
@@ -340,6 +342,12 @@ pub const Flags = packed struct {
/// Whether this symbol is weak.
weak: bool = false,
+ /// Whether the symbol has its name interned in global symbol
+ /// resolver table.
+ /// This happens for any symbol that is considered a global
+ /// symbol, but is not necessarily an import or export.
+ global: bool = false,
+
/// Whether the symbol makes into the output symtab.
output_symtab: bool = false,
@@ -373,6 +381,7 @@ pub const Flags = packed struct {
has_tlsdesc: bool = false,
/// Whether the symbol contains .zig.got indirection.
+ needs_zig_got: bool = false,
has_zig_got: bool = false,
};
diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig
index dff6f796fa..a6ac6cfc11 100644
--- a/src/link/Elf/ZigObject.zig
+++ b/src/link/Elf/ZigObject.zig
@@ -9,6 +9,7 @@ index: File.Index,
local_esyms: std.MultiArrayList(ElfSym) = .{},
global_esyms: std.MultiArrayList(ElfSym) = .{},
+strtab: StringTable = .{},
local_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
global_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{},
@@ -19,6 +20,7 @@ relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{},
num_dynrelocs: u32 = 0,
output_symtab_size: Elf.SymtabSize = .{},
+output_ar_state: Archive.ArState = .{},
dwarf: ?Dwarf = null,
@@ -74,8 +76,9 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void {
const gpa = elf_file.base.allocator;
try self.atoms.append(gpa, 0); // null input section
+ try self.strtab.buffer.append(gpa, 0);
- const name_off = try elf_file.strtab.insert(gpa, std.fs.path.stem(self.path));
+ const name_off = try self.strtab.insert(gpa, std.fs.path.stem(self.path));
const symbol_index = try elf_file.addSymbol();
try self.local_symbols.append(gpa, symbol_index);
const symbol_ptr = elf_file.symbol(symbol_index);
@@ -85,7 +88,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void {
const esym_index = try self.addLocalEsym(gpa);
const esym = &self.local_esyms.items(.elf_sym)[esym_index];
esym.st_name = name_off;
- esym.st_info |= elf.STT_FILE;
+ esym.st_info = elf.STT_FILE;
esym.st_shndx = elf.SHN_ABS;
symbol_ptr.esym_index = esym_index;
@@ -97,6 +100,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void {
pub fn deinit(self: *ZigObject, allocator: Allocator) void {
self.local_esyms.deinit(allocator);
self.global_esyms.deinit(allocator);
+ self.strtab.deinit(allocator);
self.local_symbols.deinit(allocator);
self.global_symbols.deinit(allocator);
self.globals_lookup.deinit(allocator);
@@ -177,16 +181,16 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf) !void {
}
if (self.debug_info_header_dirty) {
- const text_phdr = &elf_file.phdrs.items[elf_file.phdr_zig_load_re_index.?];
- const low_pc = text_phdr.p_vaddr;
- const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
+ const text_shdr = elf_file.shdrs.items[elf_file.zig_text_section_index.?];
+ const low_pc = text_shdr.sh_addr;
+ const high_pc = text_shdr.sh_addr + text_shdr.sh_size;
try dw.writeDbgInfoHeader(elf_file.base.options.module.?, low_pc, high_pc);
self.debug_info_header_dirty = false;
}
if (self.debug_aranges_section_dirty) {
- const text_phdr = &elf_file.phdrs.items[elf_file.phdr_zig_load_re_index.?];
- try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz);
+ const text_shdr = elf_file.shdrs.items[elf_file.zig_text_section_index.?];
+ try dw.writeDbgAranges(text_shdr.sh_addr, text_shdr.sh_size);
self.debug_aranges_section_dirty = false;
}
@@ -207,6 +211,8 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf) !void {
self.saveDebugSectionsSizes(elf_file);
}
+ try self.sortSymbols(elf_file);
+
// The point of flushModule() is to commit changes, so in theory, nothing should
// be dirty after this. However, it is possible for some things to remain
// dirty because they fail to be written in the event of compile errors,
@@ -281,6 +287,22 @@ pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index {
return symbol_index;
}
+pub fn addSectionSymbol(self: *ZigObject, shndx: u16, elf_file: *Elf) !void {
+ assert(elf_file.isRelocatable());
+ const gpa = elf_file.base.allocator;
+ const symbol_index = try elf_file.addSymbol();
+ try self.local_symbols.append(gpa, symbol_index);
+ const symbol_ptr = elf_file.symbol(symbol_index);
+ symbol_ptr.file_index = self.index;
+ symbol_ptr.output_section_index = shndx;
+
+ const esym_index = try self.addLocalEsym(gpa);
+ const esym = &self.local_esyms.items(.elf_sym)[esym_index];
+ esym.st_info = elf.STT_SECTION;
+ esym.st_shndx = shndx;
+ symbol_ptr.esym_index = esym_index;
+}
+
/// TODO actually create fake input shdrs and return that instead.
pub fn inputShdr(self: ZigObject, atom_index: Atom.Index, elf_file: *Elf) Object.ElfShdr {
_ = self;
@@ -334,7 +356,7 @@ pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) void {
}
}
-pub fn claimUnresolved(self: *ZigObject, elf_file: *Elf) void {
+pub fn claimUnresolved(self: ZigObject, elf_file: *Elf) void {
for (self.globals(), 0..) |index, i| {
const esym_index = @as(Symbol.Index, @intCast(i)) | global_symbol_bit;
const esym = self.global_esyms.items(.elf_sym)[i];
@@ -362,6 +384,26 @@ pub fn claimUnresolved(self: *ZigObject, elf_file: *Elf) void {
}
}
+pub fn claimUnresolvedObject(self: ZigObject, elf_file: *Elf) void {
+ for (self.globals(), 0..) |index, i| {
+ const esym_index = @as(Symbol.Index, @intCast(i)) | global_symbol_bit;
+ const esym = self.global_esyms.items(.elf_sym)[i];
+
+ if (esym.st_shndx != elf.SHN_UNDEF) continue;
+
+ const global = elf_file.symbol(index);
+ if (global.file(elf_file)) |file| {
+ if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF or
+ file.index() <= self.index) continue;
+ }
+
+ global.value = 0;
+ global.atom_index = 0;
+ global.esym_index = esym_index;
+ global.file_index = self.index;
+ }
+}
+
pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void {
for (self.atoms.items) |atom_index| {
const atom = elf_file.atom(atom_index) orelse continue;
@@ -379,15 +421,6 @@ pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void {
}
}
-pub fn resetGlobals(self: *ZigObject, elf_file: *Elf) void {
- for (self.globals()) |index| {
- const global = elf_file.symbol(index);
- const off = global.name_offset;
- global.* = .{};
- global.name_offset = off;
- }
-}
-
pub fn markLive(self: *ZigObject, elf_file: *Elf) void {
for (self.globals(), 0..) |index, i| {
const esym = self.global_esyms.items(.elf_sym)[i];
@@ -404,79 +437,241 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void {
}
}
-pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) void {
- for (self.locals()) |local_index| {
- const local = elf_file.symbol(local_index);
- const esym = local.elfSym(elf_file);
- switch (esym.st_type()) {
- elf.STT_SECTION, elf.STT_NOTYPE => {
- local.flags.output_symtab = false;
- continue;
- },
- else => {},
- }
- local.flags.output_symtab = true;
- self.output_symtab_size.nlocals += 1;
- }
+fn sortSymbols(self: *ZigObject, elf_file: *Elf) error{OutOfMemory}!void {
+ _ = self;
+ _ = elf_file;
+ // const Entry = struct {
+ // index: Symbol.Index,
+
+ // const Ctx = struct {
+ // zobj: ZigObject,
+ // efile: *Elf,
+ // };
+
+ // pub fn lessThan(ctx: Ctx, lhs: @This(), rhs: @This()) bool {
+ // const lhs_sym = ctx.efile.symbol(zobj.symbol(lhs.index));
+ // const rhs_sym = ctx.efile.symbol(zobj.symbol(rhs.index));
+ // if (lhs_sym.outputShndx() != null and rhs_sym.outputShndx() != null) {
+ // if (lhs_sym.output_section_index == rhs_sym.output_section_index) {
+ // if (lhs_sym.value == rhs_sym.value) {
+ // return lhs_sym.name_offset < rhs_sym.name_offset;
+ // }
+ // return lhs_sym.value < rhs_sym.value;
+ // }
+ // return lhs_sym.output_section_index < rhs_sym.output_section_index;
+ // }
+ // if (lhs_sym.outputShndx() != null) {
+ // if (rhs_sym.isAbs(ctx.efile)) return false;
+ // return true;
+ // }
+ // return false;
+ // }
+ // };
+
+ // const gpa = elf_file.base.allocator;
+
+ // {
+ // const sorted = try gpa.alloc(Entry, self.local_symbols.items.len);
+ // defer gpa.free(sorted);
+ // for (0..self.local_symbols.items.len) |index| {
+ // sorted[i] = .{ .index = @as(Symbol.Index, @intCast(index)) };
+ // }
+ // mem.sort(Entry, sorted, .{ .zobj = self, .efile = elf_file }, Entry.lessThan);
+
+ // const backlinks = try gpa.alloc(Symbol.Index, sorted.len);
+ // defer gpa.free(backlinks);
+ // for (sorted, 0..) |entry, i| {
+ // backlinks[entry.index] = @as(Symbol.Index, @intCast(i));
+ // }
+
+ // const local_symbols = try self.local_symbols.toOwnedSlice(gpa);
+ // defer gpa.free(local_symbols);
+
+ // try self.local_symbols.ensureTotalCapacityPrecise(gpa, local_symbols.len);
+ // for (sorted) |entry| {
+ // self.local_symbols.appendAssumeCapacity(local_symbols[entry.index]);
+ // }
+
+ // for (self.)
+ // }
+
+ // const sorted_globals = try gpa.alloc(Entry, self.global_symbols.items.len);
+ // defer gpa.free(sorted_globals);
+ // for (self.global_symbols.items, 0..) |index, i| {
+ // sorted_globals[i] = .{ .index = index };
+ // }
+ // mem.sort(Entry, sorted_globals, elf_file, Entry.lessThan);
+}
+
+pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void {
+ const gpa = elf_file.base.allocator;
+
+ try ar_symtab.symtab.ensureUnusedCapacity(gpa, self.globals().len);
for (self.globals()) |global_index| {
const global = elf_file.symbol(global_index);
- if (global.file(elf_file)) |file| if (file.index() != self.index) {
- global.flags.output_symtab = false;
- continue;
- };
- global.flags.output_symtab = true;
- if (global.isLocal()) {
- self.output_symtab_size.nlocals += 1;
- } else {
- self.output_symtab_size.nglobals += 1;
- }
+ const file_ptr = global.file(elf_file).?;
+ assert(file_ptr.index() == self.index);
+ if (global.type(elf_file) == elf.SHN_UNDEF) continue;
+
+ const off = try ar_symtab.strtab.insert(gpa, global.name(elf_file));
+ ar_symtab.symtab.appendAssumeCapacity(.{ .off = off, .file_index = self.index });
}
}
-pub fn writeSymtab(self: *ZigObject, elf_file: *Elf, ctx: anytype) void {
- var ilocal = ctx.ilocal;
- for (self.locals()) |local_index| {
- const local = elf_file.symbol(local_index);
- if (!local.flags.output_symtab) continue;
- local.setOutputSym(elf_file, &ctx.symtab[ilocal]);
- ilocal += 1;
+pub fn updateArStrtab(
+ self: *ZigObject,
+ allocator: Allocator,
+ ar_strtab: *Archive.ArStrtab,
+) error{OutOfMemory}!void {
+ const name = try std.fmt.allocPrint(allocator, "{s}.o", .{std.fs.path.stem(self.path)});
+ defer allocator.free(name);
+ if (name.len <= 15) return;
+ const name_off = try ar_strtab.insert(allocator, name);
+ self.output_ar_state.name_off = name_off;
+}
+
+pub fn updateArSize(self: *ZigObject, elf_file: *Elf) void {
+ var end_pos: u64 = elf_file.shdr_table_offset.?;
+ for (elf_file.shdrs.items) |shdr| {
+ end_pos = @max(end_pos, shdr.sh_offset + shdr.sh_size);
}
+ self.output_ar_state.size = end_pos;
+}
- var iglobal = ctx.iglobal;
- for (self.globals()) |global_index| {
- const global = elf_file.symbol(global_index);
- if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
- if (!global.flags.output_symtab) continue;
- if (global.isLocal()) {
- global.setOutputSym(elf_file, &ctx.symtab[ilocal]);
- ilocal += 1;
- } else {
- global.setOutputSym(elf_file, &ctx.symtab[iglobal]);
- iglobal += 1;
+pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void {
+ const gpa = elf_file.base.allocator;
+
+ const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
+ const contents = try gpa.alloc(u8, size);
+ defer gpa.free(contents);
+
+ const amt = try elf_file.base.file.?.preadAll(contents, 0);
+ if (amt != self.output_ar_state.size) return error.InputOutput;
+
+ const name = try std.fmt.allocPrint(gpa, "{s}.o", .{std.fs.path.stem(self.path)});
+ defer gpa.free(name);
+
+ const hdr = Archive.setArHdr(.{
+ .name = if (name.len <= 15) .{ .name = name } else .{ .name_off = self.output_ar_state.name_off },
+ .size = @intCast(size),
+ });
+ try writer.writeAll(mem.asBytes(&hdr));
+ try writer.writeAll(contents);
+}
+
+pub fn updateRelaSectionSizes(self: ZigObject, elf_file: *Elf) void {
+ _ = self;
+
+ for (&[_]?u16{
+ elf_file.zig_text_rela_section_index,
+ elf_file.zig_data_rel_ro_rela_section_index,
+ elf_file.zig_data_rela_section_index,
+ }) |maybe_index| {
+ const index = maybe_index orelse continue;
+ const shdr = &elf_file.shdrs.items[index];
+ const meta = elf_file.last_atom_and_free_list_table.get(@intCast(shdr.sh_info)).?;
+ const last_atom_index = meta.last_atom_index;
+
+ var atom = elf_file.atom(last_atom_index) orelse continue;
+ while (true) {
+ const relocs = atom.relocs(elf_file);
+ shdr.sh_size += relocs.len * shdr.sh_entsize;
+ if (elf_file.atom(atom.prev_index)) |prev| {
+ atom = prev;
+ } else break;
+ }
+ }
+
+ for (&[_]?u16{
+ elf_file.zig_text_rela_section_index,
+ elf_file.zig_data_rel_ro_rela_section_index,
+ elf_file.zig_data_rela_section_index,
+ }) |maybe_index| {
+ const index = maybe_index orelse continue;
+ const shdr = &elf_file.shdrs.items[index];
+ if (shdr.sh_size == 0) shdr.sh_offset = 0;
+ }
+}
+
+pub fn writeRelaSections(self: ZigObject, elf_file: *Elf) !void {
+ const gpa = elf_file.base.allocator;
+
+ for (&[_]?u16{
+ elf_file.zig_text_rela_section_index,
+ elf_file.zig_data_rel_ro_rela_section_index,
+ elf_file.zig_data_rela_section_index,
+ }) |maybe_index| {
+ const index = maybe_index orelse continue;
+ const shdr = elf_file.shdrs.items[index];
+ const meta = elf_file.last_atom_and_free_list_table.get(@intCast(shdr.sh_info)).?;
+ const last_atom_index = meta.last_atom_index;
+
+ var atom = elf_file.atom(last_atom_index) orelse continue;
+
+ var relocs = std.ArrayList(elf.Elf64_Rela).init(gpa);
+ defer relocs.deinit();
+ try relocs.ensureTotalCapacityPrecise(@intCast(@divExact(shdr.sh_size, shdr.sh_entsize)));
+
+ while (true) {
+ for (atom.relocs(elf_file)) |rel| {
+ const target = elf_file.symbol(self.symbol(rel.r_sym()));
+ const r_offset = atom.value + rel.r_offset;
+ const r_sym: u32 = if (target.flags.global)
+ (target.esym_index & symbol_mask) + @as(u32, @intCast(self.local_esyms.slice().len))
+ else
+ target.esym_index;
+ const r_type = switch (rel.r_type()) {
+ Elf.R_X86_64_ZIG_GOT32,
+ Elf.R_X86_64_ZIG_GOTPCREL,
+ => unreachable, // Sanity check if we accidentally emitted those.
+ else => |r_type| r_type,
+ };
+ relocs.appendAssumeCapacity(.{
+ .r_offset = r_offset,
+ .r_addend = rel.r_addend,
+ .r_info = (@as(u64, @intCast(r_sym + 1)) << 32) | r_type,
+ });
+ }
+ if (elf_file.atom(atom.prev_index)) |prev| {
+ atom = prev;
+ } else break;
}
+
+ const SortRelocs = struct {
+ pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
+ _ = ctx;
+ return lhs.r_offset < rhs.r_offset;
+ }
+ };
+
+ mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan);
+
+ try elf_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), shdr.sh_offset);
}
}
-pub fn symbol(self: *ZigObject, index: Symbol.Index) Symbol.Index {
- const is_global = index & global_symbol_bit != 0;
+inline fn isGlobal(index: Symbol.Index) bool {
+ return index & global_symbol_bit != 0;
+}
+
+pub fn symbol(self: ZigObject, index: Symbol.Index) Symbol.Index {
const actual_index = index & symbol_mask;
- if (is_global) return self.global_symbols.items[actual_index];
+ if (isGlobal(index)) return self.global_symbols.items[actual_index];
return self.local_symbols.items[actual_index];
}
pub fn elfSym(self: *ZigObject, index: Symbol.Index) *elf.Elf64_Sym {
- const is_global = index & global_symbol_bit != 0;
const actual_index = index & symbol_mask;
- if (is_global) return &self.global_esyms.items(.elf_sym)[actual_index];
+ if (isGlobal(index)) return &self.global_esyms.items(.elf_sym)[actual_index];
return &self.local_esyms.items(.elf_sym)[actual_index];
}
-pub fn locals(self: *ZigObject) []const Symbol.Index {
+pub fn locals(self: ZigObject) []const Symbol.Index {
return self.local_symbols.items;
}
-pub fn globals(self: *ZigObject) []const Symbol.Index {
+pub fn globals(self: ZigObject) []const Symbol.Index {
return self.global_symbols.items;
}
@@ -570,7 +765,7 @@ pub fn lowerAnonDecl(
name,
tv,
decl_alignment,
- elf_file.zig_rodata_section_index.?,
+ elf_file.zig_data_rel_ro_section_index.?,
src_loc,
) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
@@ -682,7 +877,7 @@ fn getDeclShdrIndex(self: *ZigObject, elf_file: *Elf, decl_index: Module.Decl.In
.Fn => elf_file.zig_text_section_index.?,
else => blk: {
if (decl.getOwnedVariable(mod)) |variable| {
- if (variable.is_const) break :blk elf_file.zig_rodata_section_index.?;
+ if (variable.is_const) break :blk elf_file.zig_data_rel_ro_section_index.?;
if (variable.init.toValue().isUndefDeep(mod)) {
const mode = elf_file.base.options.optimize_mode;
if (mode == .Debug or mode == .ReleaseSafe) break :blk elf_file.zig_data_section_index.?;
@@ -696,7 +891,7 @@ fn getDeclShdrIndex(self: *ZigObject, elf_file: *Elf, decl_index: Module.Decl.In
if (is_all_zeroes) break :blk elf_file.zig_bss_section_index.?;
break :blk elf_file.zig_data_section_index.?;
}
- break :blk elf_file.zig_rodata_section_index.?;
+ break :blk elf_file.zig_data_rel_ro_section_index.?;
},
};
return shdr_index;
@@ -727,7 +922,7 @@ fn updateDeclCode(
sym.output_section_index = shdr_index;
atom_ptr.output_section_index = shdr_index;
- sym.name_offset = try elf_file.strtab.insert(gpa, decl_name);
+ sym.name_offset = try self.strtab.insert(gpa, decl_name);
atom_ptr.flags.alive = true;
atom_ptr.name_offset = sym.name_offset;
esym.st_name = sym.name_offset;
@@ -749,10 +944,12 @@ fn updateDeclCode(
sym.value = atom_ptr.value;
esym.st_value = atom_ptr.value;
- log.debug(" (writing new offset table entry)", .{});
- assert(sym.flags.has_zig_got);
- const extra = sym.extra(elf_file).?;
- try elf_file.zig_got.writeOne(elf_file, extra.zig_got);
+ if (!elf_file.isRelocatable()) {
+ log.debug(" (writing new offset table entry)", .{});
+ assert(sym.flags.has_zig_got);
+ const extra = sym.extra(elf_file).?;
+ try elf_file.zig_got.writeOne(elf_file, extra.zig_got);
+ }
}
} else if (code.len < old_size) {
atom_ptr.shrink(elf_file);
@@ -762,10 +959,13 @@ fn updateDeclCode(
errdefer self.freeDeclMetadata(elf_file, sym_index);
sym.value = atom_ptr.value;
+ sym.flags.needs_zig_got = true;
esym.st_value = atom_ptr.value;
- const gop = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
- try elf_file.zig_got.writeOne(elf_file, gop.index);
+ if (!elf_file.isRelocatable()) {
+ const gop = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
+ try elf_file.zig_got.writeOne(elf_file, gop.index);
+ }
}
if (elf_file.base.child_pid) |pid| {
@@ -791,9 +991,7 @@ fn updateDeclCode(
const shdr = elf_file.shdrs.items[shdr_index];
if (shdr.sh_type != elf.SHT_NOBITS) {
- const phdr_index = elf_file.phdr_to_shdr_table.get(shdr_index).?;
- const section_offset = sym.value - elf_file.phdrs.items[phdr_index].p_vaddr;
- const file_offset = shdr.sh_offset + section_offset;
+ const file_offset = shdr.sh_offset + sym.value - shdr.sh_addr;
try elf_file.base.file.?.pwriteAll(code, file_offset);
}
}
@@ -967,7 +1165,7 @@ fn updateLazySymbol(
sym.ty.fmt(mod),
});
defer gpa.free(name);
- break :blk try elf_file.strtab.insert(gpa, name);
+ break :blk try self.strtab.insert(gpa, name);
};
const src = if (sym.ty.getOwnerDeclOrNull(mod)) |owner_decl|
@@ -997,10 +1195,9 @@ fn updateLazySymbol(
const output_section_index = switch (sym.kind) {
.code => elf_file.zig_text_section_index.?,
- .const_data => elf_file.zig_rodata_section_index.?,
+ .const_data => elf_file.zig_data_rel_ro_section_index.?,
};
const local_sym = elf_file.symbol(symbol_index);
- const phdr_index = elf_file.phdr_to_shdr_table.get(output_section_index).?;
local_sym.name_offset = name_str_index;
local_sym.output_section_index = output_section_index;
const local_esym = &self.local_esyms.items(.elf_sym)[local_sym.esym_index];
@@ -1018,13 +1215,16 @@ fn updateLazySymbol(
errdefer self.freeDeclMetadata(elf_file, symbol_index);
local_sym.value = atom_ptr.value;
+ local_sym.flags.needs_zig_got = true;
local_esym.st_value = atom_ptr.value;
- const gop = try local_sym.getOrCreateZigGotEntry(symbol_index, elf_file);
- try elf_file.zig_got.writeOne(elf_file, gop.index);
+ if (!elf_file.isRelocatable()) {
+ const gop = try local_sym.getOrCreateZigGotEntry(symbol_index, elf_file);
+ try elf_file.zig_got.writeOne(elf_file, gop.index);
+ }
- const section_offset = atom_ptr.value - elf_file.phdrs.items[phdr_index].p_vaddr;
- const file_offset = elf_file.shdrs.items[output_section_index].sh_offset + section_offset;
+ const shdr = elf_file.shdrs.items[output_section_index];
+ const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;
try elf_file.base.file.?.pwriteAll(code, file_offset);
}
@@ -1051,7 +1251,7 @@ pub fn lowerUnnamedConst(
name,
typed_value,
typed_value.ty.abiAlignment(mod),
- elf_file.zig_rodata_section_index.?,
+ elf_file.zig_data_rel_ro_section_index.?,
decl.srcLoc(mod),
)) {
.ok => |sym_index| sym_index,
@@ -1098,9 +1298,8 @@ fn lowerConst(
.fail => |em| return .{ .fail = em },
};
- const phdr_index = elf_file.phdr_to_shdr_table.get(output_section_index).?;
const local_sym = elf_file.symbol(sym_index);
- const name_str_index = try elf_file.strtab.insert(gpa, name);
+ const name_str_index = try self.strtab.insert(gpa, name);
local_sym.name_offset = name_str_index;
local_sym.output_section_index = output_section_index;
const local_esym = &self.local_esyms.items(.elf_sym)[local_sym.esym_index];
@@ -1121,8 +1320,8 @@ fn lowerConst(
local_sym.value = atom_ptr.value;
local_esym.st_value = atom_ptr.value;
- const section_offset = atom_ptr.value - elf_file.phdrs.items[phdr_index].p_vaddr;
- const file_offset = elf_file.shdrs.items[output_section_index].sh_offset + section_offset;
+ const shdr = elf_file.shdrs.items[output_section_index];
+ const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;
try elf_file.base.file.?.pwriteAll(code, file_offset);
return .{ .ok = sym_index };
@@ -1195,18 +1394,12 @@ pub fn updateExports(
};
const stt_bits: u8 = @as(u4, @truncate(esym.st_info));
const exp_name = mod.intern_pool.stringToSlice(exp.opts.name);
- const name_off = try elf_file.strtab.insert(gpa, exp_name);
- const global_esym_index = if (metadata.@"export"(self, elf_file, exp_name)) |exp_index|
+ const name_off = try self.strtab.insert(gpa, exp_name);
+ const global_esym_index = if (metadata.@"export"(self, exp_name)) |exp_index|
exp_index.*
else blk: {
- const global_esym_index = try self.addGlobalEsym(gpa);
- const lookup_gop = try self.globals_lookup.getOrPut(gpa, name_off);
- const global_esym = self.elfSym(global_esym_index);
- global_esym.st_name = name_off;
- lookup_gop.value_ptr.* = global_esym_index;
+ const global_esym_index = try self.getGlobalSymbol(elf_file, exp_name, null);
try metadata.exports.append(gpa, global_esym_index);
- const gop = try elf_file.getOrPutGlobal(name_off);
- try self.global_symbols.append(gpa, gop.index);
break :blk global_esym_index;
};
@@ -1216,6 +1409,7 @@ pub fn updateExports(
global_esym.st_shndx = esym.st_shndx;
global_esym.st_info = (stb_bits << 4) | stt_bits;
global_esym.st_name = name_off;
+ global_esym.st_size = esym.st_size;
self.global_esyms.items(.shndx)[actual_esym_index] = esym_shndx;
}
}
@@ -1248,7 +1442,7 @@ pub fn deleteDeclExport(
const metadata = self.decls.getPtr(decl_index) orelse return;
const mod = elf_file.base.options.module.?;
const exp_name = mod.intern_pool.stringToSlice(name);
- const esym_index = metadata.@"export"(self, elf_file, exp_name) orelse return;
+ const esym_index = metadata.@"export"(self, exp_name) orelse return;
log.debug("deleting export '{s}'", .{exp_name});
const esym = &self.global_esyms.items(.elf_sym)[esym_index.*];
_ = self.globals_lookup.remove(esym.st_name);
@@ -1265,19 +1459,23 @@ pub fn deleteDeclExport(
pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 {
_ = lib_name;
const gpa = elf_file.base.allocator;
- const off = try elf_file.strtab.insert(gpa, name);
+ const off = try self.strtab.insert(gpa, name);
const lookup_gop = try self.globals_lookup.getOrPut(gpa, off);
if (!lookup_gop.found_existing) {
const esym_index = try self.addGlobalEsym(gpa);
const esym = self.elfSym(esym_index);
esym.st_name = off;
lookup_gop.value_ptr.* = esym_index;
- const gop = try elf_file.getOrPutGlobal(off);
+ const gop = try elf_file.getOrPutGlobal(name);
try self.global_symbols.append(gpa, gop.index);
}
return lookup_gop.value_ptr.*;
}
+pub fn getString(self: ZigObject, off: u32) [:0]const u8 {
+ return self.strtab.getAssumeExists(off);
+}
+
pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
return .{ .data = .{
.self = self,
@@ -1350,9 +1548,9 @@ const DeclMetadata = struct {
/// A list of all exports aliases of this Decl.
exports: std.ArrayListUnmanaged(Symbol.Index) = .{},
- fn @"export"(m: DeclMetadata, zig_object: *ZigObject, elf_file: *Elf, name: []const u8) ?*u32 {
+ fn @"export"(m: DeclMetadata, zig_object: *ZigObject, name: []const u8) ?*u32 {
for (m.exports.items) |*exp| {
- const exp_name = elf_file.strtab.getAssumeExists(zig_object.elfSym(exp.*).st_name);
+ const exp_name = zig_object.getString(zig_object.elfSym(exp.*).st_name);
if (mem.eql(u8, name, exp_name)) return exp;
}
return null;
@@ -1377,6 +1575,7 @@ const std = @import("std");
const Air = @import("../../Air.zig");
const Allocator = std.mem.Allocator;
+const Archive = @import("Archive.zig");
const Atom = @import("Atom.zig");
const Dwarf = @import("../Dwarf.zig");
const Elf = @import("../Elf.zig");
@@ -1386,5 +1585,6 @@ const Liveness = @import("../../Liveness.zig");
const Module = @import("../../Module.zig");
const Object = @import("Object.zig");
const Symbol = @import("Symbol.zig");
+const StringTable = @import("../StringTable.zig");
const TypedValue = @import("../../TypedValue.zig");
const ZigObject = @This();
diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig
index caf6f9a051..8c0fa0f769 100644
--- a/src/link/Elf/eh_frame.zig
+++ b/src/link/Elf/eh_frame.zig
@@ -43,7 +43,7 @@ pub const Fde = struct {
pub fn atom(fde: Fde, elf_file: *Elf) *Atom {
const object = elf_file.file(fde.file_index).?.object;
const rel = fde.relocs(elf_file)[0];
- const sym = object.symtab[rel.r_sym()];
+ const sym = object.symtab.items[rel.r_sym()];
const atom_index = object.atoms.items[sym.st_shndx];
return elf_file.atom(atom_index).?;
}
diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig
index 88620e2e5b..e31170a178 100644
--- a/src/link/Elf/file.zig
+++ b/src/link/Elf/file.zig
@@ -68,9 +68,12 @@ pub const File = union(enum) {
}
pub fn resetGlobals(file: File, elf_file: *Elf) void {
- switch (file) {
- .linker_defined => unreachable,
- inline else => |x| x.resetGlobals(elf_file),
+ for (file.globals()) |global_index| {
+ const global = elf_file.symbol(global_index);
+ const name_offset = global.name_offset;
+ global.* = .{};
+ global.name_offset = name_offset;
+ global.flags.global = true;
}
}
@@ -83,24 +86,37 @@ pub const File = union(enum) {
pub fn markLive(file: File, elf_file: *Elf) void {
switch (file) {
- .linker_defined => unreachable,
+ .linker_defined => {},
inline else => |x| x.markLive(elf_file),
}
}
pub fn atoms(file: File) []const Atom.Index {
return switch (file) {
- .linker_defined => unreachable,
- .shared_object => unreachable,
+ .linker_defined, .shared_object => &[0]Atom.Index{},
.zig_object => |x| x.atoms.items,
.object => |x| x.atoms.items,
};
}
+ pub fn cies(file: File) []const Cie {
+ return switch (file) {
+ .zig_object => &[0]Cie{},
+ .object => |x| x.cies.items,
+ inline else => unreachable,
+ };
+ }
+
+ pub fn symbol(file: File, ind: Symbol.Index) Symbol.Index {
+ return switch (file) {
+ .zig_object => |x| x.symbol(ind),
+ inline else => |x| x.symbols.items[ind],
+ };
+ }
+
pub fn locals(file: File) []const Symbol.Index {
return switch (file) {
- .linker_defined => unreachable,
- .shared_object => unreachable,
+ .linker_defined, .shared_object => &[0]Symbol.Index{},
inline else => |x| x.locals(),
};
}
@@ -111,6 +127,83 @@ pub const File = union(enum) {
};
}
+ pub fn updateSymtabSize(file: File, elf_file: *Elf) void {
+ const output_symtab_size = switch (file) {
+ inline else => |x| &x.output_symtab_size,
+ };
+ for (file.locals()) |local_index| {
+ const local = elf_file.symbol(local_index);
+ if (local.atom(elf_file)) |atom| if (!atom.flags.alive) continue;
+ const esym = local.elfSym(elf_file);
+ switch (esym.st_type()) {
+ elf.STT_SECTION => if (!elf_file.isRelocatable()) continue,
+ elf.STT_NOTYPE => continue,
+ else => {},
+ }
+ local.flags.output_symtab = true;
+ output_symtab_size.nlocals += 1;
+ output_symtab_size.strsize += @as(u32, @intCast(local.name(elf_file).len)) + 1;
+ }
+
+ for (file.globals()) |global_index| {
+ const global = elf_file.symbol(global_index);
+ const file_ptr = global.file(elf_file) orelse continue;
+ if (file_ptr.index() != file.index()) continue;
+ if (global.atom(elf_file)) |atom| if (!atom.flags.alive) continue;
+ global.flags.output_symtab = true;
+ if (global.isLocal(elf_file)) {
+ output_symtab_size.nlocals += 1;
+ } else {
+ output_symtab_size.nglobals += 1;
+ }
+ output_symtab_size.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1;
+ }
+ }
+
+ pub fn writeSymtab(file: File, elf_file: *Elf, ctx: anytype) void {
+ var ilocal = ctx.ilocal;
+ for (file.locals()) |local_index| {
+ const local = elf_file.symbol(local_index);
+ if (!local.flags.output_symtab) continue;
+ const out_sym = &elf_file.symtab.items[ilocal];
+ out_sym.st_name = @intCast(elf_file.strtab.items.len);
+ elf_file.strtab.appendSliceAssumeCapacity(local.name(elf_file));
+ elf_file.strtab.appendAssumeCapacity(0);
+ local.setOutputSym(elf_file, out_sym);
+ ilocal += 1;
+ }
+
+ var iglobal = ctx.iglobal;
+ for (file.globals()) |global_index| {
+ const global = elf_file.symbol(global_index);
+ const file_ptr = global.file(elf_file) orelse continue;
+ if (file_ptr.index() != file.index()) continue;
+ if (!global.flags.output_symtab) continue;
+ const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
+ elf_file.strtab.appendSliceAssumeCapacity(global.name(elf_file));
+ elf_file.strtab.appendAssumeCapacity(0);
+ if (global.isLocal(elf_file)) {
+ const out_sym = &elf_file.symtab.items[ilocal];
+ out_sym.st_name = st_name;
+ global.setOutputSym(elf_file, out_sym);
+ ilocal += 1;
+ } else {
+ const out_sym = &elf_file.symtab.items[iglobal];
+ out_sym.st_name = st_name;
+ global.setOutputSym(elf_file, out_sym);
+ iglobal += 1;
+ }
+ }
+ }
+
+ pub fn updateArSymtab(file: File, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void {
+ return switch (file) {
+ .zig_object => |x| x.updateArSymtab(ar_symtab, elf_file),
+ .object => @panic("TODO"),
+ inline else => unreachable,
+ };
+ }
+
pub const Index = u32;
pub const Entry = union(enum) {
@@ -126,7 +219,9 @@ const std = @import("std");
const elf = std.elf;
const Allocator = std.mem.Allocator;
+const Archive = @import("Archive.zig");
const Atom = @import("Atom.zig");
+const Cie = @import("eh_frame.zig").Cie;
const Elf = @import("../Elf.zig");
const LinkerDefined = @import("LinkerDefined.zig");
const Object = @import("Object.zig");
diff --git a/src/link/Elf/gc.zig b/src/link/Elf/gc.zig
index 26489d3d1a..ade2b75782 100644
--- a/src/link/Elf/gc.zig
+++ b/src/link/Elf/gc.zig
@@ -1,19 +1,27 @@
pub fn gcAtoms(elf_file: *Elf) !void {
- var roots = std.ArrayList(*Atom).init(elf_file.base.allocator);
+ const gpa = elf_file.base.allocator;
+ const num_files = elf_file.objects.items.len + @intFromBool(elf_file.zig_object_index != null);
+ var files = try std.ArrayList(File.Index).initCapacity(gpa, num_files);
+ defer files.deinit();
+ if (elf_file.zig_object_index) |index| files.appendAssumeCapacity(index);
+ for (elf_file.objects.items) |index| files.appendAssumeCapacity(index);
+
+ var roots = std.ArrayList(*Atom).init(gpa);
defer roots.deinit();
- try collectRoots(&roots, elf_file);
+ try collectRoots(&roots, files.items, elf_file);
+
mark(roots, elf_file);
- prune(elf_file);
+ prune(files.items, elf_file);
}
-fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void {
+fn collectRoots(roots: *std.ArrayList(*Atom), files: []const File.Index, elf_file: *Elf) !void {
if (elf_file.entry_index) |index| {
const global = elf_file.symbol(index);
try markSymbol(global, roots, elf_file);
}
- for (elf_file.objects.items) |index| {
- for (elf_file.file(index).?.object.globals()) |global_index| {
+ for (files) |index| {
+ for (elf_file.file(index).?.globals()) |global_index| {
const global = elf_file.symbol(global_index);
if (global.file(elf_file)) |file| {
if (file.index() == index and global.flags.@"export")
@@ -22,10 +30,10 @@ fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void {
}
}
- for (elf_file.objects.items) |index| {
- const object = elf_file.file(index).?.object;
+ for (files) |index| {
+ const file = elf_file.file(index).?;
- for (object.atoms.items) |atom_index| {
+ for (file.atoms()) |atom_index| {
const atom = elf_file.atom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
@@ -49,9 +57,9 @@ fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void {
}
// Mark every atom referenced by CIE as alive.
- for (object.cies.items) |cie| {
+ for (file.cies()) |cie| {
for (cie.relocs(elf_file)) |rel| {
- const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
+ const sym = elf_file.symbol(file.symbol(rel.r_sym()));
try markSymbol(sym, roots, elf_file);
}
}
@@ -73,11 +81,11 @@ fn markLive(atom: *Atom, elf_file: *Elf) void {
if (@import("build_options").enable_logging) track_live_level.incr();
assert(atom.flags.visited);
- const object = atom.file(elf_file).?.object;
+ const file = atom.file(elf_file).?;
for (atom.fdes(elf_file)) |fde| {
for (fde.relocs(elf_file)[1..]) |rel| {
- const target_sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
+ const target_sym = elf_file.symbol(file.symbol(rel.r_sym()));
const target_atom = target_sym.atom(elf_file) orelse continue;
target_atom.flags.alive = true;
gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index });
@@ -86,7 +94,7 @@ fn markLive(atom: *Atom, elf_file: *Elf) void {
}
for (atom.relocs(elf_file)) |rel| {
- const target_sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
+ const target_sym = elf_file.symbol(file.symbol(rel.r_sym()));
const target_atom = target_sym.atom(elf_file) orelse continue;
target_atom.flags.alive = true;
gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index });
@@ -101,9 +109,9 @@ fn mark(roots: std.ArrayList(*Atom), elf_file: *Elf) void {
}
}
-fn prune(elf_file: *Elf) void {
- for (elf_file.objects.items) |index| {
- for (elf_file.file(index).?.object.atoms.items) |atom_index| {
+fn prune(files: []const File.Index, elf_file: *Elf) void {
+ for (files) |index| {
+ for (elf_file.file(index).?.atoms()) |atom_index| {
const atom = elf_file.atom(atom_index) orelse continue;
if (atom.flags.alive and !atom.flags.visited) {
atom.flags.alive = false;
@@ -158,4 +166,5 @@ const mem = std.mem;
const Allocator = mem.Allocator;
const Atom = @import("Atom.zig");
const Elf = @import("../Elf.zig");
+const File = @import("file.zig").File;
const Symbol = @import("Symbol.zig");
diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig
index cfc2064fbe..2602940d41 100644
--- a/src/link/Elf/synthetic_sections.zig
+++ b/src/link/Elf/synthetic_sections.zig
@@ -9,7 +9,7 @@ pub const DynamicSection = struct {
pub fn addNeeded(dt: *DynamicSection, shared: *SharedObject, elf_file: *Elf) !void {
const gpa = elf_file.base.allocator;
- const off = try elf_file.dynstrtab.insert(gpa, shared.soname());
+ const off = try elf_file.insertDynString(shared.soname());
try dt.needed.append(gpa, off);
}
@@ -22,11 +22,11 @@ pub const DynamicSection = struct {
if (i > 0) try rpath.append(':');
try rpath.appendSlice(path);
}
- dt.rpath = try elf_file.dynstrtab.insert(gpa, rpath.items);
+ dt.rpath = try elf_file.insertDynString(rpath.items);
}
pub fn setSoname(dt: *DynamicSection, soname: []const u8, elf_file: *Elf) !void {
- dt.soname = try elf_file.dynstrtab.insert(elf_file.base.allocator, soname);
+ dt.soname = try elf_file.insertDynString(soname);
}
fn getFlags(dt: DynamicSection, elf_file: *Elf) ?u64 {
@@ -359,31 +359,24 @@ pub const ZigGotSection = struct {
}
pub fn updateSymtabSize(zig_got: *ZigGotSection, elf_file: *Elf) void {
- _ = elf_file;
zig_got.output_symtab_size.nlocals = @as(u32, @intCast(zig_got.entries.items.len));
- }
-
- pub fn updateStrtab(zig_got: ZigGotSection, elf_file: *Elf) !void {
- const gpa = elf_file.base.allocator;
for (zig_got.entries.items) |entry| {
- const symbol_name = elf_file.symbol(entry).name(elf_file);
- const name = try std.fmt.allocPrint(gpa, "{s}$ziggot", .{symbol_name});
- defer gpa.free(name);
- _ = try elf_file.strtab.insert(gpa, name);
+ const name = elf_file.symbol(entry).name(elf_file);
+ zig_got.output_symtab_size.strsize += @as(u32, @intCast(name.len + "$ziggot".len)) + 1;
}
}
- pub fn writeSymtab(zig_got: ZigGotSection, elf_file: *Elf, ctx: anytype) !void {
- const gpa = elf_file.base.allocator;
+ pub fn writeSymtab(zig_got: ZigGotSection, elf_file: *Elf, ctx: anytype) void {
for (zig_got.entries.items, ctx.ilocal.., 0..) |entry, ilocal, index| {
const symbol = elf_file.symbol(entry);
const symbol_name = symbol.name(elf_file);
- const name = try std.fmt.allocPrint(gpa, "{s}$ziggot", .{symbol_name});
- defer gpa.free(name);
- const st_name = try elf_file.strtab.insert(gpa, name);
+ const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
+ elf_file.strtab.appendSliceAssumeCapacity(symbol_name);
+ elf_file.strtab.appendSliceAssumeCapacity("$ziggot");
+ elf_file.strtab.appendAssumeCapacity(0);
const st_value = zig_got.entryAddress(@intCast(index), elf_file);
const st_size = elf_file.archPtrWidthBytes();
- ctx.symtab[ilocal] = .{
+ elf_file.symtab.items[ilocal] = .{
.st_name = st_name,
.st_info = elf.STT_OBJECT,
.st_other = 0,
@@ -767,25 +760,17 @@ pub const GotSection = struct {
}
pub fn updateSymtabSize(got: *GotSection, elf_file: *Elf) void {
- _ = elf_file;
got.output_symtab_size.nlocals = @as(u32, @intCast(got.entries.items.len));
- }
-
- pub fn updateStrtab(got: GotSection, elf_file: *Elf) !void {
- const gpa = elf_file.base.allocator;
for (got.entries.items) |entry| {
const symbol_name = switch (entry.tag) {
.tlsld => "",
inline else => elf_file.symbol(entry.symbol_index).name(elf_file),
};
- const name = try std.fmt.allocPrint(gpa, "{s}${s}", .{ symbol_name, @tagName(entry.tag) });
- defer gpa.free(name);
- _ = try elf_file.strtab.insert(gpa, name);
+ got.output_symtab_size.strsize += @as(u32, @intCast(symbol_name.len + @tagName(entry.tag).len)) + 1 + 1;
}
}
- pub fn writeSymtab(got: GotSection, elf_file: *Elf, ctx: anytype) !void {
- const gpa = elf_file.base.allocator;
+ pub fn writeSymtab(got: GotSection, elf_file: *Elf, ctx: anytype) void {
for (got.entries.items, ctx.ilocal..) |entry, ilocal| {
const symbol = switch (entry.tag) {
.tlsld => null,
@@ -795,12 +780,14 @@ pub const GotSection = struct {
.tlsld => "",
inline else => symbol.?.name(elf_file),
};
- const name = try std.fmt.allocPrint(gpa, "{s}${s}", .{ symbol_name, @tagName(entry.tag) });
- defer gpa.free(name);
- const st_name = try elf_file.strtab.insert(gpa, name);
+ const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
+ elf_file.strtab.appendSliceAssumeCapacity(symbol_name);
+ elf_file.strtab.appendAssumeCapacity('$');
+ elf_file.strtab.appendSliceAssumeCapacity(@tagName(entry.tag));
+ elf_file.strtab.appendAssumeCapacity(0);
const st_value = entry.address(elf_file);
const st_size: u64 = entry.len() * elf_file.archPtrWidthBytes();
- ctx.symtab[ilocal] = .{
+ elf_file.symtab.items[ilocal] = .{
.st_name = st_name,
.st_info = elf.STT_OBJECT,
.st_other = 0,
@@ -922,30 +909,22 @@ pub const PltSection = struct {
}
pub fn updateSymtabSize(plt: *PltSection, elf_file: *Elf) void {
- _ = elf_file;
plt.output_symtab_size.nlocals = @as(u32, @intCast(plt.symbols.items.len));
- }
-
- pub fn updateStrtab(plt: PltSection, elf_file: *Elf) !void {
- const gpa = elf_file.base.allocator;
for (plt.symbols.items) |sym_index| {
- const sym = elf_file.symbol(sym_index);
- const name = try std.fmt.allocPrint(gpa, "{s}$plt", .{sym.name(elf_file)});
- defer gpa.free(name);
- _ = try elf_file.strtab.insert(gpa, name);
+ const name = elf_file.symbol(sym_index).name(elf_file);
+ plt.output_symtab_size.strsize += @as(u32, @intCast(name.len + "$plt".len)) + 1;
}
}
- pub fn writeSymtab(plt: PltSection, elf_file: *Elf, ctx: anytype) !void {
- const gpa = elf_file.base.allocator;
-
+ pub fn writeSymtab(plt: PltSection, elf_file: *Elf, ctx: anytype) void {
var ilocal = ctx.ilocal;
for (plt.symbols.items) |sym_index| {
const sym = elf_file.symbol(sym_index);
- const name = try std.fmt.allocPrint(gpa, "{s}$plt", .{sym.name(elf_file)});
- defer gpa.free(name);
- const st_name = try elf_file.strtab.insert(gpa, name);
- ctx.symtab[ilocal] = .{
+ const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
+ elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file));
+ elf_file.strtab.appendSliceAssumeCapacity("$plt");
+ elf_file.strtab.appendAssumeCapacity(0);
+ elf_file.symtab.items[ilocal] = .{
.st_name = st_name,
.st_info = elf.STT_FUNC,
.st_other = 0,
@@ -1029,29 +1008,22 @@ pub const PltGotSection = struct {
}
pub fn updateSymtabSize(plt_got: *PltGotSection, elf_file: *Elf) void {
- _ = elf_file;
plt_got.output_symtab_size.nlocals = @as(u32, @intCast(plt_got.symbols.items.len));
- }
-
- pub fn updateStrtab(plt_got: PltGotSection, elf_file: *Elf) !void {
- const gpa = elf_file.base.allocator;
for (plt_got.symbols.items) |sym_index| {
- const sym = elf_file.symbol(sym_index);
- const name = try std.fmt.allocPrint(gpa, "{s}$pltgot", .{sym.name(elf_file)});
- defer gpa.free(name);
- _ = try elf_file.strtab.insert(gpa, name);
+ const name = elf_file.symbol(sym_index).name(elf_file);
+ plt_got.output_symtab_size.strsize += @as(u32, @intCast(name.len + "$pltgot".len)) + 1;
}
}
- pub fn writeSymtab(plt_got: PltGotSection, elf_file: *Elf, ctx: anytype) !void {
- const gpa = elf_file.base.allocator;
+ pub fn writeSymtab(plt_got: PltGotSection, elf_file: *Elf, ctx: anytype) void {
var ilocal = ctx.ilocal;
for (plt_got.symbols.items) |sym_index| {
const sym = elf_file.symbol(sym_index);
- const name = try std.fmt.allocPrint(gpa, "{s}$pltgot", .{sym.name(elf_file)});
- defer gpa.free(name);
- const st_name = try elf_file.strtab.insert(gpa, name);
- ctx.symtab[ilocal] = .{
+ const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
+ elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file));
+ elf_file.strtab.appendSliceAssumeCapacity("$pltgot");
+ elf_file.strtab.appendAssumeCapacity(0);
+ elf_file.symtab.items[ilocal] = .{
.st_name = st_name,
.st_info = elf.STT_FUNC,
.st_other = 0,
@@ -1166,7 +1138,7 @@ pub const DynsymSection = struct {
new_extra.dynamic = index;
sym.setExtra(new_extra, elf_file);
} else try sym.addExtra(.{ .dynamic = index }, elf_file);
- const off = try elf_file.dynstrtab.insert(gpa, sym.name(elf_file));
+ const off = try elf_file.insertDynString(sym.name(elf_file));
try dynsym.entries.append(gpa, .{ .symbol_index = sym_index, .off = off });
}
@@ -1251,7 +1223,7 @@ pub const HashSection = struct {
@memset(chains, 0);
for (elf_file.dynsym.entries.items, 1..) |entry, i| {
- const name = elf_file.dynstrtab.getAssumeExists(entry.off);
+ const name = elf_file.getDynString(entry.off);
const hash = hasher(name) % buckets.len;
chains[@as(u32, @intCast(i))] = buckets[hash];
buckets[hash] = @as(u32, @intCast(i));
@@ -1490,7 +1462,7 @@ pub const VerneedSection = struct {
sym.* = .{
.vn_version = 1,
.vn_cnt = 0,
- .vn_file = try elf_file.dynstrtab.insert(gpa, soname),
+ .vn_file = try elf_file.insertDynString(soname),
.vn_aux = 0,
.vn_next = 0,
};
@@ -1509,7 +1481,7 @@ pub const VerneedSection = struct {
.vna_hash = HashSection.hasher(version),
.vna_flags = 0,
.vna_other = vern.index,
- .vna_name = try elf_file.dynstrtab.insert(gpa, version),
+ .vna_name = try elf_file.insertDynString(version),
.vna_next = 0,
};
verneed_sym.vn_cnt += 1;
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index 5a246bcfd1..10c0702217 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -58,7 +58,7 @@ globals_free_list: std.ArrayListUnmanaged(u32) = .{},
dyld_stub_binder_index: ?u32 = null,
dyld_private_atom_index: ?Atom.Index = null,
-strtab: StringTable(.strtab) = .{},
+strtab: StringTable = .{},
got_table: TableSection(SymbolWithLoc) = .{},
stub_table: TableSection(SymbolWithLoc) = .{},
@@ -5643,7 +5643,7 @@ const Module = @import("../Module.zig");
const InternPool = @import("../InternPool.zig");
const Platform = load_commands.Platform;
const Relocation = @import("MachO/Relocation.zig");
-const StringTable = @import("strtab.zig").StringTable;
+const StringTable = @import("StringTable.zig");
const TableSection = @import("table_section.zig").TableSection;
const Trie = @import("MachO/Trie.zig");
const Type = @import("../type.zig").Type;
diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig
index d20f32c14c..f204093290 100644
--- a/src/link/MachO/DebugSymbols.zig
+++ b/src/link/MachO/DebugSymbols.zig
@@ -22,7 +22,7 @@ debug_aranges_section_dirty: bool = false,
debug_info_header_dirty: bool = false,
debug_line_header_dirty: bool = false,
-strtab: StringTable(.strtab) = .{},
+strtab: StringTable = .{},
relocs: std.ArrayListUnmanaged(Reloc) = .{},
pub const Reloc = struct {
@@ -567,5 +567,5 @@ const Allocator = mem.Allocator;
const Dwarf = @import("../Dwarf.zig");
const MachO = @import("../MachO.zig");
const Module = @import("../../Module.zig");
-const StringTable = @import("../strtab.zig").StringTable;
+const StringTable = @import("../StringTable.zig");
const Type = @import("../../type.zig").Type;
diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig
index b94da1d284..79433b3b1b 100644
--- a/src/link/MachO/zld.zig
+++ b/src/link/MachO/zld.zig
@@ -1227,7 +1227,6 @@ const LibStub = @import("../tapi.zig").LibStub;
const Object = @import("Object.zig");
const Platform = load_commands.Platform;
const Section = MachO.Section;
-const StringTable = @import("../strtab.zig").StringTable;
const SymbolWithLoc = MachO.SymbolWithLoc;
const TableSection = @import("../table_section.zig").TableSection;
const Trie = @import("Trie.zig");
diff --git a/src/link/StringTable.zig b/src/link/StringTable.zig
new file mode 100644
index 0000000000..2375bf4449
--- /dev/null
+++ b/src/link/StringTable.zig
@@ -0,0 +1,49 @@
+buffer: std.ArrayListUnmanaged(u8) = .{},
+table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
+
+pub fn deinit(self: *Self, gpa: Allocator) void {
+ self.buffer.deinit(gpa);
+ self.table.deinit(gpa);
+}
+
+pub fn insert(self: *Self, gpa: Allocator, string: []const u8) !u32 {
+ const gop = try self.table.getOrPutContextAdapted(gpa, @as([]const u8, string), StringIndexAdapter{
+ .bytes = &self.buffer,
+ }, StringIndexContext{
+ .bytes = &self.buffer,
+ });
+ if (gop.found_existing) return gop.key_ptr.*;
+
+ try self.buffer.ensureUnusedCapacity(gpa, string.len + 1);
+ const new_off = @as(u32, @intCast(self.buffer.items.len));
+
+ self.buffer.appendSliceAssumeCapacity(string);
+ self.buffer.appendAssumeCapacity(0);
+
+ gop.key_ptr.* = new_off;
+
+ return new_off;
+}
+
+pub fn getOffset(self: *Self, string: []const u8) ?u32 {
+ return self.table.getKeyAdapted(string, StringIndexAdapter{
+ .bytes = &self.buffer,
+ });
+}
+
+pub fn get(self: Self, off: u32) ?[:0]const u8 {
+ if (off >= self.buffer.items.len) return null;
+ return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.buffer.items.ptr + off)), 0);
+}
+
+pub fn getAssumeExists(self: Self, off: u32) [:0]const u8 {
+ return self.get(off) orelse unreachable;
+}
+
+const std = @import("std");
+const mem = std.mem;
+
+const Allocator = mem.Allocator;
+const Self = @This();
+const StringIndexAdapter = std.hash_map.StringIndexAdapter;
+const StringIndexContext = std.hash_map.StringIndexContext;
diff --git a/src/link/strtab.zig b/src/link/strtab.zig
deleted file mode 100644
index f854225ef6..0000000000
--- a/src/link/strtab.zig
+++ /dev/null
@@ -1,121 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-
-const Allocator = mem.Allocator;
-const StringIndexAdapter = std.hash_map.StringIndexAdapter;
-const StringIndexContext = std.hash_map.StringIndexContext;
-
-pub fn StringTable(comptime log_scope: @Type(.EnumLiteral)) type {
- return struct {
- const Self = @This();
-
- const log = std.log.scoped(log_scope);
-
- buffer: std.ArrayListUnmanaged(u8) = .{},
- table: std.HashMapUnmanaged(u32, bool, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
-
- pub fn deinit(self: *Self, gpa: Allocator) void {
- self.buffer.deinit(gpa);
- self.table.deinit(gpa);
- }
-
- pub fn toOwnedSlice(self: *Self, gpa: Allocator) []const u8 {
- const result = self.buffer.toOwnedSlice(gpa);
- self.table.clearRetainingCapacity();
- return result;
- }
-
- pub const PrunedResult = struct {
- buffer: []const u8,
- idx_map: std.AutoHashMap(u32, u32),
- };
-
- pub fn toPrunedResult(self: *Self, gpa: Allocator) !PrunedResult {
- var buffer = std.ArrayList(u8).init(gpa);
- defer buffer.deinit();
- try buffer.ensureTotalCapacity(self.buffer.items.len);
- buffer.appendAssumeCapacity(0);
-
- var idx_map = std.AutoHashMap(u32, u32).init(gpa);
- errdefer idx_map.deinit();
- try idx_map.ensureTotalCapacity(self.table.count());
-
- var it = self.table.iterator();
- while (it.next()) |entry| {
- const off = entry.key_ptr.*;
- const save = entry.value_ptr.*;
- if (!save) continue;
- const new_off = @as(u32, @intCast(buffer.items.len));
- buffer.appendSliceAssumeCapacity(self.getAssumeExists(off));
- idx_map.putAssumeCapacityNoClobber(off, new_off);
- }
-
- self.buffer.clearRetainingCapacity();
- self.table.clearRetainingCapacity();
-
- return PrunedResult{
- .buffer = buffer.toOwnedSlice(),
- .idx_map = idx_map,
- };
- }
-
- pub fn insert(self: *Self, gpa: Allocator, string: []const u8) !u32 {
- const gop = try self.table.getOrPutContextAdapted(gpa, @as([]const u8, string), StringIndexAdapter{
- .bytes = &self.buffer,
- }, StringIndexContext{
- .bytes = &self.buffer,
- });
- if (gop.found_existing) {
- const off = gop.key_ptr.*;
- gop.value_ptr.* = true;
- log.debug("reusing string '{s}' at offset 0x{x}", .{ string, off });
- return off;
- }
-
- try self.buffer.ensureUnusedCapacity(gpa, string.len + 1);
- const new_off = @as(u32, @intCast(self.buffer.items.len));
-
- log.debug("writing new string '{s}' at offset 0x{x}", .{ string, new_off });
-
- self.buffer.appendSliceAssumeCapacity(string);
- self.buffer.appendAssumeCapacity(0);
-
- gop.key_ptr.* = new_off;
- gop.value_ptr.* = true;
-
- return new_off;
- }
-
- pub fn delete(self: *Self, string: []const u8) void {
- const value_ptr = self.table.getPtrAdapted(@as([]const u8, string), StringIndexAdapter{
- .bytes = &self.buffer,
- }) orelse return;
- value_ptr.* = false;
- log.debug("marked '{s}' for deletion", .{string});
- }
-
- pub fn getOffset(self: *Self, string: []const u8) ?u32 {
- return self.table.getKeyAdapted(string, StringIndexAdapter{
- .bytes = &self.buffer,
- });
- }
-
- pub fn get(self: Self, off: u32) ?[:0]const u8 {
- log.debug("getting string at 0x{x}", .{off});
- if (off >= self.buffer.items.len) return null;
- return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.buffer.items.ptr + off)), 0);
- }
-
- pub fn getAssumeExists(self: Self, off: u32) [:0]const u8 {
- return self.get(off) orelse unreachable;
- }
-
- pub fn items(self: Self) []const u8 {
- return self.buffer.items;
- }
-
- pub fn len(self: Self) usize {
- return self.buffer.items.len;
- }
- };
-}
diff --git a/test/link/elf.zig b/test/link/elf.zig
index dad3604c31..75cc34ec32 100644
--- a/test/link/elf.zig
+++ b/test/link/elf.zig
@@ -6,9 +6,13 @@ pub fn build(b: *Build) void {
const elf_step = b.step("test-elf", "Run ELF tests");
b.default_step = elf_step;
- const musl_target = CrossTarget{
+ const default_target = CrossTarget{
.cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs
.os_tag = .linux,
+ };
+ const musl_target = CrossTarget{
+ .cpu_arch = .x86_64,
+ .os_tag = .linux,
.abi = .musl,
};
const glibc_target = CrossTarget{
@@ -18,7 +22,10 @@ pub fn build(b: *Build) void {
};
// Exercise linker with self-hosted backend (no LLVM)
- elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false }));
+ elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target }));
+ elf_step.dependOn(testLinkingObj(b, .{ .use_llvm = false, .target = default_target }));
+ elf_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = default_target }));
+ elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false, .target = default_target }));
elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = glibc_target }));
elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = musl_target }));
@@ -876,6 +883,110 @@ fn testGcSections(b: *Build, opts: Options) *Step {
return test_step;
}
+fn testGcSectionsZig(b: *Build, opts: Options) *Step {
+ const test_step = addTestStep(b, "gc-sections-zig", opts);
+
+ const obj = addObject(b, "obj", .{
+ .target = opts.target,
+ .use_llvm = true,
+ .use_lld = true,
+ });
+ addCSourceBytes(obj,
+ \\int live_var1 = 1;
+ \\int live_var2 = 2;
+ \\int dead_var1 = 3;
+ \\int dead_var2 = 4;
+ \\void live_fn1() {}
+ \\void live_fn2() { live_fn1(); }
+ \\void dead_fn1() {}
+ \\void dead_fn2() { dead_fn1(); }
+ , &.{});
+ obj.link_function_sections = true;
+ obj.link_data_sections = true;
+
+ {
+ const exe = addExecutable(b, "test1", opts);
+ addZigSourceBytes(exe,
+ \\const std = @import("std");
+ \\extern var live_var1: i32;
+ \\extern var live_var2: i32;
+ \\extern fn live_fn2() void;
+ \\pub fn main() void {
+ \\ const stdout = std.io.getStdOut();
+ \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable;
+ \\ live_fn2();
+ \\}
+ );
+ exe.addObject(obj);
+ exe.link_gc_sections = false;
+
+ const run = addRunArtifact(exe);
+ run.expectStdOutEqual("1 2\n");
+ test_step.dependOn(&run.step);
+
+ const check = exe.checkObject();
+ check.checkInSymtab();
+ check.checkContains("live_var1");
+ check.checkInSymtab();
+ check.checkContains("live_var2");
+ check.checkInSymtab();
+ check.checkContains("dead_var1");
+ check.checkInSymtab();
+ check.checkContains("dead_var2");
+ check.checkInSymtab();
+ check.checkContains("live_fn1");
+ check.checkInSymtab();
+ check.checkContains("live_fn2");
+ check.checkInSymtab();
+ check.checkContains("dead_fn1");
+ check.checkInSymtab();
+ check.checkContains("dead_fn2");
+ test_step.dependOn(&check.step);
+ }
+
+ {
+ const exe = addExecutable(b, "test2", opts);
+ addZigSourceBytes(exe,
+ \\const std = @import("std");
+ \\extern var live_var1: i32;
+ \\extern var live_var2: i32;
+ \\extern fn live_fn2() void;
+ \\pub fn main() void {
+ \\ const stdout = std.io.getStdOut();
+ \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable;
+ \\ live_fn2();
+ \\}
+ );
+ exe.addObject(obj);
+ exe.link_gc_sections = true;
+
+ const run = addRunArtifact(exe);
+ run.expectStdOutEqual("1 2\n");
+ test_step.dependOn(&run.step);
+
+ const check = exe.checkObject();
+ check.checkInSymtab();
+ check.checkContains("live_var1");
+ check.checkInSymtab();
+ check.checkContains("live_var2");
+ check.checkInSymtab();
+ check.checkNotPresent("dead_var1");
+ check.checkInSymtab();
+ check.checkNotPresent("dead_var2");
+ check.checkInSymtab();
+ check.checkContains("live_fn1");
+ check.checkInSymtab();
+ check.checkContains("live_fn2");
+ check.checkInSymtab();
+ check.checkNotPresent("dead_fn1");
+ check.checkInSymtab();
+ check.checkNotPresent("dead_fn2");
+ test_step.dependOn(&check.step);
+ }
+
+ return test_step;
+}
+
fn testHiddenWeakUndef(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "hidden-weak-undef", opts);
@@ -1714,6 +1825,72 @@ fn testLinkingCpp(b: *Build, opts: Options) *Step {
return test_step;
}
+fn testLinkingObj(b: *Build, opts: Options) *Step {
+ const test_step = addTestStep(b, "linking-obj", opts);
+
+ const obj = addObject(b, "aobj", opts);
+ addZigSourceBytes(obj,
+ \\extern var mod: usize;
+ \\export fn callMe() usize {
+ \\ return me * mod;
+ \\}
+ \\var me: usize = 42;
+ );
+
+ const exe = addExecutable(b, "testobj", opts);
+ addZigSourceBytes(exe,
+ \\const std = @import("std");
+ \\extern fn callMe() usize;
+ \\export var mod: usize = 2;
+ \\pub fn main() void {
+ \\ std.debug.print("{d}\n", .{callMe()});
+ \\}
+ );
+ exe.addObject(obj);
+
+ const run = addRunArtifact(exe);
+ run.expectStdErrEqual("84\n");
+ test_step.dependOn(&run.step);
+
+ return test_step;
+}
+
+fn testLinkingStaticLib(b: *Build, opts: Options) *Step {
+ const test_step = addTestStep(b, "linking-static-lib", opts);
+
+ const lib = b.addStaticLibrary(.{
+ .name = "alib",
+ .target = opts.target,
+ .optimize = opts.optimize,
+ .use_llvm = opts.use_llvm,
+ .use_lld = false,
+ });
+ addZigSourceBytes(lib,
+ \\extern var mod: usize;
+ \\export fn callMe() usize {
+ \\ return me * mod;
+ \\}
+ \\var me: usize = 42;
+ );
+
+ const exe = addExecutable(b, "testlib", opts);
+ addZigSourceBytes(exe,
+ \\const std = @import("std");
+ \\extern fn callMe() usize;
+ \\export var mod: usize = 2;
+ \\pub fn main() void {
+ \\ std.debug.print("{d}\n", .{callMe()});
+ \\}
+ );
+ exe.linkLibrary(lib);
+
+ const run = addRunArtifact(exe);
+ run.expectStdErrEqual("84\n");
+ test_step.dependOn(&run.step);
+
+ return test_step;
+}
+
fn testLinkingZig(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "linking-zig-static", opts);
@@ -3114,6 +3291,7 @@ const Options = struct {
target: CrossTarget = .{ .cpu_arch = .x86_64, .os_tag = .linux },
optimize: std.builtin.OptimizeMode = .Debug,
use_llvm: bool = true,
+ use_lld: bool = false,
};
fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {
@@ -3134,7 +3312,7 @@ fn addExecutable(b: *Build, name: []const u8, opts: Options) *Compile {
.target = opts.target,
.optimize = opts.optimize,
.use_llvm = opts.use_llvm,
- .use_lld = false,
+ .use_lld = opts.use_lld,
});
}
@@ -3144,7 +3322,7 @@ fn addObject(b: *Build, name: []const u8, opts: Options) *Compile {
.target = opts.target,
.optimize = opts.optimize,
.use_llvm = opts.use_llvm,
- .use_lld = false,
+ .use_lld = opts.use_lld,
});
}
@@ -3164,7 +3342,7 @@ fn addSharedLibrary(b: *Build, name: []const u8, opts: Options) *Compile {
.target = opts.target,
.optimize = opts.optimize,
.use_llvm = opts.use_llvm,
- .use_lld = false,
+ .use_lld = opts.use_lld,
});
}