compiler/
lib/
examples/
std/
arch/
rv64/
asm/
image/
shared/
asm.rad
24.8 KiB
atomicTests.rad
5.0 KiB
atomics.rad
3.6 KiB
bounds.rad
21.0 KiB
decode.rad
15.2 KiB
emit.rad
33.2 KiB
encode.rad
22.0 KiB
image.rad
4.5 KiB
isel.rad
52.1 KiB
printer.rad
13.9 KiB
shared.rad
16.8 KiB
tests.rad
17.2 KiB
rv64.rad
17.5 KiB
char/
collections/
lang/
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.3 KiB
intrinsics.rad
467 B
io.rad
1.7 KiB
lang.rad
276 B
mem.rad
2.3 KiB
sys.rad
179 B
testing.rad
2.4 KiB
tests.rad
15.7 KiB
vec.rad
3.2 KiB
std.rad
281 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
5.4 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
662 B
lib/std/arch/rv64/image.rad
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 | } |