Commit b9b1ab0240
Changed files (7)
src
src/arch/arm/CodeGen.zig
@@ -3931,23 +3931,20 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
switch (typed_value.ty.zigTypeTag()) {
.Pointer => switch (typed_value.ty.ptrSize()) {
.Slice => {
- var buf: Type.SlicePtrFieldTypeBuffer = undefined;
- const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
- const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val });
- const slice_len = typed_value.val.sliceLen();
- // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean
- // the Sema code needs to use anonymous Decls or alloca instructions to store data.
- const ptr_imm = ptr_mcv.memory;
- _ = slice_len;
- _ = ptr_imm;
- // We need more general support for const data being stored in memory to make this work.
- return self.fail("TODO codegen for const slices", .{});
+ return self.lowerUnnamedConst(typed_value);
},
else => {
- if (typed_value.val.tag() == .int_u64) {
- return MCValue{ .immediate = @intCast(u32, typed_value.val.toUnsignedInt()) };
+ switch (typed_value.val.tag()) {
+ .int_u64 => {
+ return MCValue{ .immediate = @intCast(u32, typed_value.val.toUnsignedInt()) };
+ },
+ .slice => {
+ return self.lowerUnnamedConst(typed_value);
+ },
+ else => {
+ return self.fail("TODO codegen more kinds of const pointers", .{});
+ },
}
- return self.fail("TODO codegen more kinds of const pointers", .{});
},
},
.Int => {
src/link/Coff.zig
@@ -726,7 +726,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
var code_buffer = std.ArrayList(u8).init(self.base.allocator);
defer code_buffer.deinit();
- const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+ const res = try codegen.generateSymbol(&self.base, 0, decl.srcLoc(), .{
.ty = decl.ty,
.val = decl.val,
}, &code_buffer, .none);
@@ -751,7 +751,7 @@ fn finishUpdateDecl(self: *Coff, module: *Module, decl: *Module.Decl, code: []co
const need_realloc = code.len > capacity or
!mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment);
if (need_realloc) {
- const curr_vaddr = self.getDeclVAddr(decl);
+ const curr_vaddr = self.text_section_virtual_address + decl.link.coff.text_offset;
const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment);
log.debug("growing {s} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr });
if (vaddr != curr_vaddr) {
@@ -1465,7 +1465,9 @@ fn findLib(self: *Coff, arena: Allocator, name: []const u8) !?[]const u8 {
return null;
}
-pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 {
+pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl, parent_atom_index: u32, offset: u64) !u64 {
+ _ = parent_atom_index;
+ _ = offset;
assert(self.llvm_object == null);
return self.text_section_virtual_address + decl.link.coff.text_offset;
}
src/link/Elf.zig
@@ -145,6 +145,7 @@ decls: std.AutoHashMapUnmanaged(*Module.Decl, ?u16) = .{},
/// at present owned by Module.Decl.
/// TODO consolidate this.
managed_atoms: std.ArrayListUnmanaged(*TextBlock) = .{},
+atom_by_index_table: std.AutoHashMapUnmanaged(u32, *TextBlock) = .{},
/// Table of unnamed constants associated with a parent `Decl`.
/// We store them here so that we can free the constants whenever the `Decl`
@@ -179,6 +180,18 @@ dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{},
dbg_info_decl_first: ?*TextBlock = null,
dbg_info_decl_last: ?*TextBlock = null,
+/// A table of relocations indexed by the owning them `TextBlock`.
+/// Note that once we refactor `TextBlock`'s lifetime and ownership rules,
+/// this will be a table indexed by index into the list of Atoms.
+relocs: RelocTable = .{},
+
+const Reloc = struct {
+ target: u32,
+ offset: u64,
+ prev_vaddr: u64,
+};
+
+const RelocTable = std.AutoHashMapUnmanaged(*TextBlock, std.ArrayListUnmanaged(Reloc));
const UnnamedConstTable = std.AutoHashMapUnmanaged(*Module.Decl, std.ArrayListUnmanaged(*TextBlock));
/// When allocating, the ideal_capacity is calculated by
@@ -397,12 +410,36 @@ pub fn deinit(self: *Elf) void {
}
self.unnamed_const_atoms.deinit(self.base.allocator);
}
+
+ {
+ var it = self.relocs.valueIterator();
+ while (it.next()) |relocs| {
+ relocs.deinit(self.base.allocator);
+ }
+ self.relocs.deinit(self.base.allocator);
+ }
+
+ self.atom_by_index_table.deinit(self.base.allocator);
}
-pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl) u64 {
+pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl, parent_atom_index: u32, offset: u64) !u64 {
assert(self.llvm_object == null);
assert(decl.link.elf.local_sym_index != 0);
- return self.local_symbols.items[decl.link.elf.local_sym_index].st_value;
+
+ const target = decl.link.elf.local_sym_index;
+ const vaddr = self.local_symbols.items[target].st_value;
+ const atom = self.atom_by_index_table.get(parent_atom_index).?;
+ const gop = try self.relocs.getOrPut(self.base.allocator, atom);
+ if (!gop.found_existing) {
+ gop.value_ptr.* = .{};
+ }
+ try gop.value_ptr.append(self.base.allocator, .{
+ .target = target,
+ .offset = offset,
+ .prev_vaddr = vaddr,
+ });
+
+ return vaddr;
}
fn getDebugLineProgramOff(self: Elf) u32 {
@@ -991,6 +1028,41 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
.p64 => 12,
};
+ {
+ var it = self.relocs.iterator();
+ while (it.next()) |entry| {
+ const atom = entry.key_ptr.*;
+ const relocs = entry.value_ptr.*;
+ const source_sym = self.local_symbols.items[atom.local_sym_index];
+ const source_shdr = self.sections.items[source_sym.st_shndx];
+
+ log.debug("relocating '{s}'", .{self.getString(source_sym.st_name)});
+
+ for (relocs.items) |*reloc| {
+ const target_sym = self.local_symbols.items[reloc.target];
+ const target_vaddr = target_sym.st_value;
+
+ if (target_vaddr == reloc.prev_vaddr) continue;
+
+ const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr;
+ const file_offset = source_shdr.sh_offset + section_offset;
+
+ log.debug(" ({x}: [() => 0x{x}] ({s}))", .{
+ reloc.offset,
+ target_vaddr,
+ self.getString(target_sym.st_name),
+ });
+
+ switch (self.ptr_width) {
+ .p32 => try self.base.file.?.pwriteAll(mem.asBytes(&@intCast(u32, target_vaddr)), file_offset),
+ .p64 => try self.base.file.?.pwriteAll(mem.asBytes(&target_vaddr), file_offset),
+ }
+
+ reloc.prev_vaddr = target_vaddr;
+ }
+ }
+ }
+
// Unfortunately these have to be buffered and done at the end because ELF does not allow
// mixing local and global symbols within a symbol table.
try self.writeAllGlobalSymbols();
@@ -2508,6 +2580,7 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
log.debug("allocating symbol indexes for {s}", .{decl.name});
decl.link.elf.local_sym_index = try self.allocateLocalSymbol();
+ try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.elf.local_sym_index, &decl.link.elf);
if (self.offset_table_free_list.popOrNull()) |i| {
decl.link.elf.offset_table_index = i;
@@ -2525,6 +2598,7 @@ fn freeUnnamedConsts(self: *Elf, decl: *Module.Decl) void {
self.freeTextBlock(atom, self.phdr_load_ro_index.?);
self.local_symbol_free_list.append(self.base.allocator, atom.local_sym_index) catch {};
self.local_symbols.items[atom.local_sym_index].st_info = 0;
+ _ = self.atom_by_index_table.remove(atom.local_sym_index);
}
unnamed_consts.clearAndFree(self.base.allocator);
}
@@ -2543,11 +2617,11 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
// Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
if (decl.link.elf.local_sym_index != 0) {
self.local_symbol_free_list.append(self.base.allocator, decl.link.elf.local_sym_index) catch {};
- self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {};
-
self.local_symbols.items[decl.link.elf.local_sym_index].st_info = 0;
-
+ _ = self.atom_by_index_table.remove(decl.link.elf.local_sym_index);
decl.link.elf.local_sym_index = 0;
+
+ self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {};
}
// TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing
// is desired for both.
@@ -2993,7 +3067,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
// TODO implement .debug_info for global variables
const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
- const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+ const res = try codegen.generateSymbol(&self.base, decl.link.elf.local_sym_index, decl.srcLoc(), .{
.ty = decl.ty,
.val = decl_val,
}, &code_buffer, .{
@@ -3028,19 +3102,6 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl
}
const unnamed_consts = gop.value_ptr;
- const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{
- .none = .{},
- });
- const code = switch (res) {
- .externally_managed => |x| x,
- .appended => code_buffer.items,
- .fail => |em| {
- decl.analysis = .codegen_failure;
- try module.failed_decls.put(module.gpa, decl, em);
- return error.AnalysisFail;
- },
- };
-
const atom = try self.base.allocator.create(TextBlock);
errdefer self.base.allocator.destroy(atom);
atom.* = TextBlock.empty;
@@ -3056,6 +3117,20 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl
log.debug("allocating symbol indexes for {s}", .{name});
atom.local_sym_index = try self.allocateLocalSymbol();
+ try self.atom_by_index_table.putNoClobber(self.base.allocator, atom.local_sym_index, atom);
+
+ const res = try codegen.generateSymbol(&self.base, atom.local_sym_index, decl.srcLoc(), typed_value, &code_buffer, .{
+ .none = .{},
+ });
+ const code = switch (res) {
+ .externally_managed => |x| x,
+ .appended => code_buffer.items,
+ .fail => |em| {
+ decl.analysis = .codegen_failure;
+ try module.failed_decls.put(module.gpa, decl, em);
+ return error.AnalysisFail;
+ },
+ };
const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
const phdr_index = self.phdr_load_ro_index.?;
src/link/MachO.zig
@@ -3745,19 +3745,6 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.De
}
const unnamed_consts = gop.value_ptr;
- const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{
- .none = .{},
- });
- const code = switch (res) {
- .externally_managed => |x| x,
- .appended => code_buffer.items,
- .fail => |em| {
- decl.analysis = .codegen_failure;
- try module.failed_decls.put(module.gpa, decl, em);
- return error.AnalysisFail;
- },
- };
-
const name_str_index = blk: {
const index = unnamed_consts.items.len;
const name = try std.fmt.allocPrint(self.base.allocator, "__unnamed_{s}_{d}", .{ decl.name, index });
@@ -3772,12 +3759,27 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.De
const match = (try self.getMatchingSection(.{
.segname = makeStaticString("__TEXT"),
.sectname = makeStaticString("__const"),
- .size = code.len,
+ .size = @sizeOf(u64),
.@"align" = math.log2(required_alignment),
})).?;
const local_sym_index = try self.allocateLocalSymbol();
- const atom = try self.createEmptyAtom(local_sym_index, code.len, math.log2(required_alignment));
- mem.copy(u8, atom.code.items, code);
+ const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), math.log2(required_alignment));
+
+ const res = try codegen.generateSymbol(&self.base, local_sym_index, decl.srcLoc(), typed_value, &code_buffer, .{
+ .none = .{},
+ });
+ const code = switch (res) {
+ .externally_managed => |x| x,
+ .appended => code_buffer.items,
+ .fail => |em| {
+ decl.analysis = .codegen_failure;
+ try module.failed_decls.put(module.gpa, decl, em);
+ return error.AnalysisFail;
+ },
+ };
+
+ atom.code.clearRetainingCapacity();
+ try atom.code.appendSlice(self.base.allocator, code);
const addr = try self.allocateAtom(atom, code.len, required_alignment, match);
log.debug("allocated atom for {s} at 0x{x}", .{ name, addr });
@@ -3841,7 +3843,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
const res = if (debug_buffers) |dbg|
- try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+ try codegen.generateSymbol(&self.base, decl.link.elf.local_sym_index, decl.srcLoc(), .{
.ty = decl.ty,
.val = decl_val,
}, &code_buffer, .{
@@ -3852,7 +3854,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
},
})
else
- try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+ try codegen.generateSymbol(&self.base, decl.link.elf.local_sym_index, decl.srcLoc(), .{
.ty = decl.ty,
.val = decl_val,
}, &code_buffer, .none);
@@ -4341,16 +4343,17 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
}
}
-pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
+pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl, parent_atom_index: u32, offset: u64) !u64 {
+ assert(self.llvm_object == null);
assert(decl.link.macho.local_sym_index != 0);
- return self.locals.items[decl.link.macho.local_sym_index].n_value;
-}
-pub fn getDeclVAddrWithReloc(self: *MachO, decl: *const Module.Decl, offset: u64) !u64 {
- assert(decl.link.macho.local_sym_index != 0);
- assert(self.active_decl != null);
+ // TODO cache local_sym_index => atom!!!
+ const atom: *Atom = blk: for (self.managed_atoms.items) |atom| {
+ if (atom.local_sym_index == parent_atom_index) {
+ break :blk atom;
+ }
+ } else unreachable;
- const atom = &self.active_decl.?.link.macho;
try atom.relocs.append(self.base.allocator, .{
.offset = @intCast(u32, offset),
.target = .{ .local = decl.link.macho.local_sym_index },
src/link/Plan9.zig
@@ -302,7 +302,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {
var code_buffer = std.ArrayList(u8).init(self.base.allocator);
defer code_buffer.deinit();
const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
- const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+ const res = try codegen.generateSymbol(&self.base, @intCast(u32, decl.link.plan9.sym_index.?), decl.srcLoc(), .{
.ty = decl.ty,
.val = decl_val,
}, &code_buffer, .{ .none = .{} });
@@ -749,7 +749,9 @@ pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {
_ = self;
_ = decl;
}
-pub fn getDeclVAddr(self: *Plan9, decl: *const Module.Decl) u64 {
+pub fn getDeclVAddr(self: *Plan9, decl: *const Module.Decl, parent_atom_index: u32, offset: u64) !u64 {
+ _ = parent_atom_index;
+ _ = offset;
if (decl.ty.zigTypeTag() == .Fn) {
var start = self.bases.text;
var it_file = self.fn_decl_table.iterator();
src/codegen.zig
@@ -142,6 +142,7 @@ pub fn generateFunction(
pub fn generateSymbol(
bin_file: *link.File,
+ parent_atom_index: u32,
src_loc: Module.SrcLoc,
typed_value: TypedValue,
code: *std.ArrayList(u8),
@@ -177,7 +178,7 @@ pub fn generateSymbol(
if (typed_value.ty.sentinel()) |sentinel| {
try code.ensureUnusedCapacity(payload.data.len + 1);
code.appendSliceAssumeCapacity(payload.data);
- switch (try generateSymbol(bin_file, src_loc, .{
+ switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
.ty = typed_value.ty.elemType(),
.val = sentinel,
}, code, debug_output)) {
@@ -197,7 +198,7 @@ pub fn generateSymbol(
const elem_vals = typed_value.val.castTag(.array).?.data;
const elem_ty = typed_value.ty.elemType();
for (elem_vals) |elem_val| {
- switch (try generateSymbol(bin_file, src_loc, .{
+ switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
.ty = elem_ty,
.val = elem_val,
}, code, debug_output)) {
@@ -223,11 +224,11 @@ pub fn generateSymbol(
.Pointer => switch (typed_value.val.tag()) {
.variable => {
const decl = typed_value.val.castTag(.variable).?.data.owner_decl;
- return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output);
+ return lowerDeclRef(bin_file, parent_atom_index, src_loc, typed_value, decl, code, debug_output);
},
.decl_ref => {
const decl = typed_value.val.castTag(.decl_ref).?.data;
- return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output);
+ return lowerDeclRef(bin_file, parent_atom_index, src_loc, typed_value, decl, code, debug_output);
},
.slice => {
const slice = typed_value.val.castTag(.slice).?.data;
@@ -235,7 +236,7 @@ pub fn generateSymbol(
// generate ptr
var buf: Type.SlicePtrFieldTypeBuffer = undefined;
const slice_ptr_field_type = typed_value.ty.slicePtrFieldType(&buf);
- switch (try generateSymbol(bin_file, src_loc, .{
+ switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
.ty = slice_ptr_field_type,
.val = slice.ptr,
}, code, debug_output)) {
@@ -247,7 +248,7 @@ pub fn generateSymbol(
}
// generate length
- switch (try generateSymbol(bin_file, src_loc, .{
+ switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
.ty = Type.initTag(.usize),
.val = slice.len,
}, code, debug_output)) {
@@ -391,7 +392,7 @@ pub fn generateSymbol(
const field_ty = typed_value.ty.structFieldType(index);
if (!field_ty.hasRuntimeBits()) continue;
- switch (try generateSymbol(bin_file, src_loc, .{
+ switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
.ty = field_ty,
.val = field_val,
}, code, debug_output)) {
@@ -446,6 +447,7 @@ pub fn generateSymbol(
fn lowerDeclRef(
bin_file: *link.File,
+ parent_atom_index: u32,
src_loc: Module.SrcLoc,
typed_value: TypedValue,
decl: *Module.Decl,
@@ -456,7 +458,7 @@ fn lowerDeclRef(
// generate ptr
var buf: Type.SlicePtrFieldTypeBuffer = undefined;
const slice_ptr_field_type = typed_value.ty.slicePtrFieldType(&buf);
- switch (try generateSymbol(bin_file, src_loc, .{
+ switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
.ty = slice_ptr_field_type,
.val = typed_value.val,
}, code, debug_output)) {
@@ -472,7 +474,7 @@ fn lowerDeclRef(
.base = .{ .tag = .int_u64 },
.data = typed_value.val.sliceLen(),
};
- switch (try generateSymbol(bin_file, src_loc, .{
+ switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
.ty = Type.initTag(.usize),
.val = Value.initPayload(&slice_len.base),
}, code, debug_output)) {
@@ -495,15 +497,7 @@ fn lowerDeclRef(
}
decl.markAlive();
- const vaddr = vaddr: {
- if (bin_file.cast(link.File.MachO)) |macho_file| {
- break :vaddr try macho_file.getDeclVAddrWithReloc(decl, code.items.len);
- }
- // TODO handle the dependency of this symbol on the decl's vaddr.
- // If the decl changes vaddr, then this symbol needs to get regenerated.
- break :vaddr bin_file.getDeclVAddr(decl);
- };
-
+ const vaddr = try bin_file.getDeclVAddr(decl, parent_atom_index, code.items.len);
const endian = target.cpu.arch.endian();
switch (ptr_width) {
16 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(u16, vaddr), endian),
src/link.zig
@@ -684,12 +684,16 @@ pub const File = struct {
}
}
- pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 {
+ /// Get allocated `Decl`'s address in virtual memory.
+ /// The linker is passed information about the containing atom, `parent_atom_index`, and offset within it's
+ /// memory buffer, `offset`, so that it can make a note of potential relocation sites, should the
+ /// `Decl`'s address was not yet resolved, or the containing atom gets moved in virtual memory.
+ pub fn getDeclVAddr(base: *File, decl: *const Module.Decl, parent_atom_index: u32, offset: u64) !u64 {
switch (base.tag) {
- .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl),
- .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),
- .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),
- .plan9 => return @fieldParentPtr(Plan9, "base", base).getDeclVAddr(decl),
+ .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl, parent_atom_index, offset),
+ .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl, parent_atom_index, offset),
+ .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl, parent_atom_index, offset),
+ .plan9 => return @fieldParentPtr(Plan9, "base", base).getDeclVAddr(decl, parent_atom_index, offset),
.c => unreachable,
.wasm => unreachable,
.spirv => unreachable,