lib/std/arch/rv64/image.rad 4.5 KiB raw
1
//! Native image segment placement and versioned wire headers.
2
3
@test export mod tests;
4
5
/// Native image signature, encoded as RAD0 in little-endian order.
6
export constant MAGIC: u32 = 0x30444152;
7
/// Native image format with explicit segment addresses and memory extents.
8
export constant VERSION: u32 = 2;
9
/// Fixed header size: signature, version, entry, and three segment records.
10
export constant HEADER_SIZE: u32 = 64;
11
12
/// Invalid native image placement.
13
export union Error: Copy {
14
    /// A size or end address cannot be represented.
15
    Overflow,
16
    /// A segment or entry violates its alignment requirement.
17
    Alignment,
18
    /// Segment memory intervals overlap.
19
    Overlap,
20
    /// An initialized extent exceeds its memory extent.
21
    Size,
22
    /// The entry is outside initialized code.
23
    Entry,
24
    /// A PC-relative address load exceeds its instruction range.
25
    Relocation,
26
}
27
28
/// File and memory extents for one image segment.
29
export record Segment: Copy {
30
    /// Physical start address.
31
    address: u64,
32
    /// Number of bytes stored in the image file.
33
    initialized: u32,
34
    /// Total memory size, including the zero-filled tail.
35
    memory: u32,
36
}
37
38
/// Explicit native image layout. File payload order is code, rodata, rwdata.
39
export record Layout: Copy {
40
    /// Physical entry address.
41
    entry: u64,
42
    /// Executable instructions.
43
    code: Segment,
44
    /// Read-only data.
45
    roData: Segment,
46
    /// Writable data.
47
    rwData: Segment,
48
}
49
50
/// Check one segment's extent and required base alignment.
51
fn segment(item: Segment, alignment: u64) throws (Error) {
52
    if item.initialized > item.memory {
53
        throw Error::Size;
54
    }
55
    if item.address > 0xffffffffffffffff - item.memory as u64 {
56
        throw Error::Overflow;
57
    }
58
    if item.memory > 0 and (item.address & (alignment - 1)) <> 0 {
59
        throw Error::Alignment;
60
    }
61
}
62
63
/// Check that two nonempty memory intervals are disjoint.
64
fn disjoint(a: Segment, b: Segment) throws (Error) {
65
    if a.memory > 0 and b.memory > 0 and
66
        a.address < b.address + b.memory as u64 and b.address < a.address + a.memory as u64
67
    {
68
        throw Error::Overlap;
69
    }
70
}
71
72
/// Validate segment extents, alignment, overlap, and the executable entry.
73
export fn validate(layout: Layout) throws (Error) {
74
    try segment(layout.code, 4);
75
    try segment(layout.roData, 8);
76
    try segment(layout.rwData, 8);
77
    if (layout.code.initialized & 3) <> 0 or (layout.entry & 3) <> 0 {
78
        throw Error::Alignment;
79
    }
80
    if layout.entry < layout.code.address or
81
        layout.entry - layout.code.address >= layout.code.initialized as u64
82
    {
83
        throw Error::Entry;
84
    }
85
    try disjoint(layout.code, layout.roData);
86
    try disjoint(layout.code, layout.rwData);
87
    try disjoint(layout.roData, layout.rwData);
88
}
89
90
/// Encode a validated native header in little-endian order.
91
export fn header(layout: Layout) -> [u8; 64] throws (Error) {
92
    try validate(layout);
93
    let words: [u64; 8] = [
94
        MAGIC as u64 | (VERSION as u64 << 32), layout.entry,
95
        layout.code.address, layout.code.initialized as u64 | (layout.code.memory as u64 << 32),
96
        layout.roData.address, layout.roData.initialized as u64 | (layout.roData.memory as u64 << 32),
97
        layout.rwData.address, layout.rwData.initialized as u64 | (layout.rwData.memory as u64 << 32),
98
    ];
99
    let mut bytes: [u8; HEADER_SIZE] = [0; HEADER_SIZE];
100
    for word, i in &words[..] {
101
        for j in 0..8 {
102
            set bytes[i * 8 + j] = (word >> (j as u64 * 8)) as u8;
103
        }
104
    }
105
    return bytes;
106
}
107
108
/// Address policy for generated code and data.
109
export union Placement: Copy {
110
    /// Hosted layout with code after read-only data.
111
    Hosted,
112
    /// Fixed physical segment addresses and entry.
113
    Physical {
114
        /// Executable segment base.
115
        code: u64,
116
        /// Read-only segment base.
117
        roData: u64,
118
        /// Writable segment base.
119
        rwData: u64,
120
        /// First instruction to execute.
121
        entry: u64,
122
    },
123
}
124
125
/// Compute a signed displacement for a two-instruction AUIPC/ADDI load.
126
export fn displacement(source: u64, target: u64) -> ?i32 {
127
    if target >= source {
128
        let distance = target - source;
129
        // AUIPC sign-extends its upper immediate before ADDI applies the low part.
130
        if distance > 0x7ffff7ff {
131
            return nil;
132
        }
133
        return distance as i32;
134
    }
135
    let distance = source - target;
136
    if distance > 0x80000000 {
137
        return nil;
138
    }
139
    return (-(distance as i64)) as i32;
140
}