lib/std/lang/gen/data.rad 8.6 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 {
96
        throw Error::Alignment;
97
    }
98
    let aligned = (*offset as u64 + item.alignment as u64 - 1) & ~(item.alignment as u64 - 1);
99
    let end = aligned + item.size as u64;
100
    if end > 0xffffffff or base > 0xffffffffffffffff - end {
101
        throw Error::Overflow;
102
    }
103
    if *count >= syms.len {
104
        throw Error::Capacity;
105
    }
106
    set syms[*count] = DataSym { name: item.name, addr: base + aligned };
107
    set *count += 1;
108
    set *offset = end as u32;
109
}
110
111
/// Emit data bytes for a single section (read-only or read-write) into `buf`.
112
/// Iterates data requiring sidecar image bytes, serializing each data item.
113
/// Returns the total number of bytes written.
114
export fn emitSection(
115
    items: &[il::Data],
116
    dataSymMap: &DataSymMap,
117
    fnLabels: &labels::Labels,
118
    codeBase: u64,
119
    buf: &mut [u8],
120
    readOnly: bool
121
) -> u32 throws (Error) {
122
    return try emitSectionAtOffset(items, dataSymMap, fnLabels, codeBase, buf, readOnly, 0);
123
}
124
125
/// Emit data bytes for a single section starting at `startOffset`.
126
export fn emitSectionAtOffset(
127
    items: &[il::Data],
128
    dataSymMap: &DataSymMap,
129
    fnLabels: &labels::Labels,
130
    codeBase: u64,
131
    buf: &mut [u8],
132
    readOnly: bool,
133
    startOffset: u32
134
) -> u32 throws (Error) {
135
    let mut offset: u32 = startOffset;
136
    if offset > buf.len {
137
        throw Error::Capacity;
138
    }
139
140
    for i in 0..items.len {
141
        let data = items[i];
142
        if data.readOnly == readOnly and not data.isZeroInit {
143
            let start = offset;
144
            if data.alignment == 0 or (data.alignment & (data.alignment - 1)) <> 0 {
145
                throw Error::Alignment;
146
            }
147
            let aligned = (offset as u64 + data.alignment as u64 - 1) & ~(data.alignment as u64 - 1);
148
            if aligned > buf.len as u64 {
149
                throw Error::Capacity;
150
            }
151
            set offset = aligned as u32;
152
            if data.size > buf.len - offset {
153
                throw Error::Capacity;
154
            }
155
            let end = offset + data.size;
156
            for j in start..end {
157
                set buf[j] = 0;
158
            }
159
            for j in 0..data.values.len {
160
                let v = &data.values[j];
161
                let mut width: u32 = 1;
162
                match v.item {
163
                    case il::DataItem::Val { typ, .. } => {
164
                        set width = il::typeSize(typ);
165
                    },
166
                    case il::DataItem::Sym(_), il::DataItem::Fn(_) => {
167
                        set width = 8;
168
                    },
169
                    case il::DataItem::Str(bytes) => {
170
                        set width = bytes.len;
171
                    },
172
                    else => {
173
                    },
174
                }
175
                if width > 0 and v.count > (end - offset) / width {
176
                    throw Error::Overflow;
177
                }
178
                if width == 0 {
179
                    continue;
180
                }
181
                for _ in 0..v.count {
182
                    match v.item {
183
                        case il::DataItem::Val { typ, val } => {
184
                            let size = il::typeSize(typ);
185
                            writeInteger(&mut buf[offset..offset + size], val as u64);
186
187
                            set offset += size;
188
                        },
189
                        case il::DataItem::Sym(name) => {
190
                            let addr = lookupAddr(dataSymMap, name) else {
191
                                throw Error::Symbol;
192
                            };
193
                            writeInteger(&mut buf[offset..offset + 8], addr);
194
195
                            set offset += @sizeOf(u64);
196
                        },
197
                        case il::DataItem::Fn(name) => {
198
                            let fnOffset = dict::get(&fnLabels.funcs, name) else {
199
                                throw Error::Symbol;
200
                            };
201
                            if fnOffset < 0 or codeBase > 0xffffffffffffffff - fnOffset as u64 {
202
                                throw Error::Overflow;
203
                            }
204
                            let addr = codeBase + fnOffset as u64;
205
                            writeInteger(&mut buf[offset..offset + 8], addr);
206
207
                            set offset += @sizeOf(u64);
208
                        },
209
                        case il::DataItem::Str(s) => {
210
                            try! mem::copy(&mut buf[offset..], s);
211
                            set offset += s.len;
212
                        },
213
                        case il::DataItem::Undef => {
214
                            set buf[offset] = 0;
215
                            set offset += 1;
216
                        },
217
                    }
218
                }
219
            }
220
            set offset = end;
221
        }
222
    }
223
    return offset;
224
}
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
234
/// Build a hash-indexed data symbol map from the laid-out symbols.
235
/// The entry count must be a power of two, at least twice the symbol count.
236
export fn buildMap(syms: *[DataSym], entries: *mut [dict::Entry]) -> DataSymMap throws (Error) {
237
    if entries.len == 0 or (entries.len & (entries.len - 1)) <> 0 or syms.len > entries.len / 2 {
238
        throw Error::Capacity;
239
    }
240
    let mut d = dict::init(entries);
241
    for i in 0..syms.len {
242
        if syms[i].name.len == 0 or dict::get(&d, syms[i].name) <> nil {
243
            throw Error::Symbol;
244
        }
245
        dict::insert(&mut d, syms[i].name, i as i32);
246
    }
247
    return DataSymMap { dict: d, syms };
248
}
249
250
/// Resolve a data symbol to its final absolute address using the hash map.
251
export fn lookupAddr(m: &DataSymMap, name: *[u8]) -> ?u64 {
252
    if let v = dict::get(&m.dict, name) {
253
        return m.syms[v as u32].addr;
254
    }
255
    return nil;
256
}