lib/std/arch/rv64/shared.rad 15.6 KiB raw
1
//! Shared package code, private data templates, and qualified symbol linking.
2
3
@test export mod tests;
4
export mod catalog;
5
6
use std::mem;
7
use std::lang::alloc;
8
use std::lang::il;
9
use std::lang::il::binary;
10
use std::lang::gen;
11
use std::lang::gen::data;
12
use super::emit;
13
use super::encode;
14
use super::image;
15
use super::asm;
16
17
/// Maximum number of resident package slots in a domain state table.
18
export constant MAX_PACKAGES: u32 = 256;
19
20
/// Package linking or instance storage failure.
21
export union Error: Copy {
22
    /// Reusable backend generation failed.
23
    Codegen(super::Error),
24
    /// A caller-supplied output buffer is full.
25
    Capacity,
26
    /// A symbol is absent, duplicated, or has the wrong kind.
27
    Symbol,
28
    /// An address or data extent is outside the supported range.
29
    Range,
30
    /// A data or code address has invalid alignment.
31
    Alignment,
32
    /// A referenced package has no private instance.
33
    Instance,
34
}
35
36
/// Address of data within a domain's package graph.
37
export record DataRef: Copy {
38
    /// Stable global package slot.
39
    slot: u32,
40
    /// Byte offset in the package's private state.
41
    offset: u32,
42
}
43
44
/// Resolved function or domain-relative data address.
45
export union Target: Copy {
46
    /// Absolute address of shared executable code.
47
    Function(u64),
48
    /// Private data location selected through gp.
49
    Data(DataRef),
50
}
51
52
/// Resolved qualified symbol.
53
export record Symbol: Copy {
54
    /// Fully qualified symbol name.
55
    name: *[u8],
56
    /// Code address or private data location.
57
    target: Target,
58
}
59
60
/// Private pointer fixup applied when a domain is instantiated.
61
export record Relocation: Copy {
62
    /// Byte offset of the first pointer in the private template.
63
    offset: u32,
64
    /// Number of consecutive 64-bit pointers.
65
    count: u32,
66
    /// Target data location in the domain graph.
67
    target: DataRef,
68
}
69
70
/// Caller-owned storage for one package's persistent link results.
71
export record Storage {
72
    /// Local data layout workspace.
73
    data: *mut [data::DataSym],
74
    /// Local function and data definitions.
75
    symbols: *mut [Symbol],
76
    /// Public symbols for dependent packages.
77
    exports: *mut [Symbol],
78
    /// Private data initializer bytes.
79
    template: *mut [u8],
80
    /// Private pointer fixups.
81
    relocations: *mut [Relocation],
82
}
83
84
/// A resident package entry used by boot catalogs and runtime loading.
85
export record Package: Copy {
86
    /// Immutable package identity.
87
    name: *[u8],
88
    /// Required package names, in binary package order.
89
    dependencies: *unsafe [*[u8]],
90
    /// Stable slot in each domain's package-state table.
91
    slot: u32,
92
    /// Address at which the shared instructions execute.
93
    codeAddress: u64,
94
    /// Generated shared instructions.
95
    code: *[u32],
96
    /// Public function and data definitions.
97
    exports: *[Symbol],
98
    /// Exported default entry, if present.
99
    entry: ?u64,
100
    /// Initialized private bytes. The rest of memory must be zero-filled.
101
    template: *[u8],
102
    /// Total private state extent.
103
    memory: u32,
104
    /// Required private state base alignment.
105
    alignment: u32,
106
    /// Private pointers to resolve after all graph instances have storage.
107
    relocations: *[Relocation],
108
}
109
110
/// Find exactly one qualified symbol in a bounded table.
111
export fn lookup(symbols: &[Symbol], name: &[u8]) -> ?Target {
112
    for symbol in symbols {
113
        if mem::eq(symbol.name, name) { return symbol.target; }
114
    }
115
    return nil;
116
}
117
118
/// Resolve a local or imported symbol.
119
fn resolve(local: &[Symbol], imports: &[Symbol], name: *[u8]) -> Target throws (Error) {
120
    if let target = lookup(local, name) { return target; }
121
    let target = lookup(imports, name) else { throw Error::Symbol; };
122
    return target;
123
}
124
125
/// Append a unique local definition to bounded symbol storage.
126
fn define(symbols: &mut [Symbol], count: &mut u32, name: *[u8], target: Target) throws (Error) {
127
    if lookup(&symbols[..*count], name) <> nil { throw Error::Symbol; }
128
    if *count == symbols.len { throw Error::Capacity; }
129
    set symbols[*count] = Symbol { name, target };
130
    set *count += 1;
131
}
132
133
/// Resolve a shared function address with its required kind.
134
fn function(local: &[Symbol], imports: &[Symbol], name: *[u8]) -> u64 throws (Error) {
135
    match try resolve(local, imports, name) {
136
        case Target::Function(address) => return address,
137
        else => throw Error::Symbol,
138
    }
139
}
140
141
/// Require a private data target.
142
fn dataRef(target: Target) -> DataRef throws (Error) {
143
    match target {
144
        case Target::Data(location) => return location,
145
        else => throw Error::Symbol,
146
    }
147
}
148
149
/// Patch a PC-relative call or function address load.
150
unsafe fn relative(e: &mut emit::Emitter, index: u32, base: u64, target: u64, rd: gen::Reg, call: bool)
151
    throws (Error)
