1use crate::compiler::{CompiledObjects, emit_metadata_and_link};
6use crate::dwarf::{EhRelocation, EhTarget};
7use crate::misc::{CompiledFunctionExt, CompiledKind};
8use crate::object::get_object_for_target;
9use crate::types::function::{Compilation, FunctionBody};
10use crate::types::relocation::{Relocation, RelocationKind, RelocationTarget};
11use crate::types::section::CustomSection;
12use object::{
13 RelocationEncoding, RelocationFlags, RelocationKind as ObjectRelocationKind, SectionKind,
14 SymbolFlags, SymbolKind, SymbolScope, elf,
15 write::{
16 Object, Relocation as ObjectRelocation, SectionId, StandardSection, StandardSegment,
17 Symbol, SymbolId, SymbolSection,
18 },
19};
20use std::{
21 fs::OpenOptions,
22 io::Read,
23 path::{Path, PathBuf},
24};
25use tempfile::NamedTempFile;
26use wasmer_types::{
27 CompileError, LibCall, LocalFunctionIndex, TrapInformation, entity::PrimaryMap, target::Target,
28};
29use wasmer_types::{FunctionIndex, FunctionType};
30
31pub enum CompileOutput<T> {
36 InMemory(T),
38 Object(PathBuf, Option<usize>),
41}
42
43impl<T: crate::compiler::CompiledFunction> crate::compiler::CompiledFunction for CompileOutput<T> {}
44
45pub fn compile_output_paths<T>(outputs: Vec<CompileOutput<T>>) -> Vec<PathBuf> {
47 outputs
48 .into_iter()
49 .map(|output| match output {
50 CompileOutput::Object(path, _) => path,
51 CompileOutput::InMemory(_) => unreachable!(),
52 })
53 .collect()
54}
55
56pub fn compile_output_in_memory<T>(outputs: Vec<CompileOutput<T>>) -> Vec<T> {
58 outputs
59 .into_iter()
60 .map(|output| match output {
61 CompileOutput::InMemory(body) => body,
62 CompileOutput::Object(..) => unreachable!(),
63 })
64 .collect()
65}
66
67pub fn save_object(
69 object: Object<'static>,
70 build_directory: &Path,
71 filename: String,
72) -> Result<PathBuf, CompileError> {
73 let path = build_directory.join(filename);
74 let mut file = OpenOptions::new()
75 .write(true)
76 .create(true)
77 .truncate(true)
78 .open(&path)
79 .map_err(|e| {
80 CompileError::Codegen(format!("failed to create object {}: {e}", path.display()))
81 })?;
82 object.write_stream(&mut file).map_err(|e| {
83 CompileError::Codegen(format!("failed to write object {}: {e}", path.display()))
84 })?;
85 Ok(path)
86}
87
88pub fn add_undefined_symbol(object: &mut Object<'static>, name: String) -> SymbolId {
90 object.add_symbol(Symbol {
91 name: name.into_bytes(),
92 value: 0,
93 size: 0,
94 kind: SymbolKind::Text,
95 scope: SymbolScope::Linkage,
96 weak: false,
97 section: SymbolSection::Undefined,
98 flags: SymbolFlags::None,
99 })
100}
101
102pub fn add_libcall_symbol(object: &mut Object<'static>, libcall: LibCall) -> SymbolId {
105 object.add_symbol(Symbol {
106 name: libcall.to_function_name().to_string().into_bytes(),
107 value: 0,
108 size: 0,
109 kind: SymbolKind::Unknown,
110 scope: SymbolScope::Dynamic,
111 weak: false,
112 section: SymbolSection::Undefined,
113 flags: SymbolFlags::None,
114 })
115}
116
117pub fn relocation_kind_to_flags(kind: RelocationKind) -> Result<RelocationFlags, CompileError> {
120 use ObjectRelocationKind as K;
121 Ok(match kind {
122 RelocationKind::Abs4 => RelocationFlags::Generic {
123 kind: K::Absolute,
124 encoding: RelocationEncoding::Generic,
125 size: 32,
126 },
127 RelocationKind::Abs8 => RelocationFlags::Generic {
128 kind: K::Absolute,
129 encoding: RelocationEncoding::Generic,
130 size: 64,
131 },
132 RelocationKind::PCRel4 => RelocationFlags::Generic {
133 kind: K::Relative,
134 encoding: RelocationEncoding::Generic,
135 size: 32,
136 },
137 RelocationKind::X86CallPCRel4 => RelocationFlags::Generic {
138 kind: K::Relative,
139 encoding: RelocationEncoding::X86Branch,
140 size: 32,
141 },
142 RelocationKind::X86CallPLTRel4 => RelocationFlags::Generic {
143 kind: K::PltRelative,
144 encoding: RelocationEncoding::X86Branch,
145 size: 32,
146 },
147 RelocationKind::X86GOTPCRel4 => RelocationFlags::Generic {
148 kind: K::GotRelative,
149 encoding: RelocationEncoding::Generic,
150 size: 32,
151 },
152 RelocationKind::Arm64Call => RelocationFlags::Elf {
153 r_type: elf::R_AARCH64_CALL26,
154 },
155 RelocationKind::RiscvPCRelHi20 => RelocationFlags::Elf {
158 r_type: elf::R_RISCV_PCREL_HI20,
159 },
160 RelocationKind::RiscvPCRelLo12I => RelocationFlags::Elf {
161 r_type: elf::R_RISCV_PCREL_LO12_I,
162 },
163 RelocationKind::RiscvCall => RelocationFlags::Elf {
164 r_type: elf::R_RISCV_CALL_PLT,
165 },
166 kind => {
167 return Err(CompileError::Codegen(format!(
168 "unsupported ELF relocation kind: {kind:?}"
169 )));
170 }
171 })
172}
173
174pub fn add_relocations(
176 object: &mut Object<'static>,
177 section: SectionId,
178 relocations: &[Relocation],
179 local_symbol: Option<(LocalFunctionIndex, SymbolId)>,
180) -> Result<(), CompileError> {
181 for relocation in relocations {
182 let symbol = match relocation.reloc_target {
183 RelocationTarget::LocalFunc(index) => local_symbol
184 .filter(|(local_index, _)| *local_index == index)
185 .map_or_else(
186 || {
187 add_undefined_symbol(
188 object,
189 CompiledKind::Local(index, String::new()).linkage_name(),
190 )
191 },
192 |(_, symbol)| symbol,
193 ),
194 RelocationTarget::CustomSection(index) => add_undefined_symbol(
195 object,
196 CompiledKind::ImportFunctionTrampoline(
197 FunctionIndex::from_u32(index.as_u32()),
198 FunctionType::default(),
199 )
200 .linkage_name(),
201 ),
202 RelocationTarget::LibCall(libcall) => add_libcall_symbol(object, libcall),
203 RelocationTarget::DynamicTrampoline(index) => add_undefined_symbol(
204 object,
205 CompiledKind::DynamicFunctionTrampoline(index, FunctionType::default())
206 .linkage_name(),
207 ),
208 };
209 let flags = relocation_kind_to_flags(relocation.kind)?;
210 object
211 .add_relocation(
212 section,
213 ObjectRelocation {
214 offset: relocation.offset as u64,
215 flags,
216 symbol,
217 addend: relocation.addend,
218 },
219 )
220 .map_err(|e| CompileError::Codegen(format!("failed to add ELF relocation: {e}")))?;
221 }
222 Ok(())
223}
224
225pub fn emit_trap_section(
228 object: &mut Object<'static>,
229 kind: &CompiledKind,
230 traps: &[TrapInformation],
231) {
232 let mut trap_data = Vec::with_capacity(traps.len() * 8 + size_of::<u32>());
233 trap_data.extend_from_slice(&(traps.len() as u32).to_le_bytes());
234 for trap in traps {
235 trap_data.extend_from_slice(&trap.code_offset.to_le_bytes());
236 trap_data.extend_from_slice(&(trap.trap_code as u32).to_le_bytes());
237 }
238 let traps_section = object.add_section(
239 object.segment_name(StandardSegment::Data).to_vec(),
240 crate::WASMER_TRAPS_SECTION_NAME.to_vec(),
241 SectionKind::Other,
242 );
243 let traps_symbol = object.add_symbol(Symbol {
244 name: kind.traps_name().into_bytes(),
245 value: 0,
246 size: trap_data.len() as u64,
247 kind: SymbolKind::Data,
248 scope: SymbolScope::Linkage,
249 weak: true,
250 section: SymbolSection::Section(traps_section),
251 flags: SymbolFlags::None,
252 });
253 object.add_symbol_data(traps_symbol, traps_section, &trap_data, 4);
254}
255
256pub fn emit_eh_frame_section(
260 object: &mut Object<'static>,
261 eh_frame_bytes: &[u8],
262 relocations: &[EhRelocation],
263 function_symbol: SymbolId,
264 lsda_section_symbol: Option<SymbolId>,
265) -> Result<(), CompileError> {
266 let section = object.add_section(
267 object.segment_name(StandardSegment::Debug).to_vec(),
268 crate::EH_FRAME_SECTION_NAME.to_vec(),
269 SectionKind::Other,
270 );
271 let data_offset = object.append_section_data(section, eh_frame_bytes, 4);
272
273 let mut personality_symbol = None;
276 for relocation in relocations {
277 let symbol = match relocation.target {
278 EhTarget::Function => function_symbol,
279 EhTarget::Personality => *personality_symbol
280 .get_or_insert_with(|| add_libcall_symbol(object, LibCall::EHPersonality)),
281 EhTarget::Lsda => lsda_section_symbol.ok_or_else(|| {
282 CompileError::Codegen(
283 ".eh_frame references an LSDA but none was emitted".to_string(),
284 )
285 })?,
286 };
287 object
288 .add_relocation(
289 section,
290 ObjectRelocation {
291 offset: data_offset + relocation.offset,
292 flags: RelocationFlags::Generic {
293 kind: relocation.kind,
294 encoding: RelocationEncoding::Generic,
295 size: 8 * relocation.size,
296 },
297 symbol,
298 addend: relocation.addend,
299 },
300 )
301 .map_err(|e| {
302 CompileError::Codegen(format!("failed to add .eh_frame relocation: {e}"))
303 })?;
304 }
305 Ok(())
306}
307
308pub fn emit_function_body(
310 target: &Target,
311 build_directory: &Path,
312 kind: &CompiledKind,
313 body: &FunctionBody,
314) -> Result<PathBuf, CompileError> {
315 let mut object = get_object_for_target(target.triple())
316 .map_err(|e| CompileError::Codegen(format!("cannot create object: {e}")))?;
317 let symbol = object.add_symbol(Symbol {
318 name: kind.linkage_name().into_bytes(),
319 value: 0,
320 size: body.body.len() as u64,
321 kind: SymbolKind::Text,
322 scope: SymbolScope::Linkage,
323 weak: false,
324 section: SymbolSection::Undefined,
325 flags: SymbolFlags::None,
326 });
327 let text = object.section_id(StandardSection::Text);
328 object.add_symbol_data(symbol, text, &body.body, 4);
329 save_object(object, build_directory, kind.object_filename())
330}
331
332pub fn emit_import_trampoline(
334 target: &Target,
335 build_directory: &Path,
336 kind: &CompiledKind,
337 section: &CustomSection,
338) -> Result<PathBuf, CompileError> {
339 let mut object = get_object_for_target(target.triple())
340 .map_err(|e| CompileError::Codegen(format!("cannot create object: {e}")))?;
341 let symbol = object.add_symbol(Symbol {
342 name: kind.linkage_name().into_bytes(),
343 value: 0,
344 size: section.bytes.len() as u64,
345 kind: SymbolKind::Text,
346 scope: SymbolScope::Linkage,
347 weak: false,
348 section: SymbolSection::Undefined,
349 flags: SymbolFlags::None,
350 });
351 let text = object.section_id(StandardSection::Text);
352 object.add_symbol_data(symbol, text, section.bytes.as_slice(), 4);
353 save_object(object, build_directory, kind.object_filename())
354}
355
356#[allow(clippy::too_many_arguments)]
359pub fn link_module(
360 target: &Target,
361 compile_info_blob: &[u8],
362 build_directory: &Path,
363 object_files: &[PathBuf],
364 import_trampoline_objects: &[PathBuf],
365 trampoline_objects: &[PathBuf],
366 dynamic_trampoline_objects: &[PathBuf],
367 debug_dir: Option<PathBuf>,
368 module_hash: Option<String>,
369 function_max_stack_usage: PrimaryMap<LocalFunctionIndex, Option<usize>>,
370) -> Result<Compilation, CompileError> {
371 let module_file = NamedTempFile::new_in(build_directory)
372 .map_err(|e| CompileError::Codegen(format!("cannot create temporary module file: {e}")))?;
373 let mut module_file = emit_metadata_and_link(
374 target,
375 compile_info_blob,
376 build_directory,
377 module_file,
378 &CompiledObjects {
379 object_files,
380 import_trampoline_object_files: import_trampoline_objects,
381 trampoline_object_files: trampoline_objects,
382 dynamic_trampoline_object_files: dynamic_trampoline_objects,
383 },
384 debug_dir,
385 module_hash,
386 )?;
387 let mut elf = Vec::new();
388 module_file
389 .read_to_end(&mut elf)
390 .map_err(|e| CompileError::Codegen(format!("cannot read linked module artifact: {e}")))?;
391 Ok(Compilation::Elf {
392 data: elf,
393 function_max_stack_usage,
394 })
395}