diff --git a/src/link/Coff.zig b/src/link/Coff.zig index d31344a23e..433355b54d 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -126,6 +126,15 @@ pub const Reloc = struct { pcrel: bool, length: u2, dirty: bool = true, + + /// Returns an Atom which is the target node of this relocation edge (if any). + fn getTargetAtom(self: Reloc, coff_file: *Coff) ?*Atom { + switch (self.@"type") { + .got => return coff_file.getGotAtomForSymbol(self.target), + .direct => return coff_file.getAtomForSymbol(self.target), + .imports => return coff_file.getImportAtomForSymbol(self.target), + } + } }; const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Reloc)); @@ -355,7 +364,7 @@ fn populateMissingMetadata(self: *Coff) !void { } if (self.rdata_section_index == null) { - const file_size: u32 = 1024; + const file_size: u32 = self.page_size; self.rdata_section_index = try self.allocateSection(".rdata", file_size, .{ .CNT_INITIALIZED_DATA = 1, .MEM_READ = 1, @@ -363,7 +372,7 @@ fn populateMissingMetadata(self: *Coff) !void { } if (self.data_section_index == null) { - const file_size: u32 = 1024; + const file_size: u32 = self.page_size; self.data_section_index = try self.allocateSection(".data", file_size, .{ .CNT_INITIALIZED_DATA = 1, .MEM_READ = 1, @@ -371,6 +380,14 @@ fn populateMissingMetadata(self: *Coff) !void { }); } + if (self.idata_section_index == null) { + const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize(); + self.idata_section_index = try self.allocateSection(".idata", file_size, .{ + .CNT_INITIALIZED_DATA = 1, + .MEM_READ = 1, + }); + } + if (self.reloc_section_index == null) { const file_size = @intCast(u32, self.base.options.symbol_count_hint) * @sizeOf(coff.BaseRelocation); self.reloc_section_index = try self.allocateSection(".reloc", file_size, .{ @@ -380,14 +397,6 @@ fn populateMissingMetadata(self: *Coff) !void { }); } - if (self.idata_section_index == null) { - const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize(); - self.idata_section_index = try self.allocateSection(".idata", file_size, .{ - .CNT_INITIALIZED_DATA = 1, - .MEM_READ = 1, - }); - } - if (self.strtab_offset == null) { const file_size = @intCast(u32, self.strtab.len()); self.strtab_offset = self.findFreeSpace(file_size, @alignOf(u32)); // 4bytes aligned seems like a good idea here @@ -437,6 +446,35 @@ fn allocateSection(self: *Coff, name: []const u8, size: u32, flags: coff.Section return index; } +fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void { + const header = &self.sections.items(.header)[sect_id]; + const increased_size = padToIdeal(needed_size); + const old_aligned_end = header.virtual_address + mem.alignForwardGeneric(u32, header.virtual_size, self.page_size); + const new_aligned_end = header.virtual_address + mem.alignForwardGeneric(u32, increased_size, self.page_size); + const diff = new_aligned_end - old_aligned_end; + + // TODO: enforce order by increasing VM addresses in self.sections container. + // This is required by the loader anyhow as far as I can tell. + for (self.sections.items(.header)[sect_id + 1 ..]) |*next_header, next_sect_id| { + const maybe_last_atom = &self.sections.items(.last_atom)[sect_id + 1 + next_sect_id]; + next_header.virtual_address += diff; + + if (maybe_last_atom.*) |last_atom| { + var atom = last_atom; + while (true) { + const sym = atom.getSymbolPtr(self); + sym.value += diff; + + if (atom.prev) |prev| { + atom = prev; + } else break; + } + } + } + + header.virtual_size = increased_size; +} + pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void { if (self.llvm_object) |_| return; const decl = self.base.options.module.?.declPtr(decl_index); @@ -525,13 +563,7 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u const sym = last_atom.getSymbol(self); break :blk (sym.value + last_atom.size) - header.virtual_address; } else 0; - log.debug("moving {s} from (0x{x} - 0x{x}) to (0x{x} - 0x{x})", .{ - self.getSectionName(header), - header.pointer_to_raw_data, - header.pointer_to_raw_data + current_size, - new_offset, - new_offset + current_size, - }); + log.debug("moving {s} from 0x{x} to 0x{x}", .{ self.getSectionName(header), header.pointer_to_raw_data, new_offset }); const amt = try self.base.file.?.copyRangeAll( header.pointer_to_raw_data, self.base.file.?, @@ -544,8 +576,8 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u const sect_vm_capacity = self.allocatedSizeVM(header.virtual_address); if (needed_size > sect_vm_capacity) { - log.err("needed {x}, available {x}", .{ needed_size, sect_vm_capacity }); - @panic("TODO expand section in virtual address space"); + try self.growSectionVM(sect_id, needed_size); + self.markRelocsDirtyByAddress(header.virtual_address + needed_size); } header.virtual_size = @maximum(header.virtual_size, needed_size); @@ -747,7 +779,7 @@ fn writePtrWidthAtom(self: *Coff, atom: *Atom) !void { } } -fn markRelocsDirty(self: *Coff, target: SymbolWithLoc) void { +fn markRelocsDirtyByTarget(self: *Coff, target: SymbolWithLoc) void { // TODO: reverse-lookup might come in handy here var it = self.relocs.valueIterator(); while (it.next()) |relocs| { @@ -758,6 +790,18 @@ fn markRelocsDirty(self: *Coff, target: SymbolWithLoc) void { } } +fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void { + var it = self.relocs.valueIterator(); + while (it.next()) |relocs| { + for (relocs.items) |*reloc| { + const target_atom = reloc.getTargetAtom(self) orelse continue; + const target_sym = target_atom.getSymbol(self); + if (target_sym.value < addr) continue; + reloc.dirty = true; + } + } +} + fn resolveRelocs(self: *Coff, atom: *Atom) !void { const relocs = self.relocs.get(atom) orelse return; const source_sym = atom.getSymbol(self); @@ -769,19 +813,8 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void { for (relocs.items) |*reloc| { if (!reloc.dirty) continue; - const target_vaddr = switch (reloc.@"type") { - .got => blk: { - const got_atom = self.getGotAtomForSymbol(reloc.target) orelse continue; - break :blk got_atom.getSymbol(self).value; - }, - .direct => blk: { - break :blk self.getSymbol(reloc.target).value; - }, - .imports => blk: { - const import_atom = self.getImportAtomForSymbol(reloc.target) orelse continue; - break :blk import_atom.getSymbol(self).value; - }, - }; + const target_atom = reloc.getTargetAtom(self) orelse continue; + const target_vaddr = target_atom.getSymbol(self).value; const target_vaddr_with_addend = target_vaddr + reloc.addend; log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{ @@ -1095,7 +1128,7 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, log.debug(" (updating GOT entry)", .{}); const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null }; const got_atom = self.getGotAtomForSymbol(got_target).?; - self.markRelocsDirty(got_target); + self.markRelocsDirtyByTarget(got_target); try self.writePtrWidthAtom(got_atom); } } else if (code_len < atom.size) { @@ -1120,7 +1153,7 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, try self.writePtrWidthAtom(got_atom); } - self.markRelocsDirty(atom.getSymbolWithLoc()); + self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); try self.writeAtom(atom, code); } @@ -1546,7 +1579,8 @@ fn writeBaseRelocations(self: *Coff) !void { const sect_vm_capacity = self.allocatedSizeVM(header.virtual_address); if (needed_size > sect_vm_capacity) { - @panic("TODO expand section in virtual address space"); + // TODO: we want to enforce .reloc after every alloc section. + try self.growSectionVM(self.reloc_section_index.?, needed_size); } } header.virtual_size = @maximum(header.virtual_size, needed_size); @@ -1881,9 +1915,6 @@ fn allocatedSizeVM(self: *Coff, start: u32) u32 { if (start == 0) return 0; var min_pos: u32 = std.math.maxInt(u32); - if (self.strtab_offset) |off| { - if (off > start and off < min_pos) min_pos = off; - } for (self.sections.items(.header)) |header| { if (header.virtual_address <= start) continue; if (header.virtual_address < min_pos) min_pos = header.virtual_address; @@ -2116,3 +2147,16 @@ fn logSymtab(self: *Coff) void { } } } + +fn logSections(self: *Coff) void { + log.debug("sections:", .{}); + for (self.sections.items(.header)) |*header| { + log.debug(" {s}: VM({x}, {x}) FILE({x}, {x})", .{ + self.getSectionName(header), + header.virtual_address, + header.virtual_address + header.virtual_size, + header.pointer_to_raw_data, + header.pointer_to_raw_data + header.size_of_raw_data, + }); + } +}