mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-05-22 00:49:35 +03:00
Merge pull request #17791 from ziglang/elf-object
elf: rename ZigModule to ZigObject and move all codegen hooks into it
This commit is contained in:
+1
-1
@@ -594,7 +594,7 @@ set(ZIG_STAGE2_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/src/link/Elf/Object.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/Elf/SharedObject.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/Elf/Symbol.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/Elf/ZigModule.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/Elf/ZigObject.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/Elf/eh_frame.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/Elf/file.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/Elf/gc.zig"
|
||||
|
||||
@@ -4316,7 +4316,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
|
||||
if (try self.air.value(callee, mod)) |func_value| {
|
||||
if (func_value.getFunction(mod)) |func| {
|
||||
if (self.bin_file.cast(link.File.Elf)) |elf_file| {
|
||||
const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);
|
||||
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);
|
||||
const got_addr = @as(u32, @intCast(sym.zigGotAddress(elf_file)));
|
||||
|
||||
@@ -4302,7 +4302,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
|
||||
if (try self.air.value(callee, mod)) |func_value| {
|
||||
if (func_value.getFunction(mod)) |func| {
|
||||
if (self.bin_file.cast(link.File.Elf)) |elf_file| {
|
||||
const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);
|
||||
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);
|
||||
const got_addr = @as(u32, @intCast(sym.zigGotAddress(elf_file)));
|
||||
|
||||
@@ -1752,7 +1752,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
|
||||
if (try self.air.value(callee, mod)) |func_value| {
|
||||
switch (mod.intern_pool.indexToKey(func_value.ip_index)) {
|
||||
.func => |func| {
|
||||
const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);
|
||||
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);
|
||||
const got_addr = @as(u32, @intCast(sym.zigGotAddress(elf_file)));
|
||||
|
||||
@@ -1347,7 +1347,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
|
||||
switch (mod.intern_pool.indexToKey(func_value.ip_index)) {
|
||||
.func => |func| {
|
||||
const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
|
||||
const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);
|
||||
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);
|
||||
break :blk @as(u32, @intCast(sym.zigGotAddress(elf_file)));
|
||||
|
||||
@@ -134,7 +134,7 @@ const Owner = union(enum) {
|
||||
const mod = ctx.bin_file.options.module.?;
|
||||
const decl_index = mod.funcOwnerDeclIndex(func_index);
|
||||
if (ctx.bin_file.cast(link.File.Elf)) |elf_file| {
|
||||
return elf_file.getOrCreateMetadataForDecl(decl_index);
|
||||
return elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index);
|
||||
} else if (ctx.bin_file.cast(link.File.MachO)) |macho_file| {
|
||||
const atom = try macho_file.getOrCreateAtomForDecl(decl_index);
|
||||
return macho_file.getAtom(atom).getSymbolIndex().?;
|
||||
@@ -147,7 +147,7 @@ const Owner = union(enum) {
|
||||
},
|
||||
.lazy_sym => |lazy_sym| {
|
||||
if (ctx.bin_file.cast(link.File.Elf)) |elf_file| {
|
||||
return elf_file.getOrCreateMetadataForLazySymbol(lazy_sym) catch |err|
|
||||
return elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, lazy_sym) catch |err|
|
||||
ctx.fail("{s} creating lazy symbol", .{@errorName(err)});
|
||||
} else if (ctx.bin_file.cast(link.File.MachO)) |macho_file| {
|
||||
const atom = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err|
|
||||
@@ -10233,7 +10233,7 @@ fn genCall(self: *Self, info: union(enum) {
|
||||
.func => |func| {
|
||||
try mod.markDeclAlive(mod.declPtr(func.owner_decl));
|
||||
if (self.bin_file.cast(link.File.Elf)) |elf_file| {
|
||||
const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);
|
||||
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);
|
||||
if (self.bin_file.options.pic) {
|
||||
@@ -13100,7 +13100,7 @@ fn genLazySymbolRef(
|
||||
lazy_sym: link.File.LazySymbol,
|
||||
) InnerError!void {
|
||||
if (self.bin_file.cast(link.File.Elf)) |elf_file| {
|
||||
const sym_index = elf_file.getOrCreateMetadataForLazySymbol(lazy_sym) catch |err|
|
||||
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);
|
||||
|
||||
@@ -86,7 +86,7 @@ pub fn emitMir(emit: *Emit) Error!void {
|
||||
}),
|
||||
.linker_reloc => |data| if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| {
|
||||
const atom = elf_file.symbol(data.atom_index).atom(elf_file).?;
|
||||
const sym = elf_file.symbol(elf_file.zigModulePtr().symbol(data.sym_index));
|
||||
const sym = elf_file.symbol(elf_file.zigObjectPtr().?.symbol(data.sym_index));
|
||||
if (emit.lower.bin_file.options.pic) {
|
||||
const r_type: u32 = if (sym.flags.has_zig_got)
|
||||
link.File.Elf.R_X86_64_ZIG_GOTPCREL
|
||||
|
||||
+2
-2
@@ -904,10 +904,10 @@ fn genDeclRef(
|
||||
else
|
||||
null;
|
||||
const sym_index = try elf_file.getGlobalSymbol(name, lib_name);
|
||||
elf_file.symbol(elf_file.zigModulePtr().symbol(sym_index)).flags.needs_got = true;
|
||||
elf_file.symbol(elf_file.zigObjectPtr().?.symbol(sym_index)).flags.needs_got = true;
|
||||
return GenResult.mcv(.{ .load_symbol = sym_index });
|
||||
}
|
||||
const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index);
|
||||
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);
|
||||
return GenResult.mcv(.{ .load_symbol = sym.esym_index });
|
||||
|
||||
+121
-987
@@ -1,7 +1,5 @@
|
||||
base: link.File,
|
||||
|
||||
dwarf: ?Dwarf = null,
|
||||
|
||||
ptr_width: PtrWidth,
|
||||
|
||||
/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
|
||||
@@ -11,7 +9,7 @@ llvm_object: ?*LlvmObject = null,
|
||||
/// Index of each input file also encodes the priority or precedence of one input file
|
||||
/// over another.
|
||||
files: std.MultiArrayList(File.Entry) = .{},
|
||||
zig_module_index: ?File.Index = null,
|
||||
zig_object_index: ?File.Index = null,
|
||||
linker_defined_index: ?File.Index = null,
|
||||
objects: std.ArrayListUnmanaged(File.Index) = .{},
|
||||
shared_objects: std.ArrayListUnmanaged(File.Index) = .{},
|
||||
@@ -102,7 +100,7 @@ rela_plt: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{},
|
||||
/// .zig.got section
|
||||
zig_got: ZigGotSection = .{},
|
||||
|
||||
/// Tracked section headers with incremental updates to Zig module
|
||||
/// Tracked section headers with incremental updates to Zig object
|
||||
zig_text_section_index: ?u16 = null,
|
||||
zig_rodata_section_index: ?u16 = null,
|
||||
zig_data_section_index: ?u16 = null,
|
||||
@@ -115,14 +113,6 @@ debug_str_section_index: ?u16 = null,
|
||||
debug_aranges_section_index: ?u16 = null,
|
||||
debug_line_section_index: ?u16 = null,
|
||||
|
||||
/// Size contribution of Zig's metadata to each debug section.
|
||||
/// Used to track start of metadata from input object files.
|
||||
debug_info_section_zig_size: u64 = 0,
|
||||
debug_abbrev_section_zig_size: u64 = 0,
|
||||
debug_str_section_zig_size: u64 = 0,
|
||||
debug_aranges_section_zig_size: u64 = 0,
|
||||
debug_line_section_zig_size: u64 = 0,
|
||||
|
||||
copy_rel_section_index: ?u16 = null,
|
||||
dynamic_section_index: ?u16 = null,
|
||||
dynstrtab_section_index: ?u16 = null,
|
||||
@@ -172,59 +162,19 @@ symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},
|
||||
has_text_reloc: bool = false,
|
||||
num_ifunc_dynrelocs: usize = 0,
|
||||
|
||||
debug_strtab_dirty: bool = false,
|
||||
debug_abbrev_section_dirty: bool = false,
|
||||
debug_aranges_section_dirty: bool = false,
|
||||
debug_info_header_dirty: bool = false,
|
||||
debug_line_header_dirty: bool = false,
|
||||
|
||||
error_flags: link.File.ErrorFlags = link.File.ErrorFlags{},
|
||||
misc_errors: std.ArrayListUnmanaged(link.File.ErrorMsg) = .{},
|
||||
|
||||
/// Table of tracked LazySymbols.
|
||||
lazy_syms: LazySymbolTable = .{},
|
||||
|
||||
/// Table of tracked Decls.
|
||||
decls: DeclTable = .{},
|
||||
|
||||
/// List of atoms that are owned directly by the linker.
|
||||
atoms: std.ArrayListUnmanaged(Atom) = .{},
|
||||
|
||||
/// Table of last atom index in a section and matching atom free list if any.
|
||||
last_atom_and_free_list_table: LastAtomAndFreeListTable = .{},
|
||||
|
||||
/// Table of unnamed constants associated with a parent `Decl`.
|
||||
/// We store them here so that we can free the constants whenever the `Decl`
|
||||
/// needs updating or is freed.
|
||||
///
|
||||
/// For example,
|
||||
///
|
||||
/// ```zig
|
||||
/// const Foo = struct{
|
||||
/// a: u8,
|
||||
/// };
|
||||
///
|
||||
/// pub fn main() void {
|
||||
/// var foo = Foo{ .a = 1 };
|
||||
/// _ = foo;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// value assigned to label `foo` is an unnamed constant belonging/associated
|
||||
/// with `Decl` `main`, and lives as long as that `Decl`.
|
||||
unnamed_consts: UnnamedConstTable = .{},
|
||||
anon_decls: AnonDeclTable = .{},
|
||||
|
||||
comdat_groups: std.ArrayListUnmanaged(ComdatGroup) = .{},
|
||||
comdat_groups_owners: std.ArrayListUnmanaged(ComdatGroupOwner) = .{},
|
||||
comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{},
|
||||
|
||||
const AtomList = std.ArrayListUnmanaged(Atom.Index);
|
||||
const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index));
|
||||
const DeclTable = std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata);
|
||||
const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, DeclMetadata);
|
||||
const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata);
|
||||
const LastAtomAndFreeListTable = std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFreeList);
|
||||
|
||||
/// When allocating, the ideal_capacity is calculated by
|
||||
/// actual_capacity + (actual_capacity / ideal_factor)
|
||||
const ideal_factor = 3;
|
||||
@@ -322,34 +272,13 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
|
||||
}
|
||||
|
||||
if (options.module != null and !options.use_llvm) {
|
||||
if (!options.strip) {
|
||||
self.dwarf = Dwarf.init(allocator, &self.base, .dwarf32);
|
||||
}
|
||||
|
||||
const index = @as(File.Index, @intCast(try self.files.addOne(allocator)));
|
||||
self.files.set(index, .{ .zig_module = .{
|
||||
self.files.set(index, .{ .zig_object = .{
|
||||
.index = index,
|
||||
.path = options.module.?.main_mod.root_src_path,
|
||||
} });
|
||||
self.zig_module_index = index;
|
||||
const zig_module = self.file(index).?.zig_module;
|
||||
|
||||
try zig_module.atoms.append(allocator, 0); // null input section
|
||||
|
||||
const name_off = try self.strtab.insert(allocator, std.fs.path.stem(options.module.?.main_mod.root_src_path));
|
||||
const symbol_index = try self.addSymbol();
|
||||
try zig_module.local_symbols.append(allocator, symbol_index);
|
||||
const symbol_ptr = self.symbol(symbol_index);
|
||||
symbol_ptr.file_index = zig_module.index;
|
||||
symbol_ptr.name_offset = name_off;
|
||||
|
||||
const esym_index = try zig_module.addLocalEsym(allocator);
|
||||
const esym = &zig_module.local_esyms.items(.elf_sym)[esym_index];
|
||||
esym.st_name = name_off;
|
||||
esym.st_info |= elf.STT_FILE;
|
||||
esym.st_shndx = elf.SHN_ABS;
|
||||
symbol_ptr.esym_index = esym_index;
|
||||
|
||||
self.zig_object_index = index;
|
||||
try self.zigObjectPtr().?.init(self);
|
||||
try self.initMetadata();
|
||||
}
|
||||
|
||||
@@ -401,7 +330,7 @@ pub fn deinit(self: *Elf) void {
|
||||
|
||||
for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) {
|
||||
.null => {},
|
||||
.zig_module => data.zig_module.deinit(gpa),
|
||||
.zig_object => data.zig_object.deinit(gpa),
|
||||
.linker_defined => data.linker_defined.deinit(gpa),
|
||||
.object => data.object.deinit(gpa),
|
||||
.shared_object => data.shared_object.deinit(gpa),
|
||||
@@ -425,40 +354,11 @@ pub fn deinit(self: *Elf) void {
|
||||
self.resolver.deinit(gpa);
|
||||
self.start_stop_indexes.deinit(gpa);
|
||||
|
||||
{
|
||||
var it = self.decls.iterator();
|
||||
while (it.next()) |entry| {
|
||||
entry.value_ptr.exports.deinit(gpa);
|
||||
}
|
||||
self.decls.deinit(gpa);
|
||||
}
|
||||
|
||||
self.atoms.deinit(gpa);
|
||||
for (self.last_atom_and_free_list_table.values()) |*value| {
|
||||
value.free_list.deinit(gpa);
|
||||
}
|
||||
self.last_atom_and_free_list_table.deinit(gpa);
|
||||
self.lazy_syms.deinit(gpa);
|
||||
|
||||
{
|
||||
var it = self.unnamed_consts.valueIterator();
|
||||
while (it.next()) |syms| {
|
||||
syms.deinit(gpa);
|
||||
}
|
||||
self.unnamed_consts.deinit(gpa);
|
||||
}
|
||||
|
||||
{
|
||||
var it = self.anon_decls.iterator();
|
||||
while (it.next()) |entry| {
|
||||
entry.value_ptr.exports.deinit(gpa);
|
||||
}
|
||||
self.anon_decls.deinit(gpa);
|
||||
}
|
||||
|
||||
if (self.dwarf) |*dw| {
|
||||
dw.deinit();
|
||||
}
|
||||
|
||||
self.misc_errors.deinit(gpa);
|
||||
self.comdat_groups.deinit(gpa);
|
||||
@@ -481,16 +381,7 @@ pub fn deinit(self: *Elf) void {
|
||||
|
||||
pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 {
|
||||
assert(self.llvm_object == null);
|
||||
const this_sym_index = try self.getOrCreateMetadataForDecl(decl_index);
|
||||
const this_sym = self.symbol(this_sym_index);
|
||||
const vaddr = this_sym.value;
|
||||
const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(self).?;
|
||||
try parent_atom.addReloc(self, .{
|
||||
.r_offset = reloc_info.offset,
|
||||
.r_info = (@as(u64, @intCast(this_sym.esym_index)) << 32) | elf.R_X86_64_64,
|
||||
.r_addend = reloc_info.addend,
|
||||
});
|
||||
return vaddr;
|
||||
return self.zigObjectPtr().?.getDeclVAddr(self, decl_index, reloc_info);
|
||||
}
|
||||
|
||||
pub fn lowerAnonDecl(
|
||||
@@ -499,60 +390,12 @@ pub fn lowerAnonDecl(
|
||||
explicit_alignment: InternPool.Alignment,
|
||||
src_loc: Module.SrcLoc,
|
||||
) !codegen.Result {
|
||||
const gpa = self.base.allocator;
|
||||
const mod = self.base.options.module.?;
|
||||
const ty = mod.intern_pool.typeOf(decl_val).toType();
|
||||
const decl_alignment = switch (explicit_alignment) {
|
||||
.none => ty.abiAlignment(mod),
|
||||
else => explicit_alignment,
|
||||
};
|
||||
if (self.anon_decls.get(decl_val)) |metadata| {
|
||||
const existing_alignment = self.symbol(metadata.symbol_index).atom(self).?.alignment;
|
||||
if (decl_alignment.order(existing_alignment).compare(.lte))
|
||||
return .ok;
|
||||
}
|
||||
|
||||
const val = decl_val.toValue();
|
||||
const tv = TypedValue{ .ty = ty, .val = val };
|
||||
var name_buf: [32]u8 = undefined;
|
||||
const name = std.fmt.bufPrint(&name_buf, "__anon_{d}", .{
|
||||
@intFromEnum(decl_val),
|
||||
}) catch unreachable;
|
||||
const res = self.lowerConst(
|
||||
name,
|
||||
tv,
|
||||
decl_alignment,
|
||||
self.zig_rodata_section_index.?,
|
||||
src_loc,
|
||||
) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
else => |e| return .{ .fail = try Module.ErrorMsg.create(
|
||||
gpa,
|
||||
src_loc,
|
||||
"unable to lower constant value: {s}",
|
||||
.{@errorName(e)},
|
||||
) },
|
||||
};
|
||||
const sym_index = switch (res) {
|
||||
.ok => |sym_index| sym_index,
|
||||
.fail => |em| return .{ .fail = em },
|
||||
};
|
||||
try self.anon_decls.put(gpa, decl_val, .{ .symbol_index = sym_index });
|
||||
return .ok;
|
||||
return self.zigObjectPtr().?.lowerAnonDecl(self, decl_val, explicit_alignment, src_loc);
|
||||
}
|
||||
|
||||
pub fn getAnonDeclVAddr(self: *Elf, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {
|
||||
assert(self.llvm_object == null);
|
||||
const sym_index = self.anon_decls.get(decl_val).?.symbol_index;
|
||||
const sym = self.symbol(sym_index);
|
||||
const vaddr = sym.value;
|
||||
const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(self).?;
|
||||
try parent_atom.addReloc(self, .{
|
||||
.r_offset = reloc_info.offset,
|
||||
.r_info = (@as(u64, @intCast(sym.esym_index)) << 32) | elf.R_X86_64_64,
|
||||
.r_addend = reloc_info.addend,
|
||||
});
|
||||
return vaddr;
|
||||
return self.zigObjectPtr().?.getAnonDeclVAddr(self, decl_val, reloc_info);
|
||||
}
|
||||
|
||||
/// Returns end pos of collision, if any.
|
||||
@@ -726,7 +569,7 @@ fn allocateNonAllocSection(self: *Elf, opts: AllocateNonAllocSectionOpts) error{
|
||||
return index;
|
||||
}
|
||||
|
||||
/// TODO move to ZigModule
|
||||
/// TODO move to ZigObject
|
||||
pub fn initMetadata(self: *Elf) !void {
|
||||
const gpa = self.base.allocator;
|
||||
const ptr_size = self.ptrWidthBytes();
|
||||
@@ -839,7 +682,8 @@ pub fn initMetadata(self: *Elf) !void {
|
||||
try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_bss_section_index.?, .{});
|
||||
}
|
||||
|
||||
if (self.dwarf) |*dw| {
|
||||
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);
|
||||
@@ -849,7 +693,7 @@ pub fn initMetadata(self: *Elf) !void {
|
||||
.flags = elf.SHF_MERGE | elf.SHF_STRINGS,
|
||||
.entsize = 1,
|
||||
});
|
||||
self.debug_strtab_dirty = true;
|
||||
zig_object.debug_strtab_dirty = true;
|
||||
}
|
||||
|
||||
if (self.debug_info_section_index == null) {
|
||||
@@ -858,7 +702,7 @@ pub fn initMetadata(self: *Elf) !void {
|
||||
.size = 200,
|
||||
.alignment = 1,
|
||||
});
|
||||
self.debug_info_header_dirty = true;
|
||||
zig_object.debug_info_header_dirty = true;
|
||||
}
|
||||
|
||||
if (self.debug_abbrev_section_index == null) {
|
||||
@@ -867,7 +711,7 @@ pub fn initMetadata(self: *Elf) !void {
|
||||
.size = 128,
|
||||
.alignment = 1,
|
||||
});
|
||||
self.debug_abbrev_section_dirty = true;
|
||||
zig_object.debug_abbrev_section_dirty = true;
|
||||
}
|
||||
|
||||
if (self.debug_aranges_section_index == null) {
|
||||
@@ -876,7 +720,7 @@ pub fn initMetadata(self: *Elf) !void {
|
||||
.size = 160,
|
||||
.alignment = 16,
|
||||
});
|
||||
self.debug_aranges_section_dirty = true;
|
||||
zig_object.debug_aranges_section_dirty = true;
|
||||
}
|
||||
|
||||
if (self.debug_line_section_index == null) {
|
||||
@@ -885,7 +729,7 @@ pub fn initMetadata(self: *Elf) !void {
|
||||
.size = 250,
|
||||
.alignment = 1,
|
||||
});
|
||||
self.debug_line_header_dirty = true;
|
||||
zig_object.debug_line_header_dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -976,17 +820,18 @@ pub fn growNonAllocSection(
|
||||
}
|
||||
|
||||
pub fn markDirty(self: *Elf, shdr_index: u16) void {
|
||||
if (self.dwarf) |_| {
|
||||
const zig_object = self.zigObjectPtr().?;
|
||||
if (zig_object.dwarf) |_| {
|
||||
if (self.debug_info_section_index.? == shdr_index) {
|
||||
self.debug_info_header_dirty = true;
|
||||
zig_object.debug_info_header_dirty = true;
|
||||
} else if (self.debug_line_section_index.? == shdr_index) {
|
||||
self.debug_line_header_dirty = true;
|
||||
zig_object.debug_line_header_dirty = true;
|
||||
} else if (self.debug_abbrev_section_index.? == shdr_index) {
|
||||
self.debug_abbrev_section_dirty = true;
|
||||
zig_object.debug_abbrev_section_dirty = true;
|
||||
} else if (self.debug_str_section_index.? == shdr_index) {
|
||||
self.debug_strtab_dirty = true;
|
||||
zig_object.debug_strtab_dirty = true;
|
||||
} else if (self.debug_aranges_section_index.? == shdr_index) {
|
||||
self.debug_aranges_section_dirty = true;
|
||||
zig_object.debug_aranges_section_dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1041,7 +886,7 @@ 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_module_index == null) {
|
||||
if (self.base.options.output_mode == .Obj and self.zig_object_index == null) {
|
||||
// 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: {
|
||||
@@ -1486,35 +1331,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
|
||||
try self.handleAndReportParseError(obj.path, err, &parse_ctx);
|
||||
}
|
||||
|
||||
// Handle any lazy symbols that were emitted by incremental compilation.
|
||||
if (self.lazy_syms.getPtr(.none)) |metadata| {
|
||||
const module = self.base.options.module.?;
|
||||
|
||||
// Most lazy symbols can be updated on first use, but
|
||||
// anyerror needs to wait for everything to be flushed.
|
||||
if (metadata.text_state != .unused) self.updateLazySymbol(
|
||||
link.File.LazySymbol.initDecl(.code, null, module),
|
||||
metadata.text_symbol_index,
|
||||
) catch |err| return switch (err) {
|
||||
error.CodegenFail => error.FlushFailure,
|
||||
else => |e| e,
|
||||
};
|
||||
if (metadata.rodata_state != .unused) self.updateLazySymbol(
|
||||
link.File.LazySymbol.initDecl(.const_data, null, module),
|
||||
metadata.rodata_symbol_index,
|
||||
) catch |err| return switch (err) {
|
||||
error.CodegenFail => error.FlushFailure,
|
||||
else => |e| e,
|
||||
};
|
||||
}
|
||||
for (self.lazy_syms.values()) |*metadata| {
|
||||
if (metadata.text_state != .unused) metadata.text_state = .flushed;
|
||||
if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed;
|
||||
}
|
||||
|
||||
if (self.dwarf) |*dw| {
|
||||
try dw.flushModule(self.base.options.module.?);
|
||||
}
|
||||
if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self);
|
||||
|
||||
// Dedup shared objects
|
||||
{
|
||||
@@ -1543,7 +1360,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
|
||||
}
|
||||
|
||||
// Now, we are ready to resolve the symbols across all input files.
|
||||
// We will first resolve the files in the ZigModule, next in the parsed
|
||||
// We will first resolve the files in the ZigObject, next in the parsed
|
||||
// input Object files.
|
||||
// Any qualifing unresolved symbol will be upgraded to an absolute, weak
|
||||
// symbol for potential resolution at load-time.
|
||||
@@ -1576,45 +1393,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
|
||||
// Scan and create missing synthetic entries such as GOT indirection.
|
||||
try self.scanRelocs();
|
||||
|
||||
// TODO I need to re-think how to handle ZigModule's debug sections AND debug sections
|
||||
// extracted from input object files correctly.
|
||||
if (self.dwarf) |*dw| {
|
||||
if (self.debug_abbrev_section_dirty) {
|
||||
try dw.writeDbgAbbrev();
|
||||
self.debug_abbrev_section_dirty = false;
|
||||
}
|
||||
|
||||
if (self.debug_info_header_dirty) {
|
||||
const text_phdr = &self.phdrs.items[self.phdr_zig_load_re_index.?];
|
||||
const low_pc = text_phdr.p_vaddr;
|
||||
const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
|
||||
try dw.writeDbgInfoHeader(self.base.options.module.?, low_pc, high_pc);
|
||||
self.debug_info_header_dirty = false;
|
||||
}
|
||||
|
||||
if (self.debug_aranges_section_dirty) {
|
||||
const text_phdr = &self.phdrs.items[self.phdr_zig_load_re_index.?];
|
||||
try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz);
|
||||
self.debug_aranges_section_dirty = false;
|
||||
}
|
||||
|
||||
if (self.debug_line_header_dirty) {
|
||||
try dw.writeDbgLineHeader();
|
||||
self.debug_line_header_dirty = false;
|
||||
}
|
||||
|
||||
if (self.debug_str_section_index) |shndx| {
|
||||
if (self.debug_strtab_dirty or dw.strtab.buffer.items.len != self.shdrs.items[shndx].sh_size) {
|
||||
try self.growNonAllocSection(shndx, dw.strtab.buffer.items.len, 1, false);
|
||||
const shdr = self.shdrs.items[shndx];
|
||||
try self.base.file.?.pwriteAll(dw.strtab.buffer.items, shdr.sh_offset);
|
||||
self.debug_strtab_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
self.saveDebugSectionsSizes();
|
||||
}
|
||||
|
||||
// Generate and emit non-incremental sections.
|
||||
try self.initSections();
|
||||
try self.initSpecialPhdrs();
|
||||
@@ -1645,15 +1423,14 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
|
||||
|
||||
// Beyond this point, everything has been allocated a virtual address and we can resolve
|
||||
// the relocations, and commit objects to file.
|
||||
if (self.zig_module_index) |index| {
|
||||
const zig_module = self.file(index).?.zig_module;
|
||||
for (zig_module.atoms.items) |atom_index| {
|
||||
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;
|
||||
const shdr = &self.shdrs.items[out_shndx];
|
||||
if (shdr.sh_type == elf.SHT_NOBITS) continue;
|
||||
const code = try zig_module.codeAlloc(self, atom_index);
|
||||
const code = try zig_object.codeAlloc(self, atom_index);
|
||||
defer gpa.free(code);
|
||||
const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;
|
||||
atom_ptr.resolveRelocsAlloc(self, code) catch |err| switch (err) {
|
||||
@@ -1680,14 +1457,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
|
||||
self.error_flags.no_entry_point_found = false;
|
||||
try self.writeHeader();
|
||||
}
|
||||
|
||||
// The point of flush() 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,
|
||||
// such as debug_line_header_dirty and debug_info_header_dirty.
|
||||
assert(!self.debug_abbrev_section_dirty);
|
||||
assert(!self.debug_aranges_section_dirty);
|
||||
assert(!self.debug_strtab_dirty);
|
||||
}
|
||||
|
||||
const ParseError = error{
|
||||
@@ -1926,8 +1695,8 @@ fn accessLibPath(
|
||||
/// 5. Remove references to dead objects/shared objects
|
||||
/// 6. Re-run symbol resolution on pruned objects and shared objects sets.
|
||||
fn resolveSymbols(self: *Elf) void {
|
||||
// Resolve symbols in the ZigModule. For now, we assume that it's always live.
|
||||
if (self.zig_module_index) |index| self.file(index).?.resolveSymbols(self);
|
||||
// Resolve symbols in the ZigObject. For now, we assume that it's always live.
|
||||
if (self.zigObjectPtr()) |zig_object| zig_object.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);
|
||||
@@ -1936,7 +1705,7 @@ fn resolveSymbols(self: *Elf) void {
|
||||
self.markLive();
|
||||
|
||||
// Reset state of all globals after marking live objects.
|
||||
if (self.zig_module_index) |index| self.file(index).?.resetGlobals(self);
|
||||
if (self.zigObjectPtr()) |zig_object| zig_object.resetGlobals(self);
|
||||
for (self.objects.items) |index| self.file(index).?.resetGlobals(self);
|
||||
for (self.shared_objects.items) |index| self.file(index).?.resetGlobals(self);
|
||||
|
||||
@@ -1988,7 +1757,7 @@ fn resolveSymbols(self: *Elf) void {
|
||||
}
|
||||
|
||||
// Re-resolve the symbols.
|
||||
if (self.zig_module_index) |index| self.file(index).?.resolveSymbols(self);
|
||||
if (self.zigObjectPtr()) |zig_object| zig_object.resolveSymbols(self);
|
||||
for (self.objects.items) |index| self.file(index).?.resolveSymbols(self);
|
||||
for (self.shared_objects.items) |index| self.file(index).?.resolveSymbols(self);
|
||||
}
|
||||
@@ -1998,7 +1767,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.zig_module_index) |index| self.file(index).?.markLive(self);
|
||||
if (self.zigObjectPtr()) |zig_object| zig_object.markLive(self);
|
||||
for (self.objects.items) |index| {
|
||||
const file_ptr = self.file(index).?;
|
||||
if (file_ptr.isAlive()) file_ptr.markLive(self);
|
||||
@@ -2057,7 +1826,7 @@ fn markImportsExports(self: *Elf) void {
|
||||
}
|
||||
}
|
||||
|
||||
if (self.zig_module_index) |index| {
|
||||
if (self.zig_object_index) |index| {
|
||||
mark(self, index);
|
||||
}
|
||||
|
||||
@@ -2067,9 +1836,8 @@ fn markImportsExports(self: *Elf) void {
|
||||
}
|
||||
|
||||
fn claimUnresolved(self: *Elf) void {
|
||||
if (self.zig_module_index) |index| {
|
||||
const zig_module = self.file(index).?.zig_module;
|
||||
zig_module.claimUnresolved(self);
|
||||
if (self.zigObjectPtr()) |zig_object| {
|
||||
zig_object.claimUnresolved(self);
|
||||
}
|
||||
for (self.objects.items) |index| {
|
||||
const object = self.file(index).?.object;
|
||||
@@ -2093,9 +1861,8 @@ fn scanRelocs(self: *Elf) !void {
|
||||
undefs.deinit();
|
||||
}
|
||||
|
||||
if (self.zig_module_index) |index| {
|
||||
const zig_module = self.file(index).?.zig_module;
|
||||
try zig_module.scanRelocs(self, &undefs);
|
||||
if (self.zigObjectPtr()) |zig_object| {
|
||||
try zig_object.scanRelocs(self, &undefs);
|
||||
}
|
||||
for (self.objects.items) |index| {
|
||||
const object = self.file(index).?.object;
|
||||
@@ -3058,206 +2825,9 @@ fn writeHeader(self: *Elf) !void {
|
||||
try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);
|
||||
}
|
||||
|
||||
fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void {
|
||||
const unnamed_consts = self.unnamed_consts.getPtr(decl_index) orelse return;
|
||||
for (unnamed_consts.items) |sym_index| {
|
||||
self.freeDeclMetadata(sym_index);
|
||||
}
|
||||
unnamed_consts.clearAndFree(self.base.allocator);
|
||||
}
|
||||
|
||||
fn freeDeclMetadata(self: *Elf, sym_index: Symbol.Index) void {
|
||||
const sym = self.symbol(sym_index);
|
||||
sym.atom(self).?.free(self);
|
||||
log.debug("adding %{d} to local symbols free list", .{sym_index});
|
||||
self.symbols_free_list.append(self.base.allocator, sym_index) catch {};
|
||||
self.symbols.items[sym_index] = .{};
|
||||
// TODO free GOT entry here
|
||||
}
|
||||
|
||||
pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {
|
||||
if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
|
||||
|
||||
const mod = self.base.options.module.?;
|
||||
const decl = mod.declPtr(decl_index);
|
||||
|
||||
log.debug("freeDecl {*}", .{decl});
|
||||
|
||||
if (self.decls.fetchRemove(decl_index)) |const_kv| {
|
||||
var kv = const_kv;
|
||||
const sym_index = kv.value.symbol_index;
|
||||
self.freeDeclMetadata(sym_index);
|
||||
self.freeUnnamedConsts(decl_index);
|
||||
kv.value.exports.deinit(self.base.allocator);
|
||||
}
|
||||
|
||||
if (self.dwarf) |*dw| {
|
||||
dw.freeDecl(decl_index);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getOrCreateMetadataForLazySymbol(self: *Elf, lazy_sym: link.File.LazySymbol) !Symbol.Index {
|
||||
const mod = self.base.options.module.?;
|
||||
const gop = try self.lazy_syms.getOrPut(self.base.allocator, lazy_sym.getDecl(mod));
|
||||
errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();
|
||||
if (!gop.found_existing) gop.value_ptr.* = .{};
|
||||
const metadata: struct {
|
||||
symbol_index: *Symbol.Index,
|
||||
state: *LazySymbolMetadata.State,
|
||||
} = switch (lazy_sym.kind) {
|
||||
.code => .{
|
||||
.symbol_index = &gop.value_ptr.text_symbol_index,
|
||||
.state = &gop.value_ptr.text_state,
|
||||
},
|
||||
.const_data => .{
|
||||
.symbol_index = &gop.value_ptr.rodata_symbol_index,
|
||||
.state = &gop.value_ptr.rodata_state,
|
||||
},
|
||||
};
|
||||
const zig_module = self.file(self.zig_module_index.?).?.zig_module;
|
||||
switch (metadata.state.*) {
|
||||
.unused => metadata.symbol_index.* = try zig_module.addAtom(self),
|
||||
.pending_flush => return metadata.symbol_index.*,
|
||||
.flushed => {},
|
||||
}
|
||||
metadata.state.* = .pending_flush;
|
||||
const symbol_index = metadata.symbol_index.*;
|
||||
// anyerror needs to be deferred until flushModule
|
||||
if (lazy_sym.getDecl(mod) != .none) try self.updateLazySymbol(lazy_sym, symbol_index);
|
||||
return symbol_index;
|
||||
}
|
||||
|
||||
pub fn getOrCreateMetadataForDecl(self: *Elf, decl_index: Module.Decl.Index) !Symbol.Index {
|
||||
const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
|
||||
if (!gop.found_existing) {
|
||||
const zig_module = self.file(self.zig_module_index.?).?.zig_module;
|
||||
gop.value_ptr.* = .{ .symbol_index = try zig_module.addAtom(self) };
|
||||
}
|
||||
return gop.value_ptr.symbol_index;
|
||||
}
|
||||
|
||||
fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index, code: []const u8) u16 {
|
||||
const mod = self.base.options.module.?;
|
||||
const decl = mod.declPtr(decl_index);
|
||||
const shdr_index = switch (decl.ty.zigTypeTag(mod)) {
|
||||
// TODO: what if this is a function pointer?
|
||||
.Fn => self.zig_text_section_index.?,
|
||||
else => blk: {
|
||||
if (decl.getOwnedVariable(mod)) |variable| {
|
||||
if (variable.is_const) break :blk self.zig_rodata_section_index.?;
|
||||
if (variable.init.toValue().isUndefDeep(mod)) {
|
||||
const mode = self.base.options.optimize_mode;
|
||||
if (mode == .Debug or mode == .ReleaseSafe) break :blk self.zig_data_section_index.?;
|
||||
break :blk self.zig_bss_section_index.?;
|
||||
}
|
||||
// TODO I blatantly copied the logic from the Wasm linker, but is there a less
|
||||
// intrusive check for all zeroes than this?
|
||||
const is_all_zeroes = for (code) |byte| {
|
||||
if (byte != 0) break false;
|
||||
} else true;
|
||||
if (is_all_zeroes) break :blk self.zig_bss_section_index.?;
|
||||
break :blk self.zig_data_section_index.?;
|
||||
}
|
||||
break :blk self.zig_rodata_section_index.?;
|
||||
},
|
||||
};
|
||||
return shdr_index;
|
||||
}
|
||||
|
||||
fn updateDeclCode(
|
||||
self: *Elf,
|
||||
decl_index: Module.Decl.Index,
|
||||
sym_index: Symbol.Index,
|
||||
code: []const u8,
|
||||
stt_bits: u8,
|
||||
) !void {
|
||||
const gpa = self.base.allocator;
|
||||
const mod = self.base.options.module.?;
|
||||
const zig_module = self.file(self.zig_module_index.?).?.zig_module;
|
||||
const decl = mod.declPtr(decl_index);
|
||||
|
||||
const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
|
||||
|
||||
log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });
|
||||
const required_alignment = decl.getAlignment(mod);
|
||||
|
||||
const sym = self.symbol(sym_index);
|
||||
const esym = &zig_module.local_esyms.items(.elf_sym)[sym.esym_index];
|
||||
const atom_ptr = sym.atom(self).?;
|
||||
|
||||
const shdr_index = self.getDeclShdrIndex(decl_index, code);
|
||||
sym.output_section_index = shdr_index;
|
||||
atom_ptr.output_section_index = shdr_index;
|
||||
|
||||
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;
|
||||
esym.st_info |= stt_bits;
|
||||
esym.st_size = code.len;
|
||||
|
||||
const old_size = atom_ptr.size;
|
||||
const old_vaddr = atom_ptr.value;
|
||||
atom_ptr.alignment = required_alignment;
|
||||
atom_ptr.size = code.len;
|
||||
|
||||
if (old_size > 0 and self.base.child_pid == null) {
|
||||
const capacity = atom_ptr.capacity(self);
|
||||
const need_realloc = code.len > capacity or !required_alignment.check(sym.value);
|
||||
if (need_realloc) {
|
||||
try atom_ptr.grow(self);
|
||||
log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, old_vaddr, atom_ptr.value });
|
||||
if (old_vaddr != atom_ptr.value) {
|
||||
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(self).?;
|
||||
try self.zig_got.writeOne(self, extra.zig_got);
|
||||
}
|
||||
} else if (code.len < old_size) {
|
||||
atom_ptr.shrink(self);
|
||||
}
|
||||
} else {
|
||||
try atom_ptr.allocate(self);
|
||||
errdefer self.freeDeclMetadata(sym_index);
|
||||
|
||||
sym.value = atom_ptr.value;
|
||||
esym.st_value = atom_ptr.value;
|
||||
|
||||
const gop = try sym.getOrCreateZigGotEntry(sym_index, self);
|
||||
try self.zig_got.writeOne(self, gop.index);
|
||||
}
|
||||
|
||||
if (self.base.child_pid) |pid| {
|
||||
switch (builtin.os.tag) {
|
||||
.linux => {
|
||||
var code_vec: [1]std.os.iovec_const = .{.{
|
||||
.iov_base = code.ptr,
|
||||
.iov_len = code.len,
|
||||
}};
|
||||
var remote_vec: [1]std.os.iovec_const = .{.{
|
||||
.iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(sym.value)))),
|
||||
.iov_len = code.len,
|
||||
}};
|
||||
const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0);
|
||||
switch (std.os.errno(rc)) {
|
||||
.SUCCESS => assert(rc == code.len),
|
||||
else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
|
||||
}
|
||||
},
|
||||
else => return error.HotSwapUnavailableOnHostOperatingSystem,
|
||||
}
|
||||
}
|
||||
|
||||
const shdr = self.shdrs.items[shdr_index];
|
||||
if (shdr.sh_type != elf.SHT_NOBITS) {
|
||||
const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?;
|
||||
const section_offset = sym.value - self.phdrs.items[phdr_index].p_vaddr;
|
||||
const file_offset = shdr.sh_offset + section_offset;
|
||||
try self.base.file.?.pwriteAll(code, file_offset);
|
||||
}
|
||||
return self.zigObjectPtr().?.freeDecl(self, decl_index);
|
||||
}
|
||||
|
||||
pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
|
||||
@@ -3265,54 +2835,7 @@ pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: A
|
||||
@panic("Attempted to compile for object format that was disabled by build configuration");
|
||||
}
|
||||
if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func_index, air, liveness);
|
||||
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const func = mod.funcInfo(func_index);
|
||||
const decl_index = func.owner_decl;
|
||||
const decl = mod.declPtr(decl_index);
|
||||
|
||||
const sym_index = try self.getOrCreateMetadataForDecl(decl_index);
|
||||
self.freeUnnamedConsts(decl_index);
|
||||
self.symbol(sym_index).atom(self).?.freeRelocs(self);
|
||||
|
||||
var code_buffer = std.ArrayList(u8).init(self.base.allocator);
|
||||
defer code_buffer.deinit();
|
||||
|
||||
var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(mod, decl_index) else null;
|
||||
defer if (decl_state) |*ds| ds.deinit();
|
||||
|
||||
const res = if (decl_state) |*ds|
|
||||
try codegen.generateFunction(&self.base, decl.srcLoc(mod), func_index, air, liveness, &code_buffer, .{
|
||||
.dwarf = ds,
|
||||
})
|
||||
else
|
||||
try codegen.generateFunction(&self.base, decl.srcLoc(mod), func_index, air, liveness, &code_buffer, .none);
|
||||
|
||||
const code = switch (res) {
|
||||
.ok => code_buffer.items,
|
||||
.fail => |em| {
|
||||
decl.analysis = .codegen_failure;
|
||||
try mod.failed_decls.put(mod.gpa, decl_index, em);
|
||||
return;
|
||||
},
|
||||
};
|
||||
try self.updateDeclCode(decl_index, sym_index, code, elf.STT_FUNC);
|
||||
if (decl_state) |*ds| {
|
||||
const sym = self.symbol(sym_index);
|
||||
try self.dwarf.?.commitDeclState(
|
||||
mod,
|
||||
decl_index,
|
||||
sym.value,
|
||||
sym.atom(self).?.size,
|
||||
ds,
|
||||
);
|
||||
}
|
||||
|
||||
// Since we updated the vaddr and the size, each corresponding export
|
||||
// symbol also needs to be updated.
|
||||
return self.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
|
||||
return self.zigObjectPtr().?.updateFunc(self, mod, func_index, air, liveness);
|
||||
}
|
||||
|
||||
pub fn updateDecl(
|
||||
@@ -3324,242 +2847,11 @@ pub fn updateDecl(
|
||||
@panic("Attempted to compile for object format that was disabled by build configuration");
|
||||
}
|
||||
if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index);
|
||||
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const decl = mod.declPtr(decl_index);
|
||||
|
||||
if (decl.val.getExternFunc(mod)) |_| {
|
||||
return;
|
||||
}
|
||||
|
||||
if (decl.isExtern(mod)) {
|
||||
// Extern variable gets a .got entry only.
|
||||
const variable = decl.getOwnedVariable(mod).?;
|
||||
const name = mod.intern_pool.stringToSlice(decl.name);
|
||||
const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name);
|
||||
const esym_index = try self.getGlobalSymbol(name, lib_name);
|
||||
self.symbol(self.zigModulePtr().symbol(esym_index)).flags.needs_got = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const sym_index = try self.getOrCreateMetadataForDecl(decl_index);
|
||||
self.symbol(sym_index).atom(self).?.freeRelocs(self);
|
||||
|
||||
var code_buffer = std.ArrayList(u8).init(self.base.allocator);
|
||||
defer code_buffer.deinit();
|
||||
|
||||
var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(mod, decl_index) else null;
|
||||
defer if (decl_state) |*ds| ds.deinit();
|
||||
|
||||
// TODO implement .debug_info for global variables
|
||||
const decl_val = if (decl.val.getVariable(mod)) |variable| variable.init.toValue() else decl.val;
|
||||
const res = if (decl_state) |*ds|
|
||||
try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{
|
||||
.ty = decl.ty,
|
||||
.val = decl_val,
|
||||
}, &code_buffer, .{
|
||||
.dwarf = ds,
|
||||
}, .{
|
||||
.parent_atom_index = sym_index,
|
||||
})
|
||||
else
|
||||
try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{
|
||||
.ty = decl.ty,
|
||||
.val = decl_val,
|
||||
}, &code_buffer, .none, .{
|
||||
.parent_atom_index = sym_index,
|
||||
});
|
||||
|
||||
const code = switch (res) {
|
||||
.ok => code_buffer.items,
|
||||
.fail => |em| {
|
||||
decl.analysis = .codegen_failure;
|
||||
try mod.failed_decls.put(mod.gpa, decl_index, em);
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
try self.updateDeclCode(decl_index, sym_index, code, elf.STT_OBJECT);
|
||||
if (decl_state) |*ds| {
|
||||
const sym = self.symbol(sym_index);
|
||||
try self.dwarf.?.commitDeclState(
|
||||
mod,
|
||||
decl_index,
|
||||
sym.value,
|
||||
sym.atom(self).?.size,
|
||||
ds,
|
||||
);
|
||||
}
|
||||
|
||||
// Since we updated the vaddr and the size, each corresponding export
|
||||
// symbol also needs to be updated.
|
||||
return self.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
|
||||
}
|
||||
|
||||
fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.Index) !void {
|
||||
const gpa = self.base.allocator;
|
||||
const mod = self.base.options.module.?;
|
||||
const zig_module = self.file(self.zig_module_index.?).?.zig_module;
|
||||
|
||||
var required_alignment: InternPool.Alignment = .none;
|
||||
var code_buffer = std.ArrayList(u8).init(gpa);
|
||||
defer code_buffer.deinit();
|
||||
|
||||
const name_str_index = blk: {
|
||||
const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{
|
||||
@tagName(sym.kind),
|
||||
sym.ty.fmt(mod),
|
||||
});
|
||||
defer gpa.free(name);
|
||||
break :blk try self.strtab.insert(gpa, name);
|
||||
};
|
||||
|
||||
const src = if (sym.ty.getOwnerDeclOrNull(mod)) |owner_decl|
|
||||
mod.declPtr(owner_decl).srcLoc(mod)
|
||||
else
|
||||
Module.SrcLoc{
|
||||
.file_scope = undefined,
|
||||
.parent_decl_node = undefined,
|
||||
.lazy = .unneeded,
|
||||
};
|
||||
const res = try codegen.generateLazySymbol(
|
||||
&self.base,
|
||||
src,
|
||||
sym,
|
||||
&required_alignment,
|
||||
&code_buffer,
|
||||
.none,
|
||||
.{ .parent_atom_index = symbol_index },
|
||||
);
|
||||
const code = switch (res) {
|
||||
.ok => code_buffer.items,
|
||||
.fail => |em| {
|
||||
log.err("{s}", .{em.msg});
|
||||
return error.CodegenFail;
|
||||
},
|
||||
};
|
||||
|
||||
const output_section_index = switch (sym.kind) {
|
||||
.code => self.zig_text_section_index.?,
|
||||
.const_data => self.zig_rodata_section_index.?,
|
||||
};
|
||||
const local_sym = self.symbol(symbol_index);
|
||||
const phdr_index = self.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 = &zig_module.local_esyms.items(.elf_sym)[local_sym.esym_index];
|
||||
local_esym.st_name = name_str_index;
|
||||
local_esym.st_info |= elf.STT_OBJECT;
|
||||
local_esym.st_size = code.len;
|
||||
const atom_ptr = local_sym.atom(self).?;
|
||||
atom_ptr.flags.alive = true;
|
||||
atom_ptr.name_offset = name_str_index;
|
||||
atom_ptr.alignment = required_alignment;
|
||||
atom_ptr.size = code.len;
|
||||
atom_ptr.output_section_index = output_section_index;
|
||||
|
||||
try atom_ptr.allocate(self);
|
||||
errdefer self.freeDeclMetadata(symbol_index);
|
||||
|
||||
local_sym.value = atom_ptr.value;
|
||||
local_esym.st_value = atom_ptr.value;
|
||||
|
||||
const gop = try local_sym.getOrCreateZigGotEntry(symbol_index, self);
|
||||
try self.zig_got.writeOne(self, gop.index);
|
||||
|
||||
const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr;
|
||||
const file_offset = self.shdrs.items[output_section_index].sh_offset + section_offset;
|
||||
try self.base.file.?.pwriteAll(code, file_offset);
|
||||
return self.zigObjectPtr().?.updateDecl(self, mod, decl_index);
|
||||
}
|
||||
|
||||
pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {
|
||||
const gpa = self.base.allocator;
|
||||
const mod = self.base.options.module.?;
|
||||
const gop = try self.unnamed_consts.getOrPut(gpa, decl_index);
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = .{};
|
||||
}
|
||||
const unnamed_consts = gop.value_ptr;
|
||||
const decl = mod.declPtr(decl_index);
|
||||
const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
|
||||
const index = unnamed_consts.items.len;
|
||||
const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index });
|
||||
defer gpa.free(name);
|
||||
const sym_index = switch (try self.lowerConst(name, typed_value, typed_value.ty.abiAlignment(mod), self.zig_rodata_section_index.?, decl.srcLoc(mod))) {
|
||||
.ok => |sym_index| sym_index,
|
||||
.fail => |em| {
|
||||
decl.analysis = .codegen_failure;
|
||||
try mod.failed_decls.put(mod.gpa, decl_index, em);
|
||||
log.err("{s}", .{em.msg});
|
||||
return error.CodegenFail;
|
||||
},
|
||||
};
|
||||
const sym = self.symbol(sym_index);
|
||||
try unnamed_consts.append(gpa, sym.atom_index);
|
||||
return sym_index;
|
||||
}
|
||||
|
||||
const LowerConstResult = union(enum) {
|
||||
ok: Symbol.Index,
|
||||
fail: *Module.ErrorMsg,
|
||||
};
|
||||
|
||||
fn lowerConst(
|
||||
self: *Elf,
|
||||
name: []const u8,
|
||||
tv: TypedValue,
|
||||
required_alignment: InternPool.Alignment,
|
||||
output_section_index: u16,
|
||||
src_loc: Module.SrcLoc,
|
||||
) !LowerConstResult {
|
||||
const gpa = self.base.allocator;
|
||||
|
||||
var code_buffer = std.ArrayList(u8).init(gpa);
|
||||
defer code_buffer.deinit();
|
||||
|
||||
const zig_module = self.file(self.zig_module_index.?).?.zig_module;
|
||||
const sym_index = try zig_module.addAtom(self);
|
||||
|
||||
const res = try codegen.generateSymbol(&self.base, src_loc, tv, &code_buffer, .{
|
||||
.none = {},
|
||||
}, .{
|
||||
.parent_atom_index = sym_index,
|
||||
});
|
||||
const code = switch (res) {
|
||||
.ok => code_buffer.items,
|
||||
.fail => |em| return .{ .fail = em },
|
||||
};
|
||||
|
||||
const phdr_index = self.phdr_to_shdr_table.get(output_section_index).?;
|
||||
const local_sym = self.symbol(sym_index);
|
||||
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 = &zig_module.local_esyms.items(.elf_sym)[local_sym.esym_index];
|
||||
local_esym.st_name = name_str_index;
|
||||
local_esym.st_info |= elf.STT_OBJECT;
|
||||
local_esym.st_size = code.len;
|
||||
const atom_ptr = local_sym.atom(self).?;
|
||||
atom_ptr.flags.alive = true;
|
||||
atom_ptr.name_offset = name_str_index;
|
||||
atom_ptr.alignment = required_alignment;
|
||||
atom_ptr.size = code.len;
|
||||
atom_ptr.output_section_index = output_section_index;
|
||||
|
||||
try atom_ptr.allocate(self);
|
||||
// TODO rename and re-audit this method
|
||||
errdefer self.freeDeclMetadata(sym_index);
|
||||
|
||||
local_sym.value = atom_ptr.value;
|
||||
local_esym.st_value = atom_ptr.value;
|
||||
|
||||
const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr;
|
||||
const file_offset = self.shdrs.items[output_section_index].sh_offset + section_offset;
|
||||
try self.base.file.?.pwriteAll(code, file_offset);
|
||||
|
||||
return .{ .ok = sym_index };
|
||||
return self.zigObjectPtr().?.lowerUnnamedConst(self, typed_value, decl_index);
|
||||
}
|
||||
|
||||
pub fn updateExports(
|
||||
@@ -3572,107 +2864,13 @@ pub fn updateExports(
|
||||
@panic("Attempted to compile for object format that was disabled by build configuration");
|
||||
}
|
||||
if (self.llvm_object) |llvm_object| return llvm_object.updateExports(mod, exported, exports);
|
||||
|
||||
if (self.base.options.emit == null) return;
|
||||
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const gpa = self.base.allocator;
|
||||
const zig_module = self.file(self.zig_module_index.?).?.zig_module;
|
||||
const metadata = switch (exported) {
|
||||
.decl_index => |decl_index| blk: {
|
||||
_ = try self.getOrCreateMetadataForDecl(decl_index);
|
||||
break :blk self.decls.getPtr(decl_index).?;
|
||||
},
|
||||
.value => |value| self.anon_decls.getPtr(value) orelse blk: {
|
||||
const first_exp = exports[0];
|
||||
const res = try self.lowerAnonDecl(value, .none, first_exp.getSrcLoc(mod));
|
||||
switch (res) {
|
||||
.ok => {},
|
||||
.fail => |em| {
|
||||
// TODO maybe it's enough to return an error here and let Module.processExportsInner
|
||||
// handle the error?
|
||||
try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);
|
||||
mod.failed_exports.putAssumeCapacityNoClobber(first_exp, em);
|
||||
return;
|
||||
},
|
||||
}
|
||||
break :blk self.anon_decls.getPtr(value).?;
|
||||
},
|
||||
};
|
||||
const sym_index = metadata.symbol_index;
|
||||
const esym_index = self.symbol(sym_index).esym_index;
|
||||
const esym = zig_module.local_esyms.items(.elf_sym)[esym_index];
|
||||
const esym_shndx = zig_module.local_esyms.items(.shndx)[esym_index];
|
||||
|
||||
for (exports) |exp| {
|
||||
if (exp.opts.section.unwrap()) |section_name| {
|
||||
if (!mod.intern_pool.stringEqlSlice(section_name, ".text")) {
|
||||
try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);
|
||||
mod.failed_exports.putAssumeCapacityNoClobber(exp, try Module.ErrorMsg.create(
|
||||
gpa,
|
||||
exp.getSrcLoc(mod),
|
||||
"Unimplemented: ExportOptions.section",
|
||||
.{},
|
||||
));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const stb_bits: u8 = switch (exp.opts.linkage) {
|
||||
.Internal => elf.STB_LOCAL,
|
||||
.Strong => elf.STB_GLOBAL,
|
||||
.Weak => elf.STB_WEAK,
|
||||
.LinkOnce => {
|
||||
try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);
|
||||
mod.failed_exports.putAssumeCapacityNoClobber(exp, try Module.ErrorMsg.create(
|
||||
gpa,
|
||||
exp.getSrcLoc(mod),
|
||||
"Unimplemented: GlobalLinkage.LinkOnce",
|
||||
.{},
|
||||
));
|
||||
continue;
|
||||
},
|
||||
};
|
||||
const stt_bits: u8 = @as(u4, @truncate(esym.st_info));
|
||||
const exp_name = mod.intern_pool.stringToSlice(exp.opts.name);
|
||||
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 zig_module.addGlobalEsym(gpa);
|
||||
const lookup_gop = try zig_module.globals_lookup.getOrPut(gpa, name_off);
|
||||
const global_esym = zig_module.elfSym(global_esym_index);
|
||||
global_esym.st_name = name_off;
|
||||
lookup_gop.value_ptr.* = global_esym_index;
|
||||
try metadata.exports.append(gpa, global_esym_index);
|
||||
const gop = try self.getOrPutGlobal(name_off);
|
||||
try zig_module.global_symbols.append(gpa, gop.index);
|
||||
break :blk global_esym_index;
|
||||
};
|
||||
|
||||
const actual_esym_index = global_esym_index & ZigModule.symbol_mask;
|
||||
const global_esym = &zig_module.global_esyms.items(.elf_sym)[actual_esym_index];
|
||||
global_esym.st_value = self.symbol(sym_index).value;
|
||||
global_esym.st_shndx = esym.st_shndx;
|
||||
global_esym.st_info = (stb_bits << 4) | stt_bits;
|
||||
global_esym.st_name = name_off;
|
||||
zig_module.global_esyms.items(.shndx)[actual_esym_index] = esym_shndx;
|
||||
}
|
||||
return self.zigObjectPtr().?.updateExports(self, mod, exported, exports);
|
||||
}
|
||||
|
||||
/// Must be called only after a successful call to `updateDecl`.
|
||||
pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl_index: Module.Decl.Index) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const decl = mod.declPtr(decl_index);
|
||||
const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
|
||||
|
||||
log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });
|
||||
|
||||
if (self.llvm_object) |_| return;
|
||||
if (self.dwarf) |*dw| {
|
||||
try dw.updateDeclLineNumber(mod, decl_index);
|
||||
}
|
||||
return self.zigObjectPtr().?.updateDeclLineNumber(mod, decl_index);
|
||||
}
|
||||
|
||||
pub fn deleteDeclExport(
|
||||
@@ -3681,22 +2879,7 @@ pub fn deleteDeclExport(
|
||||
name: InternPool.NullTerminatedString,
|
||||
) void {
|
||||
if (self.llvm_object) |_| return;
|
||||
const metadata = self.decls.getPtr(decl_index) orelse return;
|
||||
const mod = self.base.options.module.?;
|
||||
const zig_module = self.file(self.zig_module_index.?).?.zig_module;
|
||||
const exp_name = mod.intern_pool.stringToSlice(name);
|
||||
const esym_index = metadata.@"export"(self, exp_name) orelse return;
|
||||
log.debug("deleting export '{s}'", .{exp_name});
|
||||
const esym = &zig_module.global_esyms.items(.elf_sym)[esym_index.*];
|
||||
_ = zig_module.globals_lookup.remove(esym.st_name);
|
||||
const sym_index = self.resolver.get(esym.st_name).?;
|
||||
const sym = self.symbol(sym_index);
|
||||
if (sym.file_index == zig_module.index) {
|
||||
_ = self.resolver.swapRemove(esym.st_name);
|
||||
sym.* = .{};
|
||||
}
|
||||
esym.* = null_sym;
|
||||
zig_module.global_esyms.items(.shndx)[esym_index.*] = elf.SHN_UNDEF;
|
||||
return self.zigObjectPtr().?.deleteDeclExport(self, decl_index, name);
|
||||
}
|
||||
|
||||
fn addLinkerDefinedSymbols(self: *Elf) !void {
|
||||
@@ -3917,8 +3100,8 @@ fn initSections(self: *Elf) !void {
|
||||
const needs_rela_dyn = blk: {
|
||||
if (self.got.flags.needs_rela or self.got.flags.needs_tlsld or
|
||||
self.zig_got.flags.needs_rela or self.copy_rel.symbols.items.len > 0) break :blk true;
|
||||
if (self.zig_module_index) |index| {
|
||||
if (self.file(index).?.zig_module.num_dynrelocs > 0) break :blk true;
|
||||
if (self.zigObjectPtr()) |zig_object| {
|
||||
if (zig_object.num_dynrelocs > 0) break :blk true;
|
||||
}
|
||||
for (self.objects.items) |index| {
|
||||
if (self.file(index).?.object.num_dynrelocs > 0) break :blk true;
|
||||
@@ -4512,16 +3695,15 @@ fn sortShdrs(self: *Elf) !void {
|
||||
}
|
||||
}
|
||||
|
||||
if (self.zig_module_index) |index| {
|
||||
const zig_module = self.file(index).?.zig_module;
|
||||
for (zig_module.atoms.items) |atom_index| {
|
||||
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];
|
||||
}
|
||||
|
||||
for (zig_module.locals()) |local_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;
|
||||
@@ -4529,35 +3711,17 @@ fn sortShdrs(self: *Elf) !void {
|
||||
local.output_section_index = backlinks[out_shndx];
|
||||
}
|
||||
|
||||
for (zig_module.globals()) |global_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;
|
||||
if (global.file(self).?.index() != index) continue;
|
||||
if (global.file(self).?.index() != zig_object.index) continue;
|
||||
const out_shndx = global.outputShndx() orelse continue;
|
||||
global.output_section_index = backlinks[out_shndx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn saveDebugSectionsSizes(self: *Elf) void {
|
||||
if (self.debug_info_section_index) |shndx| {
|
||||
self.debug_info_section_zig_size = self.shdrs.items[shndx].sh_size;
|
||||
}
|
||||
if (self.debug_abbrev_section_index) |shndx| {
|
||||
self.debug_abbrev_section_zig_size = self.shdrs.items[shndx].sh_size;
|
||||
}
|
||||
if (self.debug_str_section_index) |shndx| {
|
||||
self.debug_str_section_zig_size = self.shdrs.items[shndx].sh_size;
|
||||
}
|
||||
if (self.debug_aranges_section_index) |shndx| {
|
||||
self.debug_aranges_section_zig_size = self.shdrs.items[shndx].sh_size;
|
||||
}
|
||||
if (self.debug_line_section_index) |shndx| {
|
||||
self.debug_line_section_zig_size = self.shdrs.items[shndx].sh_size;
|
||||
}
|
||||
}
|
||||
|
||||
fn updateSectionSizes(self: *Elf) !void {
|
||||
for (self.output_sections.keys(), self.output_sections.values()) |shndx, atom_list| {
|
||||
if (atom_list.items.len == 0) continue;
|
||||
@@ -4599,8 +3763,8 @@ fn updateSectionSizes(self: *Elf) !void {
|
||||
|
||||
if (self.rela_dyn_section_index) |shndx| {
|
||||
var num = self.got.numRela(self) + self.copy_rel.numRela() + self.zig_got.numRela();
|
||||
if (self.zig_module_index) |index| {
|
||||
num += self.file(index).?.zig_module.num_dynrelocs;
|
||||
if (self.zigObjectPtr()) |zig_object| {
|
||||
num += zig_object.num_dynrelocs;
|
||||
}
|
||||
for (self.objects.items) |index| {
|
||||
num += self.file(index).?.object.num_dynrelocs;
|
||||
@@ -4914,12 +4078,18 @@ fn allocateNonAllocSections(self: *Elf) !void {
|
||||
shdr.sh_offset,
|
||||
new_offset,
|
||||
});
|
||||
const zig_object = self.zigObjectPtr().?;
|
||||
const existing_size = blk: {
|
||||
if (shndx == self.debug_info_section_index.?) break :blk self.debug_info_section_zig_size;
|
||||
if (shndx == self.debug_abbrev_section_index.?) break :blk self.debug_abbrev_section_zig_size;
|
||||
if (shndx == self.debug_str_section_index.?) break :blk self.debug_str_section_zig_size;
|
||||
if (shndx == self.debug_aranges_section_index.?) break :blk self.debug_aranges_section_zig_size;
|
||||
if (shndx == self.debug_line_section_index.?) break :blk self.debug_line_section_zig_size;
|
||||
if (shndx == self.debug_info_section_index.?)
|
||||
break :blk zig_object.debug_info_section_zig_size;
|
||||
if (shndx == self.debug_abbrev_section_index.?)
|
||||
break :blk zig_object.debug_abbrev_section_zig_size;
|
||||
if (shndx == self.debug_str_section_index.?)
|
||||
break :blk zig_object.debug_str_section_zig_size;
|
||||
if (shndx == self.debug_aranges_section_index.?)
|
||||
break :blk zig_object.debug_aranges_section_zig_size;
|
||||
if (shndx == self.debug_line_section_index.?)
|
||||
break :blk zig_object.debug_line_section_zig_size;
|
||||
unreachable;
|
||||
};
|
||||
const amt = try self.base.file.?.copyRangeAll(
|
||||
@@ -5021,11 +4191,17 @@ fn writeAtoms(self: *Elf) !void {
|
||||
|
||||
// TODO really, really handle debug section separately
|
||||
const base_offset = if (self.isDebugSection(@intCast(shndx))) blk: {
|
||||
if (shndx == self.debug_info_section_index.?) break :blk self.debug_info_section_zig_size;
|
||||
if (shndx == self.debug_abbrev_section_index.?) break :blk self.debug_abbrev_section_zig_size;
|
||||
if (shndx == self.debug_str_section_index.?) break :blk self.debug_str_section_zig_size;
|
||||
if (shndx == self.debug_aranges_section_index.?) break :blk self.debug_aranges_section_zig_size;
|
||||
if (shndx == self.debug_line_section_index.?) break :blk self.debug_line_section_zig_size;
|
||||
const zig_object = self.zigObjectPtr().?;
|
||||
if (shndx == self.debug_info_section_index.?)
|
||||
break :blk zig_object.debug_info_section_zig_size;
|
||||
if (shndx == self.debug_abbrev_section_index.?)
|
||||
break :blk zig_object.debug_abbrev_section_zig_size;
|
||||
if (shndx == self.debug_str_section_index.?)
|
||||
break :blk zig_object.debug_str_section_zig_size;
|
||||
if (shndx == self.debug_aranges_section_index.?)
|
||||
break :blk zig_object.debug_aranges_section_zig_size;
|
||||
if (shndx == self.debug_line_section_index.?)
|
||||
break :blk zig_object.debug_line_section_zig_size;
|
||||
unreachable;
|
||||
} else 0;
|
||||
const sh_offset = shdr.sh_offset + base_offset;
|
||||
@@ -5079,11 +4255,10 @@ fn writeAtoms(self: *Elf) !void {
|
||||
fn updateSymtabSize(self: *Elf) !void {
|
||||
var sizes = SymtabSize{};
|
||||
|
||||
if (self.zig_module_index) |index| {
|
||||
const zig_module = self.file(index).?.zig_module;
|
||||
zig_module.updateSymtabSize(self);
|
||||
sizes.nlocals += zig_module.output_symtab_size.nlocals;
|
||||
sizes.nglobals += zig_module.output_symtab_size.nglobals;
|
||||
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;
|
||||
}
|
||||
|
||||
for (self.objects.items) |index| {
|
||||
@@ -5299,11 +4474,10 @@ fn writeSymtab(self: *Elf) !void {
|
||||
.symtab = symtab,
|
||||
};
|
||||
|
||||
if (self.zig_module_index) |index| {
|
||||
const zig_module = self.file(index).?.zig_module;
|
||||
zig_module.writeSymtab(self, ctx);
|
||||
ctx.ilocal += zig_module.output_symtab_size.nlocals;
|
||||
ctx.iglobal += zig_module.output_symtab_size.nglobals;
|
||||
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;
|
||||
}
|
||||
|
||||
for (self.objects.items) |index| {
|
||||
@@ -5863,7 +5037,7 @@ pub fn file(self: *Elf, index: File.Index) ?File {
|
||||
return switch (tag) {
|
||||
.null => null,
|
||||
.linker_defined => .{ .linker_defined = &self.files.items(.data)[index].linker_defined },
|
||||
.zig_module => .{ .zig_module = &self.files.items(.data)[index].zig_module },
|
||||
.zig_object => .{ .zig_object = &self.files.items(.data)[index].zig_object },
|
||||
.object => .{ .object = &self.files.items(.data)[index].object },
|
||||
.shared_object => .{ .shared_object = &self.files.items(.data)[index].shared_object },
|
||||
};
|
||||
@@ -5961,26 +5135,12 @@ pub fn globalByName(self: *Elf, name: []const u8) ?Symbol.Index {
|
||||
}
|
||||
|
||||
pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 {
|
||||
_ = lib_name;
|
||||
const gpa = self.base.allocator;
|
||||
const off = try self.strtab.insert(gpa, name);
|
||||
const zig_module = self.file(self.zig_module_index.?).?.zig_module;
|
||||
const lookup_gop = try zig_module.globals_lookup.getOrPut(gpa, off);
|
||||
if (!lookup_gop.found_existing) {
|
||||
const esym_index = try zig_module.addGlobalEsym(gpa);
|
||||
const esym = zig_module.elfSym(esym_index);
|
||||
esym.st_name = off;
|
||||
lookup_gop.value_ptr.* = esym_index;
|
||||
const gop = try self.getOrPutGlobal(off);
|
||||
try zig_module.global_symbols.append(gpa, gop.index);
|
||||
}
|
||||
return lookup_gop.value_ptr.*;
|
||||
return self.zigObjectPtr().?.getGlobalSymbol(self, name, lib_name);
|
||||
}
|
||||
|
||||
pub fn zigModulePtr(self: *Elf) *ZigModule {
|
||||
assert(self.zig_module_index != null);
|
||||
const file_ptr = self.file(self.zig_module_index.?).?;
|
||||
return file_ptr.zig_module;
|
||||
pub fn zigObjectPtr(self: *Elf) ?*ZigObject {
|
||||
const index = self.zig_object_index orelse return null;
|
||||
return self.file(index).?.zig_object;
|
||||
}
|
||||
|
||||
const GetOrCreateComdatGroupOwnerResult = struct {
|
||||
@@ -6245,12 +5405,11 @@ fn fmtDumpState(
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
|
||||
if (self.zig_module_index) |index| {
|
||||
const zig_module = self.file(index).?.zig_module;
|
||||
try writer.print("zig_module({d}) : {s}\n", .{ index, zig_module.path });
|
||||
if (self.zigObjectPtr()) |zig_object| {
|
||||
try writer.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.path });
|
||||
try writer.print("{}{}\n", .{
|
||||
zig_module.fmtAtoms(self),
|
||||
zig_module.fmtSymtab(self),
|
||||
zig_object.fmtAtoms(self),
|
||||
zig_object.fmtSymtab(self),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6343,51 +5502,6 @@ const default_entry_addr = 0x8000000;
|
||||
|
||||
pub const base_tag: link.File.Tag = .elf;
|
||||
|
||||
const LastAtomAndFreeList = struct {
|
||||
/// Index of the last allocated atom in this section.
|
||||
last_atom_index: Atom.Index = 0,
|
||||
|
||||
/// A list of atoms that have surplus capacity. This list can have false
|
||||
/// positives, as functions grow and shrink over time, only sometimes being added
|
||||
/// or removed from the freelist.
|
||||
///
|
||||
/// An atom has surplus capacity when its overcapacity value is greater than
|
||||
/// padToIdeal(minimum_atom_size). That is, when it has so
|
||||
/// much extra capacity, that we could fit a small new symbol in it, itself with
|
||||
/// ideal_capacity or more.
|
||||
///
|
||||
/// Ideal capacity is defined by size + (size / ideal_factor)
|
||||
///
|
||||
/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
|
||||
/// overcapacity can be negative. A simple way to have negative overcapacity is to
|
||||
/// allocate a fresh text block, which will have ideal capacity, and then grow it
|
||||
/// by 1 byte. It will then have -1 overcapacity.
|
||||
free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
|
||||
};
|
||||
|
||||
const LazySymbolMetadata = struct {
|
||||
const State = enum { unused, pending_flush, flushed };
|
||||
text_symbol_index: Symbol.Index = undefined,
|
||||
rodata_symbol_index: Symbol.Index = undefined,
|
||||
text_state: State = .unused,
|
||||
rodata_state: State = .unused,
|
||||
};
|
||||
|
||||
const DeclMetadata = struct {
|
||||
symbol_index: Symbol.Index,
|
||||
/// A list of all exports aliases of this Decl.
|
||||
exports: std.ArrayListUnmanaged(Symbol.Index) = .{},
|
||||
|
||||
fn @"export"(m: DeclMetadata, elf_file: *Elf, name: []const u8) ?*u32 {
|
||||
const zig_module = elf_file.file(elf_file.zig_module_index.?).?.zig_module;
|
||||
for (m.exports.items) |*exp| {
|
||||
const exp_name = elf_file.strtab.getAssumeExists(zig_module.elfSym(exp.*).st_name);
|
||||
if (mem.eql(u8, name, exp_name)) return exp;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const ComdatGroupOwner = struct {
|
||||
file: File.Index = 0,
|
||||
const Index = u32;
|
||||
@@ -6431,6 +5545,30 @@ pub const SystemLib = struct {
|
||||
path: []const u8,
|
||||
};
|
||||
|
||||
const LastAtomAndFreeList = struct {
|
||||
/// Index of the last allocated atom in this section.
|
||||
last_atom_index: Atom.Index = 0,
|
||||
|
||||
/// A list of atoms that have surplus capacity. This list can have false
|
||||
/// positives, as functions grow and shrink over time, only sometimes being added
|
||||
/// or removed from the freelist.
|
||||
///
|
||||
/// An atom has surplus capacity when its overcapacity value is greater than
|
||||
/// padToIdeal(minimum_atom_size). That is, when it has so
|
||||
/// much extra capacity, that we could fit a small new symbol in it, itself with
|
||||
/// ideal_capacity or more.
|
||||
///
|
||||
/// Ideal capacity is defined by size + (size / ideal_factor)
|
||||
///
|
||||
/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
|
||||
/// overcapacity can be negative. A simple way to have negative overcapacity is to
|
||||
/// allocate a fresh text block, which will have ideal capacity, and then grow it
|
||||
/// by 1 byte. It will then have -1 overcapacity.
|
||||
free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
|
||||
};
|
||||
|
||||
const LastAtomAndFreeListTable = std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFreeList);
|
||||
|
||||
pub const R_X86_64_ZIG_GOT32 = elf.R_X86_64_NUM + 1;
|
||||
pub const R_X86_64_ZIG_GOTPCREL = elf.R_X86_64_NUM + 2;
|
||||
|
||||
@@ -6479,16 +5617,12 @@ const LlvmObject = @import("../codegen/llvm.zig").Object;
|
||||
const Module = @import("../Module.zig");
|
||||
const Object = @import("Elf/Object.zig");
|
||||
const InternPool = @import("../InternPool.zig");
|
||||
const Package = @import("../Package.zig");
|
||||
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 TableSection = @import("table_section.zig").TableSection;
|
||||
const Type = @import("../type.zig").Type;
|
||||
const TypedValue = @import("../TypedValue.zig");
|
||||
const Value = @import("../value.zig").Value;
|
||||
const VerneedSection = synthetic_sections.VerneedSection;
|
||||
const ZigGotSection = synthetic_sections.ZigGotSection;
|
||||
const ZigModule = @import("Elf/ZigModule.zig");
|
||||
const ZigObject = @import("Elf/ZigObject.zig");
|
||||
|
||||
+18
-17
@@ -52,7 +52,7 @@ pub fn file(self: Atom, elf_file: *Elf) ?File {
|
||||
pub fn inputShdr(self: Atom, elf_file: *Elf) Object.ElfShdr {
|
||||
return switch (self.file(elf_file).?) {
|
||||
.object => |x| x.shdrs.items[self.input_section_index],
|
||||
.zig_module => |x| x.inputShdr(self.atom_index, elf_file),
|
||||
.zig_object => |x| x.inputShdr(self.atom_index, elf_file),
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
@@ -166,15 +166,16 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
|
||||
try elf_file.growAllocSection(self.outputShndx().?, needed_size);
|
||||
last_atom_index.* = self.atom_index;
|
||||
|
||||
if (elf_file.dwarf) |_| {
|
||||
const zig_object = elf_file.zigObjectPtr().?;
|
||||
if (zig_object.dwarf) |_| {
|
||||
// The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
|
||||
// range of the compilation unit. When we expand the text section, this range changes,
|
||||
// so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty.
|
||||
elf_file.debug_info_header_dirty = true;
|
||||
zig_object.debug_info_header_dirty = true;
|
||||
// This becomes dirty for the same reason. We could potentially make this more
|
||||
// fine-grained with the addition of support for more compilation units. It is planned to
|
||||
// model each package as a different compilation unit.
|
||||
elf_file.debug_aranges_section_dirty = true;
|
||||
zig_object.debug_aranges_section_dirty = true;
|
||||
}
|
||||
}
|
||||
shdr.sh_addralign = @max(shdr.sh_addralign, self.alignment.toByteUnitsOptional().?);
|
||||
@@ -270,14 +271,14 @@ pub fn free(self: *Atom, elf_file: *Elf) void {
|
||||
// TODO create relocs free list
|
||||
self.freeRelocs(elf_file);
|
||||
// TODO figure out how to free input section mappind in ZigModule
|
||||
// const zig_module = self.file(elf_file).?.zig_module;
|
||||
// assert(zig_module.atoms.swapRemove(self.atom_index));
|
||||
// const zig_object = elf_file.zigObjectPtr().?
|
||||
// assert(zig_object.atoms.swapRemove(self.atom_index));
|
||||
self.* = .{};
|
||||
}
|
||||
|
||||
pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
|
||||
return switch (self.file(elf_file).?) {
|
||||
.zig_module => |x| x.relocs.items[self.relocs_section_index].items,
|
||||
.zig_object => |x| x.relocs.items[self.relocs_section_index].items,
|
||||
.object => |x| x.getRelocs(self.relocs_section_index),
|
||||
else => unreachable,
|
||||
};
|
||||
@@ -298,17 +299,17 @@ pub fn markFdesDead(self: Atom, elf_file: *Elf) void {
|
||||
pub fn addReloc(self: Atom, elf_file: *Elf, reloc: elf.Elf64_Rela) !void {
|
||||
const gpa = elf_file.base.allocator;
|
||||
const file_ptr = self.file(elf_file).?;
|
||||
assert(file_ptr == .zig_module);
|
||||
const zig_module = file_ptr.zig_module;
|
||||
const rels = &zig_module.relocs.items[self.relocs_section_index];
|
||||
assert(file_ptr == .zig_object);
|
||||
const zig_object = file_ptr.zig_object;
|
||||
const rels = &zig_object.relocs.items[self.relocs_section_index];
|
||||
try rels.append(gpa, reloc);
|
||||
}
|
||||
|
||||
pub fn freeRelocs(self: Atom, elf_file: *Elf) void {
|
||||
const file_ptr = self.file(elf_file).?;
|
||||
assert(file_ptr == .zig_module);
|
||||
const zig_module = file_ptr.zig_module;
|
||||
zig_module.relocs.items[self.relocs_section_index].clearRetainingCapacity();
|
||||
assert(file_ptr == .zig_object);
|
||||
const zig_object = file_ptr.zig_object;
|
||||
zig_object.relocs.items[self.relocs_section_index].clearRetainingCapacity();
|
||||
}
|
||||
|
||||
pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) bool {
|
||||
@@ -332,7 +333,7 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
|
||||
const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
|
||||
|
||||
const symbol_index = switch (file_ptr) {
|
||||
.zig_module => |x| x.symbol(rel.r_sym()),
|
||||
.zig_object => |x| x.symbol(rel.r_sym()),
|
||||
.object => |x| x.symbols.items[rel.r_sym()],
|
||||
else => unreachable,
|
||||
};
|
||||
@@ -690,7 +691,7 @@ fn reportUndefined(
|
||||
undefs: anytype,
|
||||
) !void {
|
||||
const rel_esym = switch (self.file(elf_file).?) {
|
||||
.zig_module => |x| x.elfSym(rel.r_sym()).*,
|
||||
.zig_object => |x| x.elfSym(rel.r_sym()).*,
|
||||
.object => |x| x.symtab[rel.r_sym()],
|
||||
else => unreachable,
|
||||
};
|
||||
@@ -724,7 +725,7 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
|
||||
if (r_type == elf.R_X86_64_NONE) continue;
|
||||
|
||||
const target = switch (file_ptr) {
|
||||
.zig_module => |x| elf_file.symbol(x.symbol(rel.r_sym())),
|
||||
.zig_object => |x| elf_file.symbol(x.symbol(rel.r_sym())),
|
||||
.object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]),
|
||||
else => unreachable,
|
||||
};
|
||||
@@ -1004,7 +1005,7 @@ pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: any
|
||||
const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
|
||||
|
||||
const target_index = switch (file_ptr) {
|
||||
.zig_module => |x| x.symbol(rel.r_sym()),
|
||||
.zig_object => |x| x.symbol(rel.r_sym()),
|
||||
.object => |x| x.symbols.items[rel.r_sym()],
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
@@ -72,7 +72,7 @@ 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_module => |x| return x.elfSym(symbol.esym_index).*,
|
||||
.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],
|
||||
}
|
||||
@@ -406,4 +406,3 @@ const PltSection = synthetic_sections.PltSection;
|
||||
const SharedObject = @import("SharedObject.zig");
|
||||
const Symbol = @This();
|
||||
const ZigGotSection = synthetic_sections.ZigGotSection;
|
||||
const ZigModule = @import("ZigModule.zig");
|
||||
|
||||
@@ -1,381 +0,0 @@
|
||||
//! ZigModule encapsulates the state of the incrementally compiled Zig module.
|
||||
//! It stores the associated input local and global symbols, allocated atoms,
|
||||
//! and any relocations that may have been emitted.
|
||||
//! Think about this as fake in-memory Object file for the Zig module.
|
||||
|
||||
/// Path is owned by Module and lives as long as *Module.
|
||||
path: []const u8,
|
||||
index: File.Index,
|
||||
|
||||
local_esyms: std.MultiArrayList(ElfSym) = .{},
|
||||
global_esyms: std.MultiArrayList(ElfSym) = .{},
|
||||
local_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
|
||||
global_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
|
||||
globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{},
|
||||
|
||||
atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
|
||||
relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{},
|
||||
|
||||
num_dynrelocs: u32 = 0,
|
||||
|
||||
output_symtab_size: Elf.SymtabSize = .{},
|
||||
|
||||
pub const global_symbol_bit: u32 = 0x80000000;
|
||||
pub const symbol_mask: u32 = 0x7fffffff;
|
||||
pub const SHN_ATOM: u16 = 0x100;
|
||||
|
||||
pub fn deinit(self: *ZigModule, allocator: Allocator) void {
|
||||
self.local_esyms.deinit(allocator);
|
||||
self.global_esyms.deinit(allocator);
|
||||
self.local_symbols.deinit(allocator);
|
||||
self.global_symbols.deinit(allocator);
|
||||
self.globals_lookup.deinit(allocator);
|
||||
self.atoms.deinit(allocator);
|
||||
for (self.relocs.items) |*list| {
|
||||
list.deinit(allocator);
|
||||
}
|
||||
self.relocs.deinit(allocator);
|
||||
}
|
||||
|
||||
pub fn addLocalEsym(self: *ZigModule, allocator: Allocator) !Symbol.Index {
|
||||
try self.local_esyms.ensureUnusedCapacity(allocator, 1);
|
||||
const index = @as(Symbol.Index, @intCast(self.local_esyms.addOneAssumeCapacity()));
|
||||
var esym = ElfSym{ .elf_sym = Elf.null_sym };
|
||||
esym.elf_sym.st_info = elf.STB_LOCAL << 4;
|
||||
self.local_esyms.set(index, esym);
|
||||
return index;
|
||||
}
|
||||
|
||||
pub fn addGlobalEsym(self: *ZigModule, allocator: Allocator) !Symbol.Index {
|
||||
try self.global_esyms.ensureUnusedCapacity(allocator, 1);
|
||||
const index = @as(Symbol.Index, @intCast(self.global_esyms.addOneAssumeCapacity()));
|
||||
var esym = ElfSym{ .elf_sym = Elf.null_sym };
|
||||
esym.elf_sym.st_info = elf.STB_GLOBAL << 4;
|
||||
self.global_esyms.set(index, esym);
|
||||
return index | global_symbol_bit;
|
||||
}
|
||||
|
||||
pub fn addAtom(self: *ZigModule, elf_file: *Elf) !Symbol.Index {
|
||||
const gpa = elf_file.base.allocator;
|
||||
|
||||
const atom_index = try elf_file.addAtom();
|
||||
const symbol_index = try elf_file.addSymbol();
|
||||
const esym_index = try self.addLocalEsym(gpa);
|
||||
|
||||
const shndx = @as(u32, @intCast(self.atoms.items.len));
|
||||
try self.atoms.append(gpa, atom_index);
|
||||
try self.local_symbols.append(gpa, symbol_index);
|
||||
|
||||
const atom_ptr = elf_file.atom(atom_index).?;
|
||||
atom_ptr.file_index = self.index;
|
||||
|
||||
const symbol_ptr = elf_file.symbol(symbol_index);
|
||||
symbol_ptr.file_index = self.index;
|
||||
symbol_ptr.atom_index = atom_index;
|
||||
|
||||
self.local_esyms.items(.shndx)[esym_index] = shndx;
|
||||
self.local_esyms.items(.elf_sym)[esym_index].st_shndx = SHN_ATOM;
|
||||
symbol_ptr.esym_index = esym_index;
|
||||
|
||||
const relocs_index = @as(u32, @intCast(self.relocs.items.len));
|
||||
const relocs = try self.relocs.addOne(gpa);
|
||||
relocs.* = .{};
|
||||
atom_ptr.relocs_section_index = relocs_index;
|
||||
|
||||
return symbol_index;
|
||||
}
|
||||
|
||||
/// TODO actually create fake input shdrs and return that instead.
|
||||
pub fn inputShdr(self: ZigModule, atom_index: Atom.Index, elf_file: *Elf) Object.ElfShdr {
|
||||
_ = self;
|
||||
const shdr = shdr: {
|
||||
const atom = elf_file.atom(atom_index) orelse break :shdr Elf.null_shdr;
|
||||
const shndx = atom.outputShndx() orelse break :shdr Elf.null_shdr;
|
||||
var shdr = elf_file.shdrs.items[shndx];
|
||||
shdr.sh_addr = 0;
|
||||
shdr.sh_offset = 0;
|
||||
shdr.sh_size = atom.size;
|
||||
shdr.sh_addralign = atom.alignment.toByteUnits(1);
|
||||
break :shdr shdr;
|
||||
};
|
||||
return Object.ElfShdr.fromElf64Shdr(shdr) catch unreachable;
|
||||
}
|
||||
|
||||
pub fn resolveSymbols(self: *ZigModule, 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];
|
||||
const shndx = self.global_esyms.items(.shndx)[i];
|
||||
|
||||
if (esym.st_shndx == elf.SHN_UNDEF) continue;
|
||||
|
||||
if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) {
|
||||
assert(esym.st_shndx == SHN_ATOM);
|
||||
const atom_index = self.atoms.items[shndx];
|
||||
const atom = elf_file.atom(atom_index) orelse continue;
|
||||
if (!atom.flags.alive) continue;
|
||||
}
|
||||
|
||||
const global = elf_file.symbol(index);
|
||||
if (self.asFile().symbolRank(esym, false) < global.symbolRank(elf_file)) {
|
||||
const atom_index = switch (esym.st_shndx) {
|
||||
elf.SHN_ABS, elf.SHN_COMMON => 0,
|
||||
SHN_ATOM => self.atoms.items[shndx],
|
||||
else => unreachable,
|
||||
};
|
||||
const output_section_index = if (elf_file.atom(atom_index)) |atom|
|
||||
atom.outputShndx().?
|
||||
else
|
||||
elf.SHN_UNDEF;
|
||||
global.value = esym.st_value;
|
||||
global.atom_index = atom_index;
|
||||
global.esym_index = esym_index;
|
||||
global.file_index = self.index;
|
||||
global.output_section_index = output_section_index;
|
||||
global.version_index = elf_file.default_sym_version;
|
||||
if (esym.st_bind() == elf.STB_WEAK) global.flags.weak = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn claimUnresolved(self: *ZigModule, 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)) |_| {
|
||||
if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF) continue;
|
||||
}
|
||||
|
||||
const is_import = blk: {
|
||||
if (!elf_file.isDynLib()) break :blk false;
|
||||
const vis = @as(elf.STV, @enumFromInt(esym.st_other));
|
||||
if (vis == .HIDDEN) break :blk false;
|
||||
break :blk true;
|
||||
};
|
||||
|
||||
global.value = 0;
|
||||
global.atom_index = 0;
|
||||
global.esym_index = esym_index;
|
||||
global.file_index = self.index;
|
||||
global.version_index = if (is_import) elf.VER_NDX_LOCAL else elf_file.default_sym_version;
|
||||
global.flags.import = is_import;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scanRelocs(self: *ZigModule, elf_file: *Elf, undefs: anytype) !void {
|
||||
for (self.atoms.items) |atom_index| {
|
||||
const atom = elf_file.atom(atom_index) orelse continue;
|
||||
if (!atom.flags.alive) continue;
|
||||
const shdr = atom.inputShdr(elf_file);
|
||||
if (shdr.sh_type == elf.SHT_NOBITS) continue;
|
||||
if (atom.scanRelocsRequiresCode(elf_file)) {
|
||||
// TODO ideally we don't have to fetch the code here.
|
||||
// Perhaps it would make sense to save the code until flushModule where we
|
||||
// would free all of generated code?
|
||||
const code = try self.codeAlloc(elf_file, atom_index);
|
||||
defer elf_file.base.allocator.free(code);
|
||||
try atom.scanRelocs(elf_file, code, undefs);
|
||||
} else try atom.scanRelocs(elf_file, null, undefs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resetGlobals(self: *ZigModule, 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: *ZigModule, elf_file: *Elf) void {
|
||||
for (self.globals(), 0..) |index, i| {
|
||||
const esym = self.global_esyms.items(.elf_sym)[i];
|
||||
if (esym.st_bind() == elf.STB_WEAK) continue;
|
||||
|
||||
const global = elf_file.symbol(index);
|
||||
const file = global.file(elf_file) orelse continue;
|
||||
const should_keep = esym.st_shndx == elf.SHN_UNDEF or
|
||||
(esym.st_shndx == elf.SHN_COMMON and global.elfSym(elf_file).st_shndx != elf.SHN_COMMON);
|
||||
if (should_keep and !file.isAlive()) {
|
||||
file.setAlive();
|
||||
file.markLive(elf_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updateSymtabSize(self: *ZigModule, 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeSymtab(self: *ZigModule, 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 symbol(self: *ZigModule, index: Symbol.Index) Symbol.Index {
|
||||
const is_global = index & global_symbol_bit != 0;
|
||||
const actual_index = index & symbol_mask;
|
||||
if (is_global) return self.global_symbols.items[actual_index];
|
||||
return self.local_symbols.items[actual_index];
|
||||
}
|
||||
|
||||
pub fn elfSym(self: *ZigModule, 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];
|
||||
return &self.local_esyms.items(.elf_sym)[actual_index];
|
||||
}
|
||||
|
||||
pub fn locals(self: *ZigModule) []const Symbol.Index {
|
||||
return self.local_symbols.items;
|
||||
}
|
||||
|
||||
pub fn globals(self: *ZigModule) []const Symbol.Index {
|
||||
return self.global_symbols.items;
|
||||
}
|
||||
|
||||
pub fn asFile(self: *ZigModule) File {
|
||||
return .{ .zig_module = self };
|
||||
}
|
||||
|
||||
/// Returns atom's code.
|
||||
/// Caller owns the memory.
|
||||
pub fn codeAlloc(self: ZigModule, elf_file: *Elf, atom_index: Atom.Index) ![]u8 {
|
||||
const gpa = elf_file.base.allocator;
|
||||
const atom = elf_file.atom(atom_index).?;
|
||||
assert(atom.file_index == self.index);
|
||||
const shdr = &elf_file.shdrs.items[atom.outputShndx().?];
|
||||
const file_offset = shdr.sh_offset + atom.value - shdr.sh_addr;
|
||||
const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
|
||||
const code = try gpa.alloc(u8, size);
|
||||
errdefer gpa.free(code);
|
||||
const amt = try elf_file.base.file.?.preadAll(code, file_offset);
|
||||
if (amt != code.len) {
|
||||
log.err("fetching code for {s} failed", .{atom.name(elf_file)});
|
||||
return error.InputOutput;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
pub fn fmtSymtab(self: *ZigModule, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
|
||||
return .{ .data = .{
|
||||
.self = self,
|
||||
.elf_file = elf_file,
|
||||
} };
|
||||
}
|
||||
|
||||
const FormatContext = struct {
|
||||
self: *ZigModule,
|
||||
elf_file: *Elf,
|
||||
};
|
||||
|
||||
fn formatSymtab(
|
||||
ctx: FormatContext,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
try writer.writeAll(" locals\n");
|
||||
for (ctx.self.locals()) |index| {
|
||||
const local = ctx.elf_file.symbol(index);
|
||||
try writer.print(" {}\n", .{local.fmt(ctx.elf_file)});
|
||||
}
|
||||
try writer.writeAll(" globals\n");
|
||||
for (ctx.self.globals()) |index| {
|
||||
const global = ctx.elf_file.symbol(index);
|
||||
try writer.print(" {}\n", .{global.fmt(ctx.elf_file)});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmtAtoms(self: *ZigModule, elf_file: *Elf) std.fmt.Formatter(formatAtoms) {
|
||||
return .{ .data = .{
|
||||
.self = self,
|
||||
.elf_file = elf_file,
|
||||
} };
|
||||
}
|
||||
|
||||
fn formatAtoms(
|
||||
ctx: FormatContext,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
try writer.writeAll(" atoms\n");
|
||||
for (ctx.self.atoms.items) |atom_index| {
|
||||
const atom = ctx.elf_file.atom(atom_index) orelse continue;
|
||||
try writer.print(" {}\n", .{atom.fmt(ctx.elf_file)});
|
||||
}
|
||||
}
|
||||
|
||||
const ElfSym = struct {
|
||||
elf_sym: elf.Elf64_Sym,
|
||||
shndx: u32 = elf.SHN_UNDEF,
|
||||
};
|
||||
|
||||
const assert = std.debug.assert;
|
||||
const std = @import("std");
|
||||
const elf = std.elf;
|
||||
const log = std.log.scoped(.link);
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Atom = @import("Atom.zig");
|
||||
const Elf = @import("../Elf.zig");
|
||||
const File = @import("file.zig").File;
|
||||
const Module = @import("../../Module.zig");
|
||||
const Object = @import("Object.zig");
|
||||
const Symbol = @import("Symbol.zig");
|
||||
const ZigModule = @This();
|
||||
@@ -0,0 +1,1390 @@
|
||||
//! ZigObject encapsulates the state of the incrementally compiled Zig module.
|
||||
//! It stores the associated input local and global symbols, allocated atoms,
|
||||
//! and any relocations that may have been emitted.
|
||||
//! Think about this as fake in-memory Object file for the Zig module.
|
||||
|
||||
/// Path is owned by Module and lives as long as *Module.
|
||||
path: []const u8,
|
||||
index: File.Index,
|
||||
|
||||
local_esyms: std.MultiArrayList(ElfSym) = .{},
|
||||
global_esyms: std.MultiArrayList(ElfSym) = .{},
|
||||
local_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
|
||||
global_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
|
||||
globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{},
|
||||
|
||||
atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
|
||||
relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{},
|
||||
|
||||
num_dynrelocs: u32 = 0,
|
||||
|
||||
output_symtab_size: Elf.SymtabSize = .{},
|
||||
|
||||
dwarf: ?Dwarf = null,
|
||||
|
||||
/// Table of tracked LazySymbols.
|
||||
lazy_syms: LazySymbolTable = .{},
|
||||
|
||||
/// Table of tracked Decls.
|
||||
decls: DeclTable = .{},
|
||||
|
||||
/// Table of unnamed constants associated with a parent `Decl`.
|
||||
/// We store them here so that we can free the constants whenever the `Decl`
|
||||
/// needs updating or is freed.
|
||||
///
|
||||
/// For example,
|
||||
///
|
||||
/// ```zig
|
||||
/// const Foo = struct{
|
||||
/// a: u8,
|
||||
/// };
|
||||
///
|
||||
/// pub fn main() void {
|
||||
/// var foo = Foo{ .a = 1 };
|
||||
/// _ = foo;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// value assigned to label `foo` is an unnamed constant belonging/associated
|
||||
/// with `Decl` `main`, and lives as long as that `Decl`.
|
||||
unnamed_consts: UnnamedConstTable = .{},
|
||||
|
||||
/// Table of tracked AnonDecls.
|
||||
anon_decls: AnonDeclTable = .{},
|
||||
|
||||
debug_strtab_dirty: bool = false,
|
||||
debug_abbrev_section_dirty: bool = false,
|
||||
debug_aranges_section_dirty: bool = false,
|
||||
debug_info_header_dirty: bool = false,
|
||||
debug_line_header_dirty: bool = false,
|
||||
|
||||
/// Size contribution of Zig's metadata to each debug section.
|
||||
/// Used to track start of metadata from input object files.
|
||||
debug_info_section_zig_size: u64 = 0,
|
||||
debug_abbrev_section_zig_size: u64 = 0,
|
||||
debug_str_section_zig_size: u64 = 0,
|
||||
debug_aranges_section_zig_size: u64 = 0,
|
||||
debug_line_section_zig_size: u64 = 0,
|
||||
|
||||
pub const global_symbol_bit: u32 = 0x80000000;
|
||||
pub const symbol_mask: u32 = 0x7fffffff;
|
||||
pub const SHN_ATOM: u16 = 0x100;
|
||||
|
||||
pub fn init(self: *ZigObject, elf_file: *Elf) !void {
|
||||
const gpa = elf_file.base.allocator;
|
||||
|
||||
try self.atoms.append(gpa, 0); // null input section
|
||||
|
||||
const name_off = try elf_file.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);
|
||||
symbol_ptr.file_index = self.index;
|
||||
symbol_ptr.name_offset = name_off;
|
||||
|
||||
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_shndx = elf.SHN_ABS;
|
||||
symbol_ptr.esym_index = esym_index;
|
||||
|
||||
if (!elf_file.base.options.strip) {
|
||||
self.dwarf = Dwarf.init(gpa, &elf_file.base, .dwarf32);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *ZigObject, allocator: Allocator) void {
|
||||
self.local_esyms.deinit(allocator);
|
||||
self.global_esyms.deinit(allocator);
|
||||
self.local_symbols.deinit(allocator);
|
||||
self.global_symbols.deinit(allocator);
|
||||
self.globals_lookup.deinit(allocator);
|
||||
self.atoms.deinit(allocator);
|
||||
for (self.relocs.items) |*list| {
|
||||
list.deinit(allocator);
|
||||
}
|
||||
self.relocs.deinit(allocator);
|
||||
|
||||
{
|
||||
var it = self.decls.iterator();
|
||||
while (it.next()) |entry| {
|
||||
entry.value_ptr.exports.deinit(allocator);
|
||||
}
|
||||
self.decls.deinit(allocator);
|
||||
}
|
||||
|
||||
self.lazy_syms.deinit(allocator);
|
||||
|
||||
{
|
||||
var it = self.unnamed_consts.valueIterator();
|
||||
while (it.next()) |syms| {
|
||||
syms.deinit(allocator);
|
||||
}
|
||||
self.unnamed_consts.deinit(allocator);
|
||||
}
|
||||
|
||||
{
|
||||
var it = self.anon_decls.iterator();
|
||||
while (it.next()) |entry| {
|
||||
entry.value_ptr.exports.deinit(allocator);
|
||||
}
|
||||
self.anon_decls.deinit(allocator);
|
||||
}
|
||||
|
||||
if (self.dwarf) |*dw| {
|
||||
dw.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flushModule(self: *ZigObject, elf_file: *Elf) !void {
|
||||
// Handle any lazy symbols that were emitted by incremental compilation.
|
||||
if (self.lazy_syms.getPtr(.none)) |metadata| {
|
||||
const module = elf_file.base.options.module.?;
|
||||
|
||||
// Most lazy symbols can be updated on first use, but
|
||||
// anyerror needs to wait for everything to be flushed.
|
||||
if (metadata.text_state != .unused) self.updateLazySymbol(
|
||||
elf_file,
|
||||
link.File.LazySymbol.initDecl(.code, null, module),
|
||||
metadata.text_symbol_index,
|
||||
) catch |err| return switch (err) {
|
||||
error.CodegenFail => error.FlushFailure,
|
||||
else => |e| e,
|
||||
};
|
||||
if (metadata.rodata_state != .unused) self.updateLazySymbol(
|
||||
elf_file,
|
||||
link.File.LazySymbol.initDecl(.const_data, null, module),
|
||||
metadata.rodata_symbol_index,
|
||||
) catch |err| return switch (err) {
|
||||
error.CodegenFail => error.FlushFailure,
|
||||
else => |e| e,
|
||||
};
|
||||
}
|
||||
for (self.lazy_syms.values()) |*metadata| {
|
||||
if (metadata.text_state != .unused) metadata.text_state = .flushed;
|
||||
if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed;
|
||||
}
|
||||
|
||||
if (self.dwarf) |*dw| {
|
||||
try dw.flushModule(elf_file.base.options.module.?);
|
||||
|
||||
// TODO I need to re-think how to handle ZigObject's debug sections AND debug sections
|
||||
// extracted from input object files correctly.
|
||||
if (self.debug_abbrev_section_dirty) {
|
||||
try dw.writeDbgAbbrev();
|
||||
self.debug_abbrev_section_dirty = false;
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
self.debug_aranges_section_dirty = false;
|
||||
}
|
||||
|
||||
if (self.debug_line_header_dirty) {
|
||||
try dw.writeDbgLineHeader();
|
||||
self.debug_line_header_dirty = false;
|
||||
}
|
||||
|
||||
if (elf_file.debug_str_section_index) |shndx| {
|
||||
if (self.debug_strtab_dirty or dw.strtab.buffer.items.len != elf_file.shdrs.items[shndx].sh_size) {
|
||||
try elf_file.growNonAllocSection(shndx, dw.strtab.buffer.items.len, 1, false);
|
||||
const shdr = elf_file.shdrs.items[shndx];
|
||||
try elf_file.base.file.?.pwriteAll(dw.strtab.buffer.items, shdr.sh_offset);
|
||||
self.debug_strtab_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
self.saveDebugSectionsSizes(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,
|
||||
// such as debug_line_header_dirty and debug_info_header_dirty.
|
||||
assert(!self.debug_abbrev_section_dirty);
|
||||
assert(!self.debug_aranges_section_dirty);
|
||||
assert(!self.debug_strtab_dirty);
|
||||
}
|
||||
|
||||
fn saveDebugSectionsSizes(self: *ZigObject, elf_file: *Elf) void {
|
||||
if (elf_file.debug_info_section_index) |shndx| {
|
||||
self.debug_info_section_zig_size = elf_file.shdrs.items[shndx].sh_size;
|
||||
}
|
||||
if (elf_file.debug_abbrev_section_index) |shndx| {
|
||||
self.debug_abbrev_section_zig_size = elf_file.shdrs.items[shndx].sh_size;
|
||||
}
|
||||
if (elf_file.debug_str_section_index) |shndx| {
|
||||
self.debug_str_section_zig_size = elf_file.shdrs.items[shndx].sh_size;
|
||||
}
|
||||
if (elf_file.debug_aranges_section_index) |shndx| {
|
||||
self.debug_aranges_section_zig_size = elf_file.shdrs.items[shndx].sh_size;
|
||||
}
|
||||
if (elf_file.debug_line_section_index) |shndx| {
|
||||
self.debug_line_section_zig_size = elf_file.shdrs.items[shndx].sh_size;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addLocalEsym(self: *ZigObject, allocator: Allocator) !Symbol.Index {
|
||||
try self.local_esyms.ensureUnusedCapacity(allocator, 1);
|
||||
const index = @as(Symbol.Index, @intCast(self.local_esyms.addOneAssumeCapacity()));
|
||||
var esym = ElfSym{ .elf_sym = Elf.null_sym };
|
||||
esym.elf_sym.st_info = elf.STB_LOCAL << 4;
|
||||
self.local_esyms.set(index, esym);
|
||||
return index;
|
||||
}
|
||||
|
||||
pub fn addGlobalEsym(self: *ZigObject, allocator: Allocator) !Symbol.Index {
|
||||
try self.global_esyms.ensureUnusedCapacity(allocator, 1);
|
||||
const index = @as(Symbol.Index, @intCast(self.global_esyms.addOneAssumeCapacity()));
|
||||
var esym = ElfSym{ .elf_sym = Elf.null_sym };
|
||||
esym.elf_sym.st_info = elf.STB_GLOBAL << 4;
|
||||
self.global_esyms.set(index, esym);
|
||||
return index | global_symbol_bit;
|
||||
}
|
||||
|
||||
pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index {
|
||||
const gpa = elf_file.base.allocator;
|
||||
const atom_index = try elf_file.addAtom();
|
||||
const symbol_index = try elf_file.addSymbol();
|
||||
const esym_index = try self.addLocalEsym(gpa);
|
||||
|
||||
const shndx = @as(u32, @intCast(self.atoms.items.len));
|
||||
try self.atoms.append(gpa, atom_index);
|
||||
try self.local_symbols.append(gpa, symbol_index);
|
||||
|
||||
const atom_ptr = elf_file.atom(atom_index).?;
|
||||
atom_ptr.file_index = self.index;
|
||||
|
||||
const symbol_ptr = elf_file.symbol(symbol_index);
|
||||
symbol_ptr.file_index = self.index;
|
||||
symbol_ptr.atom_index = atom_index;
|
||||
|
||||
self.local_esyms.items(.shndx)[esym_index] = shndx;
|
||||
self.local_esyms.items(.elf_sym)[esym_index].st_shndx = SHN_ATOM;
|
||||
symbol_ptr.esym_index = esym_index;
|
||||
|
||||
const relocs_index = @as(u32, @intCast(self.relocs.items.len));
|
||||
const relocs = try self.relocs.addOne(gpa);
|
||||
relocs.* = .{};
|
||||
atom_ptr.relocs_section_index = relocs_index;
|
||||
|
||||
return symbol_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;
|
||||
const shdr = shdr: {
|
||||
const atom = elf_file.atom(atom_index) orelse break :shdr Elf.null_shdr;
|
||||
const shndx = atom.outputShndx() orelse break :shdr Elf.null_shdr;
|
||||
var shdr = elf_file.shdrs.items[shndx];
|
||||
shdr.sh_addr = 0;
|
||||
shdr.sh_offset = 0;
|
||||
shdr.sh_size = atom.size;
|
||||
shdr.sh_addralign = atom.alignment.toByteUnits(1);
|
||||
break :shdr shdr;
|
||||
};
|
||||
return Object.ElfShdr.fromElf64Shdr(shdr) catch unreachable;
|
||||
}
|
||||
|
||||
pub fn resolveSymbols(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];
|
||||
const shndx = self.global_esyms.items(.shndx)[i];
|
||||
|
||||
if (esym.st_shndx == elf.SHN_UNDEF) continue;
|
||||
|
||||
if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) {
|
||||
assert(esym.st_shndx == SHN_ATOM);
|
||||
const atom_index = self.atoms.items[shndx];
|
||||
const atom = elf_file.atom(atom_index) orelse continue;
|
||||
if (!atom.flags.alive) continue;
|
||||
}
|
||||
|
||||
const global = elf_file.symbol(index);
|
||||
if (self.asFile().symbolRank(esym, false) < global.symbolRank(elf_file)) {
|
||||
const atom_index = switch (esym.st_shndx) {
|
||||
elf.SHN_ABS, elf.SHN_COMMON => 0,
|
||||
SHN_ATOM => self.atoms.items[shndx],
|
||||
else => unreachable,
|
||||
};
|
||||
const output_section_index = if (elf_file.atom(atom_index)) |atom|
|
||||
atom.outputShndx().?
|
||||
else
|
||||
elf.SHN_UNDEF;
|
||||
global.value = esym.st_value;
|
||||
global.atom_index = atom_index;
|
||||
global.esym_index = esym_index;
|
||||
global.file_index = self.index;
|
||||
global.output_section_index = output_section_index;
|
||||
global.version_index = elf_file.default_sym_version;
|
||||
if (esym.st_bind() == elf.STB_WEAK) global.flags.weak = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
if (esym.st_shndx != elf.SHN_UNDEF) continue;
|
||||
|
||||
const global = elf_file.symbol(index);
|
||||
if (global.file(elf_file)) |_| {
|
||||
if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF) continue;
|
||||
}
|
||||
|
||||
const is_import = blk: {
|
||||
if (!elf_file.isDynLib()) break :blk false;
|
||||
const vis = @as(elf.STV, @enumFromInt(esym.st_other));
|
||||
if (vis == .HIDDEN) break :blk false;
|
||||
break :blk true;
|
||||
};
|
||||
|
||||
global.value = 0;
|
||||
global.atom_index = 0;
|
||||
global.esym_index = esym_index;
|
||||
global.file_index = self.index;
|
||||
global.version_index = if (is_import) elf.VER_NDX_LOCAL else elf_file.default_sym_version;
|
||||
global.flags.import = is_import;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if (!atom.flags.alive) continue;
|
||||
const shdr = atom.inputShdr(elf_file);
|
||||
if (shdr.sh_type == elf.SHT_NOBITS) continue;
|
||||
if (atom.scanRelocsRequiresCode(elf_file)) {
|
||||
// TODO ideally we don't have to fetch the code here.
|
||||
// Perhaps it would make sense to save the code until flushModule where we
|
||||
// would free all of generated code?
|
||||
const code = try self.codeAlloc(elf_file, atom_index);
|
||||
defer elf_file.base.allocator.free(code);
|
||||
try atom.scanRelocs(elf_file, code, undefs);
|
||||
} else try atom.scanRelocs(elf_file, null, undefs);
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
if (esym.st_bind() == elf.STB_WEAK) continue;
|
||||
|
||||
const global = elf_file.symbol(index);
|
||||
const file = global.file(elf_file) orelse continue;
|
||||
const should_keep = esym.st_shndx == elf.SHN_UNDEF or
|
||||
(esym.st_shndx == elf.SHN_COMMON and global.elfSym(elf_file).st_shndx != elf.SHN_COMMON);
|
||||
if (should_keep and !file.isAlive()) {
|
||||
file.setAlive();
|
||||
file.markLive(elf_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 symbol(self: *ZigObject, index: Symbol.Index) Symbol.Index {
|
||||
const is_global = index & global_symbol_bit != 0;
|
||||
const actual_index = index & symbol_mask;
|
||||
if (is_global) 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];
|
||||
return &self.local_esyms.items(.elf_sym)[actual_index];
|
||||
}
|
||||
|
||||
pub fn locals(self: *ZigObject) []const Symbol.Index {
|
||||
return self.local_symbols.items;
|
||||
}
|
||||
|
||||
pub fn globals(self: *ZigObject) []const Symbol.Index {
|
||||
return self.global_symbols.items;
|
||||
}
|
||||
|
||||
pub fn asFile(self: *ZigObject) File {
|
||||
return .{ .zig_object = self };
|
||||
}
|
||||
|
||||
/// Returns atom's code.
|
||||
/// Caller owns the memory.
|
||||
pub fn codeAlloc(self: ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8 {
|
||||
const gpa = elf_file.base.allocator;
|
||||
const atom = elf_file.atom(atom_index).?;
|
||||
assert(atom.file_index == self.index);
|
||||
const shdr = &elf_file.shdrs.items[atom.outputShndx().?];
|
||||
const file_offset = shdr.sh_offset + atom.value - shdr.sh_addr;
|
||||
const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
|
||||
const code = try gpa.alloc(u8, size);
|
||||
errdefer gpa.free(code);
|
||||
const amt = try elf_file.base.file.?.preadAll(code, file_offset);
|
||||
if (amt != code.len) {
|
||||
log.err("fetching code for {s} failed", .{atom.name(elf_file)});
|
||||
return error.InputOutput;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
pub fn getDeclVAddr(
|
||||
self: *ZigObject,
|
||||
elf_file: *Elf,
|
||||
decl_index: Module.Decl.Index,
|
||||
reloc_info: link.File.RelocInfo,
|
||||
) !u64 {
|
||||
const this_sym_index = try self.getOrCreateMetadataForDecl(elf_file, decl_index);
|
||||
const this_sym = elf_file.symbol(this_sym_index);
|
||||
const vaddr = this_sym.value;
|
||||
const parent_atom = elf_file.symbol(reloc_info.parent_atom_index).atom(elf_file).?;
|
||||
try parent_atom.addReloc(elf_file, .{
|
||||
.r_offset = reloc_info.offset,
|
||||
.r_info = (@as(u64, @intCast(this_sym.esym_index)) << 32) | elf.R_X86_64_64,
|
||||
.r_addend = reloc_info.addend,
|
||||
});
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
pub fn getAnonDeclVAddr(
|
||||
self: *ZigObject,
|
||||
elf_file: *Elf,
|
||||
decl_val: InternPool.Index,
|
||||
reloc_info: link.File.RelocInfo,
|
||||
) !u64 {
|
||||
const sym_index = self.anon_decls.get(decl_val).?.symbol_index;
|
||||
const sym = elf_file.symbol(sym_index);
|
||||
const vaddr = sym.value;
|
||||
const parent_atom = elf_file.symbol(reloc_info.parent_atom_index).atom(elf_file).?;
|
||||
try parent_atom.addReloc(elf_file, .{
|
||||
.r_offset = reloc_info.offset,
|
||||
.r_info = (@as(u64, @intCast(sym.esym_index)) << 32) | elf.R_X86_64_64,
|
||||
.r_addend = reloc_info.addend,
|
||||
});
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
pub fn lowerAnonDecl(
|
||||
self: *ZigObject,
|
||||
elf_file: *Elf,
|
||||
decl_val: InternPool.Index,
|
||||
explicit_alignment: InternPool.Alignment,
|
||||
src_loc: Module.SrcLoc,
|
||||
) !codegen.Result {
|
||||
const gpa = elf_file.base.allocator;
|
||||
const mod = elf_file.base.options.module.?;
|
||||
const ty = mod.intern_pool.typeOf(decl_val).toType();
|
||||
const decl_alignment = switch (explicit_alignment) {
|
||||
.none => ty.abiAlignment(mod),
|
||||
else => explicit_alignment,
|
||||
};
|
||||
if (self.anon_decls.get(decl_val)) |metadata| {
|
||||
const existing_alignment = elf_file.symbol(metadata.symbol_index).atom(elf_file).?.alignment;
|
||||
if (decl_alignment.order(existing_alignment).compare(.lte))
|
||||
return .ok;
|
||||
}
|
||||
|
||||
const val = decl_val.toValue();
|
||||
const tv = TypedValue{ .ty = ty, .val = val };
|
||||
var name_buf: [32]u8 = undefined;
|
||||
const name = std.fmt.bufPrint(&name_buf, "__anon_{d}", .{
|
||||
@intFromEnum(decl_val),
|
||||
}) catch unreachable;
|
||||
const res = self.lowerConst(
|
||||
elf_file,
|
||||
name,
|
||||
tv,
|
||||
decl_alignment,
|
||||
elf_file.zig_rodata_section_index.?,
|
||||
src_loc,
|
||||
) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
else => |e| return .{ .fail = try Module.ErrorMsg.create(
|
||||
gpa,
|
||||
src_loc,
|
||||
"unable to lower constant value: {s}",
|
||||
.{@errorName(e)},
|
||||
) },
|
||||
};
|
||||
const sym_index = switch (res) {
|
||||
.ok => |sym_index| sym_index,
|
||||
.fail => |em| return .{ .fail = em },
|
||||
};
|
||||
try self.anon_decls.put(gpa, decl_val, .{ .symbol_index = sym_index });
|
||||
return .ok;
|
||||
}
|
||||
|
||||
pub fn getOrCreateMetadataForLazySymbol(
|
||||
self: *ZigObject,
|
||||
elf_file: *Elf,
|
||||
lazy_sym: link.File.LazySymbol,
|
||||
) !Symbol.Index {
|
||||
const gpa = elf_file.base.allocator;
|
||||
const mod = elf_file.base.options.module.?;
|
||||
const gop = try self.lazy_syms.getOrPut(gpa, lazy_sym.getDecl(mod));
|
||||
errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();
|
||||
if (!gop.found_existing) gop.value_ptr.* = .{};
|
||||
const metadata: struct {
|
||||
symbol_index: *Symbol.Index,
|
||||
state: *LazySymbolMetadata.State,
|
||||
} = switch (lazy_sym.kind) {
|
||||
.code => .{
|
||||
.symbol_index = &gop.value_ptr.text_symbol_index,
|
||||
.state = &gop.value_ptr.text_state,
|
||||
},
|
||||
.const_data => .{
|
||||
.symbol_index = &gop.value_ptr.rodata_symbol_index,
|
||||
.state = &gop.value_ptr.rodata_state,
|
||||
},
|
||||
};
|
||||
switch (metadata.state.*) {
|
||||
.unused => metadata.symbol_index.* = try self.addAtom(elf_file),
|
||||
.pending_flush => return metadata.symbol_index.*,
|
||||
.flushed => {},
|
||||
}
|
||||
metadata.state.* = .pending_flush;
|
||||
const symbol_index = metadata.symbol_index.*;
|
||||
// anyerror needs to be deferred until flushModule
|
||||
if (lazy_sym.getDecl(mod) != .none) try self.updateLazySymbol(elf_file, lazy_sym, symbol_index);
|
||||
return symbol_index;
|
||||
}
|
||||
|
||||
fn freeUnnamedConsts(self: *ZigObject, elf_file: *Elf, decl_index: Module.Decl.Index) void {
|
||||
const unnamed_consts = self.unnamed_consts.getPtr(decl_index) orelse return;
|
||||
for (unnamed_consts.items) |sym_index| {
|
||||
self.freeDeclMetadata(elf_file, sym_index);
|
||||
}
|
||||
unnamed_consts.clearAndFree(elf_file.base.allocator);
|
||||
}
|
||||
|
||||
fn freeDeclMetadata(self: *ZigObject, elf_file: *Elf, sym_index: Symbol.Index) void {
|
||||
_ = self;
|
||||
const sym = elf_file.symbol(sym_index);
|
||||
sym.atom(elf_file).?.free(elf_file);
|
||||
log.debug("adding %{d} to local symbols free list", .{sym_index});
|
||||
elf_file.symbols_free_list.append(elf_file.base.allocator, sym_index) catch {};
|
||||
elf_file.symbols.items[sym_index] = .{};
|
||||
// TODO free GOT entry here
|
||||
}
|
||||
|
||||
pub fn freeDecl(self: *ZigObject, elf_file: *Elf, decl_index: Module.Decl.Index) void {
|
||||
const mod = elf_file.base.options.module.?;
|
||||
const decl = mod.declPtr(decl_index);
|
||||
|
||||
log.debug("freeDecl {*}", .{decl});
|
||||
|
||||
if (self.decls.fetchRemove(decl_index)) |const_kv| {
|
||||
var kv = const_kv;
|
||||
const sym_index = kv.value.symbol_index;
|
||||
self.freeDeclMetadata(elf_file, sym_index);
|
||||
self.freeUnnamedConsts(elf_file, decl_index);
|
||||
kv.value.exports.deinit(elf_file.base.allocator);
|
||||
}
|
||||
|
||||
if (self.dwarf) |*dw| {
|
||||
dw.freeDecl(decl_index);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getOrCreateMetadataForDecl(
|
||||
self: *ZigObject,
|
||||
elf_file: *Elf,
|
||||
decl_index: Module.Decl.Index,
|
||||
) !Symbol.Index {
|
||||
const gop = try self.decls.getOrPut(elf_file.base.allocator, decl_index);
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = .{ .symbol_index = try self.addAtom(elf_file) };
|
||||
}
|
||||
return gop.value_ptr.symbol_index;
|
||||
}
|
||||
|
||||
fn getDeclShdrIndex(self: *ZigObject, elf_file: *Elf, decl_index: Module.Decl.Index, code: []const u8) u16 {
|
||||
_ = self;
|
||||
const mod = elf_file.base.options.module.?;
|
||||
const decl = mod.declPtr(decl_index);
|
||||
const shdr_index = switch (decl.ty.zigTypeTag(mod)) {
|
||||
// TODO: what if this is a function pointer?
|
||||
.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.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.?;
|
||||
break :blk elf_file.zig_bss_section_index.?;
|
||||
}
|
||||
// TODO I blatantly copied the logic from the Wasm linker, but is there a less
|
||||
// intrusive check for all zeroes than this?
|
||||
const is_all_zeroes = for (code) |byte| {
|
||||
if (byte != 0) break false;
|
||||
} else true;
|
||||
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.?;
|
||||
},
|
||||
};
|
||||
return shdr_index;
|
||||
}
|
||||
|
||||
fn updateDeclCode(
|
||||
self: *ZigObject,
|
||||
elf_file: *Elf,
|
||||
decl_index: Module.Decl.Index,
|
||||
sym_index: Symbol.Index,
|
||||
code: []const u8,
|
||||
stt_bits: u8,
|
||||
) !void {
|
||||
const gpa = elf_file.base.allocator;
|
||||
const mod = elf_file.base.options.module.?;
|
||||
const decl = mod.declPtr(decl_index);
|
||||
const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
|
||||
|
||||
log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });
|
||||
|
||||
const required_alignment = decl.getAlignment(mod);
|
||||
|
||||
const sym = elf_file.symbol(sym_index);
|
||||
const esym = &self.local_esyms.items(.elf_sym)[sym.esym_index];
|
||||
const atom_ptr = sym.atom(elf_file).?;
|
||||
|
||||
const shdr_index = self.getDeclShdrIndex(elf_file, decl_index, code);
|
||||
sym.output_section_index = shdr_index;
|
||||
atom_ptr.output_section_index = shdr_index;
|
||||
|
||||
sym.name_offset = try elf_file.strtab.insert(gpa, decl_name);
|
||||
atom_ptr.flags.alive = true;
|
||||
atom_ptr.name_offset = sym.name_offset;
|
||||
esym.st_name = sym.name_offset;
|
||||
esym.st_info |= stt_bits;
|
||||
esym.st_size = code.len;
|
||||
|
||||
const old_size = atom_ptr.size;
|
||||
const old_vaddr = atom_ptr.value;
|
||||
atom_ptr.alignment = required_alignment;
|
||||
atom_ptr.size = code.len;
|
||||
|
||||
if (old_size > 0 and elf_file.base.child_pid == null) {
|
||||
const capacity = atom_ptr.capacity(elf_file);
|
||||
const need_realloc = code.len > capacity or !required_alignment.check(sym.value);
|
||||
if (need_realloc) {
|
||||
try atom_ptr.grow(elf_file);
|
||||
log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, old_vaddr, atom_ptr.value });
|
||||
if (old_vaddr != atom_ptr.value) {
|
||||
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);
|
||||
}
|
||||
} else if (code.len < old_size) {
|
||||
atom_ptr.shrink(elf_file);
|
||||
}
|
||||
} else {
|
||||
try atom_ptr.allocate(elf_file);
|
||||
errdefer self.freeDeclMetadata(elf_file, sym_index);
|
||||
|
||||
sym.value = atom_ptr.value;
|
||||
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.base.child_pid) |pid| {
|
||||
switch (builtin.os.tag) {
|
||||
.linux => {
|
||||
var code_vec: [1]std.os.iovec_const = .{.{
|
||||
.iov_base = code.ptr,
|
||||
.iov_len = code.len,
|
||||
}};
|
||||
var remote_vec: [1]std.os.iovec_const = .{.{
|
||||
.iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(sym.value)))),
|
||||
.iov_len = code.len,
|
||||
}};
|
||||
const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0);
|
||||
switch (std.os.errno(rc)) {
|
||||
.SUCCESS => assert(rc == code.len),
|
||||
else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
|
||||
}
|
||||
},
|
||||
else => return error.HotSwapUnavailableOnHostOperatingSystem,
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
try elf_file.base.file.?.pwriteAll(code, file_offset);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updateFunc(
|
||||
self: *ZigObject,
|
||||
elf_file: *Elf,
|
||||
mod: *Module,
|
||||
func_index: InternPool.Index,
|
||||
air: Air,
|
||||
liveness: Liveness,
|
||||
) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const func = mod.funcInfo(func_index);
|
||||
const decl_index = func.owner_decl;
|
||||
const decl = mod.declPtr(decl_index);
|
||||
|
||||
const sym_index = try self.getOrCreateMetadataForDecl(elf_file, decl_index);
|
||||
self.freeUnnamedConsts(elf_file, decl_index);
|
||||
elf_file.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file);
|
||||
|
||||
var code_buffer = std.ArrayList(u8).init(elf_file.base.allocator);
|
||||
defer code_buffer.deinit();
|
||||
|
||||
var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(mod, decl_index) else null;
|
||||
defer if (decl_state) |*ds| ds.deinit();
|
||||
|
||||
const res = if (decl_state) |*ds|
|
||||
try codegen.generateFunction(
|
||||
&elf_file.base,
|
||||
decl.srcLoc(mod),
|
||||
func_index,
|
||||
air,
|
||||
liveness,
|
||||
&code_buffer,
|
||||
.{ .dwarf = ds },
|
||||
)
|
||||
else
|
||||
try codegen.generateFunction(
|
||||
&elf_file.base,
|
||||
decl.srcLoc(mod),
|
||||
func_index,
|
||||
air,
|
||||
liveness,
|
||||
&code_buffer,
|
||||
.none,
|
||||
);
|
||||
|
||||
const code = switch (res) {
|
||||
.ok => code_buffer.items,
|
||||
.fail => |em| {
|
||||
decl.analysis = .codegen_failure;
|
||||
try mod.failed_decls.put(mod.gpa, decl_index, em);
|
||||
return;
|
||||
},
|
||||
};
|
||||
try self.updateDeclCode(elf_file, decl_index, sym_index, code, elf.STT_FUNC);
|
||||
if (decl_state) |*ds| {
|
||||
const sym = elf_file.symbol(sym_index);
|
||||
try self.dwarf.?.commitDeclState(
|
||||
mod,
|
||||
decl_index,
|
||||
sym.value,
|
||||
sym.atom(elf_file).?.size,
|
||||
ds,
|
||||
);
|
||||
}
|
||||
|
||||
// Since we updated the vaddr and the size, each corresponding export
|
||||
// symbol also needs to be updated.
|
||||
return self.updateExports(elf_file, mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
|
||||
}
|
||||
|
||||
pub fn updateDecl(
|
||||
self: *ZigObject,
|
||||
elf_file: *Elf,
|
||||
mod: *Module,
|
||||
decl_index: Module.Decl.Index,
|
||||
) link.File.UpdateDeclError!void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const decl = mod.declPtr(decl_index);
|
||||
|
||||
if (decl.val.getExternFunc(mod)) |_| {
|
||||
return;
|
||||
}
|
||||
|
||||
if (decl.isExtern(mod)) {
|
||||
// Extern variable gets a .got entry only.
|
||||
const variable = decl.getOwnedVariable(mod).?;
|
||||
const name = mod.intern_pool.stringToSlice(decl.name);
|
||||
const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name);
|
||||
const esym_index = try self.getGlobalSymbol(elf_file, name, lib_name);
|
||||
elf_file.symbol(self.symbol(esym_index)).flags.needs_got = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const sym_index = try self.getOrCreateMetadataForDecl(elf_file, decl_index);
|
||||
elf_file.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file);
|
||||
|
||||
var code_buffer = std.ArrayList(u8).init(elf_file.base.allocator);
|
||||
defer code_buffer.deinit();
|
||||
|
||||
var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(mod, decl_index) else null;
|
||||
defer if (decl_state) |*ds| ds.deinit();
|
||||
|
||||
// TODO implement .debug_info for global variables
|
||||
const decl_val = if (decl.val.getVariable(mod)) |variable| variable.init.toValue() else decl.val;
|
||||
const res = if (decl_state) |*ds|
|
||||
try codegen.generateSymbol(&elf_file.base, decl.srcLoc(mod), .{
|
||||
.ty = decl.ty,
|
||||
.val = decl_val,
|
||||
}, &code_buffer, .{
|
||||
.dwarf = ds,
|
||||
}, .{
|
||||
.parent_atom_index = sym_index,
|
||||
})
|
||||
else
|
||||
try codegen.generateSymbol(&elf_file.base, decl.srcLoc(mod), .{
|
||||
.ty = decl.ty,
|
||||
.val = decl_val,
|
||||
}, &code_buffer, .none, .{
|
||||
.parent_atom_index = sym_index,
|
||||
});
|
||||
|
||||
const code = switch (res) {
|
||||
.ok => code_buffer.items,
|
||||
.fail => |em| {
|
||||
decl.analysis = .codegen_failure;
|
||||
try mod.failed_decls.put(mod.gpa, decl_index, em);
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
try self.updateDeclCode(elf_file, decl_index, sym_index, code, elf.STT_OBJECT);
|
||||
if (decl_state) |*ds| {
|
||||
const sym = elf_file.symbol(sym_index);
|
||||
try self.dwarf.?.commitDeclState(
|
||||
mod,
|
||||
decl_index,
|
||||
sym.value,
|
||||
sym.atom(elf_file).?.size,
|
||||
ds,
|
||||
);
|
||||
}
|
||||
|
||||
// Since we updated the vaddr and the size, each corresponding export
|
||||
// symbol also needs to be updated.
|
||||
return self.updateExports(elf_file, mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
|
||||
}
|
||||
|
||||
fn updateLazySymbol(
|
||||
self: *ZigObject,
|
||||
elf_file: *Elf,
|
||||
sym: link.File.LazySymbol,
|
||||
symbol_index: Symbol.Index,
|
||||
) !void {
|
||||
const gpa = elf_file.base.allocator;
|
||||
const mod = elf_file.base.options.module.?;
|
||||
|
||||
var required_alignment: InternPool.Alignment = .none;
|
||||
var code_buffer = std.ArrayList(u8).init(gpa);
|
||||
defer code_buffer.deinit();
|
||||
|
||||
const name_str_index = blk: {
|
||||
const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{
|
||||
@tagName(sym.kind),
|
||||
sym.ty.fmt(mod),
|
||||
});
|
||||
defer gpa.free(name);
|
||||
break :blk try elf_file.strtab.insert(gpa, name);
|
||||
};
|
||||
|
||||
const src = if (sym.ty.getOwnerDeclOrNull(mod)) |owner_decl|
|
||||
mod.declPtr(owner_decl).srcLoc(mod)
|
||||
else
|
||||
Module.SrcLoc{
|
||||
.file_scope = undefined,
|
||||
.parent_decl_node = undefined,
|
||||
.lazy = .unneeded,
|
||||
};
|
||||
const res = try codegen.generateLazySymbol(
|
||||
&elf_file.base,
|
||||
src,
|
||||
sym,
|
||||
&required_alignment,
|
||||
&code_buffer,
|
||||
.none,
|
||||
.{ .parent_atom_index = symbol_index },
|
||||
);
|
||||
const code = switch (res) {
|
||||
.ok => code_buffer.items,
|
||||
.fail => |em| {
|
||||
log.err("{s}", .{em.msg});
|
||||
return error.CodegenFail;
|
||||
},
|
||||
};
|
||||
|
||||
const output_section_index = switch (sym.kind) {
|
||||
.code => elf_file.zig_text_section_index.?,
|
||||
.const_data => elf_file.zig_rodata_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];
|
||||
local_esym.st_name = name_str_index;
|
||||
local_esym.st_info |= elf.STT_OBJECT;
|
||||
local_esym.st_size = code.len;
|
||||
const atom_ptr = local_sym.atom(elf_file).?;
|
||||
atom_ptr.flags.alive = true;
|
||||
atom_ptr.name_offset = name_str_index;
|
||||
atom_ptr.alignment = required_alignment;
|
||||
atom_ptr.size = code.len;
|
||||
atom_ptr.output_section_index = output_section_index;
|
||||
|
||||
try atom_ptr.allocate(elf_file);
|
||||
errdefer self.freeDeclMetadata(elf_file, symbol_index);
|
||||
|
||||
local_sym.value = atom_ptr.value;
|
||||
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);
|
||||
|
||||
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;
|
||||
try elf_file.base.file.?.pwriteAll(code, file_offset);
|
||||
}
|
||||
|
||||
pub fn lowerUnnamedConst(
|
||||
self: *ZigObject,
|
||||
elf_file: *Elf,
|
||||
typed_value: TypedValue,
|
||||
decl_index: Module.Decl.Index,
|
||||
) !u32 {
|
||||
const gpa = elf_file.base.allocator;
|
||||
const mod = elf_file.base.options.module.?;
|
||||
const gop = try self.unnamed_consts.getOrPut(gpa, decl_index);
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = .{};
|
||||
}
|
||||
const unnamed_consts = gop.value_ptr;
|
||||
const decl = mod.declPtr(decl_index);
|
||||
const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
|
||||
const index = unnamed_consts.items.len;
|
||||
const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index });
|
||||
defer gpa.free(name);
|
||||
const sym_index = switch (try self.lowerConst(
|
||||
elf_file,
|
||||
name,
|
||||
typed_value,
|
||||
typed_value.ty.abiAlignment(mod),
|
||||
elf_file.zig_rodata_section_index.?,
|
||||
decl.srcLoc(mod),
|
||||
)) {
|
||||
.ok => |sym_index| sym_index,
|
||||
.fail => |em| {
|
||||
decl.analysis = .codegen_failure;
|
||||
try mod.failed_decls.put(mod.gpa, decl_index, em);
|
||||
log.err("{s}", .{em.msg});
|
||||
return error.CodegenFail;
|
||||
},
|
||||
};
|
||||
const sym = elf_file.symbol(sym_index);
|
||||
try unnamed_consts.append(gpa, sym.atom_index);
|
||||
return sym_index;
|
||||
}
|
||||
|
||||
const LowerConstResult = union(enum) {
|
||||
ok: Symbol.Index,
|
||||
fail: *Module.ErrorMsg,
|
||||
};
|
||||
|
||||
fn lowerConst(
|
||||
self: *ZigObject,
|
||||
elf_file: *Elf,
|
||||
name: []const u8,
|
||||
tv: TypedValue,
|
||||
required_alignment: InternPool.Alignment,
|
||||
output_section_index: u16,
|
||||
src_loc: Module.SrcLoc,
|
||||
) !LowerConstResult {
|
||||
const gpa = elf_file.base.allocator;
|
||||
|
||||
var code_buffer = std.ArrayList(u8).init(gpa);
|
||||
defer code_buffer.deinit();
|
||||
|
||||
const sym_index = try self.addAtom(elf_file);
|
||||
|
||||
const res = try codegen.generateSymbol(&elf_file.base, src_loc, tv, &code_buffer, .{
|
||||
.none = {},
|
||||
}, .{
|
||||
.parent_atom_index = sym_index,
|
||||
});
|
||||
const code = switch (res) {
|
||||
.ok => code_buffer.items,
|
||||
.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);
|
||||
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];
|
||||
local_esym.st_name = name_str_index;
|
||||
local_esym.st_info |= elf.STT_OBJECT;
|
||||
local_esym.st_size = code.len;
|
||||
const atom_ptr = local_sym.atom(elf_file).?;
|
||||
atom_ptr.flags.alive = true;
|
||||
atom_ptr.name_offset = name_str_index;
|
||||
atom_ptr.alignment = required_alignment;
|
||||
atom_ptr.size = code.len;
|
||||
atom_ptr.output_section_index = output_section_index;
|
||||
|
||||
try atom_ptr.allocate(elf_file);
|
||||
// TODO rename and re-audit this method
|
||||
errdefer self.freeDeclMetadata(elf_file, sym_index);
|
||||
|
||||
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;
|
||||
try elf_file.base.file.?.pwriteAll(code, file_offset);
|
||||
|
||||
return .{ .ok = sym_index };
|
||||
}
|
||||
|
||||
pub fn updateExports(
|
||||
self: *ZigObject,
|
||||
elf_file: *Elf,
|
||||
mod: *Module,
|
||||
exported: Module.Exported,
|
||||
exports: []const *Module.Export,
|
||||
) link.File.UpdateExportsError!void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const gpa = elf_file.base.allocator;
|
||||
const metadata = switch (exported) {
|
||||
.decl_index => |decl_index| blk: {
|
||||
_ = try self.getOrCreateMetadataForDecl(elf_file, decl_index);
|
||||
break :blk self.decls.getPtr(decl_index).?;
|
||||
},
|
||||
.value => |value| self.anon_decls.getPtr(value) orelse blk: {
|
||||
const first_exp = exports[0];
|
||||
const res = try self.lowerAnonDecl(elf_file, value, .none, first_exp.getSrcLoc(mod));
|
||||
switch (res) {
|
||||
.ok => {},
|
||||
.fail => |em| {
|
||||
// TODO maybe it's enough to return an error here and let Module.processExportsInner
|
||||
// handle the error?
|
||||
try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);
|
||||
mod.failed_exports.putAssumeCapacityNoClobber(first_exp, em);
|
||||
return;
|
||||
},
|
||||
}
|
||||
break :blk self.anon_decls.getPtr(value).?;
|
||||
},
|
||||
};
|
||||
const sym_index = metadata.symbol_index;
|
||||
const esym_index = elf_file.symbol(sym_index).esym_index;
|
||||
const esym = self.local_esyms.items(.elf_sym)[esym_index];
|
||||
const esym_shndx = self.local_esyms.items(.shndx)[esym_index];
|
||||
|
||||
for (exports) |exp| {
|
||||
if (exp.opts.section.unwrap()) |section_name| {
|
||||
if (!mod.intern_pool.stringEqlSlice(section_name, ".text")) {
|
||||
try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);
|
||||
mod.failed_exports.putAssumeCapacityNoClobber(exp, try Module.ErrorMsg.create(
|
||||
gpa,
|
||||
exp.getSrcLoc(mod),
|
||||
"Unimplemented: ExportOptions.section",
|
||||
.{},
|
||||
));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const stb_bits: u8 = switch (exp.opts.linkage) {
|
||||
.Internal => elf.STB_LOCAL,
|
||||
.Strong => elf.STB_GLOBAL,
|
||||
.Weak => elf.STB_WEAK,
|
||||
.LinkOnce => {
|
||||
try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);
|
||||
mod.failed_exports.putAssumeCapacityNoClobber(exp, try Module.ErrorMsg.create(
|
||||
gpa,
|
||||
exp.getSrcLoc(mod),
|
||||
"Unimplemented: GlobalLinkage.LinkOnce",
|
||||
.{},
|
||||
));
|
||||
continue;
|
||||
},
|
||||
};
|
||||
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|
|
||||
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;
|
||||
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;
|
||||
};
|
||||
|
||||
const actual_esym_index = global_esym_index & symbol_mask;
|
||||
const global_esym = &self.global_esyms.items(.elf_sym)[actual_esym_index];
|
||||
global_esym.st_value = elf_file.symbol(sym_index).value;
|
||||
global_esym.st_shndx = esym.st_shndx;
|
||||
global_esym.st_info = (stb_bits << 4) | stt_bits;
|
||||
global_esym.st_name = name_off;
|
||||
self.global_esyms.items(.shndx)[actual_esym_index] = esym_shndx;
|
||||
}
|
||||
}
|
||||
|
||||
/// Must be called only after a successful call to `updateDecl`.
|
||||
pub fn updateDeclLineNumber(
|
||||
self: *ZigObject,
|
||||
mod: *Module,
|
||||
decl_index: Module.Decl.Index,
|
||||
) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const decl = mod.declPtr(decl_index);
|
||||
const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
|
||||
|
||||
log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });
|
||||
|
||||
if (self.dwarf) |*dw| {
|
||||
try dw.updateDeclLineNumber(mod, decl_index);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deleteDeclExport(
|
||||
self: *ZigObject,
|
||||
elf_file: *Elf,
|
||||
decl_index: Module.Decl.Index,
|
||||
name: InternPool.NullTerminatedString,
|
||||
) void {
|
||||
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;
|
||||
log.debug("deleting export '{s}'", .{exp_name});
|
||||
const esym = &self.global_esyms.items(.elf_sym)[esym_index.*];
|
||||
_ = self.globals_lookup.remove(esym.st_name);
|
||||
const sym_index = elf_file.resolver.get(esym.st_name).?;
|
||||
const sym = elf_file.symbol(sym_index);
|
||||
if (sym.file_index == self.index) {
|
||||
_ = elf_file.resolver.swapRemove(esym.st_name);
|
||||
sym.* = .{};
|
||||
}
|
||||
esym.* = Elf.null_sym;
|
||||
self.global_esyms.items(.shndx)[esym_index.*] = elf.SHN_UNDEF;
|
||||
}
|
||||
|
||||
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 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);
|
||||
try self.global_symbols.append(gpa, gop.index);
|
||||
}
|
||||
return lookup_gop.value_ptr.*;
|
||||
}
|
||||
|
||||
pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
|
||||
return .{ .data = .{
|
||||
.self = self,
|
||||
.elf_file = elf_file,
|
||||
} };
|
||||
}
|
||||
|
||||
const FormatContext = struct {
|
||||
self: *ZigObject,
|
||||
elf_file: *Elf,
|
||||
};
|
||||
|
||||
fn formatSymtab(
|
||||
ctx: FormatContext,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
try writer.writeAll(" locals\n");
|
||||
for (ctx.self.locals()) |index| {
|
||||
const local = ctx.elf_file.symbol(index);
|
||||
try writer.print(" {}\n", .{local.fmt(ctx.elf_file)});
|
||||
}
|
||||
try writer.writeAll(" globals\n");
|
||||
for (ctx.self.globals()) |index| {
|
||||
const global = ctx.elf_file.symbol(index);
|
||||
try writer.print(" {}\n", .{global.fmt(ctx.elf_file)});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmtAtoms(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatAtoms) {
|
||||
return .{ .data = .{
|
||||
.self = self,
|
||||
.elf_file = elf_file,
|
||||
} };
|
||||
}
|
||||
|
||||
fn formatAtoms(
|
||||
ctx: FormatContext,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
try writer.writeAll(" atoms\n");
|
||||
for (ctx.self.atoms.items) |atom_index| {
|
||||
const atom = ctx.elf_file.atom(atom_index) orelse continue;
|
||||
try writer.print(" {}\n", .{atom.fmt(ctx.elf_file)});
|
||||
}
|
||||
}
|
||||
|
||||
const ElfSym = struct {
|
||||
elf_sym: elf.Elf64_Sym,
|
||||
shndx: u32 = elf.SHN_UNDEF,
|
||||
};
|
||||
|
||||
const LazySymbolMetadata = struct {
|
||||
const State = enum { unused, pending_flush, flushed };
|
||||
text_symbol_index: Symbol.Index = undefined,
|
||||
rodata_symbol_index: Symbol.Index = undefined,
|
||||
text_state: State = .unused,
|
||||
rodata_state: State = .unused,
|
||||
};
|
||||
|
||||
const DeclMetadata = struct {
|
||||
symbol_index: Symbol.Index,
|
||||
/// 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 {
|
||||
for (m.exports.items) |*exp| {
|
||||
const exp_name = elf_file.strtab.getAssumeExists(zig_object.elfSym(exp.*).st_name);
|
||||
if (mem.eql(u8, name, exp_name)) return exp;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const AtomList = std.ArrayListUnmanaged(Atom.Index);
|
||||
const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index));
|
||||
const DeclTable = std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata);
|
||||
const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, DeclMetadata);
|
||||
const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata);
|
||||
|
||||
const assert = std.debug.assert;
|
||||
const builtin = @import("builtin");
|
||||
const codegen = @import("../../codegen.zig");
|
||||
const elf = std.elf;
|
||||
const link = @import("../../link.zig");
|
||||
const log = std.log.scoped(.link);
|
||||
const mem = std.mem;
|
||||
const trace = @import("../../tracy.zig").trace;
|
||||
const std = @import("std");
|
||||
|
||||
const Air = @import("../../Air.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Atom = @import("Atom.zig");
|
||||
const Dwarf = @import("../Dwarf.zig");
|
||||
const Elf = @import("../Elf.zig");
|
||||
const File = @import("file.zig").File;
|
||||
const InternPool = @import("../../InternPool.zig");
|
||||
const Liveness = @import("../../Liveness.zig");
|
||||
const Module = @import("../../Module.zig");
|
||||
const Object = @import("Object.zig");
|
||||
const Symbol = @import("Symbol.zig");
|
||||
const TypedValue = @import("../../TypedValue.zig");
|
||||
const ZigObject = @This();
|
||||
@@ -1,5 +1,5 @@
|
||||
pub const File = union(enum) {
|
||||
zig_module: *ZigModule,
|
||||
zig_object: *ZigObject,
|
||||
linker_defined: *LinkerDefined,
|
||||
object: *Object,
|
||||
shared_object: *SharedObject,
|
||||
@@ -23,7 +23,7 @@ pub const File = union(enum) {
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
switch (file) {
|
||||
.zig_module => |x| try writer.print("{s}", .{x.path}),
|
||||
.zig_object => |x| try writer.print("{s}", .{x.path}),
|
||||
.linker_defined => try writer.writeAll("(linker defined)"),
|
||||
.object => |x| try writer.print("{}", .{x.fmtPath()}),
|
||||
.shared_object => |x| try writer.writeAll(x.path),
|
||||
@@ -32,7 +32,7 @@ pub const File = union(enum) {
|
||||
|
||||
pub fn isAlive(file: File) bool {
|
||||
return switch (file) {
|
||||
.zig_module => true,
|
||||
.zig_object => true,
|
||||
.linker_defined => true,
|
||||
inline else => |x| x.alive,
|
||||
};
|
||||
@@ -76,7 +76,7 @@ pub const File = union(enum) {
|
||||
|
||||
pub fn setAlive(file: File) void {
|
||||
switch (file) {
|
||||
.zig_module, .linker_defined => {},
|
||||
.zig_object, .linker_defined => {},
|
||||
inline else => |x| x.alive = true,
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ pub const File = union(enum) {
|
||||
return switch (file) {
|
||||
.linker_defined => unreachable,
|
||||
.shared_object => unreachable,
|
||||
.zig_module => |x| x.atoms.items,
|
||||
.zig_object => |x| x.atoms.items,
|
||||
.object => |x| x.atoms.items,
|
||||
};
|
||||
}
|
||||
@@ -115,7 +115,7 @@ pub const File = union(enum) {
|
||||
|
||||
pub const Entry = union(enum) {
|
||||
null: void,
|
||||
zig_module: ZigModule,
|
||||
zig_object: ZigObject,
|
||||
linker_defined: LinkerDefined,
|
||||
object: Object,
|
||||
shared_object: SharedObject,
|
||||
@@ -132,4 +132,4 @@ const LinkerDefined = @import("LinkerDefined.zig");
|
||||
const Object = @import("Object.zig");
|
||||
const SharedObject = @import("SharedObject.zig");
|
||||
const Symbol = @import("Symbol.zig");
|
||||
const ZigModule = @import("ZigModule.zig");
|
||||
const ZigObject = @import("ZigObject.zig");
|
||||
|
||||
Reference in New Issue
Block a user