lib/std/lang/gen/data.rad 5.1 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
pub const 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
pub const DATA_SYM_TABLE_SIZE: u32 = MAX_DATA_SYMS * 2;
17
18
/// Data symbol entry mapping name to address.
19
pub record DataSym {
20
    /// Symbol name.
21
    name: *[u8],
22
    /// Absolute address, including data base address.
23
    addr: u32,
24
}
25
26
/// Hash-indexed data symbol map.
27
pub record DataSymMap {
28
    /// Underlying hash table.
29
    dict: dict::Dict,
30
    /// Fallback linear array for edge cases.
31
    syms: *[DataSym],
32
}
33
34
/// Lay out data symbols for a single section.
35
/// Initialized data is placed first, then uninitialized, so that only
36
/// initialized data needs to be written to the output file.
37
/// Returns the updated offset past all placed symbols.
38
pub fn layoutSection(
39
    program: *il::Program,
40
    syms: *mut [DataSym],
41
    count: *mut u32,
42
    base: u32,
43
    readOnly: bool
44
) -> u32 {
45
    let mut offset: u32 = 0;
46
47
    // Initialized data first.
48
    for i in 0..program.data.len {
49
        let data = &program.data[i];
50
        if data.readOnly == readOnly and not data.isUndefined {
51
            offset = mem::alignUp(offset, data.alignment);
52
            syms[*count] = DataSym { name: data.name, addr: base + offset };
53
            *count += 1;
54
            offset += data.size;
55
        }
56
    }
57
    // Uninitialized data after.
58
    for i in 0..program.data.len {
59
        let data = &program.data[i];
60
        if data.readOnly == readOnly and data.isUndefined {
61
            offset = mem::alignUp(offset, data.alignment);
62
            syms[*count] = DataSym { name: data.name, addr: base + offset };
63
            *count += 1;
64
            offset += data.size;
65
        }
66
    }
67
    return offset;
68
}
69
70
/// Emit data bytes for a single section (read-only or read-write) into `buf`.
71
/// Iterates initialized data in the IL program, serializing each data item.
72
/// Returns the total number of bytes written.
73
pub fn emitSection(
74
    program: *il::Program,
75
    dataSymMap: *DataSymMap,
76
    fnLabels: *labels::Labels,
77
    codeBase: u32,
78
    buf: *mut [u8],
79
    readOnly: bool
80
) -> u32 {
81
    let mut offset: u32 = 0;
82
83
    for i in 0..program.data.len {
84
        let data = &program.data[i];
85
        if data.readOnly == readOnly and not data.isUndefined {
86
            offset = mem::alignUp(offset, data.alignment);
87
            assert offset + data.size <= buf.len, "emitSection: buffer overflow";
88
            for j in 0..data.values.len {
89
                let v = &data.values[j];
90
                for _ in 0..v.count {
91
                    match v.item {
92
                        case il::DataItem::Val { typ, val } => {
93
                            let size = il::typeSize(typ);
94
                            let valPtr = &val as *u8;
95
                            try! mem::copy(&mut buf[offset..], @sliceOf(valPtr, size));
96
                            offset += size;
97
                        },
98
                        case il::DataItem::Sym(name) => {
99
                            let addr = lookupAddr(dataSymMap, name) else {
100
                                panic "emitSection: data symbol not found";
101
                            };
102
                            let addr64: u64 = addr as u64;
103
                            let addrPtr = &addr64 as *u8;
104
105
                            try! mem::copy(&mut buf[offset..], @sliceOf(addrPtr, 8));
106
107
                            offset += 8;
108
                        },
109
                        case il::DataItem::Fn(name) => {
110
                            let addr = codeBase + labels::funcOffset(fnLabels, name) as u32;
111
                            let addr64: u64 = addr as u64;
112
                            let addrPtr = &addr64 as *u8;
113
114
                            try! mem::copy(&mut buf[offset..], @sliceOf(addrPtr, 8));
115
116
                            offset += 8;
117
                        },
118
                        case il::DataItem::Str(s) => {
119
                            try! mem::copy(&mut buf[offset..], s);
120
                            offset += s.len;
121
                        },
122
                        case il::DataItem::Undef => {
123
                            buf[offset] = 0;
124
                            offset += 1;
125
                        },
126
                    }
127
                }
128
            }
129
        }
130
    }
131
    return offset;
132
}
133
134
/// Build a hash-indexed data symbol map from the laid-out symbols.
135
/// The `entries` slice must have length `DATA_SYM_TABLE_SIZE`.
136
pub fn buildMap(syms: *[DataSym], entries: *mut [dict::Entry]) -> DataSymMap {
137
    let mut d = dict::init(entries);
138
    for i in 0..syms.len {
139
        dict::insert(&mut d, syms[i].name, syms[i].addr as i32);
140
    }
141
    return DataSymMap { dict: d, syms };
142
}
143
144
/// Resolve a data symbol to its final absolute address using the hash map.
145
pub fn lookupAddr(m: *DataSymMap, name: *[u8]) -> ?u32 {
146
    if let v = dict::get(&m.dict, name) {
147
        return v as u32;
148
    }
149
    return nil;
150
}