1#[cfg(feature = "unwind")]
5use crate::eh::FunctionLsdaData;
6#[cfg(feature = "unwind")]
7use cranelift_codegen::gimli::{
8 RunTimeEndian, SectionId as GimliSectionId, constants,
9 write::{
10 Address, EhFrame, EndianVec, FrameDescriptionEntry, FrameTable, Result as GimliResult,
11 Writer,
12 },
13};
14#[cfg(feature = "unwind")]
15use cranelift_codegen::isa::TargetIsa;
16#[cfg(feature = "unwind")]
17use object::RelocationKind as ObjectRelocationKind;
18use object::{
19 RelocationEncoding, RelocationFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope,
20 write::{
21 Object, Relocation as ObjectRelocation, StandardSection, StandardSegment, Symbol,
22 SymbolSection,
23 },
24};
25#[cfg(feature = "unwind")]
26use std::collections::HashMap;
27#[cfg(feature = "unwind")]
28use wasmer_compiler::dwarf::{EhRelocation, EhTarget, WriterRelocate};
29#[cfg(feature = "unwind")]
30use wasmer_compiler::elf::emit_eh_frame_section;
31use wasmer_compiler::{WasmSourceMap, dwarf::init_dwarf_unit};
32use wasmer_compiler::{
33 elf::{add_relocations, emit_trap_section},
34 misc::{CompiledFunctionExt, CompiledKind},
35 object::get_object_for_target,
36 types::function::CompiledFunction,
37};
38use wasmer_types::{CompileError, LocalFunctionIndex, target::Target};
39
40#[cfg(feature = "unwind")]
49struct EhFrameWriter {
50 relocs: Vec<EhRelocation>,
51 writer: EndianVec<RunTimeEndian>,
52}
53
54#[cfg(feature = "unwind")]
55impl EhFrameWriter {
56 fn new() -> Self {
57 Self {
58 relocs: Vec::new(),
59 writer: EndianVec::new(RunTimeEndian::Little),
60 }
61 }
62
63 fn into_bytes(self) -> Vec<u8> {
64 self.writer.into_vec()
65 }
66
67 fn target_for(symbol: usize) -> GimliResult<EhTarget> {
68 match symbol {
69 WriterRelocate::FUNCTION_SYMBOL => Ok(EhTarget::Function),
70 WriterRelocate::PERSONALITY_SYMBOL => Ok(EhTarget::Personality),
71 WriterRelocate::LSDA_SYMBOL => Ok(EhTarget::Lsda),
72 _ => Err(cranelift_codegen::gimli::write::Error::InvalidAddress),
73 }
74 }
75}
76
77#[cfg(feature = "unwind")]
78impl Writer for EhFrameWriter {
79 type Endian = RunTimeEndian;
80
81 fn endian(&self) -> Self::Endian {
82 self.writer.endian()
83 }
84
85 fn len(&self) -> usize {
86 self.writer.len()
87 }
88
89 fn write(&mut self, bytes: &[u8]) -> GimliResult<()> {
90 self.writer.write(bytes)
91 }
92
93 fn write_at(&mut self, offset: usize, bytes: &[u8]) -> GimliResult<()> {
94 self.writer.write_at(offset, bytes)
95 }
96
97 fn write_address(&mut self, address: Address, size: u8) -> GimliResult<()> {
98 match address {
99 Address::Constant(val) => self.write_udata(val, size),
100 Address::Symbol { symbol, addend } => {
101 let target = Self::target_for(symbol)?;
102 let offset = self.len() as u64;
103 self.relocs.push(EhRelocation {
104 offset,
105 kind: ObjectRelocationKind::Absolute,
106 size,
107 target,
108 addend,
109 });
110 self.write_udata(0, size)
111 }
112 }
113 }
114
115 fn write_eh_pointer(
116 &mut self,
117 address: Address,
118 eh_pe: constants::DwEhPe,
119 size: u8,
120 ) -> GimliResult<()> {
121 if eh_pe == constants::DW_EH_PE_absptr {
122 return self.write_address(address, size);
123 }
124
125 match address {
126 Address::Constant(_) => self.writer.write_eh_pointer(address, eh_pe, size),
127 Address::Symbol { symbol, addend }
128 if eh_pe == (constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4)
129 && size == 8 =>
130 {
131 let target = Self::target_for(symbol)?;
132 let offset = self.len() as u64;
133 self.relocs.push(EhRelocation {
134 offset,
135 kind: ObjectRelocationKind::Relative,
136 size: 4,
137 target,
138 addend,
139 });
140 self.write_udata(0, 4)
141 }
142 Address::Symbol { symbol, addend }
147 if eh_pe
148 == (constants::DW_EH_PE_indirect
149 | constants::DW_EH_PE_pcrel
150 | constants::DW_EH_PE_sdata4)
151 && size == 8 =>
152 {
153 let target = Self::target_for(symbol)?;
154 let offset = self.len() as u64;
155 self.relocs.push(EhRelocation {
156 offset,
157 kind: ObjectRelocationKind::Relative,
158 size: 4,
159 target,
160 addend,
161 });
162 self.write_udata(0, 4)
163 }
164 Address::Symbol { .. } => Err(cranelift_codegen::gimli::write::Error::InvalidAddress),
165 }
166 }
167
168 fn write_offset(
169 &mut self,
170 _val: usize,
171 _section: GimliSectionId,
172 _size: u8,
173 ) -> GimliResult<()> {
174 Err(cranelift_codegen::gimli::write::Error::OffsetOutOfBounds)
175 }
176
177 fn write_offset_at(
178 &mut self,
179 _offset: usize,
180 _val: usize,
181 _section: GimliSectionId,
182 _size: u8,
183 ) -> GimliResult<()> {
184 Err(cranelift_codegen::gimli::write::Error::OffsetOutOfBounds)
185 }
186}
187
188#[cfg(feature = "unwind")]
192fn emit_eh_tag_section(
193 object: &mut Object<'static>,
194 lsda: &FunctionLsdaData,
195) -> Option<(object::write::SymbolId, HashMap<u32, u32>)> {
196 let mut tags: Vec<u32> = lsda.relocations.iter().map(|r| r.tag).collect();
197 tags.sort_unstable();
198 tags.dedup();
199 if tags.is_empty() {
200 return None;
201 }
202
203 let mut bytes = Vec::with_capacity(tags.len() * size_of::<u32>());
204 let mut offsets = HashMap::new();
205 for tag in tags {
206 offsets.insert(tag, bytes.len() as u32);
207 bytes.extend_from_slice(&tag.to_ne_bytes());
208 }
209
210 let section = object.add_section(
211 object.segment_name(StandardSegment::Data).to_vec(),
212 b".wasmer.eh_tags".to_vec(),
213 SectionKind::ReadOnlyData,
214 );
215 object.append_section_data(section, &bytes, 4);
216 Some((object.section_symbol(section), offsets))
217}
218
219#[allow(clippy::too_many_arguments)]
221pub(crate) fn emit_local_function(
222 #[cfg(feature = "unwind")] isa: &dyn TargetIsa,
223 target: &Target,
224 index: LocalFunctionIndex,
225 function_name: &str,
226 module_name: Option<&str>,
227 function: &CompiledFunction,
228 source_map: &WasmSourceMap,
229 #[cfg(feature = "unwind")] fde: Option<FrameDescriptionEntry>,
230 #[cfg(feature = "unwind")] lsda: Option<FunctionLsdaData>,
231) -> Result<Vec<u8>, CompileError> {
232 let kind = CompiledKind::Local(index, String::new());
233 let mut object = get_object_for_target(target.triple())
234 .map_err(|e| CompileError::Codegen(format!("cannot create object: {e}")))?;
235
236 let function_symbol = object.add_symbol(Symbol {
238 name: kind.linkage_name().into_bytes(),
239 value: 0,
240 size: function.body.body.len() as u64,
241 kind: SymbolKind::Text,
242 scope: SymbolScope::Linkage,
243 weak: false,
244 section: SymbolSection::Undefined,
245 flags: SymbolFlags::None,
246 });
247 let text = object.section_id(StandardSection::Text);
248 object.add_symbol_data(function_symbol, text, &function.body.body, 16);
249 add_relocations(
250 &mut object,
251 text,
252 &function.relocations,
253 Some((index, function_symbol)),
254 )?;
255
256 if let Ok(mut dwarf_state) = init_dwarf_unit(function_name, module_name, "Wasmer (Cranelift)") {
258 for instruction in &function.frame_info.address_map.instructions {
259 dwarf_state.add_source_map_row(
260 instruction.code_offset as u64,
261 instruction.srcloc,
262 source_map,
263 );
264 }
265 dwarf_state.write_sections(
266 &mut object,
267 function_symbol,
268 function.body.body.len() as u64,
269 target.triple().endianness().ok(),
270 )?;
271 }
272
273 emit_trap_section(&mut object, &kind, &function.frame_info.traps);
274
275 #[cfg(feature = "unwind")]
278 if let Some(fde) = fde
279 && let Some(mut cie) = isa.create_systemv_cie()
280 {
281 let pointer_bytes = isa.frontend_config().pointer_bytes();
282
283 let lsda_section_symbol = if let Some(lsda) = &lsda {
286 let tag_section_symbol = emit_eh_tag_section(&mut object, lsda);
287
288 let gcc_section = object.add_section(
289 object.segment_name(StandardSegment::Data).to_vec(),
290 b".gcc_except_table".to_vec(),
291 SectionKind::ReadOnlyData,
292 );
293 let lsda_offset =
294 object.append_section_data(gcc_section, &lsda.bytes, u64::from(pointer_bytes));
295 let tag_relocation_flags = RelocationFlags::Generic {
299 kind: object::RelocationKind::Relative,
300 encoding: RelocationEncoding::Generic,
301 size: 32,
302 };
303 for reloc in &lsda.relocations {
304 let (tag_symbol, tag_offset) = tag_section_symbol
305 .as_ref()
306 .and_then(|(symbol, offsets)| {
307 offsets.get(&reloc.tag).map(|offset| (*symbol, *offset))
308 })
309 .ok_or_else(|| {
310 CompileError::Codegen(format!(
311 "missing exception tag {} for LSDA relocation",
312 reloc.tag
313 ))
314 })?;
315 object
316 .add_relocation(
317 gcc_section,
318 ObjectRelocation {
319 offset: lsda_offset + reloc.offset as u64,
320 flags: tag_relocation_flags,
321 symbol: tag_symbol,
322 addend: tag_offset as i64,
323 },
324 )
325 .map_err(|e| {
326 CompileError::Codegen(format!("failed to add LSDA relocation: {e}"))
327 })?;
328 }
329 Some(object.section_symbol(gcc_section))
330 } else {
331 None
332 };
333
334 cie.fde_address_encoding = constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4;
337 let mut fde = fde;
338 if lsda_section_symbol.is_some() {
339 cie.personality = Some((
344 constants::DW_EH_PE_indirect
345 | constants::DW_EH_PE_pcrel
346 | constants::DW_EH_PE_sdata4,
347 Address::Symbol {
348 symbol: WriterRelocate::PERSONALITY_SYMBOL,
349 addend: 0,
350 },
351 ));
352 cie.lsda_encoding = Some(constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4);
353 fde.lsda = Some(Address::Symbol {
354 symbol: WriterRelocate::LSDA_SYMBOL,
355 addend: 0,
356 });
357 }
358
359 let mut frametable = FrameTable::default();
360 let cie_id = frametable.add_cie(cie);
361 frametable.add_fde(cie_id, fde);
362
363 let mut eh_frame = EhFrame(EhFrameWriter::new());
364 frametable
365 .write_eh_frame(&mut eh_frame)
366 .map_err(|e| CompileError::Codegen(format!("failed to write .eh_frame: {e}")))?;
367
368 let relocations = std::mem::take(&mut eh_frame.0.relocs);
369 emit_eh_frame_section(
370 &mut object,
371 &eh_frame.0.into_bytes(),
372 &relocations,
373 function_symbol,
374 lsda_section_symbol,
375 )?;
376 }
377
378 object
379 .write()
380 .map_err(|e| CompileError::Codegen(format!("failed to serialize object: {e}")))
381}