lib/std/arch/rv64/shared.rad 16.8 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) {
114
            return symbol.target;
115
        }
116
    }
117
    return nil;
118
}
119
120
/// Resolve a local or imported symbol.
121
fn resolve(local: &[Symbol], imports: &[Symbol], name: *[u8]) -> Target throws (Error) {
122
    if let target = lookup(local, name) {
123
        return target;
124
    }
125
    let target = lookup(imports, name) else {
126
        throw Error::Symbol;
127
    };
128
    return target;
129
}
130
131
/// Append a unique local definition to bounded symbol storage.
132
fn define(symbols: &mut [Symbol], count: &mut u32, name: *[u8], target: Target) throws (Error) {
133
    if lookup(&symbols[..*count], name) <> nil {
134
        throw Error::Symbol;
135
    }
136
    if *count == symbols.len {
137
        throw Error::Capacity;
138
    }
139
    set symbols[*count] = Symbol { name, target };
140
    set *count += 1;
141
}
142
143
/// Resolve a shared function address with its required kind.
144
fn function(local: &[Symbol], imports: &[Symbol], name: *[u8]) -> u64 throws (Error) {
145
    match try resolve(local, imports, name) {
146
        case Target::Function(address) => return address,
147
        else => throw Error::Symbol,
148
    }
149
}
150
151
/// Require a private data target.
152
fn dataRef(target: Target) -> DataRef throws (Error) {
153
    match target {
154
        case Target::Data(location) => return location,
155
        else => throw Error::Symbol,
156
    }
157
}
158
159
/// Patch a PC-relative call or function address load.
160
unsafe fn relative(e: &mut emit::Emitter, index: u32, base: u64, target: u64, rd: gen::Reg, call: bool)
161
    throws (Error)
162
{
163
    let delta = image::displacement(base + index as u64 * 4, target) else {
164
        throw Error::Range;
165
    };
166
    let parts = emit::splitImm(delta);
167
    emit::patch(e, index, encode::auipc(rd, parts.hi));
168
    if call {
169
        emit::patch(e, index + 1, encode::jalr(super::RA, rd, parts.lo));
170
    } else {
171
        emit::patch(e, index + 1, encode::addi(rd, rd, parts.lo));
172
    }
173
}
174
175
/// Link shared code against local definitions and dependency exports.
176
unsafe fn link(e: &mut emit::Emitter, base: u64, local: &[Symbol], imports: &[Symbol]) throws (Error) {
177
    for i in 0..e.pendingCallsLen {
178
        let pending = e.pendingCalls[i];
179
        let address = try function(local, imports, pending.target);
180
        try relative(e, pending.index, base, address, super::SCRATCH1, true);
181
    }
182
    for i in 0..e.pendingJumpsLen {
183
        let pending = e.pendingJumps[i];
184
        let address = try function(local, imports, pending.target);
185
        let delta = image::displacement(base + pending.index as u64 * 4, address) else {
186
            throw Error::Range;
187
        };
188
        if not encode::isJumpImm(delta) {
189
            throw Error::Range;
190
        }
191
        emit::patch(e, pending.index, encode::jal(pending.rd, delta));
192
    }
193
    for i in 0..e.pendingAddrLoadsLen {
194
        let pending = e.pendingAddrLoads[i];
195
        if not pending.isData {
196
            try relative(e, pending.index, base, try function(local, imports, pending.target), pending.rd, false);
197
            continue;
198
        }
199
        let target = try resolve(local, imports, pending.target);
200
        let location = try dataRef(target);
201
        if location.slot >= MAX_PACKAGES or location.offset > 0x7ffff7ff {
202
            throw Error::Range;
203
        }
204
        if pending.rd == super::ADDR_SCRATCH or pending.rd == super::GP or pending.rd == super::ZERO {
205
            throw Error::Symbol;
206
        }
207
        let parts = emit::splitImm(location.offset as i32);
208
        emit::patch(e, pending.index, encode::ld(pending.rd, super::GP, location.slot as i32 * 8));
209
        emit::patch(e, pending.index + 1, encode::lui(super::ADDR_SCRATCH, parts.hi));
210
        emit::patch(e, pending.index + 2, encode::add(pending.rd, pending.rd, super::ADDR_SCRATCH));
211
        emit::patch(e, pending.index + 3, encode::addi(pending.rd, pending.rd, parts.lo));
212
    }
213
}
214
215
/// Write a little-endian integer into a validated byte extent.
216
fn integer(bytes: &mut [u8], offset: u32, value: u64, width: u32) {
217
    for i in 0..width {
218
        set bytes[offset + i] = (value >> (i as u64 * 8)) as u8;
219
    }
220
}
221
222
/// Initialized output extents.
223
record TemplateSize: Copy {
224
    /// Initialized byte count.
225
    bytes: u32,
226
    /// Private pointer fixup count.
227
    relocations: u32,
228
}
229
230
/// Build initialized bytes and compact private pointer fixups.
231
unsafe fn template(items: *[il::Data], local: &[Symbol], imports: &[Symbol], bytes: &mut [u8], relocs: &mut [Relocation])
232
    -> TemplateSize throws (Error)
