1#![allow(missing_docs)]
17
18use anyhow::bail;
19use object::write::{
20 Object, Relocation as ObjectRelocation, SectionId, StandardSegment, Symbol, SymbolId,
21 SymbolSection,
22};
23use object::{
24 elf, Architecture, BinaryFormat, Endianness, RelocationEncoding, RelocationKind, SectionKind,
25 SymbolFlags, SymbolKind, SymbolScope,
26};
27use std::collections::HashMap;
28use target_lexicon::Triple;
29use wasmtime_debug::{DwarfSection, DwarfSectionRelocTarget};
30use wasmtime_environ::entity::{EntityRef, PrimaryMap};
31use wasmtime_environ::ir::{LibCall, Reloc};
32use wasmtime_environ::isa::unwind::UnwindInfo;
33use wasmtime_environ::wasm::{DefinedFuncIndex, FuncIndex, SignatureIndex};
34use wasmtime_environ::{CompiledFunction, CompiledFunctions, Module, Relocation, RelocationTarget};
35
36fn to_object_relocations<'a>(
37 it: impl Iterator<Item = &'a Relocation> + 'a,
38 off: u64,
39 module: &'a Module,
40 funcs: &'a PrimaryMap<FuncIndex, SymbolId>,
41 libcalls: &'a HashMap<LibCall, SymbolId>,
42 compiled_funcs: &'a CompiledFunctions,
43) -> impl Iterator<Item = ObjectRelocation> + 'a {
44 it.filter_map(move |r| {
45 let (symbol, symbol_offset) = match r.reloc_target {
46 RelocationTarget::UserFunc(index) => (funcs[index], 0),
47 RelocationTarget::LibCall(call) => (libcalls[&call], 0),
48 RelocationTarget::JumpTable(f, jt) => {
49 let df = module.defined_func_index(f).unwrap();
50 let offset = *compiled_funcs
51 .get(df)
52 .and_then(|f| f.jt_offsets.get(jt))
53 .expect("func jump table");
54 (funcs[f], offset)
55 }
56 };
57 let (kind, encoding, size) = match r.reloc {
58 Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32),
59 Reloc::Abs8 => (RelocationKind::Absolute, RelocationEncoding::Generic, 64),
60 Reloc::X86PCRel4 => (RelocationKind::Relative, RelocationEncoding::Generic, 32),
61 Reloc::X86CallPCRel4 => (RelocationKind::Relative, RelocationEncoding::X86Branch, 32),
62 Reloc::X86CallPLTRel4 => (
65 RelocationKind::PltRelative,
66 RelocationEncoding::X86Branch,
67 32,
68 ),
69 Reloc::X86GOTPCRel4 => (RelocationKind::GotRelative, RelocationEncoding::Generic, 32),
70 Reloc::ElfX86_64TlsGd => (
71 RelocationKind::Elf(elf::R_X86_64_TLSGD),
72 RelocationEncoding::Generic,
73 32,
74 ),
75 Reloc::X86PCRelRodata4 => {
76 return None;
77 }
78 Reloc::Arm64Call => (
79 RelocationKind::Elf(elf::R_AARCH64_CALL26),
80 RelocationEncoding::Generic,
81 32,
82 ),
83 Reloc::S390xPCRel32Dbl => (RelocationKind::Relative, RelocationEncoding::S390xDbl, 32),
84 other => unimplemented!("Unimplemented relocation {:?}", other),
85 };
86 Some(ObjectRelocation {
87 offset: off + r.offset as u64,
88 size,
89 kind,
90 encoding,
91 symbol,
92 addend: r.addend.wrapping_add(symbol_offset as i64),
93 })
94 })
95}
96
97fn to_object_architecture(
98 arch: target_lexicon::Architecture,
99) -> Result<Architecture, anyhow::Error> {
100 use target_lexicon::Architecture::*;
101 Ok(match arch {
102 X86_32(_) => Architecture::I386,
103 X86_64 => Architecture::X86_64,
104 Arm(_) => Architecture::Arm,
105 Aarch64(_) => Architecture::Aarch64,
106 S390x => Architecture::S390x,
107 architecture => {
108 anyhow::bail!("target architecture {:?} is unsupported", architecture,);
109 }
110 })
111}
112
113const TEXT_SECTION_NAME: &[u8] = b".text";
114
115fn process_unwind_info(info: &UnwindInfo, obj: &mut Object, code_section: SectionId) {
116 if let UnwindInfo::WindowsX64(info) = &info {
117 let unwind_size = info.emit_size();
119 let mut unwind_info = vec![0; unwind_size];
120 info.emit(&mut unwind_info);
121 let _off = obj.append_section_data(code_section, &unwind_info, 4);
122 }
123}
124
125#[macro_export]
130macro_rules! for_each_libcall {
131 ($op:ident) => {
132 $op![
133 (UdivI64, wasmtime_i64_udiv),
134 (UdivI64, wasmtime_i64_udiv),
135 (SdivI64, wasmtime_i64_sdiv),
136 (UremI64, wasmtime_i64_urem),
137 (SremI64, wasmtime_i64_srem),
138 (IshlI64, wasmtime_i64_ishl),
139 (UshrI64, wasmtime_i64_ushr),
140 (SshrI64, wasmtime_i64_sshr),
141 (CeilF32, wasmtime_f32_ceil),
142 (FloorF32, wasmtime_f32_floor),
143 (TruncF32, wasmtime_f32_trunc),
144 (NearestF32, wasmtime_f32_nearest),
145 (CeilF64, wasmtime_f64_ceil),
146 (FloorF64, wasmtime_f64_floor),
147 (TruncF64, wasmtime_f64_trunc),
148 (NearestF64, wasmtime_f64_nearest)
149 ];
150 };
151}
152
153fn write_libcall_symbols(obj: &mut Object) -> HashMap<LibCall, SymbolId> {
154 let mut libcalls = HashMap::new();
155 macro_rules! add_libcall_symbol {
156 [$(($libcall:ident, $export:ident)),*] => {{
157 $(
158 let symbol_id = obj.add_symbol(Symbol {
159 name: stringify!($export).as_bytes().to_vec(),
160 value: 0,
161 size: 0,
162 kind: SymbolKind::Text,
163 scope: SymbolScope::Linkage,
164 weak: true,
165 section: SymbolSection::Undefined,
166 flags: SymbolFlags::None,
167 });
168 libcalls.insert(LibCall::$libcall, symbol_id);
169 )+
170 }};
171 }
172 for_each_libcall!(add_libcall_symbol);
173
174 libcalls
175}
176
177pub mod utils {
178 use wasmtime_environ::entity::EntityRef;
179 use wasmtime_environ::wasm::{FuncIndex, SignatureIndex};
180
181 pub const FUNCTION_PREFIX: &str = "_wasm_function_";
182 pub const TRAMPOLINE_PREFIX: &str = "_trampoline_";
183
184 pub fn func_symbol_name(index: FuncIndex) -> String {
185 format!("_wasm_function_{}", index.index())
186 }
187
188 pub fn try_parse_func_name(name: &str) -> Option<FuncIndex> {
189 if !name.starts_with(FUNCTION_PREFIX) {
190 return None;
191 }
192 name[FUNCTION_PREFIX.len()..]
193 .parse()
194 .ok()
195 .map(FuncIndex::new)
196 }
197
198 pub fn trampoline_symbol_name(index: SignatureIndex) -> String {
199 format!("_trampoline_{}", index.index())
200 }
201
202 pub fn try_parse_trampoline_name(name: &str) -> Option<SignatureIndex> {
203 if !name.starts_with(TRAMPOLINE_PREFIX) {
204 return None;
205 }
206 name[TRAMPOLINE_PREFIX.len()..]
207 .parse()
208 .ok()
209 .map(SignatureIndex::new)
210 }
211}
212
213pub struct ObjectBuilderTarget {
214 pub(crate) binary_format: BinaryFormat,
215 pub(crate) architecture: Architecture,
216 pub(crate) endianness: Endianness,
217}
218
219impl ObjectBuilderTarget {
220 pub fn new(arch: target_lexicon::Architecture) -> Result<Self, anyhow::Error> {
221 Ok(Self {
222 binary_format: BinaryFormat::Elf,
223 architecture: to_object_architecture(arch)?,
224 endianness: match arch.endianness().unwrap() {
225 target_lexicon::Endianness::Little => object::Endianness::Little,
226 target_lexicon::Endianness::Big => object::Endianness::Big,
227 },
228 })
229 }
230
231 pub fn from_triple(triple: &Triple) -> Result<Self, anyhow::Error> {
232 let binary_format = match triple.binary_format {
233 target_lexicon::BinaryFormat::Elf => object::BinaryFormat::Elf,
234 target_lexicon::BinaryFormat::Coff => object::BinaryFormat::Coff,
235 target_lexicon::BinaryFormat::Macho => object::BinaryFormat::MachO,
236 target_lexicon::BinaryFormat::Wasm => {
237 bail!("binary format wasm is unsupported");
238 }
239 target_lexicon::BinaryFormat::Unknown => {
240 bail!("binary format is unknown");
241 }
242 other => bail!("binary format {} is unsupported", other),
243 };
244 let architecture = to_object_architecture(triple.architecture)?;
245 let endianness = match triple.endianness().unwrap() {
246 target_lexicon::Endianness::Little => object::Endianness::Little,
247 target_lexicon::Endianness::Big => object::Endianness::Big,
248 };
249 Ok(Self {
250 binary_format,
251 architecture,
252 endianness,
253 })
254 }
255}
256
257pub struct ObjectBuilder<'a> {
258 target: ObjectBuilderTarget,
259 module: &'a Module,
260 code_alignment: u64,
261 compilation: &'a CompiledFunctions,
262 trampolines: Vec<(SignatureIndex, CompiledFunction)>,
263 dwarf_sections: Vec<DwarfSection>,
264}
265
266impl<'a> ObjectBuilder<'a> {
267 pub fn new(
268 target: ObjectBuilderTarget,
269 module: &'a Module,
270 compilation: &'a CompiledFunctions,
271 ) -> Self {
272 Self {
273 target,
274 module,
275 code_alignment: 1,
276 trampolines: Vec::new(),
277 dwarf_sections: vec![],
278 compilation,
279 }
280 }
281
282 pub fn set_code_alignment(&mut self, code_alignment: u64) -> &mut Self {
283 self.code_alignment = code_alignment;
284 self
285 }
286
287 pub fn set_trampolines(
288 &mut self,
289 trampolines: Vec<(SignatureIndex, CompiledFunction)>,
290 ) -> &mut Self {
291 self.trampolines = trampolines;
292 self
293 }
294
295 pub fn set_dwarf_sections(&mut self, dwarf_sections: Vec<DwarfSection>) -> &mut Self {
296 self.dwarf_sections = dwarf_sections;
297 self
298 }
299
300 pub fn build(self) -> Result<Object, anyhow::Error> {
301 let mut obj = Object::new(
302 self.target.binary_format,
303 self.target.architecture,
304 self.target.endianness,
305 );
306
307 let module = self.module;
308
309 let section_id = obj.add_section(
312 obj.segment_name(StandardSegment::Text).to_vec(),
313 TEXT_SECTION_NAME.to_vec(),
314 SectionKind::Text,
315 );
316
317 let mut func_symbols = PrimaryMap::with_capacity(self.compilation.len());
319 for index in 0..module.num_imported_funcs {
320 let symbol_id = obj.add_symbol(Symbol {
321 name: utils::func_symbol_name(FuncIndex::new(index))
322 .as_bytes()
323 .to_vec(),
324 value: 0,
325 size: 0,
326 kind: SymbolKind::Text,
327 scope: SymbolScope::Linkage,
328 weak: false,
329 section: SymbolSection::Undefined,
330 flags: SymbolFlags::None,
331 });
332 func_symbols.push(symbol_id);
333 }
334
335 let mut append_func = |name: Vec<u8>, func: &CompiledFunction| {
336 let off = obj.append_section_data(section_id, &func.body, 1);
337 let symbol_id = obj.add_symbol(Symbol {
338 name,
339 value: off,
340 size: func.body.len() as u64,
341 kind: SymbolKind::Text,
342 scope: SymbolScope::Compilation,
343 weak: false,
344 section: SymbolSection::Section(section_id),
345 flags: SymbolFlags::None,
346 });
347 if let Some(info) = &func.unwind_info {
349 process_unwind_info(info, &mut obj, section_id);
350 }
351 symbol_id
352 };
353
354 for (index, func) in self.compilation.iter() {
356 let name = utils::func_symbol_name(module.func_index(index))
357 .as_bytes()
358 .to_vec();
359 let symbol_id = append_func(name, func);
360 func_symbols.push(symbol_id);
361 }
362 let mut trampolines = Vec::new();
363 for (i, func) in self.trampolines.iter() {
364 let name = utils::trampoline_symbol_name(*i).as_bytes().to_vec();
365 trampolines.push(append_func(name, func));
366 }
367
368 obj.append_section_data(section_id, &[], self.code_alignment);
369
370 let (debug_bodies, debug_relocs) = self
372 .dwarf_sections
373 .into_iter()
374 .map(|s| ((s.name, s.body), (s.name, s.relocs)))
375 .unzip::<_, _, Vec<_>, Vec<_>>();
376 let mut dwarf_sections_ids = HashMap::new();
377 for (name, body) in debug_bodies {
378 let segment = obj.segment_name(StandardSegment::Debug).to_vec();
379 let section_id = obj.add_section(segment, name.as_bytes().to_vec(), SectionKind::Debug);
380 dwarf_sections_ids.insert(name.to_string(), section_id);
381 obj.append_section_data(section_id, &body, 1);
382 }
383
384 let libcalls = write_libcall_symbols(&mut obj);
385
386 for (index, func) in self.compilation.into_iter() {
388 let func_index = module.func_index(index);
389 let (_, off) = obj
390 .symbol_section_and_offset(func_symbols[func_index])
391 .unwrap();
392 for r in to_object_relocations(
393 func.relocations.iter(),
394 off,
395 module,
396 &func_symbols,
397 &libcalls,
398 &self.compilation,
399 ) {
400 obj.add_relocation(section_id, r)?;
401 }
402 }
403
404 for ((_, func), symbol) in self.trampolines.iter().zip(trampolines) {
405 let (_, off) = obj.symbol_section_and_offset(symbol).unwrap();
406 for r in to_object_relocations(
407 func.relocations.iter(),
408 off,
409 module,
410 &func_symbols,
411 &libcalls,
412 &self.compilation,
413 ) {
414 obj.add_relocation(section_id, r)?;
415 }
416 }
417
418 for (name, relocs) in debug_relocs {
420 let section_id = *dwarf_sections_ids.get(name).unwrap();
421 for reloc in relocs {
422 let target_symbol = match reloc.target {
423 DwarfSectionRelocTarget::Func(index) => {
424 func_symbols[module.func_index(DefinedFuncIndex::new(index))]
425 }
426 DwarfSectionRelocTarget::Section(name) => {
427 obj.section_symbol(*dwarf_sections_ids.get(name).unwrap())
428 }
429 };
430 obj.add_relocation(
431 section_id,
432 ObjectRelocation {
433 offset: u64::from(reloc.offset),
434 size: reloc.size << 3,
435 kind: RelocationKind::Absolute,
436 encoding: RelocationEncoding::Generic,
437 symbol: target_symbol,
438 addend: i64::from(reloc.addend),
439 },
440 )?;
441 }
442 }
443
444 Ok(obj)
445 }
446}