152
{
153
    let delta = image::displacement(base + index as u64 * 4, target) else { throw Error::Range; };
154
    let parts = emit::splitImm(delta);
155
    emit::patch(e, index, encode::auipc(rd, parts.hi));
156
    if call {
157
        emit::patch(e, index + 1, encode::jalr(super::RA, rd, parts.lo));
158
    } else {
159
        emit::patch(e, index + 1, encode::addi(rd, rd, parts.lo));
160
    }
161
}
162
163
/// Link shared code against local definitions and dependency exports.
164
unsafe fn link(e: &mut emit::Emitter, base: u64, local: &[Symbol], imports: &[Symbol]) throws (Error) {
165
    for i in 0..e.pendingCalls.len {
166
        let pending = e.pendingCalls[i];
167
        let address = try function(local, imports, pending.target);
168
        try relative(e, pending.index, base, address, super::SCRATCH1, true);
169
    }
170
    for i in 0..e.pendingJumps.len {
171
        let pending = e.pendingJumps[i];
172
        let address = try function(local, imports, pending.target);
173
        let delta = image::displacement(base + pending.index as u64 * 4, address) else { throw Error::Range; };
174
        if not encode::isJumpImm(delta) { throw Error::Range; }
175
        emit::patch(e, pending.index, encode::jal(pending.rd, delta));
176
    }
177
    for i in 0..e.pendingAddrLoads.len {
178
        let pending = e.pendingAddrLoads[i];
179
        if not pending.isData {
180
            try relative(e, pending.index, base, try function(local, imports, pending.target), pending.rd, false);
181
            continue;
182
        }
183
        let target = try resolve(local, imports, pending.target);
184
        let location = try dataRef(target);
185
        if location.slot >= MAX_PACKAGES or location.offset > 0x7ffff7ff { throw Error::Range; }
186
        if pending.rd == super::ADDR_SCRATCH or pending.rd == super::GP or pending.rd == super::ZERO {
187
            throw Error::Symbol;
188
        }
189
        let parts = emit::splitImm(location.offset as i32);
190
        emit::patch(e, pending.index, encode::ld(pending.rd, super::GP, location.slot as i32 * 8));
191
        emit::patch(e, pending.index + 1, encode::lui(super::ADDR_SCRATCH, parts.hi));
192
        emit::patch(e, pending.index + 2, encode::add(pending.rd, pending.rd, super::ADDR_SCRATCH));
193
        emit::patch(e, pending.index + 3, encode::addi(pending.rd, pending.rd, parts.lo));
194
    }
195
}
196
197
/// Write a little-endian integer into a validated byte extent.
198
fn integer(bytes: &mut [u8], offset: u32, value: u64, width: u32) {
199
    for i in 0..width { set bytes[offset + i] = (value >> (i as u64 * 8)) as u8; }
200
}
201
202
/// Initialized output extents.
203
record TemplateSize: Copy {
204
    /// Initialized byte count.
205
    bytes: u32,
206
    /// Private pointer fixup count.
207
    relocations: u32,
208
}
209
210
/// Build initialized bytes and compact private pointer fixups.
211
unsafe fn template(items: *[il::Data], local: &[Symbol], imports: &[Symbol], bytes: &mut [u8], relocs: &mut [Relocation])
212
    -> TemplateSize throws (Error)