233
{
234
    let mut initialized: u32 = 0;
235
    let mut count: u32 = 0;
236
    for item in items {
237
        if item.isZeroInit {
238
            continue;
239
        }
240
        let location = try dataRef(try resolve(local, &[], item.name));
241
        if location.offset > bytes.len or item.size > bytes.len - location.offset {
242
            throw Error::Capacity;
243
        }
244
        let end = location.offset + item.size;
245
        if end > initialized {
246
            set initialized = end;
247
        }
248
    }
249
    for i in 0..initialized {
250
        set bytes[i] = 0;
251
    }
252
    for item in items {
253
        if item.isZeroInit {
254
            continue;
255
        }
256
        let location = try dataRef(try resolve(local, &[], item.name));
257
        let mut offset = location.offset;
258
        let end = offset + item.size;
259
        for value in item.values {
260
            let mut width: u32 = 1;
261
            let mut number: u64 = 0;
262
            match value.item {
263
                case il::DataItem::Val { typ, val } => {
264
                    set width = il::typeSize(typ);
265
                    set number = val as u64;
266
                },
267
                case il::DataItem::Fn(name) => {
268
                    set width = 8;
269
                    set number = try function(local, imports, name);
270
                },
271
                case il::DataItem::Sym(name) => {
272
                    set width = 8;
273
                    let target = try dataRef(try resolve(local, imports, name));
274
                    if value.count > 0 {
275
                        if count == relocs.len {
276
                            throw Error::Capacity;
277
                        }
278
                        set relocs[count] = Relocation { offset, count: value.count, target };
279
                        set count += 1;
280
                    }
281
                },
282
                case il::DataItem::Str(s) => {
283
                    set width = s.len;
284
                },
285
                case il::DataItem::Undef => {
286
                },
287
            }
288
            if width > 0 and value.count > (end - offset) / width {
289
                throw Error::Range;
290
            }
291
            if width == 0 {
292
                continue;
293
            }
294
            for _ in 0..value.count {
295
                match value.item {
296
                    case il::DataItem::Str(s) => {
297
                        try! mem::copy(&mut bytes[offset..offset + width], s);
298
                    },
299
                    else => {
300
                        integer(bytes, offset, number, width);
301
                    },
302
                }
303
                set offset += width;
304
            }
305
        }
306
    }
307
    return TemplateSize { bytes: initialized, relocations: count };
308
}
309
310
/// Package definitions and their native assembly boundaries.
311
export record AssemblyInput: Copy {
312
    /// Trusted binary RIL definitions that remain valid during compilation.
313
    package: *unsafe binary::Package,
314
    /// Text-only assembly prefix with exported native boundaries.
315
    assembly: asm::Program,
316
}
317
318
/// Compile one package into caller-owned code, symbol, and private template storage.
319
/// Imports must contain unique exports from the package's admitted dependencies.
320
/// Arena storage and binary package names must outlive the returned catalog entry.
321
export unsafe fn compile(input: &binary::Package, slot: u32, codeAddress: u64, imports: &[Symbol],
322
    storage: Storage, arena: &mut alloc::Arena, scratch: &mut alloc::Arena) -> Package throws (Error)
323
{
324
    return try compileAssembly(AssemblyInput { package: input as *unsafe binary::Package,
325
        assembly: asm::Program { text: &[], data: &[], symbols: &[], externalFixups: &[] } },
326
        slot, codeAddress, imports, storage, arena, scratch);
327
}
328
329
/// Compile a package with a text-only assembly prefix and its exported boundaries.
330
/// Assembly and RIL definitions share one native code extent and package-state slot.
331
export unsafe fn compileAssembly(source: AssemblyInput, slot: u32, codeAddress: u64, imports: &[Symbol],
332
    storage: Storage, arena: &mut alloc::Arena, scratch: &mut alloc::Arena) -> Package throws (Error)
