aboutsummaryrefslogtreecommitdiff
path: root/src/link/Coff/Atom.zig
blob: 39e04b264108136fe06a60e26c33ec791e298658 (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
const Atom = @This();

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

const Coff = @import("../Coff.zig");
const Reloc = Coff.Reloc;
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,

/// Used size of the atom
size: u32,

/// Alignment of the atom
alignment: 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: ?*Atom,
next: ?*Atom,

pub const empty = Atom{
    .sym_index = 0,
    .file = null,
    .size = 0,
    .alignment = 0,
    .prev = null,
    .next = null,
};

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

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

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

/// Returns the name of this atom.
pub fn getName(self: Atom, coff_file: *const Coff) []const u8 {
    return coff_file.getSymbolName(.{
        .sym_index = self.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) |next| {
        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 = self.next orelse return false;
    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(self: *Atom, coff_file: *Coff, reloc: Reloc) !void {
    const gpa = coff_file.base.allocator;
    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, self);
    if (!gop.found_existing) {
        gop.value_ptr.* = .{};
    }
    try gop.value_ptr.append(gpa, reloc);
}

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