213
{
214
    let mut initialized: u32 = 0;
215
    let mut count: u32 = 0;
216
    for item in items {
217
        if item.isZeroInit { continue; }
218
        let location = try dataRef(try resolve(local, &[], item.name));
219
        if location.offset > bytes.len or item.size > bytes.len - location.offset { throw Error::Capacity; }
220
        let end = location.offset + item.size;
221
        if end > initialized { set initialized = end; }
222
    }
223
    for i in 0..initialized { set bytes[i] = 0; }
224
    for item in items {
225
        if item.isZeroInit { continue; }
226
        let location = try dataRef(try resolve(local, &[], item.name));
227
        let mut offset = location.offset;
228
        let end = offset + item.size;
229
        for value in item.values {
230
            let mut width: u32 = 1;
231
            let mut number: u64 = 0;
232
            match value.item {
233
                case il::DataItem::Val { typ, val } => { set width = il::typeSize(typ); set number = val as u64; },
234
                case il::DataItem::Fn(name) => { set width = 8; set number = try function(local, imports, name); },
235
                case il::DataItem::Sym(name) => {
236
                    set width = 8;
237
                    let target = try dataRef(try resolve(local, imports, name));
238
                    if value.count > 0 {
239
                        if count == relocs.len { throw Error::Capacity; }
240
                        set relocs[count] = Relocation { offset, count: value.count, target };
241
                        set count += 1;
242
                    }
243
                },
244
                case il::DataItem::Str(s) => { set width = s.len; },
245
                case il::DataItem::Undef => {},
246
            }
247
            if width > 0 and value.count > (end - offset) / width { throw Error::Range; }
248
            if width == 0 { continue; }
249
            for _ in 0..value.count {
250
                match value.item {
251
                    case il::DataItem::Str(s) => { try! mem::copy(&mut bytes[offset..offset + width], s); },
252
                    else => { integer(bytes, offset, number, width); },
253
                }
254
                set offset += width;
255
            }
256
        }
257
    }
258
    return TemplateSize { bytes: initialized, relocations: count };
259
}
260
261
/// Package definitions and their native assembly boundaries.
262
export record AssemblyInput: Copy {
263
    /// Trusted binary RIL definitions that remain valid during compilation.
264
    package: *unsafe binary::Package,
265
    /// Text-only assembly prefix with exported native boundaries.
266
    assembly: asm::Program,
267
}
268
269
/// Compile one package into caller-owned code, symbol, and private template storage.
270
/// Imports must contain unique exports from the package's admitted dependencies.
271
/// Arena storage and binary package names must outlive the returned catalog entry.
272
export unsafe fn compile(input: &binary::Package, slot: u32, codeAddress: u64, imports: &[Symbol],
273
    storage: Storage, arena: &mut alloc::Arena, scratch: &mut alloc::Arena) -> Package throws (Error)
274
{
275
    return try compileAssembly(AssemblyInput { package: input as *unsafe binary::Package,
276
        assembly: asm::Program { text: &[], data: &[], symbols: &[], externalFixups: &[] } },
277
        slot, codeAddress, imports, storage, arena, scratch);
278
}
279
280
/// Compile a package with a text-only assembly prefix and its exported boundaries.
281
/// Assembly and RIL definitions share one native code extent and package-state slot.
282
export unsafe fn compileAssembly(source: AssemblyInput, slot: u32, codeAddress: u64, imports: &[Symbol],
283
    storage: Storage, arena: &mut alloc::Arena, scratch: &mut alloc::Arena) -> Package throws (Error)