333
{
334
    let case Storage { data: dataStorage, symbols: symbolStorage, exports: exportStorage, template: templateStorage, relocations: relocationStorage } = storage
335
        else panic "expected shared linker storage";
336
    let input = source.package;
337
    let assembly = source.assembly;
338
    if assembly.data.len <> 0 {
339
        throw Error::Range;
340
    }
341
    if slot >= MAX_PACKAGES {
342
        throw Error::Range;
343
    }
344
    if (codeAddress & 3) <> 0 {
345
        throw Error::Alignment;
346
    }
347
    for imported, i in imports {
348
        if lookup(&imports[..i], imported.name) <> nil {
349
            throw Error::Symbol;
350
        }
351
    }
352
    let mut generator = try super::beginProgram(super::ProgramOptions {
353
        entryPatch: super::EntryPatch::None, debug: false, placement: image::Placement::Hosted,
354
    }, arena) catch err {
355
        throw Error::Codegen(err);
356
    };
357
    set generator.e.sharedData = true;
358
    super::addAssembly(&mut generator, assembly);
359
    for func in input.program.fns {
360
        super::generateFunction(&mut generator, func, scratch);
361
    }
362
    try emit::check(&generator.e) catch err {
363
        throw Error::Codegen(err);
364
    };
365
    if codeAddress > 0xffffffffffffffff - generator.e.codeLen as u64 * 4 {
366
        throw Error::Range;
367
    }
368
    let mut symbols: u32 = 0;
369
    for func in &generator.e.funcs[..generator.e.funcsLen] {
370
        try define(symbolStorage, &mut symbols, func.name, Target::Function(codeAddress + func.index as u64 * 4));
371
    }
372
    let mut dataCount: u32 = 0;
373
    let roSize = try data::layoutSection(input.program.data, dataStorage, &mut dataCount, 0, true)
374
        catch {
375
            throw Error::Range;
376
        };
377
    let memory = try data::layoutSectionAtOffset(input.program.data, dataStorage, &mut dataCount, 0, roSize, false)
378
        catch {
379
            throw Error::Range;
380
        };
381
    if memory > 0x7ffff7ff {
382
        throw Error::Range;
383
    }
384
    for item in &dataStorage[..dataCount] {
385
        try define(symbolStorage, &mut symbols, item.name, Target::Data(DataRef { slot, offset: item.addr as u32 }));
386
    }
387
    let local = &symbolStorage[..symbols];
388
    for symbol in local {
389
        if lookup(imports, symbol.name) <> nil {
390
            throw Error::Symbol;
391
        }
392
    }
393
    try link(&mut generator.e, codeAddress, local, imports);
394
    try emit::check(&generator.e) catch err {
395
        throw Error::Codegen(err);
396
    };
397
    let size = try template(input.program.data, local, imports, templateStorage, relocationStorage);
398
    if input.exports.len > exportStorage.len {
399
        throw Error::Capacity;
400
    }
401
    for exported, i in input.exports {
402
        let target = try resolve(local, &[], exported.name);
403
        match exported.kind {
404
            case binary::ExportKind::Function => {
405
                try function(local, &[], exported.name);
406
            },
407
            case binary::ExportKind::Data => {
408
                try dataRef(target);
409
            },
410
        }
411
        if lookup(&exportStorage[..i], exported.name) <> nil {
412
            throw Error::Symbol;
413
        }
414
        set exportStorage[i] = Symbol { name: exported.name, target };
415
    }
416
    let mut entry: ?u64 = nil;
417
    if let name = input.entry {
418
        set entry = try function(&exportStorage[..input.exports.len], &[], name);
419
    }
420
    let mut alignment: u32 = 8;
421
    for item in input.program.data {
422
        if item.alignment > alignment {
423
            set alignment = item.alignment;
424
        }
425
    }
426
    let case super::Generator { e, .. } = generator else panic "expected package generator";
427
    let case emit::Emitter { code, codeLen, .. } = e else panic "expected package emitter";
428
    return Package {
429
        name: input.name, dependencies: input.dependencies, slot, codeAddress,
430
        code: &code[..codeLen], exports: &exportStorage[..input.exports.len], entry,
431
        template: &templateStorage[..size.bytes], memory, alignment,
432
        relocations: &relocationStorage[..size.relocations],
433
    };
434
}
435
436
/// Initialize private package memory after all graph bases have been assigned.
437
/// Validate every fixup before writing bytes so a rejected instance stays intact.
438
export fn instantiate(package: &Package, bases: &[u64], memory: &mut [u8]) throws (Error) {
439
    if package.slot >= bases.len or bases[package.slot] == 0 {
440
        throw Error::Instance;
441
    }
442
    if memory.len < package.memory or package.template.len > package.memory {
443
        throw Error::Capacity;
444
    }
445
    if (bases[package.slot] & (package.alignment as u64 - 1)) <> 0 {
446
        throw Error::Alignment;
447
    }
448
    if bases[package.slot] > 0xffffffffffffffff - package.memory as u64 {
449
        throw Error::Range;
450
    }
451
    for fixup in package.relocations {
452
        if fixup.target.slot >= bases.len or bases[fixup.target.slot] == 0 {
453
            throw Error::Instance;
454
        }
455
        if bases[fixup.target.slot] > 0xffffffffffffffff - fixup.target.offset as u64 {
456
            throw Error::Range;
457
        }
458
        if fixup.offset > package.memory or fixup.count > (package.memory - fixup.offset) / 8 {
459
            throw Error::Range;
460
        }
461
    }
462
    for i in 0..package.memory {
463
        set memory[i] = 0;
464
    }
465
    try! mem::copy(memory, package.template);
466
    for fixup in package.relocations {
467
        let address = bases[fixup.target.slot] + fixup.target.offset as u64;
468
        for i in 0..fixup.count {
469
            integer(memory, fixup.offset + i * 8, address, 8);
470
        }
471
    }
472
}