lib/std/arch/rv64/image/tests.rad 11.6 KiB raw
1
//! Native image placement, relocation, and wire-header checks.
2
3
use std::testing;
4
use std::lang::il;
5
use std::lang::alloc;
6
use std::lang::gen::data;
7
use std::lang::gen::labels;
8
use std::collections::dict;
9
use std::arch::rv64;
10
use std::arch::rv64::image;
11
use std::arch::rv64::emit;
12
use std::arch::rv64::encode;
13
14
/// Arena for one emitter and its bounded relocation tables.
15
static MEMORY: [u8; 16777216] = [0; 16777216];
16
/// Data symbol lookup workspace.
17
unsafe static ENTRIES: [dict::Entry; data::DATA_SYM_TABLE_SIZE] = undefined;
18
19
/// Scalar data emission preserves widths, signs, and little-endian order.
20
@test fn safeDataEmission() throws (testing::TestError) {
21
    let symbols = data::DataSymMap { dict: dict::init(&mut []), syms: &[] };
22
    let functions = labels::init(&mut [], &mut []);
23
    let item = il::Data {
24
        name: "scalar", size: 19, alignment: 1, readOnly: true, isZeroInit: false,
25
        values: &[
26
            il::DataValue { item: il::DataItem::Val { typ: il::Type::W8, val: -1 }, count: 2 },
27
            il::DataValue { item: il::DataItem::Val { typ: il::Type::W16, val: 0x1234 }, count: 1 },
28
            il::DataValue { item: il::DataItem::Val { typ: il::Type::W32, val: 0x12345678 }, count: 1 },
29
            il::DataValue { item: il::DataItem::Val { typ: il::Type::W64, val: -2 }, count: 1 },
30
            il::DataValue { item: il::DataItem::Str("ok"), count: 1 },
31
            il::DataValue { item: il::DataItem::Undef, count: 1 },
32
        ],
33
    };
34
    let mut buffer: [u8; 21] = [42; 21];
35
    let size = try data::emitSection(&[item], &symbols, &functions, 0, &mut buffer[1..20], true) catch {
36
        throw testing::TestError::Failed;
37
    };
38
    try testing::expect(size == 19);
39
    try testing::expectBytesEq(&buffer[..], &[
40
        42, 255, 255, 52, 18, 120, 86, 52, 18,
41
        254, 255, 255, 255, 255, 255, 255, 255, 111, 107, 0, 42,
42
    ]);
43
    let mut failed = false;
44
    try data::emitSection(&[item], &symbols, &functions, 0, &mut buffer[..18], true) catch err {
45
        try testing::expect(err == data::Error::Capacity);
46
        set failed = true;
47
    };
48
    try testing::expect(failed);
49
}
50
51
/// Finalization preserves hosted placement and propagates bounded failures.
52
@test unsafe fn programFinalization() throws (testing::TestError) {
53
    for scenario in 0..6 {
54
        let mut arena = alloc::new(&mut MEMORY[..]);
55
        let placement = image::Placement::Physical {
56
            code: 0x80000000, roData: 0x80000000, rwData: 0x80002000, entry: 0x80000000,
57
        } if scenario == 5 else image::Placement::Hosted;
58
        let entryPatch = rv64::EntryPatch::Reserved(nil) if scenario == 2 else rv64::EntryPatch::None;
59
        let mut generator = try! rv64::beginProgram(rv64::ProgramOptions {
60
            entryPatch, debug: false, placement,
61
        }, &mut arena);
62
        emit::emit(&mut generator.e, encode::nop());
63
        if scenario == 3 { set generator.e.error = rv64::Error::Capacity; }
64
        if scenario == 4 { emit::recordCall(&mut generator.e, "missing"); }
65
        let mut ro: [u8; 5] = [42; 5];
66
        let mut rw: [u8; 0] = [];
67
        let capacity: u32 = 2 if scenario == 1 else 3;
68
        let result = try rv64::finishProgram(generator, &[],
69
            rv64::Storage { dataSyms: &mut [], dataSymEntries: &mut ENTRIES[..] },
70
            &[7, 8, 9], &mut ro[1..capacity + 1], &mut rw[..]
71
        ) catch err {
72
            // Record the failure before the next independent generator is built.
73
            let expected = rv64::Error::Image(image::Error::Overlap) if scenario == 5
74
                else rv64::Error::Symbol if scenario == 2 or scenario == 4
75
                else rv64::Error::Capacity;
76
            assert err == expected;
77
            assert scenario <> 0;
78
            assert ro[0] == 42 and ro[capacity + 1] == 42;
79
            continue;
80
        };
81
        assert scenario == 0;
82
        assert result.layout.code.address == rv64::RO_DATA_BASE as u64 + 8;
83
        assert result.code.len == 1 and result.code[0] == encode::nop();
84
        assert result.roDataSize == 3 and result.rwDataSize == 0;
85
        assert ro[0] == 42 and ro[4] == 42;
86
        assert ro[1] == 7 and ro[2] == 8 and ro[3] == 9;
87
    }
88
}
89
90
/// Construct a small image with three disjoint high-address segments.
91
fn layout() -> image::Layout {
92
    return image::Layout {
93
        entry: 0x80000004,
94
        code: image::Segment { address: 0x80000000, initialized: 8, memory: 8 },
95
        roData: image::Segment { address: 0x80001000, initialized: 3, memory: 16 },
96
        rwData: image::Segment { address: 0x80002000, initialized: 4, memory: 4096 },
97
    };
98
}
99
100
/// Check the exact 64-byte little-endian header.
101
@test fn header() throws (testing::TestError) {
102
    let bytes = try image::header(layout()) catch {
103
        throw testing::TestError::Failed;
104
    };
105
    try testing::expectBytesEq(&bytes[..], &[
106
        82, 65, 68, 48, 2, 0, 0, 0, 4, 0, 0, 128, 0, 0, 0, 0,
107
        0, 0, 0, 128, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0,
108
        0, 16, 0, 128, 0, 0, 0, 0, 3, 0, 0, 0, 16, 0, 0, 0,
109
        0, 32, 0, 128, 0, 0, 0, 0, 4, 0, 0, 0, 0, 16, 0, 0,
110
    ]);
111
}
112
113
/// Check a rejected image layout.
114
fn invalid(item: image::Layout, expected: image::Error) throws (testing::TestError) {
115
    let mut failed = false;
116
    try image::validate(item) catch err {
117
        try testing::expect(err == expected);
118
        set failed = true;
119
    };
120
    try testing::expect(failed);
121
}
122
123
/// Check overlap, alignment, size, entry, and address overflow failures.
124
@test fn validation() throws (testing::TestError) {
125
    let mut item = layout();
126
    set item.rwData.address = item.roData.address + 8;
127
    try invalid(item, image::Error::Overlap);
128
    set item = layout();
129
    set item.roData.address = item.code.address;
130
    try invalid(item, image::Error::Overlap);
131
    set item = layout();
132
    set item.rwData.address += 1;
133
    try invalid(item, image::Error::Alignment);
134
    set item = layout();
135
    set item.entry += 1;
136
    try invalid(item, image::Error::Alignment);
137
    set item = layout();
138
    set item.entry = item.code.address + item.code.initialized as u64;
139
    try invalid(item, image::Error::Entry);
140
    set item = layout();
141
    set item.roData.initialized = 17;
142
    try invalid(item, image::Error::Size);
143
    set item = layout();
144
    set item.rwData.address = 0xfffffffffffffff8;
145
    try invalid(item, image::Error::Overflow);
146
    set item = layout();
147
    set item.roData.address = item.code.address + 8;
148
    try image::validate(item) catch {
149
        throw testing::TestError::Failed;
150
    };
151
}
152
153
/// Check AUIPC/ADDI limits without overflowing unsigned address arithmetic.
154
@test fn displacements() throws (testing::TestError) {
155
    try testing::expect(image::displacement(0x180000000, 0x180002000) == 8192);
156
    try testing::expect(image::displacement(0x180002000, 0x180000000) == -8192);
157
    try testing::expect(image::displacement(0, 0x7ffff7ff) == 0x7ffff7ff);
158
    try testing::expect(image::displacement(0, 0x7ffff800) == nil);
159
    try testing::expect(image::displacement(0x80000000, 0) == -2147483648);
160
    try testing::expect(image::displacement(0x80000001, 0) == nil);
161
    try testing::expect(image::displacement(0, 0xffffffffffffffff) == nil);
162
    try testing::expect(image::displacement(0xffffffffffffffff, 0) == nil);
163
}
164
165
/// Check high physical placement, zero-fill extents, and 64-bit data relocations.
166
@test unsafe fn nativeProgram() throws (testing::TestError) {
167
    let mut arena = alloc::new(&mut MEMORY[..]);
168
    let mut generator = try! rv64::beginProgram(rv64::ProgramOptions {
169
        entryPatch: rv64::EntryPatch::None, debug: false,
170
        placement: image::Placement::Physical {
171
            code: 0x180000000, roData: 0x180001000, rwData: 0x180002000, entry: 0x180000000,
172
        },
173
    }, &mut arena);
174
    emit::recordFunc(&mut generator.e, "p::entry");
175
    emit::recordFuncOffset(&mut generator.e, "p::entry");
176
    emit::recordDataAddrLoad(&mut generator.e, "p::pointer", rv64::A0);
177
    let globals = &[
178
        il::Data {
179
            name: "p::pointer", size: 8, alignment: 8, readOnly: false, isZeroInit: false,
180
            values: &[il::DataValue { item: il::DataItem::Fn("p::entry"), count: 1 }],
181
        },
182
        il::Data {
183
            name: "p::zero", size: 4096, alignment: 8, readOnly: false, isZeroInit: true, values: &[],
184
        },
185
    ];
186
    unsafe static syms: [data::DataSym; 2] = undefined;
187
    let mut ro: [u8; 8] = [0; 8];
188
    let mut rw: [u8; 8] = [0; 8];
189
    let result = try rv64::finishProgram(generator, globals,
190
        rv64::Storage { dataSyms: &mut syms[..], dataSymEntries: &mut ENTRIES[..] },
191
        &[], &mut ro[..], &mut rw[..]
192
    ) catch {
193
        throw testing::TestError::Failed;
194
    };
195
    try testing::expect(result.layout.code.address == 0x180000000);
196
    try testing::expect(result.layout.rwData.initialized == 8);
197
    try testing::expect(result.layout.rwData.memory == 4104);
198
    try testing::expect(syms[1].addr == 0x180002008);
199
    try testing::expectBytesEq(&rw[..], &[0, 0, 0, 128, 1, 0, 0, 0]);
200
    try testing::expect(result.code[0] == encode::auipc(rv64::A0, 2));
201
    try testing::expect(result.code[1] == encode::addi(rv64::A0, rv64::A0, 0));
202
}
203
204
/// Check bounded data-layout failure without wrapped addresses or symbol writes.
205
@test unsafe fn dataLayout() throws (testing::TestError) {
206
    let item = il::Data {
207
        name: "p::data", size: 16, alignment: 8, readOnly: false, isZeroInit: true, values: &[],
208
    };
209
    unsafe static syms: [data::DataSym; 1] = undefined;
210
    let mut count: u32 = 0;
211
    let mut failed = false;
212
    try data::layoutSection(&[item], &mut syms[..], &mut count, 0xfffffffffffffff8, false) catch err {
213
        try testing::expect(err == data::Error::Overflow);
214
        set failed = true;
215
    };
216
    try testing::expect(failed and count == 0);
217
    set failed = false;
218
    try data::layoutSection(&[item], &mut syms[..0], &mut count, 0x80000000, false) catch err {
219
        try testing::expect(err == data::Error::Capacity);
220
        set failed = true;
221
    };
222
    try testing::expect(failed and count == 0);
223
    set failed = false;
224
    try data::layoutSectionAtOffset(&[item], &mut syms[..], &mut count, 0, 0xfffffff8, false) catch err {
225
        try testing::expect(err == data::Error::Overflow);
226
        set failed = true;
227
    };
228
    try testing::expect(failed and count == 0);
229
}
230
231
/// Keep declared symbol extents and alignment gaps in initialized data.
232
@test unsafe fn initializedExtents() throws (testing::TestError) {
233
    let mut arena = alloc::new(&mut MEMORY[..]);
234
    let mut generator = try! rv64::beginProgram(rv64::ProgramOptions {
235
        entryPatch: rv64::EntryPatch::None, debug: false,
236
        placement: image::Placement::Physical {
237
            code: 0x80000000, roData: 0x80001000, rwData: 0x80002000, entry: 0x80000000,
238
        },
239
    }, &mut arena);
240
    emit::recordDataAddrLoad(&mut generator.e, "p::second", rv64::A0);
241
    let globals = &[
242
        il::Data {
243
            name: "p::first", size: 5, alignment: 1, readOnly: false, isZeroInit: false,
244
            values: &[il::DataValue { item: il::DataItem::Str("a"), count: 1 }],
245
        },
246
        il::Data {
247
            name: "p::second", size: 8, alignment: 8, readOnly: false, isZeroInit: false,
248
            values: &[il::DataValue { item: il::DataItem::Str("b"), count: 1 }],
249
        },
250
    ];
251
    unsafe static syms: [data::DataSym; 2] = undefined;
252
    let mut ro: [u8; 0] = [];
253
    let mut rw: [u8; 16] = [255; 16];
254
    let result = try rv64::finishProgram(generator, globals,
255
        rv64::Storage { dataSyms: &mut syms[..], dataSymEntries: &mut ENTRIES[..] },
256
        &[], &mut ro[..], &mut rw[..]
257
    ) catch {
258
        throw testing::TestError::Failed;
259
    };
260
    try testing::expect(result.layout.rwData.initialized == 16);
261
    try testing::expect(syms[1].addr == 0x80002008);
262
    try testing::expectBytesEq(&rw[..], &[97, 0, 0, 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, 0, 0, 0]);
263
}