lib/std/lang/gen/data.rad 8.0 KiB raw
1
//! Data section layout and emission.
2
//!
3
//! Target-independent routines for laying out data symbols and
4
//! serializing initialized data into binary sections.
5
6
use std::mem;
7
use std::collections::dict;
8
use std::lang::il;
9
use std::lang::gen::labels;
10
11
/// Maximum number of data symbols.
12
export constant MAX_DATA_SYMS: u32 = 8192;
13
14
/// Size of the data symbol hash table. Must be a power of two
15
/// and at least twice the size of [`MAX_DATA_SYMS`].
16
export constant DATA_SYM_TABLE_SIZE: u32 = MAX_DATA_SYMS * 2;
17
18
/// A data section cannot fit its address or symbol storage.
19
export union Error: Copy {
20
    /// A required data or function symbol is missing or duplicated.
21
    Symbol,
22
    /// The symbol array is full.
23
    Capacity,
24
    /// A section size or address would overflow.
25
    Overflow,
26
    /// A data alignment is zero or is not a power of two.
27
    Alignment,
28
}
29
30
/// Data symbol entry mapping name to address.
31
export record DataSym: Copy {
32
    /// Symbol name.
33
    name: *[u8],
34
    /// Absolute address, including data base address.
35
    addr: u64,
36
}
37
38
/// Hash-indexed data symbol map.
39
export record DataSymMap {
40
    /// Underlying hash table.
41
    dict: dict::Dict,
42
    /// Fallback linear array for edge cases.
43
    syms: *[DataSym],
44
}
45
46
/// Lay out data symbols for a single section.
47
/// Data with sidecar image bytes is placed first, then zero-initialized data,
48
/// so that only meaningful bytes need to be written to the output file.
49
/// Returns the updated offset past all placed symbols.
50
export fn layoutSection(
51
    items: &[il::Data],
52
    syms: &mut [DataSym],
53
    count: &mut u32,
54
    base: u64,
55
    readOnly: bool
56
) -> u32 throws (Error) {
57
    return try layoutSectionAtOffset(items, syms, count, base, 0, readOnly);
58
}
59
60
/// Lay out data symbols for a single section starting at [`startOffset`].
61
export fn layoutSectionAtOffset(
62
    items: &[il::Data],
63
    syms: &mut [DataSym],
64
    count: &mut u32,
65
    base: u64,
66
    startOffset: u32,
67
    readOnly: bool
68
) -> u32 throws (Error) {
69
    let mut offset: u32 = startOffset;
70
71
    // Data requiring sidecar image bytes first.
72
    for i in 0..items.len {
73
        let data = items[i];
74
        if data.readOnly == readOnly and not data.isZeroInit {
75
            try place(&data, syms, count, base, &mut offset);
76
        }
77
    }
78
    // Zero-initialized data after.
79
    for i in 0..items.len {
80
        let data = items[i];
81
        if data.readOnly == readOnly and data.isZeroInit {
82
            try place(&data, syms, count, base, &mut offset);
83
        }
84
    }
85
    return offset;
86
}
87
88
/// Place a data symbol with checked offset and address arithmetic.
89
fn place(item: &il::Data, syms: &mut [DataSym], count: &mut u32, base: u64, offset: &mut u32)
90
    throws (Error)
