xref: /wasmtime-44.0.1/crates/cranelift/src/obj.rs (revision 4e8dc9d0)
1 //! Object file builder.
2 //!
3 //! Creates ELF image based on `Compilation` information. The ELF contains
4 //! functions and trampolines in the ".text" section. It also contains all
5 //! relocation records for the linking stage. If DWARF sections exist, their
6 //! content will be written as well.
7 //!
8 //! The object file has symbols for each function and trampoline, as well as
9 //! symbols that refer to libcalls.
10 //!
11 //! The function symbol names have format "_wasm_function_N", where N is
12 //! `FuncIndex`. The defined wasm function symbols refer to a JIT compiled
13 //! function body, the imported wasm function do not. The trampolines symbol
14 //! names have format "_trampoline_N", where N is `SignatureIndex`.
15 
16 use crate::{CompiledFunction, RelocationTarget};
17 use anyhow::Result;
18 use cranelift_codegen::binemit::Reloc;
19 use cranelift_codegen::isa::unwind::{systemv, UnwindInfo};
20 use cranelift_codegen::TextSectionBuilder;
21 use cranelift_control::ControlPlane;
22 use gimli::write::{Address, EhFrame, EndianVec, FrameTable, Writer};
23 use gimli::RunTimeEndian;
24 use object::write::{Object, SectionId, StandardSegment, Symbol, SymbolId, SymbolSection};
25 use object::{Architecture, SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope};
26 use std::collections::HashMap;
27 use std::ops::Range;
28 use wasmtime_environ::obj::{self, LibCall};
29 use wasmtime_environ::{Compiler, TripleExt, Unsigned};
30 
31 const TEXT_SECTION_NAME: &[u8] = b".text";
32 
33 fn text_align(compiler: &dyn Compiler) -> u64 {
34     // text pages will not be made executable with pulley, so the section
35     // doesn't need to be padded out to page alignment boundaries.
36     if compiler.triple().is_pulley() {
37         0x1
38     } else {
39         compiler.page_size_align()
40     }
41 }
42 
43 /// A helper structure used to assemble the final text section of an executable,
44 /// plus unwinding information and other related details.
45 ///
46 /// This builder relies on Cranelift-specific internals but assembles into a
47 /// generic `Object` which will get further appended to in a compiler-agnostic
48 /// fashion later.
49 pub struct ModuleTextBuilder<'a> {
50     /// The target that we're compiling for, used to query target-specific
51     /// information as necessary.
52     compiler: &'a dyn Compiler,
53 
54     /// The object file that we're generating code into.
55     obj: &'a mut Object<'static>,
56 
57     /// The WebAssembly module we're generating code for.
58     text_section: SectionId,
59 
60     unwind_info: UnwindInfoBuilder<'a>,
61 
62     /// In-progress text section that we're using cranelift's `MachBuffer` to
63     /// build to resolve relocations (calls) between functions.
64     text: Box<dyn TextSectionBuilder>,
65 
66     /// Symbols defined in the object for libcalls that relocations are applied
67     /// against.
68     ///
69     /// Note that this isn't typically used. It's only used for SSE-disabled
70     /// builds without SIMD on x86_64 right now.
71     libcall_symbols: HashMap<LibCall, SymbolId>,
72 
73     ctrl_plane: ControlPlane,
74 }
75 
76 impl<'a> ModuleTextBuilder<'a> {
77     /// Creates a new builder for the text section of an executable.
78     ///
79     /// The `.text` section will be appended to the specified `obj` along with
80     /// any unwinding or such information as necessary. The `num_funcs`
81     /// parameter indicates the number of times the `append_func` function will
82     /// be called. The `finish` function will panic if this contract is not met.
83     pub fn new(
84         obj: &'a mut Object<'static>,
85         compiler: &'a dyn Compiler,
86         text: Box<dyn TextSectionBuilder>,
87     ) -> Self {
88         // Entire code (functions and trampolines) will be placed
89         // in the ".text" section.
90         let text_section = obj.add_section(
91             obj.segment_name(StandardSegment::Text).to_vec(),
92             TEXT_SECTION_NAME.to_vec(),
93             SectionKind::Text,
94         );
95 
96         // If this target is Pulley then flag the text section as not needing the
97         // executable bit in virtual memory which means that the runtime won't
98         // try to call `Mmap::make_executable`, which makes Pulley more
99         // portable.
100         if compiler.triple().is_pulley() {
101             let section = obj.section_mut(text_section);
102             assert!(matches!(section.flags, SectionFlags::None));
103             section.flags = SectionFlags::Elf {
104                 sh_flags: obj::SH_WASMTIME_NOT_EXECUTED,
105             };
106         }
107 
108         Self {
109             compiler,
110             obj,
111             text_section,
112             unwind_info: Default::default(),
113             text,
114             libcall_symbols: HashMap::default(),
115             ctrl_plane: ControlPlane::default(),
116         }
117     }
118 
119     /// Appends the `func` specified named `name` to this object.
120     ///
121     /// The `resolve_reloc_target` closure is used to resolve a relocation
122     /// target to an adjacent function which has already been added or will be
123     /// added to this object. The argument is the relocation target specified
124     /// within `CompiledFunction` and the return value must be an index where
125     /// the target will be defined by the `n`th call to `append_func`.
126     ///
127     /// Returns the symbol associated with the function as well as the range
128     /// that the function resides within the text section.
129     pub fn append_func(
130         &mut self,
131         name: &str,
132         compiled_func: &'a CompiledFunction,
133         resolve_reloc_target: impl Fn(wasmtime_environ::RelocationTarget) -> usize,
134     ) -> (SymbolId, Range<u64>) {
135         let body = compiled_func.buffer.data();
136         let alignment = compiled_func.alignment;
137         let body_len = body.len() as u64;
138         let off = self
139             .text
140             .append(true, &body, alignment, &mut self.ctrl_plane);
141 
142         let symbol_id = self.obj.add_symbol(Symbol {
143             name: name.as_bytes().to_vec(),
144             value: off,
145             size: body_len,
146             kind: SymbolKind::Text,
147             scope: SymbolScope::Compilation,
148             weak: false,
149             section: SymbolSection::Section(self.text_section),
150             flags: SymbolFlags::None,
151         });
152 
153         if let Some(info) = compiled_func.unwind_info() {
154             self.unwind_info.push(off, body_len, info);
155         }
156 
157         for r in compiled_func.relocations() {
158             let reloc_offset = off + u64::from(r.offset);
159             match r.reloc_target {
160                 // Relocations against user-defined functions means that this is
161                 // a relocation against a module-local function, typically a
162                 // call between functions. The `text` field is given priority to
163                 // resolve this relocation before we actually emit an object
164                 // file, but if it can't handle it then we pass through the
165                 // relocation.
166                 RelocationTarget::Wasm(_) | RelocationTarget::Builtin(_) => {
167                     let target = resolve_reloc_target(r.reloc_target);
168                     if self
169                         .text
170                         .resolve_reloc(reloc_offset, r.reloc, r.addend, target)
171                     {
172                         continue;
173                     }
174 
175                     // At this time it's expected that all relocations are
176                     // handled by `text.resolve_reloc`, and anything that isn't
177                     // handled is a bug in `text.resolve_reloc` or something
178                     // transitively there. If truly necessary, though, then this
179                     // loop could also be updated to forward the relocation to
180                     // the final object file as well.
181                     panic!(
182                         "unresolved relocation could not be processed against \
183                          {:?}: {r:?}",
184                         r.reloc_target,
185                     );
186                 }
187 
188                 // Relocations against libcalls are not common at this time and
189                 // are only used in non-default configurations that disable wasm
190                 // SIMD, disable SSE features, and for wasm modules that still
191                 // use floating point operations.
192                 //
193                 // Currently these relocations are all expected to be absolute
194                 // 8-byte relocations so that's asserted here and then encoded
195                 // directly into the object as a normal object relocation. This
196                 // is processed at module load time to resolve the relocations.
197                 RelocationTarget::HostLibcall(call) => {
198                     let symbol = *self.libcall_symbols.entry(call).or_insert_with(|| {
199                         self.obj.add_symbol(Symbol {
200                             name: call.symbol().as_bytes().to_vec(),
201                             value: 0,
202                             size: 0,
203                             kind: SymbolKind::Text,
204                             scope: SymbolScope::Linkage,
205                             weak: false,
206                             section: SymbolSection::Undefined,
207                             flags: SymbolFlags::None,
208                         })
209                     });
210                     let flags = match r.reloc {
211                         Reloc::Abs8 => object::RelocationFlags::Generic {
212                             encoding: object::RelocationEncoding::Generic,
213                             kind: object::RelocationKind::Absolute,
214                             size: 64,
215                         },
216                         other => unimplemented!("unimplemented relocation kind {other:?}"),
217                     };
218                     self.obj
219                         .add_relocation(
220                             self.text_section,
221                             object::write::Relocation {
222                                 symbol,
223                                 flags,
224                                 offset: reloc_offset,
225                                 addend: r.addend,
226                             },
227                         )
228                         .unwrap();
229                 }
230 
231                 // This relocation is used to fill in which hostcall id is
232                 // desired within the `call_indirect_host` opcode of Pulley
233                 // itself. The relocation target is the start of the instruction
234                 // and the goal is to insert the static signature number, `n`,
235                 // into the instruction.
236                 //
237                 // At this time the instruction looks like:
238                 //
239                 //      +------+------+------+------+
240                 //      | OP   | OP_EXTENDED |  N   |
241                 //      +------+------+------+------+
242                 //
243                 // This 4-byte encoding has `OP` indicating this is an "extended
244                 // opcode" where `OP_EXTENDED` is a 16-bit extended opcode.
245                 // The `N` byte is the index of the signature being called and
246                 // is what's b eing filled in.
247                 //
248                 // See the `test_call_indirect_host_width` in
249                 // `pulley/tests/all.rs` for this guarantee as well.
250                 RelocationTarget::PulleyHostcall(n) => {
251                     #[cfg(feature = "pulley")]
252                     {
253                         use pulley_interpreter::encode::Encode;
254                         assert_eq!(pulley_interpreter::CallIndirectHost::WIDTH, 4);
255                     }
256                     let byte = u8::try_from(n).unwrap();
257                     self.text.write(reloc_offset + 3, &[byte]);
258                 }
259             };
260         }
261         (symbol_id, off..off + body_len)
262     }
263 
264     /// Forces "veneers" to be used for inter-function calls in the text
265     /// section which means that in-bounds optimized addresses are never used.
266     ///
267     /// This is only useful for debugging cranelift itself and typically this
268     /// option is disabled.
269     pub fn force_veneers(&mut self) {
270         self.text.force_veneers();
271     }
272 
273     /// Appends the specified amount of bytes of padding into the text section.
274     ///
275     /// This is only useful when fuzzing and/or debugging cranelift itself and
276     /// for production scenarios `padding` is 0 and this function does nothing.
277     pub fn append_padding(&mut self, padding: usize) {
278         if padding == 0 {
279             return;
280         }
281         self.text
282             .append(false, &vec![0; padding], 1, &mut self.ctrl_plane);
283     }
284 
285     /// Indicates that the text section has been written completely and this
286     /// will finish appending it to the original object.
287     ///
288     /// Note that this will also write out the unwind information sections if
289     /// necessary.
290     pub fn finish(mut self) {
291         // Finish up the text section now that we're done adding functions.
292         let text = self.text.finish(&mut self.ctrl_plane);
293         self.obj
294             .section_mut(self.text_section)
295             .set_data(text, text_align(self.compiler));
296 
297         // Append the unwind information for all our functions, if necessary.
298         self.unwind_info
299             .append_section(self.compiler, self.obj, self.text_section);
300     }
301 }
302 
303 /// Builder used to create unwind information for a set of functions added to a
304 /// text section.
305 #[derive(Default)]
306 struct UnwindInfoBuilder<'a> {
307     windows_xdata: Vec<u8>,
308     windows_pdata: Vec<RUNTIME_FUNCTION>,
309     systemv_unwind_info: Vec<(u64, &'a systemv::UnwindInfo)>,
310 }
311 
312 // This is a mirror of `RUNTIME_FUNCTION` in the Windows API, but defined here
313 // to ensure everything is always `u32` and to have it available on all
314 // platforms. Note that all of these specifiers here are relative to a "base
315 // address" which we define as the base of where the text section is eventually
316 // loaded.
317 #[expect(non_camel_case_types, reason = "matching Windows style, not Rust")]
318 struct RUNTIME_FUNCTION {
319     begin: u32,
320     end: u32,
321     unwind_address: u32,
322 }
323 
324 impl<'a> UnwindInfoBuilder<'a> {
325     /// Pushes the unwind information for a function into this builder.
326     ///
327     /// The function being described must be located at `function_offset` within
328     /// the text section itself, and the function's size is specified by
329     /// `function_len`.
330     ///
331     /// The `info` should come from Cranelift. and is handled here depending on
332     /// its flavor.
333     fn push(&mut self, function_offset: u64, function_len: u64, info: &'a UnwindInfo) {
334         match info {
335             // Windows unwind information is stored in two locations:
336             //
337             // * First is the actual unwinding information which is stored
338             //   in the `.xdata` section. This is where `info`'s emitted
339             //   information will go into.
340             // * Second are pointers to connect all this unwind information,
341             //   stored in the `.pdata` section. The `.pdata` section is an
342             //   array of `RUNTIME_FUNCTION` structures.
343             //
344             // Due to how these will be loaded at runtime the `.pdata` isn't
345             // actually assembled byte-wise here. Instead that's deferred to
346             // happen later during `write_windows_unwind_info` which will apply
347             // a further offset to `unwind_address`.
348             //
349             // FIXME: in theory we could "intern" the `unwind_info` value
350             // here within the `.xdata` section. Most of our unwind
351             // information for functions is probably pretty similar in which
352             // case the `.xdata` could be quite small and `.pdata` could
353             // have multiple functions point to the same unwinding
354             // information.
355             UnwindInfo::WindowsX64(info) => {
356                 let unwind_size = info.emit_size();
357                 let mut unwind_info = vec![0; unwind_size];
358                 info.emit(&mut unwind_info);
359 
360                 // `.xdata` entries are always 4-byte aligned
361                 while self.windows_xdata.len() % 4 != 0 {
362                     self.windows_xdata.push(0x00);
363                 }
364                 let unwind_address = self.windows_xdata.len();
365                 self.windows_xdata.extend_from_slice(&unwind_info);
366 
367                 // Record a `RUNTIME_FUNCTION` which this will point to.
368                 self.windows_pdata.push(RUNTIME_FUNCTION {
369                     begin: u32::try_from(function_offset).unwrap(),
370                     end: u32::try_from(function_offset + function_len).unwrap(),
371                     unwind_address: u32::try_from(unwind_address).unwrap(),
372                 });
373             }
374 
375             // See https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling
376             UnwindInfo::WindowsArm64(info) => {
377                 let code_words = info.code_words();
378                 let mut unwind_codes = vec![0; (code_words * 4) as usize];
379                 info.emit(&mut unwind_codes);
380 
381                 // `.xdata` entries are always 4-byte aligned
382                 while self.windows_xdata.len() % 4 != 0 {
383                     self.windows_xdata.push(0x00);
384                 }
385 
386                 // First word:
387                 // 0-17:    Function Length
388                 // 18-19:   Version (must be 0)
389                 // 20:      X bit (is exception data present?)
390                 // 21:      E bit (has single packed epilogue?)
391                 // 22-26:   Epilogue count
392                 // 27-31:   Code words count
393                 let requires_extended_counts = code_words > (1 << 5);
394                 let encoded_function_len = function_len / 4;
395                 assert!(encoded_function_len < (1 << 18), "function too large");
396                 let mut word1 = u32::try_from(encoded_function_len).unwrap();
397                 if !requires_extended_counts {
398                     word1 |= u32::from(code_words) << 27;
399                 }
400                 let unwind_address = self.windows_xdata.len();
401                 self.windows_xdata.extend_from_slice(&word1.to_le_bytes());
402 
403                 if requires_extended_counts {
404                     // Extended counts word:
405                     // 0-15:    Epilogue count
406                     // 16-23:   Code words count
407                     let extended_counts_word = (code_words as u32) << 16;
408                     self.windows_xdata
409                         .extend_from_slice(&extended_counts_word.to_le_bytes());
410                 }
411 
412                 // Skip epilogue information: Per comment on [`UnwindInst`], we
413                 // do not emit information about epilogues.
414 
415                 // Emit the unwind codes.
416                 self.windows_xdata.extend_from_slice(&unwind_codes);
417 
418                 // Record a `RUNTIME_FUNCTION` which this will point to.
419                 // NOTE: `end` is not used, so leave it as 0.
420                 self.windows_pdata.push(RUNTIME_FUNCTION {
421                     begin: u32::try_from(function_offset).unwrap(),
422                     end: 0,
423                     unwind_address: u32::try_from(unwind_address).unwrap(),
424                 });
425             }
426 
427             // System-V is different enough that we just record the unwinding
428             // information to get processed at a later time.
429             UnwindInfo::SystemV(info) => {
430                 self.systemv_unwind_info.push((function_offset, info));
431             }
432 
433             _ => panic!("some unwind info isn't handled here"),
434         }
435     }
436 
437     /// Appends the unwind information section, if any, to the `obj` specified.
438     ///
439     /// This function must be called immediately after the text section was
440     /// added to a builder. The unwind information section must trail the text
441     /// section immediately.
442     ///
443     /// The `text_section`'s section identifier is passed into this function.
444     fn append_section(
445         &self,
446         compiler: &dyn Compiler,
447         obj: &mut Object<'_>,
448         text_section: SectionId,
449     ) {
450         // This write will align the text section to a page boundary and then
451         // return the offset at that point. This gives us the full size of the
452         // text section at that point, after alignment.
453         let text_section_size = obj.append_section_data(text_section, &[], text_align(compiler));
454 
455         if self.windows_xdata.len() > 0 {
456             assert!(self.systemv_unwind_info.len() == 0);
457             // The `.xdata` section must come first to be just-after the `.text`
458             // section for the reasons documented in `write_windows_unwind_info`
459             // below.
460             let segment = obj.segment_name(StandardSegment::Data).to_vec();
461             let xdata_id = obj.add_section(segment, b".xdata".to_vec(), SectionKind::ReadOnlyData);
462             let segment = obj.segment_name(StandardSegment::Data).to_vec();
463             let pdata_id = obj.add_section(segment, b".pdata".to_vec(), SectionKind::ReadOnlyData);
464             self.write_windows_unwind_info(obj, xdata_id, pdata_id, text_section_size);
465         }
466 
467         if self.systemv_unwind_info.len() > 0 {
468             let segment = obj.segment_name(StandardSegment::Data).to_vec();
469             let section_id =
470                 obj.add_section(segment, b".eh_frame".to_vec(), SectionKind::ReadOnlyData);
471             self.write_systemv_unwind_info(compiler, obj, section_id, text_section_size)
472         }
473     }
474 
475     /// This function appends a nonstandard section to the object which is only
476     /// used during `CodeMemory::publish`.
477     ///
478     /// This custom section effectively stores a `[RUNTIME_FUNCTION; N]` into
479     /// the object file itself. This way registration of unwind info can simply
480     /// pass this slice to the OS itself and there's no need to recalculate
481     /// anything on the other end of loading a module from a precompiled object.
482     ///
483     /// Support for reading this is in `crates/jit/src/unwind/winx64.rs`.
484     fn write_windows_unwind_info(
485         &self,
486         obj: &mut Object<'_>,
487         xdata_id: SectionId,
488         pdata_id: SectionId,
489         text_section_size: u64,
490     ) {
491         // Append the `.xdata` section, or the actual unwinding information
492         // codes and such which were built as we found unwind information for
493         // functions.
494         obj.append_section_data(xdata_id, &self.windows_xdata, 4);
495 
496         // Next append the `.pdata` section, or the array of `RUNTIME_FUNCTION`
497         // structures stored in the binary.
498         //
499         // This memory will be passed at runtime to `RtlAddFunctionTable` which
500         // takes a "base address" and the entries within `RUNTIME_FUNCTION` are
501         // all relative to this base address. The base address we pass is the
502         // address of the text section itself so all the pointers here must be
503         // text-section-relative. The `begin` and `end` fields for the function
504         // it describes are already text-section-relative, but the
505         // `unwind_address` field needs to be updated here since the value
506         // stored right now is `xdata`-section-relative. We know that the
507         // `xdata` section follows the `.text` section so the
508         // `text_section_size` is added in to calculate the final
509         // `.text`-section-relative address of the unwind information.
510         let xdata_rva = |address| {
511             let address = u64::from(address);
512             let address = address + text_section_size;
513             u32::try_from(address).unwrap()
514         };
515         let pdata = match obj.architecture() {
516             Architecture::X86_64 => {
517                 let mut pdata = Vec::with_capacity(self.windows_pdata.len() * 3 * 4);
518                 for info in self.windows_pdata.iter() {
519                     pdata.extend_from_slice(&info.begin.to_le_bytes());
520                     pdata.extend_from_slice(&info.end.to_le_bytes());
521                     pdata.extend_from_slice(&xdata_rva(info.unwind_address).to_le_bytes());
522                 }
523                 pdata
524             }
525 
526             Architecture::Aarch64 => {
527                 // Windows Arm64 .pdata also supports packed unwind data, but
528                 // we're not currently using that.
529                 let mut pdata = Vec::with_capacity(self.windows_pdata.len() * 2 * 4);
530                 for info in self.windows_pdata.iter() {
531                     pdata.extend_from_slice(&info.begin.to_le_bytes());
532                     pdata.extend_from_slice(&xdata_rva(info.unwind_address).to_le_bytes());
533                 }
534                 pdata
535             }
536 
537             _ => unimplemented!("unsupported architecture for windows unwind info"),
538         };
539         obj.append_section_data(pdata_id, &pdata, 4);
540     }
541 
542     /// This function appends a nonstandard section to the object which is only
543     /// used during `CodeMemory::publish`.
544     ///
545     /// This will generate a `.eh_frame` section, but not one that can be
546     /// naively loaded. The goal of this section is that we can create the
547     /// section once here and never again does it need to change. To describe
548     /// dynamically loaded functions though each individual FDE needs to talk
549     /// about the function's absolute address that it's referencing. Naturally
550     /// we don't actually know the function's absolute address when we're
551     /// creating an object here.
552     ///
553     /// To solve this problem the FDE address encoding mode is set to
554     /// `DW_EH_PE_pcrel`. This means that the actual effective address that the
555     /// FDE describes is a relative to the address of the FDE itself. By
556     /// leveraging this relative-ness we can assume that the relative distance
557     /// between the FDE and the function it describes is constant, which should
558     /// allow us to generate an FDE ahead-of-time here.
559     ///
560     /// For now this assumes that all the code of functions will start at a
561     /// page-aligned address when loaded into memory. The eh_frame encoded here
562     /// then assumes that the text section is itself page aligned to its size
563     /// and the eh_frame will follow just after the text section. This means
564     /// that the relative offsets we're using here is the FDE going backwards
565     /// into the text section itself.
566     ///
567     /// Note that the library we're using to create the FDEs, `gimli`, doesn't
568     /// actually encode addresses relative to the FDE itself. Instead the
569     /// addresses are encoded relative to the start of the `.eh_frame` section.
570     /// This makes it much easier for us where we provide the relative offset
571     /// from the start of `.eh_frame` to the function in the text section, which
572     /// given our layout basically means the offset of the function in the text
573     /// section from the end of the text section.
574     ///
575     /// A final note is that the reason we page-align the text section's size is
576     /// so the .eh_frame lives on a separate page from the text section itself.
577     /// This allows `.eh_frame` to have different virtual memory permissions,
578     /// such as being purely read-only instead of read/execute like the code
579     /// bits.
580     fn write_systemv_unwind_info(
581         &self,
582         compiler: &dyn Compiler,
583         obj: &mut Object<'_>,
584         section_id: SectionId,
585         text_section_size: u64,
586     ) {
587         let mut cie = match compiler.create_systemv_cie() {
588             Some(cie) => cie,
589             None => return,
590         };
591         let mut table = FrameTable::default();
592         cie.fde_address_encoding = gimli::constants::DW_EH_PE_pcrel;
593         let cie_id = table.add_cie(cie);
594 
595         for (text_section_off, unwind_info) in self.systemv_unwind_info.iter() {
596             let backwards_off = text_section_size - text_section_off;
597             let actual_offset = -i64::try_from(backwards_off).unwrap();
598             // Note that gimli wants an unsigned 64-bit integer here, but
599             // unwinders just use this constant for a relative addition with the
600             // address of the FDE, which means that the sign doesn't actually
601             // matter.
602             let fde = unwind_info.to_fde(Address::Constant(actual_offset.unsigned()));
603             table.add_fde(cie_id, fde);
604         }
605         let endian = match compiler.triple().endianness().unwrap() {
606             target_lexicon::Endianness::Little => RunTimeEndian::Little,
607             target_lexicon::Endianness::Big => RunTimeEndian::Big,
608         };
609         let mut eh_frame = EhFrame(MyVec(EndianVec::new(endian)));
610         table.write_eh_frame(&mut eh_frame).unwrap();
611 
612         // Some unwinding implementations expect a terminating "empty" length so
613         // a 0 is written at the end of the table for those implementations.
614         let mut endian_vec = (eh_frame.0).0;
615         endian_vec.write_u32(0).unwrap();
616         obj.append_section_data(section_id, endian_vec.slice(), 1);
617 
618         use gimli::constants;
619         use gimli::write::Error;
620 
621         struct MyVec(EndianVec<RunTimeEndian>);
622 
623         impl Writer for MyVec {
624             type Endian = RunTimeEndian;
625 
626             fn endian(&self) -> RunTimeEndian {
627                 self.0.endian()
628             }
629 
630             fn len(&self) -> usize {
631                 self.0.len()
632             }
633 
634             fn write(&mut self, buf: &[u8]) -> Result<(), Error> {
635                 self.0.write(buf)
636             }
637 
638             fn write_at(&mut self, pos: usize, buf: &[u8]) -> Result<(), Error> {
639                 self.0.write_at(pos, buf)
640             }
641 
642             // FIXME(gimli-rs/gimli#576) this is the definition we want for
643             // `write_eh_pointer` but the default implementation, at the time
644             // of this writing, uses `offset - val` instead of `val - offset`.
645             // A PR has been merged to fix this but until that's published we
646             // can't use it.
647             fn write_eh_pointer(
648                 &mut self,
649                 address: Address,
650                 eh_pe: constants::DwEhPe,
651                 size: u8,
652             ) -> Result<(), Error> {
653                 let val = match address {
654                     Address::Constant(val) => val,
655                     Address::Symbol { .. } => unreachable!(),
656                 };
657                 assert_eq!(eh_pe.application(), constants::DW_EH_PE_pcrel);
658                 let offset = self.len() as u64;
659                 let val = val.wrapping_sub(offset);
660                 self.write_eh_pointer_data(val, eh_pe.format(), size)
661             }
662         }
663     }
664 }
665