compiler: Emit data integers through checked byte slices
318bd58c5da3492fbacff8093418086d52825077af42ec8228ca84c961c44941
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; |