Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wasi-preview1-component-adapter/build.rs
1690 views
1
use std::env;
2
use std::path::PathBuf;
3
4
fn main() {
5
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
6
7
let wasm = build_raw_intrinsics();
8
let archive = build_archive(&wasm);
9
10
std::fs::write(out_dir.join("libwasm-raw-intrinsics.a"), &archive).unwrap();
11
println!("cargo:rustc-link-lib=static=wasm-raw-intrinsics");
12
println!(
13
"cargo:rustc-link-search=native={}",
14
out_dir.to_str().unwrap()
15
);
16
17
// Some specific flags to `wasm-ld` to inform the shape of this adapter.
18
// Notably we're importing memory from the main module and additionally our
19
// own module has no stack at all since it's specifically allocated at
20
// startup.
21
println!("cargo:rustc-link-arg=--import-memory");
22
println!("cargo:rustc-link-arg=-zstack-size=0");
23
}
24
25
/// This function will produce a wasm module which is itself an object file
26
/// that is the basic equivalent of:
27
///
28
/// ```rust
29
/// std::arch::global_asm!(
30
/// "
31
/// .globaltype internal_state_ptr, i32
32
/// internal_state_ptr:
33
/// "
34
/// );
35
///
36
/// #[unsafe(no_mangle)]
37
/// extern "C" fn get_state_ptr() -> *mut u8 {
38
/// unsafe {
39
/// let ret: *mut u8;
40
/// std::arch::asm!(
41
/// "
42
/// global.get internal_state_ptr
43
/// ",
44
/// out(local) ret,
45
/// options(nostack, readonly)
46
/// );
47
/// ret
48
/// }
49
/// }
50
///
51
/// #[unsafe(no_mangle)]
52
/// extern "C" fn set_state_ptr(val: *mut u8) {
53
/// unsafe {
54
/// std::arch::asm!(
55
/// "
56
/// local.get {}
57
/// global.set internal_state_ptr
58
/// ",
59
/// in(local) val,
60
/// options(nostack, readonly)
61
/// );
62
/// }
63
/// }
64
///
65
/// // And likewise for `allocation_state`, `get_allocation_state`, and `set_allocation_state`
66
/// ```
67
///
68
/// The main trickiness here is getting the `reloc.CODE` and `linking` sections
69
/// right.
70
fn build_raw_intrinsics() -> Vec<u8> {
71
use wasm_encoder::Instruction::*;
72
use wasm_encoder::*;
73
74
let mut module = Module::new();
75
76
let mut types = TypeSection::new();
77
types.ty().function([], [ValType::I32]);
78
types.ty().function([ValType::I32], []);
79
module.section(&types);
80
81
// Declare the functions, using the type we just added.
82
let mut funcs = FunctionSection::new();
83
funcs.function(0);
84
funcs.function(1);
85
funcs.function(0);
86
funcs.function(1);
87
module.section(&funcs);
88
89
// Declare the globals.
90
let mut globals = GlobalSection::new();
91
// internal_state_ptr
92
globals.global(
93
GlobalType {
94
val_type: ValType::I32,
95
mutable: true,
96
shared: false,
97
},
98
&ConstExpr::i32_const(0),
99
);
100
// allocation_state
101
globals.global(
102
GlobalType {
103
val_type: ValType::I32,
104
mutable: true,
105
shared: false,
106
},
107
&ConstExpr::i32_const(0),
108
);
109
module.section(&globals);
110
111
// Here the `code` section is defined. This is tricky because an offset is
112
// needed within the code section itself for the `reloc.CODE` section
113
// later. At this time `wasm-encoder` doesn't give enough functionality to
114
// use the high-level APIs. so everything is done manually here.
115
//
116
// First the function bodies are created and then they're appended into a
117
// code section.
118
119
let mut code = Vec::new();
120
4u32.encode(&mut code); // number of functions
121
122
let global_get = 0x23;
123
let global_set = 0x24;
124
125
let encode = |code: &mut _, global, instruction| {
126
assert!(global < 0x7F);
127
128
let mut body = Vec::new();
129
0u32.encode(&mut body); // no locals
130
if instruction == global_set {
131
LocalGet(0).encode(&mut body);
132
}
133
let global_offset = body.len() + 1;
134
// global.get $global ;; but with maximal encoding of $global
135
body.extend_from_slice(&[instruction, 0x80u8 + global, 0x80, 0x80, 0x80, 0x00]);
136
End.encode(&mut body);
137
body.len().encode(code); // length of the function
138
let offset = code.len() + global_offset;
139
code.extend_from_slice(&body); // the function itself
140
offset
141
};
142
143
let internal_state_ptr_ref1 = encode(&mut code, 0, global_get); // get_state_ptr
144
let internal_state_ptr_ref2 = encode(&mut code, 0, global_set); // set_state_ptr
145
let allocation_state_ref1 = encode(&mut code, 1, global_get); // get_allocation_state
146
let allocation_state_ref2 = encode(&mut code, 1, global_set); // set_allocation_state
147
148
module.section(&RawSection {
149
id: SectionId::Code as u8,
150
data: &code,
151
});
152
153
// Here the linking section is constructed. There is one symbol for each function and global. The injected
154
// globals here are referenced in the relocations below.
155
//
156
// More information about this format is at
157
// https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md
158
{
159
let mut linking = Vec::new();
160
linking.push(0x02); // version
161
162
linking.push(0x08); // `WASM_SYMBOL_TABLE`
163
let mut subsection = Vec::new();
164
6u32.encode(&mut subsection); // 6 symbols (4 functions + 2 globals)
165
166
subsection.push(0x00); // SYMTAB_FUNCTION
167
0x00.encode(&mut subsection); // flags
168
0u32.encode(&mut subsection); // function index
169
"get_state_ptr".encode(&mut subsection); // symbol name
170
171
subsection.push(0x00); // SYMTAB_FUNCTION
172
0x00.encode(&mut subsection); // flags
173
1u32.encode(&mut subsection); // function index
174
"set_state_ptr".encode(&mut subsection); // symbol name
175
176
subsection.push(0x00); // SYMTAB_FUNCTION
177
0x00.encode(&mut subsection); // flags
178
2u32.encode(&mut subsection); // function index
179
"get_allocation_state".encode(&mut subsection); // symbol name
180
181
subsection.push(0x00); // SYMTAB_FUNCTION
182
0x00.encode(&mut subsection); // flags
183
3u32.encode(&mut subsection); // function index
184
"set_allocation_state".encode(&mut subsection); // symbol name
185
186
subsection.push(0x02); // SYMTAB_GLOBAL
187
0x02.encode(&mut subsection); // flags (WASM_SYM_BINDING_LOCAL)
188
0u32.encode(&mut subsection); // global index
189
"internal_state_ptr".encode(&mut subsection); // symbol name
190
191
subsection.push(0x02); // SYMTAB_GLOBAL
192
0x00.encode(&mut subsection); // flags
193
1u32.encode(&mut subsection); // global index
194
"allocation_state".encode(&mut subsection); // symbol name
195
196
subsection.encode(&mut linking);
197
module.section(&CustomSection {
198
name: "linking".into(),
199
data: linking.into(),
200
});
201
}
202
203
// A `reloc.CODE` section is appended here with relocations for the
204
// `global`-referencing instructions that were added.
205
{
206
let mut reloc = Vec::new();
207
3u32.encode(&mut reloc); // target section (code is the 4th section, 3 when 0-indexed)
208
4u32.encode(&mut reloc); // 4 relocations
209
210
reloc.push(0x07); // R_WASM_GLOBAL_INDEX_LEB
211
internal_state_ptr_ref1.encode(&mut reloc); // offset
212
4u32.encode(&mut reloc); // symbol index
213
214
reloc.push(0x07); // R_WASM_GLOBAL_INDEX_LEB
215
internal_state_ptr_ref2.encode(&mut reloc); // offset
216
4u32.encode(&mut reloc); // symbol index
217
218
reloc.push(0x07); // R_WASM_GLOBAL_INDEX_LEB
219
allocation_state_ref1.encode(&mut reloc); // offset
220
5u32.encode(&mut reloc); // symbol index
221
222
reloc.push(0x07); // R_WASM_GLOBAL_INDEX_LEB
223
allocation_state_ref2.encode(&mut reloc); // offset
224
5u32.encode(&mut reloc); // symbol index
225
226
module.section(&CustomSection {
227
name: "reloc.CODE".into(),
228
data: reloc.into(),
229
});
230
}
231
232
module.finish()
233
}
234
235
/// This function produces the output of `llvm-ar crus libfoo.a foo.o` given
236
/// the object file above as input. The archive is what's eventually fed to
237
/// LLD.
238
///
239
/// Like above this is still tricky, mainly around the production of the symbol
240
/// table.
241
fn build_archive(wasm: &[u8]) -> Vec<u8> {
242
use object::{U32Bytes, bytes_of, endian::BigEndian};
243
244
let mut archive = Vec::new();
245
archive.extend_from_slice(&object::archive::MAGIC);
246
247
// The symbol table is in the "GNU" format which means it has a structure
248
// that looks like:
249
//
250
// * a big-endian 32-bit integer for the number of symbols
251
// * N big-endian 32-bit integers for the offset to the object file, within
252
// the entire archive, for which object has the symbol
253
// * N nul-delimited strings for each symbol
254
//
255
// Here we're building an archive with just a few symbols so it's a bit
256
// easier. Note though we don't know the offset of our `intrinsics.o` up
257
// front so it's left as 0 for now and filled in later.
258
259
let syms = [
260
"get_state_ptr",
261
"set_state_ptr",
262
"get_allocation_state",
263
"set_allocation_state",
264
"allocation_state",
265
];
266
267
let mut symbol_table = Vec::new();
268
symbol_table.extend_from_slice(bytes_of(&U32Bytes::new(BigEndian, syms.len() as u32)));
269
for _ in syms.iter() {
270
symbol_table.extend_from_slice(bytes_of(&U32Bytes::new(BigEndian, 0)));
271
}
272
for s in syms.iter() {
273
symbol_table.extend_from_slice(&std::ffi::CString::new(*s).unwrap().into_bytes_with_nul());
274
}
275
276
archive.extend_from_slice(bytes_of(&object::archive::Header {
277
name: *b"/ ",
278
date: *b"0 ",
279
uid: *b"0 ",
280
gid: *b"0 ",
281
mode: *b"0 ",
282
size: format!("{:<10}", symbol_table.len())
283
.as_bytes()
284
.try_into()
285
.unwrap(),
286
terminator: object::archive::TERMINATOR,
287
}));
288
let symtab_offset = archive.len();
289
archive.extend_from_slice(&symbol_table);
290
291
// All archive members must start on even offsets
292
if archive.len() & 1 == 1 {
293
archive.push(0x00);
294
}
295
296
// Now that we have the starting offset of the `intrinsics.o` file go back
297
// and fill in the offset within the symbol table generated earlier.
298
let member_offset = archive.len();
299
for (index, _) in syms.iter().enumerate() {
300
let index = index + 1;
301
archive[symtab_offset + (index * 4)..][..4].copy_from_slice(bytes_of(&U32Bytes::new(
302
BigEndian,
303
member_offset.try_into().unwrap(),
304
)));
305
}
306
307
archive.extend_from_slice(object::bytes_of(&object::archive::Header {
308
name: *b"intrinsics.o ",
309
date: *b"0 ",
310
uid: *b"0 ",
311
gid: *b"0 ",
312
mode: *b"644 ",
313
size: format!("{:<10}", wasm.len()).as_bytes().try_into().unwrap(),
314
terminator: object::archive::TERMINATOR,
315
}));
316
archive.extend_from_slice(&wasm);
317
archive
318
}
319
320