compiler: Emit data integers through checked byte slices

318bd58c5da3492fbacff8093418086d52825077af42ec8228ca84c961c44941
Alexis Sellier committed ago 1 parent 648ff424
lib/std/arch/rv64/image/tests.rad +33 -0
2 2
3 3
use std::testing;
4 4
use std::lang::il;
5 5
use std::lang::alloc;
6 6
use std::lang::gen::data;
7 +
use std::lang::gen::labels;
7 8
use std::collections::dict;
8 9
use std::arch::rv64;
9 10
use std::arch::rv64::image;
10 11
use std::arch::rv64::emit;
11 12
use std::arch::rv64::encode;
13 14
/// Arena for one emitter and its bounded relocation tables.
14 15
static MEMORY: [u8; 16777216] = [0; 16777216];
15 16
/// Data symbol lookup workspace.
16 17
unsafe static ENTRIES: [dict::Entry; data::DATA_SYM_TABLE_SIZE] = undefined;
17 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 +
18 51
/// Construct a small image with three disjoint high-address segments.
19 52
fn layout() -> image::Layout {
20 53
    return image::Layout {
21 54
        entry: 0x80000004,
22 55
        code: image::Segment { address: 0x80000000, initialized: 8, memory: 8 },
lib/std/lang/gen/data.rad +14 -8
109 109
}
110 110
111 111
/// Emit data bytes for a single section (read-only or read-write) into `buf`.
112 112
/// Iterates data requiring sidecar image bytes, serializing each data item.
113 113
/// Returns the total number of bytes written.
114 -
export unsafe fn emitSection(
114 +
export fn emitSection(
115 115
    items: &[il::Data],
116 116
    dataSymMap: &DataSymMap,
117 117
    fnLabels: &labels::Labels,
118 118
    codeBase: u64,
119 119
    buf: &mut [u8],
121 121
) -> u32 throws (Error) {
122 122
    return try emitSectionAtOffset(items, dataSymMap, fnLabels, codeBase, buf, readOnly, 0);
123 123
}
124 124
125 125
/// Emit data bytes for a single section starting at `startOffset`.
126 -
export unsafe fn emitSectionAtOffset(
126 +
export fn emitSectionAtOffset(
127 127
    items: &[il::Data],
128 128
    dataSymMap: &DataSymMap,
129 129
    fnLabels: &labels::Labels,
130 130
    codeBase: u64,
131 131
    buf: &mut [u8],
180 180
                }
181 181
                for _ in 0..v.count {
182 182
                    match v.item {
183 183
                        case il::DataItem::Val { typ, val } => {
184 184
                            let size = il::typeSize(typ);
185 -
                            try! mem::copy(&mut buf[offset..], @sliceOf(&val as &u8, size));
185 +
                            writeInteger(&mut buf[offset..offset + size], val as u64);
186 186
187 187
                            set offset += size;
188 188
                        },
189 189
                        case il::DataItem::Sym(name) => {
190 190
                            let addr = lookupAddr(dataSymMap, name) else {
191 191
                                throw Error::Symbol;
192 192
                            };
193 -
                            let addr64: u64 = addr as u64;
194 -
                            try! mem::copy(&mut buf[offset..], @sliceOf(&addr64 as &u8, 8));
193 +
                            writeInteger(&mut buf[offset..offset + 8], addr);
195 194
196 195
                            set offset += @sizeOf(u64);
197 196
                        },
198 197
                        case il::DataItem::Fn(name) => {
199 198
                            let fnOffset = dict::get(&fnLabels.funcs, name) else {
201 200
                            };
202 201
                            if fnOffset < 0 or codeBase > 0xffffffffffffffff - fnOffset as u64 {
203 202
                                throw Error::Overflow;
204 203
                            }
205 204
                            let addr = codeBase + fnOffset as u64;
206 -
                            let addr64: u64 = addr as u64;
207 -
                            try! mem::copy(&mut buf[offset..], @sliceOf(&addr64 as &u8, 8));
205 +
                            writeInteger(&mut buf[offset..offset + 8], addr);
208 206
209 -
                            set offset += @sizeOf(*u8);
207 +
                            set offset += @sizeOf(u64);
210 208
                        },
211 209
                        case il::DataItem::Str(s) => {
212 210
                            try! mem::copy(&mut buf[offset..], s);
213 211
                            set offset += s.len;
214 212
                        },
223 221
        }
224 222
    }
225 223
    return offset;
226 224
}
227 225
226 +
/// Write the low bytes of an integer in little-endian order.
227 +
fn writeInteger(buf: &mut [u8], value: u64) {
228 +
    assert buf.len <= 8;
229 +
    for i in 0..buf.len {
230 +
        set buf[i] = (value >> (i * 8) as u64) as u8;
231 +
    }
232 +
}
233 +
228 234
/// Build a hash-indexed data symbol map from the laid-out symbols.
229 235
/// The entry count must be a power of two, at least twice the symbol count.
230 236
export fn buildMap(syms: *[DataSym], entries: *mut [dict::Entry]) -> DataSymMap throws (Error) {
231 237
    if entries.len == 0 or (entries.len & (entries.len - 1)) <> 0 or syms.len > entries.len / 2 {
232 238
        throw Error::Capacity;