284
{
285
    let case Storage { data: dataStorage, symbols: symbolStorage, exports: exportStorage, template: templateStorage, relocations: relocationStorage } = storage
286
        else panic "expected shared linker storage";
287
    let input = source.package;
288
    let assembly = source.assembly;
289
    if assembly.data.len <> 0 { throw Error::Range; }
290
    if slot >= MAX_PACKAGES { throw Error::Range; }
291
    if (codeAddress & 3) <> 0 { throw Error::Alignment; }
292
    for imported, i in imports {
293
        if lookup(&imports[..i], imported.name) <> nil { throw Error::Symbol; }
294
    }
295
    let mut generator = try super::beginProgram(super::ProgramOptions {
296
        entryPatch: super::EntryPatch::None, debug: false, placement: image::Placement::Hosted,
297
    }, arena) catch err { throw Error::Codegen(err); };
298
    set generator.e.sharedData = true;
299
    super::addAssembly(&mut generator, assembly);
300
    for func in input.program.fns { super::generateFunction(&mut generator, func, scratch); }
301
    try emit::check(&generator.e) catch err { throw Error::Codegen(err); };
302
    if codeAddress > 0xffffffffffffffff - generator.e.codeLen as u64 * 4 { throw Error::Range; }
303
    let mut symbols: u32 = 0;
304
    for func in &generator.e.funcs[..] {
305
        try define(symbolStorage, &mut symbols, func.name, Target::Function(codeAddress + func.index as u64 * 4));
306
    }
307
    let mut dataCount: u32 = 0;
308
    let roSize = try data::layoutSection(input.program.data, dataStorage, &mut dataCount, 0, true)
309
        catch { throw Error::Range; };
310
    let memory = try data::layoutSectionAtOffset(input.program.data, dataStorage, &mut dataCount, 0, roSize, false)
311
        catch { throw Error::Range; };
312
    if memory > 0x7ffff7ff { throw Error::Range; }
313
    for item in &dataStorage[..dataCount] {
314
        try define(symbolStorage, &mut symbols, item.name, Target::Data(DataRef { slot, offset: item.addr as u32 }));
315
    }
316
    let local = &symbolStorage[..symbols];
317
    for symbol in local { if lookup(imports, symbol.name) <> nil { throw Error::Symbol; } }
318
    try link(&mut generator.e, codeAddress, local, imports);
319
    try emit::check(&generator.e) catch err { throw Error::Codegen(err); };
320
    let size = try template(input.program.data, local, imports, templateStorage, relocationStorage);
321
    if input.exports.len > exportStorage.len { throw Error::Capacity; }
322
    for exported, i in input.exports {
323
        let target = try resolve(local, &[], exported.name);
324
        match exported.kind {
325
            case binary::ExportKind::Function => { try function(local, &[], exported.name); },
326
            case binary::ExportKind::Data => { try dataRef(target); },
327
        }
328
        if lookup(&exportStorage[..i], exported.name) <> nil { throw Error::Symbol; }
329
        set exportStorage[i] = Symbol { name: exported.name, target };
330
    }
331
    let mut entry: ?u64 = nil;
332
    if let name = input.entry { set entry = try function(&exportStorage[..input.exports.len], &[], name); }
333
    let mut alignment: u32 = 8;
334
    for item in input.program.data { if item.alignment > alignment { set alignment = item.alignment; } }
335
    return Package {
336
        name: input.name, dependencies: input.dependencies, slot, codeAddress,
337
        code: emit::getCode(&generator.e), exports: &exportStorage[..input.exports.len], entry,
338
        template: &templateStorage[..size.bytes], memory, alignment,
339
        relocations: &relocationStorage[..size.relocations],
340
    };
341
}
342
343
/// Initialize private package memory after all graph bases have been assigned.
344
/// Validate every fixup before writing bytes so a rejected instance stays intact.
345
export fn instantiate(package: &Package, bases: &[u64], memory: &mut [u8]) throws (Error) {
346
    if package.slot >= bases.len or bases[package.slot] == 0 { throw Error::Instance; }
347
    if memory.len < package.memory or package.template.len > package.memory { throw Error::Capacity; }
348
    if (bases[package.slot] & (package.alignment as u64 - 1)) <> 0 { throw Error::Alignment; }
349
    if bases[package.slot] > 0xffffffffffffffff - package.memory as u64 { throw Error::Range; }
350
    for fixup in package.relocations {
351
        if fixup.target.slot >= bases.len or bases[fixup.target.slot] == 0 { throw Error::Instance; }
352
        if bases[fixup.target.slot] > 0xffffffffffffffff - fixup.target.offset as u64 { throw Error::Range; }
353
        if fixup.offset > package.memory or fixup.count > (package.memory - fixup.offset) / 8 { throw Error::Range; }
354
    }
355
    for i in 0..package.memory { set memory[i] = 0; }
356
    try! mem::copy(memory, package.template);
357
    for fixup in package.relocations {
358
        let address = bases[fixup.target.slot] + fixup.target.offset as u64;
359
        for i in 0..fixup.count { integer(memory, fixup.offset + i * 8, address, 8); }
360
    }
361
}