| ... | ... | @@ -1,36 +1,7 @@ |
| 1 | | const Coff = @This(); |
| 2 | | |
| 3 | | const std = @import("std"); |
| 4 | | const build_options = @import("build_options"); |
| 5 | | const builtin = @import("builtin"); |
| 6 | | const assert = std.debug.assert; |
| 7 | | const coff = std.coff; |
| 8 | | const fmt = std.fmt; |
| 9 | | const log = std.log.scoped(.link); |
| 10 | | const math = std.math; |
| 11 | | const mem = std.mem; |
| 12 | | |
| 13 | | const Allocator = std.mem.Allocator; |
| 14 | | |
| 15 | | const codegen = @import("../codegen.zig"); |
| 16 | | const link = @import("../link.zig"); |
| 17 | | const lld = @import("Coff/lld.zig"); |
| 18 | | const trace = @import("../tracy.zig").trace; |
| 19 | | |
| 20 | | const Air = @import("../Air.zig"); |
| 21 | | pub const Atom = @import("Coff/Atom.zig"); |
| 22 | | const Compilation = @import("../Compilation.zig"); |
| 23 | | const Liveness = @import("../Liveness.zig"); |
| 24 | | const LlvmObject = @import("../codegen/llvm.zig").Object; |
| 25 | | const Module = @import("../Module.zig"); |
| 26 | | const Object = @import("Coff/Object.zig"); |
| 27 | | const Relocation = @import("Coff/Relocation.zig"); |
| 28 | | const StringTable = @import("strtab.zig").StringTable; |
| 29 | | const TypedValue = @import("../TypedValue.zig"); |
| 30 | | |
| 31 | | pub const base_tag: link.File.Tag = .coff; |
| 32 | | |
| 33 | | const msdos_stub = @embedFile("msdos-stub.bin"); |
| 1 | //! The main driver of the COFF linker. |
| 2 | //! Currently uses our own implementation for the incremental linker, and falls back to |
| 3 | //! LLD for traditional linking (linking relocatable object files). |
| 4 | //! LLD is also the default linker for LLVM. |
| 34 | 5 | |
| 35 | 6 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| 36 | 7 | llvm_object: ?*LlvmObject = null, |
| ... | ... | @@ -64,13 +35,16 @@ globals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 64 | 35 | strtab: StringTable(.strtab) = .{}, |
| 65 | 36 | strtab_offset: ?u32 = null, |
| 66 | 37 | |
| 38 | temp_strtab: StringTable(.temp_strtab) = .{}, |
| 39 | |
| 67 | 40 | got_entries: std.ArrayListUnmanaged(Entry) = .{}, |
| 68 | 41 | got_entries_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 69 | 42 | got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{}, |
| 70 | 43 | |
| 71 | | imports: std.ArrayListUnmanaged(Entry) = .{}, |
| 72 | | imports_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 73 | | imports_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{}, |
| 44 | /// A table of ImportTables partitioned by the library name. |
| 45 | /// Key is an offset into the interning string table `temp_strtab`. |
| 46 | import_tables: std.AutoArrayHashMapUnmanaged(u32, ImportTable) = .{}, |
| 47 | imports_count_dirty: bool = true, |
| 74 | 48 | |
| 75 | 49 | /// Virtual address of the entry point procedure relative to image base. |
| 76 | 50 | entry_addr: ?u32 = null, |
| ... | ... | @@ -309,12 +283,15 @@ pub fn deinit(self: *Coff) void { |
| 309 | 283 | self.locals_free_list.deinit(gpa); |
| 310 | 284 | self.globals_free_list.deinit(gpa); |
| 311 | 285 | self.strtab.deinit(gpa); |
| 286 | self.temp_strtab.deinit(gpa); |
| 312 | 287 | self.got_entries.deinit(gpa); |
| 313 | 288 | self.got_entries_free_list.deinit(gpa); |
| 314 | 289 | self.got_entries_table.deinit(gpa); |
| 315 | | self.imports.deinit(gpa); |
| 316 | | self.imports_free_list.deinit(gpa); |
| 317 | | self.imports_table.deinit(gpa); |
| 290 | |
| 291 | for (self.import_tables.values()) |*itab| { |
| 292 | itab.deinit(gpa); |
| 293 | } |
| 294 | self.import_tables.deinit(gpa); |
| 318 | 295 | |
| 319 | 296 | { |
| 320 | 297 | var it = self.decls.iterator(); |
| ... | ... | @@ -358,6 +335,8 @@ fn populateMissingMetadata(self: *Coff) !void { |
| 358 | 335 | try self.strtab.buffer.ensureUnusedCapacity(gpa, @sizeOf(u32)); |
| 359 | 336 | self.strtab.buffer.appendNTimesAssumeCapacity(0, @sizeOf(u32)); |
| 360 | 337 | |
| 338 | try self.temp_strtab.buffer.append(gpa, 0); |
| 339 | |
| 361 | 340 | // Index 0 is always a null symbol. |
| 362 | 341 | try self.locals.append(gpa, .{ |
| 363 | 342 | .name = [_]u8{0} ** 8, |
| ... | ... | @@ -725,28 +704,6 @@ pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 { |
| 725 | 704 | return index; |
| 726 | 705 | } |
| 727 | 706 | |
| 728 | | pub fn allocateImportEntry(self: *Coff, target: SymbolWithLoc) !u32 { |
| 729 | | const gpa = self.base.allocator; |
| 730 | | try self.imports.ensureUnusedCapacity(gpa, 1); |
| 731 | | |
| 732 | | const index: u32 = blk: { |
| 733 | | if (self.imports_free_list.popOrNull()) |index| { |
| 734 | | log.debug(" (reusing import entry index {d})", .{index}); |
| 735 | | break :blk index; |
| 736 | | } else { |
| 737 | | log.debug(" (allocating import entry at index {d})", .{self.imports.items.len}); |
| 738 | | const index = @intCast(u32, self.imports.items.len); |
| 739 | | _ = self.imports.addOneAssumeCapacity(); |
| 740 | | break :blk index; |
| 741 | | } |
| 742 | | }; |
| 743 | | |
| 744 | | self.imports.items[index] = .{ .target = target, .sym_index = 0 }; |
| 745 | | try self.imports_table.putNoClobber(gpa, target, index); |
| 746 | | |
| 747 | | return index; |
| 748 | | } |
| 749 | | |
| 750 | 707 | pub fn createAtom(self: *Coff) !Atom.Index { |
| 751 | 708 | const gpa = self.base.allocator; |
| 752 | 709 | const atom_index = @intCast(Atom.Index, self.atoms.items.len); |
| ... | ... | @@ -797,21 +754,6 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !Atom.Index { |
| 797 | 754 | return atom_index; |
| 798 | 755 | } |
| 799 | 756 | |
| 800 | | fn createImportAtom(self: *Coff) !Atom.Index { |
| 801 | | const atom_index = try self.createAtom(); |
| 802 | | const atom = self.getAtomPtr(atom_index); |
| 803 | | atom.size = @sizeOf(u64); |
| 804 | | atom.alignment = @alignOf(u64); |
| 805 | | |
| 806 | | const sym = atom.getSymbolPtr(self); |
| 807 | | sym.section_number = @intToEnum(coff.SectionNumber, self.idata_section_index.? + 1); |
| 808 | | sym.value = try self.allocateAtom(atom_index, atom.size, atom.alignment); |
| 809 | | |
| 810 | | log.debug("allocated import atom at 0x{x}", .{sym.value}); |
| 811 | | |
| 812 | | return atom_index; |
| 813 | | } |
| 814 | | |
| 815 | 757 | fn growAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 { |
| 816 | 758 | const atom = self.getAtom(atom_index); |
| 817 | 759 | const sym = atom.getSymbol(self); |
| ... | ... | @@ -871,10 +813,8 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void { |
| 871 | 813 | var it = self.relocs.valueIterator(); |
| 872 | 814 | while (it.next()) |relocs| { |
| 873 | 815 | for (relocs.items) |*reloc| { |
| 874 | | const target_atom_index = reloc.getTargetAtomIndex(self) orelse continue; |
| 875 | | const target_atom = self.getAtom(target_atom_index); |
| 876 | | const target_sym = target_atom.getSymbol(self); |
| 877 | | if (target_sym.value < addr) continue; |
| 816 | const target_vaddr = reloc.getTargetAddress(self) orelse continue; |
| 817 | if (target_vaddr < addr) continue; |
| 878 | 818 | reloc.dirty = true; |
| 879 | 819 | } |
| 880 | 820 | } |
| ... | ... | @@ -1463,35 +1403,42 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod |
| 1463 | 1403 | sub_prog_node.activate(); |
| 1464 | 1404 | defer sub_prog_node.end(); |
| 1465 | 1405 | |
| 1406 | const gpa = self.base.allocator; |
| 1407 | |
| 1466 | 1408 | while (self.unresolved.popOrNull()) |entry| { |
| 1467 | 1409 | assert(entry.value); // We only expect imports generated by the incremental linker for now. |
| 1468 | 1410 | const global = self.globals.items[entry.key]; |
| 1469 | | if (self.imports_table.contains(global)) continue; |
| 1470 | | |
| 1471 | | const import_index = try self.allocateImportEntry(global); |
| 1472 | | const import_atom_index = try self.createImportAtom(); |
| 1473 | | const import_atom = self.getAtom(import_atom_index); |
| 1474 | | self.imports.items[import_index].sym_index = import_atom.getSymbolIndex().?; |
| 1475 | | try self.writePtrWidthAtom(import_atom_index); |
| 1476 | | } |
| 1477 | | |
| 1478 | | if (build_options.enable_logging) { |
| 1479 | | self.logSymtab(); |
| 1411 | const sym = self.getSymbol(global); |
| 1412 | const res = try self.import_tables.getOrPut(gpa, sym.value); |
| 1413 | const itable = res.value_ptr; |
| 1414 | if (!res.found_existing) { |
| 1415 | itable.* = .{}; |
| 1416 | } |
| 1417 | if (itable.lookup.contains(global)) continue; |
| 1418 | // TODO: we could technically write the pointer placeholder for to-be-bound import here, |
| 1419 | // but since this happens in flush, there is currently no point. |
| 1420 | _ = try itable.addImport(gpa, global); |
| 1421 | self.imports_count_dirty = true; |
| 1480 | 1422 | } |
| 1481 | 1423 | |
| 1424 | try self.writeImportTables(); |
| 1482 | 1425 | { |
| 1483 | 1426 | var it = self.relocs.keyIterator(); |
| 1484 | 1427 | while (it.next()) |atom| { |
| 1485 | 1428 | try self.resolveRelocs(atom.*); |
| 1486 | 1429 | } |
| 1487 | 1430 | } |
| 1488 | | try self.writeImportTable(); |
| 1489 | 1431 | try self.writeBaseRelocations(); |
| 1490 | 1432 | |
| 1491 | 1433 | if (self.getEntryPoint()) |entry_sym_loc| { |
| 1492 | 1434 | self.entry_addr = self.getSymbol(entry_sym_loc).value; |
| 1493 | 1435 | } |
| 1494 | 1436 | |
| 1437 | if (build_options.enable_logging) { |
| 1438 | self.logSymtab(); |
| 1439 | self.logImportTables(); |
| 1440 | } |
| 1441 | |
| 1495 | 1442 | try self.writeStrtab(); |
| 1496 | 1443 | try self.writeDataDirectoriesHeaders(); |
| 1497 | 1444 | try self.writeSectionHeaders(); |
| ... | ... | @@ -1504,6 +1451,8 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod |
| 1504 | 1451 | self.error_flags.no_entry_point_found = false; |
| 1505 | 1452 | try self.writeHeader(); |
| 1506 | 1453 | } |
| 1454 | |
| 1455 | assert(!self.imports_count_dirty); |
| 1507 | 1456 | } |
| 1508 | 1457 | |
| 1509 | 1458 | pub fn getDeclVAddr(self: *Coff, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 { |
| ... | ... | @@ -1526,7 +1475,7 @@ pub fn getDeclVAddr(self: *Coff, decl_index: Module.Decl.Index, reloc_info: link |
| 1526 | 1475 | return 0; |
| 1527 | 1476 | } |
| 1528 | 1477 | |
| 1529 | | pub fn getGlobalSymbol(self: *Coff, name: []const u8) !u32 { |
| 1478 | pub fn getGlobalSymbol(self: *Coff, name: []const u8, lib_name_name: ?[]const u8) !u32 { |
| 1530 | 1479 | const gop = try self.getOrPutGlobalPtr(name); |
| 1531 | 1480 | const global_index = self.getGlobalIndex(name).?; |
| 1532 | 1481 | |
| ... | ... | @@ -1543,6 +1492,12 @@ pub fn getGlobalSymbol(self: *Coff, name: []const u8) !u32 { |
| 1543 | 1492 | try self.setSymbolName(sym, name); |
| 1544 | 1493 | sym.storage_class = .EXTERNAL; |
| 1545 | 1494 | |
| 1495 | if (lib_name_name) |lib_name| { |
| 1496 | // We repurpose the 'value' of the Symbol struct to store an offset into |
| 1497 | // temporary string table where we will store the library name hint. |
| 1498 | sym.value = try self.temp_strtab.insert(gpa, lib_name); |
| 1499 | } |
| 1500 | |
| 1546 | 1501 | try self.unresolved.putNoClobber(gpa, global_index, true); |
| 1547 | 1502 | |
| 1548 | 1503 | return global_index; |
| ... | ... | @@ -1625,12 +1580,10 @@ fn writeBaseRelocations(self: *Coff) !void { |
| 1625 | 1580 | const needed_size = @intCast(u32, buffer.items.len); |
| 1626 | 1581 | if (needed_size > sect_capacity) { |
| 1627 | 1582 | const new_offset = self.findFreeSpace(needed_size, default_file_alignment); |
| 1628 | | log.debug("writing {s} at 0x{x} to 0x{x} (0x{x} - 0x{x})", .{ |
| 1583 | log.debug("moving {s} from 0x{x} to 0x{x}", .{ |
| 1629 | 1584 | self.getSectionName(header), |
| 1630 | 1585 | header.pointer_to_raw_data, |
| 1631 | | header.pointer_to_raw_data + needed_size, |
| 1632 | 1586 | new_offset, |
| 1633 | | new_offset + needed_size, |
| 1634 | 1587 | }); |
| 1635 | 1588 | header.pointer_to_raw_data = new_offset; |
| 1636 | 1589 | |
| ... | ... | @@ -1651,88 +1604,144 @@ fn writeBaseRelocations(self: *Coff) !void { |
| 1651 | 1604 | }; |
| 1652 | 1605 | } |
| 1653 | 1606 | |
| 1654 | | fn writeImportTable(self: *Coff) !void { |
| 1607 | fn writeImportTables(self: *Coff) !void { |
| 1655 | 1608 | if (self.idata_section_index == null) return; |
| 1609 | if (!self.imports_count_dirty) return; |
| 1656 | 1610 | |
| 1657 | 1611 | const gpa = self.base.allocator; |
| 1658 | 1612 | |
| 1659 | | const section = self.sections.get(self.idata_section_index.?); |
| 1660 | | const last_atom_index = section.last_atom_index orelse return; |
| 1661 | | const last_atom = self.getAtom(last_atom_index); |
| 1613 | const ext = ".dll"; |
| 1614 | const header = &self.sections.items(.header)[self.idata_section_index.?]; |
| 1615 | |
| 1616 | // Calculate needed size |
| 1617 | var iat_size: u32 = 0; |
| 1618 | var dir_table_size: u32 = @sizeOf(coff.ImportDirectoryEntry); // sentinel |
| 1619 | var lookup_table_size: u32 = 0; |
| 1620 | var names_table_size: u32 = 0; |
| 1621 | var dll_names_size: u32 = 0; |
| 1622 | for (self.import_tables.keys(), 0..) |off, i| { |
| 1623 | const lib_name = self.temp_strtab.getAssumeExists(off); |
| 1624 | const itable = self.import_tables.values()[i]; |
| 1625 | iat_size += itable.size() + 8; |
| 1626 | dir_table_size += @sizeOf(coff.ImportDirectoryEntry); |
| 1627 | lookup_table_size += @intCast(u32, itable.entries.items.len + 1) * @sizeOf(coff.ImportLookupEntry64.ByName); |
| 1628 | for (itable.entries.items) |entry| { |
| 1629 | const sym_name = self.getSymbolName(entry); |
| 1630 | names_table_size += 2 + mem.alignForwardGeneric(u32, @intCast(u32, sym_name.len + 1), 2); |
| 1631 | } |
| 1632 | dll_names_size += @intCast(u32, lib_name.len + ext.len + 1); |
| 1633 | } |
| 1662 | 1634 | |
| 1663 | | const iat_rva = section.header.virtual_address; |
| 1664 | | const iat_size = last_atom.getSymbol(self).value + last_atom.size * 2 - iat_rva; // account for sentinel zero pointer |
| 1635 | const needed_size = iat_size + dir_table_size + lookup_table_size + names_table_size + dll_names_size; |
| 1636 | const sect_capacity = self.allocatedSize(header.pointer_to_raw_data); |
| 1637 | if (needed_size > sect_capacity) { |
| 1638 | const new_offset = self.findFreeSpace(needed_size, default_file_alignment); |
| 1639 | log.debug("moving .idata from 0x{x} to 0x{x}", .{ header.pointer_to_raw_data, new_offset }); |
| 1640 | header.pointer_to_raw_data = new_offset; |
| 1665 | 1641 | |
| 1666 | | const dll_name = "KERNEL32.dll"; |
| 1642 | const sect_vm_capacity = self.allocatedVirtualSize(header.virtual_address); |
| 1643 | if (needed_size > sect_vm_capacity) { |
| 1644 | try self.growSectionVM(self.idata_section_index.?, needed_size); |
| 1645 | } |
| 1667 | 1646 | |
| 1668 | | var import_dir_entry = coff.ImportDirectoryEntry{ |
| 1669 | | .import_lookup_table_rva = @sizeOf(coff.ImportDirectoryEntry) * 2, |
| 1670 | | .time_date_stamp = 0, |
| 1671 | | .forwarder_chain = 0, |
| 1672 | | .name_rva = 0, |
| 1673 | | .import_address_table_rva = iat_rva, |
| 1674 | | }; |
| 1647 | header.virtual_size = @max(header.virtual_size, needed_size); |
| 1648 | header.size_of_raw_data = needed_size; |
| 1649 | } |
| 1675 | 1650 | |
| 1676 | | // TODO: we currently assume there's only one (implicit) DLL - ntdll |
| 1677 | | var lookup_table = std.ArrayList(coff.ImportLookupEntry64.ByName).init(gpa); |
| 1678 | | defer lookup_table.deinit(); |
| 1679 | | |
| 1680 | | var names_table = std.ArrayList(u8).init(gpa); |
| 1681 | | defer names_table.deinit(); |
| 1682 | | |
| 1683 | | // TODO: check if import is still valid |
| 1684 | | for (self.imports.items) |entry| { |
| 1685 | | const target_name = self.getSymbolName(entry.target); |
| 1686 | | const start = names_table.items.len; |
| 1687 | | mem.writeIntLittle(u16, try names_table.addManyAsArray(2), 0); // TODO: currently, hint is set to 0 as we haven't yet parsed any DLL |
| 1688 | | try names_table.appendSlice(target_name); |
| 1689 | | try names_table.append(0); |
| 1690 | | const end = names_table.items.len; |
| 1691 | | if (!mem.isAlignedGeneric(usize, end - start, @sizeOf(u16))) { |
| 1692 | | try names_table.append(0); |
| 1651 | // Do the actual writes |
| 1652 | var buffer = std.ArrayList(u8).init(gpa); |
| 1653 | defer buffer.deinit(); |
| 1654 | try buffer.ensureTotalCapacityPrecise(needed_size); |
| 1655 | buffer.resize(needed_size) catch unreachable; |
| 1656 | |
| 1657 | const dir_header_size = @sizeOf(coff.ImportDirectoryEntry); |
| 1658 | const lookup_entry_size = @sizeOf(coff.ImportLookupEntry64.ByName); |
| 1659 | |
| 1660 | var iat_offset: u32 = 0; |
| 1661 | var dir_table_offset = iat_size; |
| 1662 | var lookup_table_offset = dir_table_offset + dir_table_size; |
| 1663 | var names_table_offset = lookup_table_offset + lookup_table_size; |
| 1664 | var dll_names_offset = names_table_offset + names_table_size; |
| 1665 | for (self.import_tables.keys(), 0..) |off, i| { |
| 1666 | const lib_name = self.temp_strtab.getAssumeExists(off); |
| 1667 | const itable = self.import_tables.values()[i]; |
| 1668 | |
| 1669 | // Lookup table header |
| 1670 | const lookup_header = coff.ImportDirectoryEntry{ |
| 1671 | .import_lookup_table_rva = header.virtual_address + lookup_table_offset, |
| 1672 | .time_date_stamp = 0, |
| 1673 | .forwarder_chain = 0, |
| 1674 | .name_rva = header.virtual_address + dll_names_offset, |
| 1675 | .import_address_table_rva = header.virtual_address + iat_offset, |
| 1676 | }; |
| 1677 | mem.copy(u8, buffer.items[dir_table_offset..], mem.asBytes(&lookup_header)); |
| 1678 | dir_table_offset += dir_header_size; |
| 1679 | |
| 1680 | for (itable.entries.items) |entry| { |
| 1681 | const import_name = self.getSymbolName(entry); |
| 1682 | |
| 1683 | // IAT and lookup table entry |
| 1684 | const lookup = coff.ImportLookupEntry64.ByName{ .name_table_rva = @intCast(u31, header.virtual_address + names_table_offset) }; |
| 1685 | mem.copy(u8, buffer.items[iat_offset..], mem.asBytes(&lookup)); |
| 1686 | iat_offset += lookup_entry_size; |
| 1687 | mem.copy(u8, buffer.items[lookup_table_offset..], mem.asBytes(&lookup)); |
| 1688 | lookup_table_offset += lookup_entry_size; |
| 1689 | |
| 1690 | // Names table entry |
| 1691 | mem.writeIntLittle(u16, buffer.items[names_table_offset..][0..2], 0); // Hint set to 0 until we learn how to parse DLLs |
| 1692 | names_table_offset += 2; |
| 1693 | mem.copy(u8, buffer.items[names_table_offset..], import_name); |
| 1694 | names_table_offset += @intCast(u32, import_name.len); |
| 1695 | buffer.items[names_table_offset] = 0; |
| 1696 | names_table_offset += 1; |
| 1697 | if (!mem.isAlignedGeneric(usize, names_table_offset, @sizeOf(u16))) { |
| 1698 | buffer.items[names_table_offset] = 0; |
| 1699 | names_table_offset += 1; |
| 1700 | } |
| 1693 | 1701 | } |
| 1694 | | try lookup_table.append(.{ .name_table_rva = @intCast(u31, start) }); |
| 1695 | | } |
| 1696 | | try lookup_table.append(.{ .name_table_rva = 0 }); // the sentinel |
| 1697 | 1702 | |
| 1698 | | const dir_entry_size = @sizeOf(coff.ImportDirectoryEntry) + lookup_table.items.len * @sizeOf(coff.ImportLookupEntry64.ByName) + names_table.items.len + dll_name.len + 1; |
| 1699 | | const needed_size = iat_size + dir_entry_size + @sizeOf(coff.ImportDirectoryEntry); |
| 1700 | | const sect_capacity = self.allocatedSize(section.header.pointer_to_raw_data); |
| 1701 | | assert(needed_size < sect_capacity); // TODO: implement expanding .idata section |
| 1703 | // IAT sentinel |
| 1704 | mem.writeIntLittle(u64, buffer.items[iat_offset..][0..lookup_entry_size], 0); |
| 1705 | iat_offset += 8; |
| 1702 | 1706 | |
| 1703 | | // Fixup offsets |
| 1704 | | const base_rva = iat_rva + iat_size; |
| 1705 | | import_dir_entry.import_lookup_table_rva += base_rva; |
| 1706 | | import_dir_entry.name_rva = @intCast(u32, base_rva + dir_entry_size + @sizeOf(coff.ImportDirectoryEntry) - dll_name.len - 1); |
| 1707 | // Lookup table sentinel |
| 1708 | mem.copy(u8, buffer.items[lookup_table_offset..], mem.asBytes(&coff.ImportLookupEntry64.ByName{ .name_table_rva = 0 })); |
| 1709 | lookup_table_offset += lookup_entry_size; |
| 1707 | 1710 | |
| 1708 | | for (lookup_table.items[0 .. lookup_table.items.len - 1]) |*lk| { |
| 1709 | | lk.name_table_rva += @intCast(u31, base_rva + @sizeOf(coff.ImportDirectoryEntry) * 2 + lookup_table.items.len * @sizeOf(coff.ImportLookupEntry64.ByName)); |
| 1711 | // DLL name |
| 1712 | mem.copy(u8, buffer.items[dll_names_offset..], lib_name); |
| 1713 | dll_names_offset += @intCast(u32, lib_name.len); |
| 1714 | mem.copy(u8, buffer.items[dll_names_offset..], ext); |
| 1715 | dll_names_offset += @intCast(u32, ext.len); |
| 1716 | buffer.items[dll_names_offset] = 0; |
| 1717 | dll_names_offset += 1; |
| 1710 | 1718 | } |
| 1711 | 1719 | |
| 1712 | | var buffer = std.ArrayList(u8).init(gpa); |
| 1713 | | defer buffer.deinit(); |
| 1714 | | try buffer.ensureTotalCapacity(dir_entry_size + @sizeOf(coff.ImportDirectoryEntry)); |
| 1715 | | buffer.appendSliceAssumeCapacity(mem.asBytes(&import_dir_entry)); |
| 1716 | | buffer.appendNTimesAssumeCapacity(0, @sizeOf(coff.ImportDirectoryEntry)); // the sentinel; TODO: I think doing all of the above on bytes directly might be cleaner |
| 1717 | | buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(lookup_table.items)); |
| 1718 | | buffer.appendSliceAssumeCapacity(names_table.items); |
| 1719 | | buffer.appendSliceAssumeCapacity(dll_name); |
| 1720 | | buffer.appendAssumeCapacity(0); |
| 1721 | | |
| 1722 | | try self.base.file.?.pwriteAll(buffer.items, section.header.pointer_to_raw_data + iat_size); |
| 1723 | | // Override the IAT atoms |
| 1724 | | // TODO: we should rewrite only dirtied atoms, but that's for way later |
| 1725 | | try self.base.file.?.pwriteAll(mem.sliceAsBytes(lookup_table.items), section.header.pointer_to_raw_data); |
| 1720 | // Sentinel |
| 1721 | const lookup_header = coff.ImportDirectoryEntry{ |
| 1722 | .import_lookup_table_rva = 0, |
| 1723 | .time_date_stamp = 0, |
| 1724 | .forwarder_chain = 0, |
| 1725 | .name_rva = 0, |
| 1726 | .import_address_table_rva = 0, |
| 1727 | }; |
| 1728 | mem.copy(u8, buffer.items[dir_table_offset..], mem.asBytes(&lookup_header)); |
| 1729 | dir_table_offset += dir_header_size; |
| 1730 | |
| 1731 | assert(dll_names_offset == needed_size); |
| 1732 | |
| 1733 | try self.base.file.?.pwriteAll(buffer.items, header.pointer_to_raw_data); |
| 1726 | 1734 | |
| 1727 | 1735 | self.data_directories[@enumToInt(coff.DirectoryEntry.IMPORT)] = .{ |
| 1728 | | .virtual_address = iat_rva + iat_size, |
| 1729 | | .size = @intCast(u32, @sizeOf(coff.ImportDirectoryEntry) * 2), |
| 1736 | .virtual_address = header.virtual_address + iat_size, |
| 1737 | .size = dir_table_size, |
| 1730 | 1738 | }; |
| 1731 | | |
| 1732 | 1739 | self.data_directories[@enumToInt(coff.DirectoryEntry.IAT)] = .{ |
| 1733 | | .virtual_address = iat_rva, |
| 1740 | .virtual_address = header.virtual_address, |
| 1734 | 1741 | .size = iat_size, |
| 1735 | 1742 | }; |
| 1743 | |
| 1744 | self.imports_count_dirty = false; |
| 1736 | 1745 | } |
| 1737 | 1746 | |
| 1738 | 1747 | fn writeStrtab(self: *Coff) !void { |
| ... | ... | @@ -2121,14 +2130,6 @@ pub fn getGotAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom |
| 2121 | 2130 | return self.getAtomIndexForSymbol(.{ .sym_index = got_entry.sym_index, .file = null }); |
| 2122 | 2131 | } |
| 2123 | 2132 | |
| 2124 | | /// Returns import atom that references `sym_loc` if one exists. |
| 2125 | | /// Returns null otherwise. |
| 2126 | | pub fn getImportAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index { |
| 2127 | | const imports_index = self.imports_table.get(sym_loc) orelse return null; |
| 2128 | | const imports_entry = self.imports.items[imports_index]; |
| 2129 | | return self.getAtomIndexForSymbol(.{ .sym_index = imports_entry.sym_index, .file = null }); |
| 2130 | | } |
| 2131 | | |
| 2132 | 2133 | fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void { |
| 2133 | 2134 | if (name.len <= 8) { |
| 2134 | 2135 | mem.copy(u8, &header.name, name); |
| ... | ... | @@ -2249,3 +2250,50 @@ fn logSections(self: *Coff) void { |
| 2249 | 2250 | }); |
| 2250 | 2251 | } |
| 2251 | 2252 | } |
| 2253 | |
| 2254 | fn logImportTables(self: *const Coff) void { |
| 2255 | log.debug("import tables:", .{}); |
| 2256 | for (self.import_tables.keys(), 0..) |off, i| { |
| 2257 | const itable = self.import_tables.values()[i]; |
| 2258 | log.debug("{}", .{itable.fmtDebug(.{ |
| 2259 | .coff_file = self, |
| 2260 | .index = i, |
| 2261 | .name_off = off, |
| 2262 | })}); |
| 2263 | } |
| 2264 | } |
| 2265 | |
| 2266 | const Coff = @This(); |
| 2267 | |
| 2268 | const std = @import("std"); |
| 2269 | const build_options = @import("build_options"); |
| 2270 | const builtin = @import("builtin"); |
| 2271 | const assert = std.debug.assert; |
| 2272 | const coff = std.coff; |
| 2273 | const fmt = std.fmt; |
| 2274 | const log = std.log.scoped(.link); |
| 2275 | const math = std.math; |
| 2276 | const mem = std.mem; |
| 2277 | |
| 2278 | const Allocator = std.mem.Allocator; |
| 2279 | |
| 2280 | const codegen = @import("../codegen.zig"); |
| 2281 | const link = @import("../link.zig"); |
| 2282 | const lld = @import("Coff/lld.zig"); |
| 2283 | const trace = @import("../tracy.zig").trace; |
| 2284 | |
| 2285 | const Air = @import("../Air.zig"); |
| 2286 | pub const Atom = @import("Coff/Atom.zig"); |
| 2287 | const Compilation = @import("../Compilation.zig"); |
| 2288 | const ImportTable = @import("Coff/ImportTable.zig"); |
| 2289 | const Liveness = @import("../Liveness.zig"); |
| 2290 | const LlvmObject = @import("../codegen/llvm.zig").Object; |
| 2291 | const Module = @import("../Module.zig"); |
| 2292 | const Object = @import("Coff/Object.zig"); |
| 2293 | const Relocation = @import("Coff/Relocation.zig"); |
| 2294 | const StringTable = @import("strtab.zig").StringTable; |
| 2295 | const TypedValue = @import("../TypedValue.zig"); |
| 2296 | |
| 2297 | pub const base_tag: link.File.Tag = .coff; |
| 2298 | |
| 2299 | const msdos_stub = @embedFile("msdos-stub.bin"); |