aboutsummaryrefslogtreecommitdiff
path: root/src/link/Coff/Atom.zig
blob: 4aa97fa6aced07b8f3a59d85f2301123a0ea9ced (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
const Atom = @This();

const std = @import("std");
const coff = std.coff;
const log = std.log.scoped(.link);

const Coff = @import("../Coff.zig");
const Relocation = @import("Relocation.zig");
const SymbolWithLoc = Coff.SymbolWithLoc;

/// Each decl always gets a local symbol with the fully qualified name.
/// The vaddr and size are found here directly.
/// The file offset is found by computing the vaddr offset from the section vaddr
/// the symbol references, and adding that to the file offset of the section.
/// If this field is 0, it means the codegen size = 0 and there is no symbol or
/// offset table entry.
sym_index: u32,

/// null means symbol defined by Zig source.
file: ?u32,

/// Size of the atom
size: u32,

/// Points to the previous and next neighbors, based on the `text_offset`.
/// This can be used to find, for example, the capacity of this `Atom`.
prev_index: ?Index,
next_index: ?Index,

pub const Index = u32;

pub fn getSymbolIndex(self: Atom) ?u32 {
    if (self.sym_index == 0) return null;
    return self.sym_index;
}

/// Returns symbol referencing this atom.
pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol {
    const sym_index = self.getSymbolIndex().?;
    return coff_file.getSymbol(.{
        .sym_index = sym_index,
        .file = self.file,
    });
}

/// Returns pointer-to-symbol referencing this atom.
pub fn getSymbolPtr(self: Atom, coff_file: *Coff) *coff.Symbol {
    const sym_index = self.getSymbolIndex().?;
    return coff_file.getSymbolPtr(.{
        .sym_index = sym_index,
        .file = self.file,
    });
}

pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
    const sym_index = self.getSymbolIndex().?;
    return .{ .sym_index = sym_index, .file = self.file };
}

/// Returns the name of this atom.
pub fn getName(self: Atom, coff_file: *const Coff) []const u8 {
    const sym_index = self.getSymbolIndex().?;
    return coff_file.getSymbolName(.{
        .sym_index = sym_index,
        .file = self.file,
    });
}

/// Returns how much room there is to grow in virtual address space.
pub fn capacity(self: Atom, coff_file: *const Coff) u32 {
    const self_sym = self.getSymbol(coff_file);
    if (self.next_index) |next_index| {
        const next = coff_file.getAtom(next_index);
        const next_sym = next.getSymbol(coff_file);
        return next_sym.value - self_sym.value;
    } else {
        // We are the last atom.
        // The capacity is limited only by virtual address space.
        return std.math.maxInt(u32) - self_sym.value;
    }
}

pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool {
    // No need to keep a free list node for the last atom.
    const next_index = self.next_index orelse return false;
    const next = coff_file.getAtom(next_index);
    const self_sym = self.getSymbol(coff_file);
    const next_sym = next.getSymbol(coff_file);
    const cap = next_sym.value - self_sym.value;
    const ideal_cap = Coff.padToIdeal(self.size);
    if (cap <= ideal_cap) return false;
    const surplus = cap - ideal_cap;
    return surplus >= Coff.min_text_capacity;
}

pub fn addRelocation(coff_file: *Coff, atom_index: Index, reloc: Relocation) !void {
    const comp = coff_file.base.comp;
    const gpa = comp.gpa;
    log.debug("  (adding reloc of type {s} to target %{d})", .{ @tagName(reloc.type), reloc.target.sym_index });
    const gop = try coff_file.relocs.getOrPut(gpa, atom_index);
    if (!gop.found_existing) {
        gop.value_ptr.* = .{};
    }
    try gop.value_ptr.append(gpa, reloc);
}

pub fn addBaseRelocation(coff_file: *Coff, atom_index: Index, offset: u32) !void {
    const comp = coff_file.base.comp;
    const gpa = comp.gpa;
    log.debug("  (adding base relocation at offset 0x{x} in %{d})", .{
        offset,
        coff_file.getAtom(atom_index).getSymbolIndex().?,
    });
    const gop = try coff_file.base_relocs.getOrPut(gpa, atom_index);
    if (!gop.found_existing) {
        gop.value_ptr.* = .{};
    }
    try gop.value_ptr.append(gpa, offset);
}

pub fn freeRelocations(coff_file: *Coff, atom_index: Index) void {
    const comp = coff_file.base.comp;
    const gpa = comp.gpa;
    var removed_relocs = coff_file.relocs.fetchOrderedRemove(atom_index);
    if (removed_relocs) |*relocs| relocs.value.deinit(gpa);
    var removed_base_relocs = coff_file.base_relocs.fetchOrderedRemove(atom_index);
    if (removed_base_relocs) |*base_relocs| base_relocs.value.deinit(gpa);
}