mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
elf: encapsulate ZigObject-specific logic in flushModule hook
This commit is contained in:
parent
b1a735ac65
commit
ea95c74948
173
src/link/Elf.zig
173
src/link/Elf.zig
@ -113,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,
|
||||
@ -170,12 +162,6 @@ 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) = .{},
|
||||
|
||||
@ -696,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.zigObjectPtr().?.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);
|
||||
@ -706,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) {
|
||||
@ -715,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) {
|
||||
@ -724,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) {
|
||||
@ -733,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) {
|
||||
@ -742,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -833,17 +820,18 @@ pub fn growNonAllocSection(
|
||||
}
|
||||
|
||||
pub fn markDirty(self: *Elf, shdr_index: u16) void {
|
||||
if (self.zigObjectPtr().?.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1343,39 +1331,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
|
||||
try self.handleAndReportParseError(obj.path, err, &parse_ctx);
|
||||
}
|
||||
|
||||
if (self.zigObjectPtr()) |zig_object| {
|
||||
// Handle any lazy symbols that were emitted by incremental compilation.
|
||||
if (zig_object.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) zig_object.updateLazySymbol(
|
||||
self,
|
||||
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) zig_object.updateLazySymbol(
|
||||
self,
|
||||
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 (zig_object.lazy_syms.values()) |*metadata| {
|
||||
if (metadata.text_state != .unused) metadata.text_state = .flushed;
|
||||
if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed;
|
||||
}
|
||||
|
||||
if (zig_object.dwarf) |*dw| {
|
||||
try dw.flushModule(self.base.options.module.?);
|
||||
}
|
||||
}
|
||||
if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self);
|
||||
|
||||
// Dedup shared objects
|
||||
{
|
||||
@ -1437,47 +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();
|
||||
|
||||
if (self.zigObjectPtr()) |zig_object| {
|
||||
// TODO I need to re-think how to handle ZigObject's debug sections AND debug sections
|
||||
// extracted from input object files correctly.
|
||||
if (zig_object.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();
|
||||
@ -1542,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{
|
||||
@ -3815,24 +3722,6 @@ fn sortShdrs(self: *Elf) !void {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@ -4189,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(
|
||||
@ -4296,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;
|
||||
|
||||
@ -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.zigObjectPtr().?.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().?);
|
||||
|
||||
@ -52,6 +52,20 @@ 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;
|
||||
@ -123,6 +137,103 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {
|
||||
}
|
||||
}
|
||||
|
||||
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()));
|
||||
@ -837,7 +948,7 @@ pub fn updateDecl(
|
||||
return self.updateExports(elf_file, mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
|
||||
}
|
||||
|
||||
pub fn updateLazySymbol(
|
||||
fn updateLazySymbol(
|
||||
self: *ZigObject,
|
||||
elf_file: *Elf,
|
||||
sym: link.File.LazySymbol,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user