91
{
92
    if item.alignment == 0 or (item.alignment & (item.alignment - 1)) <> 0 {
93
        throw Error::Alignment;
94
    }
95
    if (base & (item.alignment as u64 - 1)) <> 0 { throw Error::Alignment; }
96
    let aligned = (*offset as u64 + item.alignment as u64 - 1) & ~(item.alignment as u64 - 1);
97
    let end = aligned + item.size as u64;
98
    if end > 0xffffffff or base > 0xffffffffffffffff - end {
99
        throw Error::Overflow;
100
    }
101
    if *count >= syms.len { throw Error::Capacity; }
102
    set syms[*count] = DataSym { name: item.name, addr: base + aligned };
103
    set *count += 1;
104
    set *offset = end as u32;
105
}
106
107
/// Emit data bytes for a single section (read-only or read-write) into `buf`.
108
/// Iterates data requiring sidecar image bytes, serializing each data item.
109
/// Returns the total number of bytes written.
110
export unsafe fn emitSection(
111
    items: &[il::Data],
112
    dataSymMap: &DataSymMap,
113
    fnLabels: &labels::Labels,
114
    codeBase: u64,
115
    buf: &mut [u8],
116
    readOnly: bool
117
) -> u32 throws (Error) {
118
    return try emitSectionAtOffset(items, dataSymMap, fnLabels, codeBase, buf, readOnly, 0);
119
}
120
121
/// Emit data bytes for a single section starting at `startOffset`.
122
export unsafe fn emitSectionAtOffset(
123
    items: &[il::Data],
124
    dataSymMap: &DataSymMap,
125
    fnLabels: &labels::Labels,
126
    codeBase: u64,
127
    buf: &mut [u8],
128
    readOnly: bool,
129
    startOffset: u32
130
) -> u32 throws (Error) {
131
    let mut offset: u32 = startOffset;
132
    if offset > buf.len { throw Error::Capacity; }
133
134
    for i in 0..items.len {
135
        let data = items[i];
136
        if data.readOnly == readOnly and not data.isZeroInit {
137
            let start = offset;
138
            if data.alignment == 0 or (data.alignment & (data.alignment - 1)) <> 0 { throw Error::Alignment; }
139
            let aligned = (offset as u64 + data.alignment as u64 - 1) & ~(data.alignment as u64 - 1);
140
            if aligned > buf.len as u64 { throw Error::Capacity; }
141
            set offset = aligned as u32;
142
            if data.size > buf.len - offset { throw Error::Capacity; }
143
            let end = offset + data.size;
144
            for j in start..end { set buf[j] = 0; }
145
            for j in 0..data.values.len {
146
                let v = &data.values[j];
147
                let mut width: u32 = 1;
148
                match v.item {
149
                    case il::DataItem::Val { typ, .. } => { set width = il::typeSize(typ); },
150
                    case il::DataItem::Sym(_), il::DataItem::Fn(_) => { set width = 8; },
151
                    case il::DataItem::Str(bytes) => { set width = bytes.len; },
152
                    else => {},
153
                }
154
                if width > 0 and v.count > (end - offset) / width { throw Error::Overflow; }
155
                if width == 0 { continue; }
156
                for _ in 0..v.count {
157
                    match v.item {
158
                        case il::DataItem::Val { typ, val } => {
159
                            let size = il::typeSize(typ);
160
                            try! mem::copy(&mut buf[offset..], @sliceOf(&val as &u8, size));
161
162
                            set offset += size;
163
                        },
164
                        case il::DataItem::Sym(name) => {
165
                            let addr = lookupAddr(dataSymMap, name) else {
166
                                throw Error::Symbol;
167
                            };
168
                            let addr64: u64 = addr as u64;
169
                            try! mem::copy(&mut buf[offset..], @sliceOf(&addr64 as &u8, 8));
170
171
                            set offset += @sizeOf(u64);
172
                        },
173
                        case il::DataItem::Fn(name) => {
174
                            let fnOffset = dict::get(&fnLabels.funcs, name) else { throw Error::Symbol; };
175
                            if fnOffset < 0 or codeBase > 0xffffffffffffffff - fnOffset as u64 { throw Error::Overflow; }
176
                            let addr = codeBase + fnOffset as u64;
177
                            let addr64: u64 = addr as u64;
178
                            try! mem::copy(&mut buf[offset..], @sliceOf(&addr64 as &u8, 8));
179
180
                            set offset += @sizeOf(*u8);
181
                        },
182
                        case il::DataItem::Str(s) => {
183
                            try! mem::copy(&mut buf[offset..], s);
184
                            set offset += s.len;
185
                        },
186
                        case il::DataItem::Undef => {
187
                            set buf[offset] = 0;
188
                            set offset += 1;
189
                        },
190
                    }
191
                }
192
            }
193
            set offset = end;
194
        }
195
    }
196
    return offset;
197
}
198
199
/// Build a hash-indexed data symbol map from the laid-out symbols.
200
/// The entry count must be a power of two, at least twice the symbol count.
201
export fn buildMap(syms: *[DataSym], entries: *mut [dict::Entry]) -> DataSymMap throws (Error) {
202
    if entries.len == 0 or (entries.len & (entries.len - 1)) <> 0 or syms.len > entries.len / 2 {
203
        throw Error::Capacity;
204
    }
205
    let mut d = dict::init(entries);
206
    for i in 0..syms.len {
207
        if syms[i].name.len == 0 or dict::get(&d, syms[i].name) <> nil { throw Error::Symbol; }
208
        dict::insert(&mut d, syms[i].name, i as i32);
209
    }
210
    return DataSymMap { dict: d, syms };
211
}
212
213
/// Resolve a data symbol to its final absolute address using the hash map.
214
export fn lookupAddr(m: &DataSymMap, name: *[u8]) -> ?u64 {
215
    if let v = dict::get(&m.dict, name) {
216
        return m.syms[v as u32].addr;
217
    }
218
    return nil;
219
}