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
/// Data section layout or emission failure.
19
export union Error: Copy {
20
    /// A required symbol is missing or duplicated.
21
    Symbol,
22
    /// A symbol table or output buffer is full.
23
    Capacity,
24
    /// A size or address exceeds the supported range.
25
    Overflow,
26
    /// 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: u32,
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: u32,
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: u32,
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
            if data.alignment == 0 or (data.alignment & (data.alignment - 1)) <> 0 {
76
                throw Error::Alignment;
77
            }
78
            let aligned = (offset as u64 + data.alignment as u64 - 1) & ~(data.alignment as u64 - 1);
79
            if aligned + data.size as u64 > 0xffffffff or base as u64 + aligned + data.size as u64 > 0xffffffff {
80
                throw Error::Overflow;
81
            }
82
            if *count >= syms.len {
83
                throw Error::Capacity;
84
            }
85
            set offset = aligned as u32;
86
            set syms[*count] = DataSym { name: data.name, addr: base + offset };
87
            set *count += 1;
88
            set offset += data.size;
89
        }
90
    }
91
    // Zero-initialized data after.
92
    for i in 0..items.len {
93
        let data = items[i];
94
        if data.readOnly == readOnly and data.isZeroInit {
95
            if data.alignment == 0 or (data.alignment & (data.alignment - 1)) <> 0 {
96
                throw Error::Alignment;
97
            }
98
            let aligned = (offset as u64 + data.alignment as u64 - 1) & ~(data.alignment as u64 - 1);
99
            if aligned + data.size as u64 > 0xffffffff or base as u64 + aligned + data.size as u64 > 0xffffffff {
100
                throw Error::Overflow;
101
            }
102
            if *count >= syms.len {
103
                throw Error::Capacity;
104
            }
105
            set offset = aligned as u32;
106
            set syms[*count] = DataSym { name: data.name, addr: base + offset };
107
            set *count += 1;
108
            set offset += data.size;
109
        }
110
    }
111
    return offset;
112
}
113
114
/// Emit data bytes for a single section (read-only or read-write) into `buf`.
115
/// Iterates data requiring sidecar image bytes, serializing each data item.
116
/// Returns the total number of bytes written.
117
export unsafe fn emitSection(
118
    items: &[il::Data],
119
    dataSymMap: &DataSymMap,
120
    fnLabels: &labels::Labels,
121
    codeBase: u32,
122
    buf: &mut [u8],
123
    readOnly: bool
124
) -> u32 throws (Error) {
125
    return try emitSectionAtOffset(items, dataSymMap, fnLabels, codeBase, buf, readOnly, 0);
126
}
127
128
/// Emit data bytes for a single section starting at `startOffset`.
129
export unsafe fn emitSectionAtOffset(
130
    items: &[il::Data],
131
    dataSymMap: &DataSymMap,
132
    fnLabels: &labels::Labels,
133
    codeBase: u32,
134
    buf: &mut [u8],
135
    readOnly: bool,
136
    startOffset: u32
137
) -> u32 throws (Error) {
138
    if startOffset > buf.len {
139
        throw Error::Capacity;
140
    }
141
    let mut offset: u32 = startOffset;
142
143
    for i in 0..items.len {
144
        let data = items[i];
145
        if data.readOnly == readOnly and not data.isZeroInit {
146
            if data.alignment == 0 or (data.alignment & (data.alignment - 1)) <> 0 {
147
                throw Error::Alignment;
148
            }
149
            let aligned = (offset as u64 + data.alignment as u64 - 1) & ~(data.alignment as u64 - 1);
150
            if aligned > buf.len as u64 or data.size as u64 > buf.len as u64 - aligned {
151
                throw Error::Capacity;
152
            }
153
            set offset = aligned as u32;
154
            let end = offset + data.size;
155
            for j in 0..data.values.len {
156
                let v = &data.values[j];
157
                let mut width: u32 = 1;
158
                match v.item {
159
                    case il::DataItem::Val { typ, .. } => set width = il::typeSize(typ),
160
                    case il::DataItem::Sym(_), il::DataItem::Fn(_) => set width = 8,
161
                    case il::DataItem::Str(text) => set width = text.len,
162
                    case il::DataItem::Undef => {}
163
                }
164
                if width as u64 * v.count as u64 > (end - offset) as u64 {
165
                    throw Error::Capacity;
166
                }
167
                for _ in 0..v.count {
168
                    match v.item {
169
                        case il::DataItem::Val { typ, val } => {
170
                            let size = il::typeSize(typ);
171
                            try! mem::copy(&mut buf[offset..], @sliceOf(&val as &u8, size));
172
173
                            set offset += size;
174
                        },
175
                        case il::DataItem::Sym(name) => {
176
                            let addr = lookupAddr(dataSymMap, name) else {
177
                                throw Error::Symbol;
178
                            };
179
                            let addr64: u64 = addr as u64;
180
                            try! mem::copy(&mut buf[offset..], @sliceOf(&addr64 as &u8, 8));
181
182
                            set offset += @sizeOf(u64);
183
                        },
184
                        case il::DataItem::Fn(name) => {
185
                            let functionOffset = dict::get(&fnLabels.funcs, name) else {
186
                                throw Error::Symbol;
187
                            };
188
                            if functionOffset < 0 or codeBase as u64 + functionOffset as u64 > 0xffffffff {
189
                                throw Error::Overflow;
190
                            }
191
                            let addr64 = codeBase as u64 + functionOffset as u64;
192
                            try! mem::copy(&mut buf[offset..], @sliceOf(&addr64 as &u8, 8));
193
194
                            set offset += @sizeOf(*u8);
195
                        },
196
                        case il::DataItem::Str(s) => {
197
                            try! mem::copy(&mut buf[offset..], s);
198
                            set offset += s.len;
199
                        },
200
                        case il::DataItem::Undef => {
201
                            set buf[offset] = 0;
202
                            set offset += 1;
203
                        },
204
                    }
205
                }
206
            }
207
        }
208
    }
209
    return offset;
210
}
211
212
/// Build a hash-indexed data symbol map from the laid-out symbols.
213
/// The table length must be a power of two and at least twice the symbol count.
214
/// The minimum table length is two.
215
export fn buildMap(syms: *[DataSym], entries: *mut [dict::Entry]) -> DataSymMap throws (Error) {
216
    if entries.len < 2 or (entries.len & (entries.len - 1)) <> 0 or syms.len > entries.len / 2 {
217
        throw Error::Capacity;
218
    }
219
    let mut d = dict::init(entries);
220
    for i in 0..syms.len {
221
        if syms[i].name.len == 0 or dict::get(&d, syms[i].name) <> nil {
222
            throw Error::Symbol;
223
        }
224
        dict::insert(&mut d, syms[i].name, syms[i].addr as i32);
225
    }
226
    return DataSymMap { dict: d, syms };
227
}
228
229
/// Resolve a data symbol to its final absolute address using the hash map.
230
export fn lookupAddr(m: &DataSymMap, name: *[u8]) -> ?u32 {
231
    if let v = dict::get(&m.dict, name) {
232
        return v as u32;
233
    }
234
    return nil;
235
}