Skip to main content

cranelift_object/
backend.rs

1//! Defines `ObjectModule`.
2
3use anyhow::anyhow;
4use cranelift_codegen::binemit::{Addend, CodeOffset, Reloc};
5use cranelift_codegen::entity::SecondaryMap;
6use cranelift_codegen::ir;
7use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa};
8use cranelift_control::ControlPlane;
9use cranelift_module::{
10    DataDescription, DataId, FuncId, Init, Linkage, Module, ModuleDeclarations, ModuleError,
11    ModuleReloc, ModuleRelocTarget, ModuleResult,
12};
13use log::{info, warn};
14use object::write::{
15    Object, Relocation, SectionId, StandardSection, Symbol, SymbolId, SymbolSection,
16};
17use object::{
18    BinaryFormat, RelocationEncoding, RelocationFlags, RelocationKind, SectionFlags, SectionKind,
19    SymbolFlags, SymbolKind, SymbolScope, elf,
20};
21use std::collections::HashMap;
22use std::collections::hash_map::Entry;
23use std::fmt::Write as _;
24use std::mem;
25use target_lexicon::{PointerWidth, Triple};
26
27/// A builder for `ObjectModule`.
28pub struct ObjectBuilder {
29    isa: OwnedTargetIsa,
30    binary_format: object::BinaryFormat,
31    architecture: object::Architecture,
32    flags: object::FileFlags,
33    endian: object::Endianness,
34    name: Vec<u8>,
35    libcall_names: Box<dyn Fn(ir::LibCall) -> String + Send + Sync>,
36    per_function_section: bool,
37    per_data_object_section: bool,
38    #[cfg(feature = "unwind")]
39    unwind_info: bool,
40}
41
42impl ObjectBuilder {
43    /// Create a new `ObjectBuilder` using the given Cranelift target, that
44    /// can be passed to [`ObjectModule::new`].
45    ///
46    /// The `libcall_names` function provides a way to translate `cranelift_codegen`'s [`ir::LibCall`]
47    /// enum to symbols. LibCalls are inserted in the IR as part of the legalization for certain
48    /// floating point instructions, and for stack probes. If you don't know what to use for this
49    /// argument, use [`cranelift_module::default_libcall_names`].
50    pub fn new<V: Into<Vec<u8>>>(
51        isa: OwnedTargetIsa,
52        name: V,
53        libcall_names: Box<dyn Fn(ir::LibCall) -> String + Send + Sync>,
54    ) -> ModuleResult<Self> {
55        let mut file_flags = object::FileFlags::None;
56        let binary_format = match isa.triple().binary_format {
57            target_lexicon::BinaryFormat::Elf => object::BinaryFormat::Elf,
58            target_lexicon::BinaryFormat::Coff => object::BinaryFormat::Coff,
59            target_lexicon::BinaryFormat::Macho => object::BinaryFormat::MachO,
60            target_lexicon::BinaryFormat::Wasm => {
61                return Err(ModuleError::Backend(anyhow!(
62                    "binary format wasm is unsupported",
63                )));
64            }
65            target_lexicon::BinaryFormat::Unknown => {
66                return Err(ModuleError::Backend(anyhow!("binary format is unknown")));
67            }
68            other => {
69                return Err(ModuleError::Backend(anyhow!(
70                    "binary format {other} not recognized"
71                )));
72            }
73        };
74        let architecture = match isa.triple().architecture {
75            target_lexicon::Architecture::X86_32(_) => object::Architecture::I386,
76            target_lexicon::Architecture::X86_64 => object::Architecture::X86_64,
77            target_lexicon::Architecture::Arm(_) => object::Architecture::Arm,
78            target_lexicon::Architecture::Aarch64(_) => object::Architecture::Aarch64,
79            target_lexicon::Architecture::Riscv64(_) => {
80                if binary_format != object::BinaryFormat::Elf {
81                    return Err(ModuleError::Backend(anyhow!(
82                        "binary format {binary_format:?} is not supported for riscv64",
83                    )));
84                }
85
86                // FIXME(#4994): Get the right float ABI variant from the TargetIsa
87                let mut eflags = object::elf::EF_RISCV_FLOAT_ABI_DOUBLE;
88
89                // Set the RVC eflag if we have the C extension enabled.
90                let has_c = isa
91                    .isa_flags()
92                    .iter()
93                    .filter(|f| f.name == "has_zca" || f.name == "has_zcd")
94                    .all(|f| f.as_bool().unwrap_or_default());
95                if has_c {
96                    eflags |= object::elf::EF_RISCV_RVC;
97                }
98
99                file_flags = object::FileFlags::Elf {
100                    os_abi: object::elf::ELFOSABI_NONE,
101                    abi_version: 0,
102                    e_flags: eflags,
103                };
104                object::Architecture::Riscv64
105            }
106            target_lexicon::Architecture::S390x => object::Architecture::S390x,
107            architecture => {
108                return Err(ModuleError::Backend(anyhow!(
109                    "target architecture {architecture:?} is unsupported",
110                )));
111            }
112        };
113        let endian = match isa.triple().endianness().unwrap() {
114            target_lexicon::Endianness::Little => object::Endianness::Little,
115            target_lexicon::Endianness::Big => object::Endianness::Big,
116        };
117        Ok(Self {
118            isa,
119            binary_format,
120            architecture,
121            flags: file_flags,
122            endian,
123            name: name.into(),
124            libcall_names,
125            per_function_section: false,
126            per_data_object_section: false,
127            #[cfg(feature = "unwind")]
128            unwind_info: false,
129        })
130    }
131
132    /// Set if every function should end up in their own section.
133    pub fn per_function_section(&mut self, per_function_section: bool) -> &mut Self {
134        self.per_function_section = per_function_section;
135        self
136    }
137
138    /// Set if every data object should end up in their own section.
139    pub fn per_data_object_section(&mut self, per_data_object_section: bool) -> &mut Self {
140        self.per_data_object_section = per_data_object_section;
141        self
142    }
143
144    /// Emit a DWARF `.eh_frame` section describing the unwind information for
145    /// each compiled function.
146    ///
147    /// When enabled, ELF and COFF object files gain a `.eh_frame` section
148    /// containing one Common Information Entry and one Frame Description
149    /// Entry per function, suitable for unwinding by libgcc / libunwind.
150    ///
151    /// On Windows targets cranelift emits `.pdata`/`.xdata`-style info rather
152    /// than System V FDEs, so enabling this option is a silent no-op there.
153    /// Mach-O `__TEXT,__eh_frame` emission is not yet implemented; calling
154    /// `finish` on a Mach-O target with this enabled will panic with a
155    /// descriptive error.
156    ///
157    /// Only functions defined through [`Module::define_function`] are
158    /// captured. Functions provided as pre-compiled bytes through
159    /// [`Module::define_function_bytes`] are skipped, since their unwind
160    /// information is not available to the backend.
161    ///
162    /// Requires the `unwind` feature (enabled by default). Without it this
163    /// method does not exist, mirroring `cranelift-codegen`'s gating of
164    /// `CompiledCode::create_unwind_info`.
165    ///
166    /// [`Module::define_function`]: cranelift_module::Module::define_function
167    /// [`Module::define_function_bytes`]: cranelift_module::Module::define_function_bytes
168    #[cfg(feature = "unwind")]
169    pub fn unwind_info(&mut self, unwind_info: bool) -> &mut Self {
170        self.unwind_info = unwind_info;
171        self
172    }
173}
174
175/// See the following for details:
176/// <https://github.com/rust-lang/rust/blob/1.95.0/compiler/rustc_codegen_ssa/src/back/metadata.rs#L408-L425>
177fn macho_build_version(triple: &Triple) -> Option<object::write::MachOBuildVersion> {
178    use target_lexicon::{DeploymentTarget, OperatingSystem::*};
179
180    fn pack_version(v: DeploymentTarget) -> u32 {
181        let (major, minor, patch) = (v.major as u32, v.minor as u32, v.patch as u32);
182        (major << 16) | (minor << 8) | patch
183    }
184
185    match triple.operating_system {
186        Darwin(v) | MacOSX(v) | IOS(v) | TvOS(v) | VisionOS(v) | WatchOS(v) | XROS(v) => {
187            use object::macho::*;
188            use target_lexicon::Environment::*;
189            // Same as https://github.com/rust-lang/rust/blob/1.95.0/compiler/rustc_codegen_ssa/src/back/apple.rs#L36-L50.
190            //
191            // TODO(madsmtm): Properly support simulator after
192            // https://github.com/bytecodealliance/target-lexicon/pull/130
193            let platform = match (triple.operating_system, triple.environment) {
194                // Sometimes the target is macOS but the environment is Darwin,
195                // and sometimes it's the other way around. Support both.
196                (Darwin(_), _) => PLATFORM_MACOS,
197                (MacOSX(_), _) => PLATFORM_MACOS,
198                (_, Macabi) => PLATFORM_MACCATALYST,
199                (IOS(_), Sim) => PLATFORM_IOSSIMULATOR,
200                (IOS(_), _) => PLATFORM_IOS,
201                (TvOS(_), Sim) => PLATFORM_TVOSSIMULATOR,
202                (TvOS(_), _) => PLATFORM_TVOS,
203                (VisionOS(_) | XROS(_), Sim) => PLATFORM_XROSSIMULATOR,
204                (VisionOS(_) | XROS(_), _) => PLATFORM_XROS,
205                (WatchOS(_), Sim) => PLATFORM_WATCHOSSIMULATOR,
206                (WatchOS(_), _) => PLATFORM_WATCHOS,
207                _ => {
208                    warn!("unsupported OS/environment: {triple}");
209                    0
210                }
211            };
212
213            let mut build_version = object::write::MachOBuildVersion::default();
214            build_version.platform = platform;
215
216            build_version.minos = if let Some(v) = v {
217                pack_version(v)
218            } else {
219                // The `minos` in object files is useful for diagnostics, as
220                // it tells the linker whether the file supports a given OS -
221                // if the `minos` is higher than what you're linking against,
222                // that's a signal that something has gone wrong.
223                //
224                // Using `0.0.0` here should be fine if we don't have the data
225                // available.
226                0
227            };
228
229            // Setting a 0 SDK version is fine, it's only relevant for the
230            // final linked binary.
231            build_version.sdk = 0;
232
233            Some(build_version)
234        }
235        _ => None,
236    }
237}
238
239/// An `ObjectModule` implements `Module` and emits ".o" files using the `object` library.
240///
241/// See the `ObjectBuilder` for a convenient way to construct `ObjectModule` instances.
242pub struct ObjectModule {
243    isa: OwnedTargetIsa,
244    object: Object<'static>,
245    declarations: ModuleDeclarations,
246    functions: SecondaryMap<FuncId, Option<(SymbolId, bool)>>,
247    data_objects: SecondaryMap<DataId, Option<(SymbolId, bool)>>,
248    relocs: Vec<SymbolRelocs>,
249    libcalls: HashMap<ir::LibCall, SymbolId>,
250    libcall_names: Box<dyn Fn(ir::LibCall) -> String + Send + Sync>,
251    known_symbols: HashMap<ir::KnownSymbol, SymbolId>,
252    known_labels: HashMap<(FuncId, CodeOffset), SymbolId>,
253    per_function_section: bool,
254    per_data_object_section: bool,
255    #[cfg(feature = "unwind")]
256    unwind: Option<crate::unwind::UnwindBuilder>,
257}
258
259impl ObjectModule {
260    /// Create a new `ObjectModule` using the given Cranelift target.
261    pub fn new(builder: ObjectBuilder) -> Self {
262        let mut object = Object::new(builder.binary_format, builder.architecture, builder.endian);
263        object.flags = builder.flags;
264        object.set_subsections_via_symbols();
265        object.add_file_symbol(builder.name);
266        if let Some(info) = macho_build_version(builder.isa.triple()) {
267            // Set LC_BUILD_VERSION.
268            //
269            // Required when linking Apple targets to avoid warning, see:
270            // https://github.com/bytecodealliance/wasmtime/issues/8730
271            object.set_macho_build_version(info);
272        }
273        #[cfg(feature = "unwind")]
274        let unwind = builder
275            .unwind_info
276            .then(|| crate::unwind::UnwindBuilder::new(builder.endian));
277        Self {
278            isa: builder.isa,
279            object,
280            declarations: ModuleDeclarations::default(),
281            functions: SecondaryMap::new(),
282            data_objects: SecondaryMap::new(),
283            relocs: Vec::new(),
284            libcalls: HashMap::new(),
285            libcall_names: builder.libcall_names,
286            known_symbols: HashMap::new(),
287            known_labels: HashMap::new(),
288            per_function_section: builder.per_function_section,
289            per_data_object_section: builder.per_data_object_section,
290            #[cfg(feature = "unwind")]
291            unwind,
292        }
293    }
294}
295
296fn validate_symbol(name: &str) -> ModuleResult<()> {
297    // null bytes are not allowed in symbol names and will cause the `object`
298    // crate to panic. Let's return a clean error instead.
299    if name.contains("\0") {
300        return Err(ModuleError::Backend(anyhow::anyhow!(
301            "Symbol {name:?} has a null byte, which is disallowed"
302        )));
303    }
304    Ok(())
305}
306
307impl Module for ObjectModule {
308    fn isa(&self) -> &dyn TargetIsa {
309        &*self.isa
310    }
311
312    fn declarations(&self) -> &ModuleDeclarations {
313        &self.declarations
314    }
315
316    fn declare_function(
317        &mut self,
318        name: &str,
319        linkage: Linkage,
320        signature: &ir::Signature,
321    ) -> ModuleResult<FuncId> {
322        validate_symbol(name)?;
323
324        let (id, linkage) = self
325            .declarations
326            .declare_function(name, linkage, signature)?;
327
328        let (scope, weak) = translate_linkage(linkage);
329
330        if let Some((function, _defined)) = self.functions[id] {
331            let symbol = self.object.symbol_mut(function);
332            symbol.scope = scope;
333            symbol.weak = weak;
334        } else {
335            let symbol_id = self.object.add_symbol(Symbol {
336                name: name.as_bytes().to_vec(),
337                value: 0,
338                size: 0,
339                kind: SymbolKind::Text,
340                scope,
341                weak,
342                section: SymbolSection::Undefined,
343                flags: SymbolFlags::None,
344            });
345            self.functions[id] = Some((symbol_id, false));
346        }
347
348        Ok(id)
349    }
350
351    fn declare_anonymous_function(&mut self, signature: &ir::Signature) -> ModuleResult<FuncId> {
352        let id = self.declarations.declare_anonymous_function(signature)?;
353
354        let symbol_id = self.object.add_symbol(Symbol {
355            name: self
356                .declarations
357                .get_function_decl(id)
358                .linkage_name(id)
359                .into_owned()
360                .into_bytes(),
361            value: 0,
362            size: 0,
363            kind: SymbolKind::Text,
364            scope: SymbolScope::Compilation,
365            weak: false,
366            section: SymbolSection::Undefined,
367            flags: SymbolFlags::None,
368        });
369        self.functions[id] = Some((symbol_id, false));
370
371        Ok(id)
372    }
373
374    fn declare_data(
375        &mut self,
376        name: &str,
377        linkage: Linkage,
378        writable: bool,
379        tls: bool,
380    ) -> ModuleResult<DataId> {
381        validate_symbol(name)?;
382
383        let (id, linkage) = self
384            .declarations
385            .declare_data(name, linkage, writable, tls)?;
386
387        // Merging declarations with conflicting values for tls is not allowed, so it is safe to use
388        // the passed in tls value here.
389        let kind = if tls {
390            SymbolKind::Tls
391        } else {
392            SymbolKind::Data
393        };
394        let (scope, weak) = translate_linkage(linkage);
395
396        if let Some((data, _defined)) = self.data_objects[id] {
397            let symbol = self.object.symbol_mut(data);
398            symbol.kind = kind;
399            symbol.scope = scope;
400            symbol.weak = weak;
401        } else {
402            let symbol_id = self.object.add_symbol(Symbol {
403                name: name.as_bytes().to_vec(),
404                value: 0,
405                size: 0,
406                kind,
407                scope,
408                weak,
409                section: SymbolSection::Undefined,
410                flags: SymbolFlags::None,
411            });
412            self.data_objects[id] = Some((symbol_id, false));
413        }
414
415        Ok(id)
416    }
417
418    fn declare_anonymous_data(&mut self, writable: bool, tls: bool) -> ModuleResult<DataId> {
419        let id = self.declarations.declare_anonymous_data(writable, tls)?;
420
421        let kind = if tls {
422            SymbolKind::Tls
423        } else {
424            SymbolKind::Data
425        };
426
427        let symbol_id = self.object.add_symbol(Symbol {
428            name: self
429                .declarations
430                .get_data_decl(id)
431                .linkage_name(id)
432                .into_owned()
433                .into_bytes(),
434            value: 0,
435            size: 0,
436            kind,
437            scope: SymbolScope::Compilation,
438            weak: false,
439            section: SymbolSection::Undefined,
440            flags: SymbolFlags::None,
441        });
442        self.data_objects[id] = Some((symbol_id, false));
443
444        Ok(id)
445    }
446
447    fn define_function_with_control_plane(
448        &mut self,
449        func_id: FuncId,
450        ctx: &mut cranelift_codegen::Context,
451        ctrl_plane: &mut ControlPlane,
452    ) -> ModuleResult<()> {
453        info!("defining function {}: {}", func_id, ctx.func.display());
454
455        let res = ctx.compile(self.isa(), ctrl_plane)?;
456        let alignment = res.buffer.alignment as u64;
457
458        let compiled = ctx.compiled_code().unwrap();
459        #[cfg(feature = "unwind")]
460        let unwind_info = if self.unwind.is_some() {
461            compiled.create_unwind_info(self.isa())?
462        } else {
463            None
464        };
465        let buffer = &compiled.buffer;
466        let relocs = buffer
467            .relocs()
468            .iter()
469            .map(|reloc| {
470                self.process_reloc(&ModuleReloc::from_mach_reloc(&reloc, &ctx.func, func_id))
471            })
472            .collect::<Vec<_>>();
473        self.define_function_inner(func_id, alignment, buffer.data(), relocs)?;
474        #[cfg(feature = "unwind")]
475        if let (Some(builder), Some(info)) = (self.unwind.as_mut(), unwind_info) {
476            let symbol = self.functions[func_id].unwrap().0;
477            builder.add_function(&*self.isa, symbol, info);
478        }
479        Ok(())
480    }
481
482    fn define_function_bytes(
483        &mut self,
484        func_id: FuncId,
485        alignment: u64,
486        bytes: &[u8],
487        relocs: &[ModuleReloc],
488    ) -> ModuleResult<()> {
489        let relocs = relocs
490            .iter()
491            .map(|reloc| self.process_reloc(reloc))
492            .collect();
493        self.define_function_inner(func_id, alignment, bytes, relocs)
494    }
495
496    fn define_data(&mut self, data_id: DataId, data: &DataDescription) -> ModuleResult<()> {
497        let decl = self.declarations.get_data_decl(data_id);
498        if !decl.linkage.is_definable() {
499            return Err(ModuleError::InvalidImportDefinition(
500                decl.linkage_name(data_id).into_owned(),
501            ));
502        }
503
504        let &mut (symbol, ref mut defined) = self.data_objects[data_id].as_mut().unwrap();
505        if *defined {
506            return Err(ModuleError::DuplicateDefinition(
507                decl.linkage_name(data_id).into_owned(),
508            ));
509        }
510        *defined = true;
511
512        let &DataDescription {
513            ref init,
514            function_decls: _,
515            data_decls: _,
516            function_relocs: _,
517            data_relocs: _,
518            ref custom_section,
519            align,
520            used,
521        } = data;
522
523        let pointer_reloc = match self.isa.triple().pointer_width().unwrap() {
524            PointerWidth::U16 => unimplemented!("16bit pointers"),
525            PointerWidth::U32 => Reloc::Abs4,
526            PointerWidth::U64 => Reloc::Abs8,
527        };
528        let relocs = data
529            .all_relocs(pointer_reloc)
530            .map(|record| self.process_reloc(&record))
531            .collect::<Vec<_>>();
532
533        let section = if custom_section.is_none() {
534            let section_kind = if let Init::Zeros { .. } = *init {
535                if decl.tls {
536                    StandardSection::UninitializedTls
537                } else {
538                    StandardSection::UninitializedData
539                }
540            } else if decl.tls {
541                StandardSection::Tls
542            } else if decl.writable {
543                StandardSection::Data
544            } else if relocs.is_empty() {
545                StandardSection::ReadOnlyData
546            } else {
547                StandardSection::ReadOnlyDataWithRel
548            };
549            if self.per_data_object_section || used {
550                // FIXME pass empty symbol name once add_subsection produces `.text` as section name
551                // instead of `.text.` when passed an empty symbol name. (object#748) Until then
552                // pass `subsection` to produce `.text.subsection` as section name to reduce
553                // confusion.
554                self.object.add_subsection(section_kind, b"subsection")
555            } else {
556                self.object.section_id(section_kind)
557            }
558        } else {
559            if decl.tls {
560                return Err(cranelift_module::ModuleError::Backend(anyhow::anyhow!(
561                    "Custom section not supported for TLS"
562                )));
563            }
564            let (segment, section, macho_flags) =
565                parse_section(custom_section.as_ref().unwrap(), self.object.format())
566                    .map_err(ModuleError::Backend)?;
567            let section = self.object.add_section(
568                segment.to_string().into_bytes(),
569                section.to_string().into_bytes(),
570                if decl.writable {
571                    SectionKind::Data
572                } else if relocs.is_empty() {
573                    SectionKind::ReadOnlyData
574                } else {
575                    SectionKind::ReadOnlyDataWithRel
576                },
577            );
578
579            match self.object.section_flags_mut(section) {
580                SectionFlags::MachO { flags } => {
581                    // There are no default flags for the `SectionKind`s that
582                    // we've specified above, so it's fine to override.
583                    //
584                    // (If we don't want to override, we'll have to be careful
585                    // with how we set these, to ensure we set the section
586                    // type properly).
587                    assert_eq!(*flags, 0);
588                    *flags = macho_flags;
589                }
590                _ => {
591                    if macho_flags != 0 {
592                        unreachable!("unsupported Mach-O flags for this platform: {macho_flags:?}");
593                    }
594                }
595            }
596
597            section
598        };
599
600        if used {
601            match self.object.format() {
602                object::BinaryFormat::Elf => match self.object.section_flags_mut(section) {
603                    SectionFlags::Elf { sh_flags } => *sh_flags |= u64::from(elf::SHF_GNU_RETAIN),
604                    _ => unreachable!(),
605                },
606                object::BinaryFormat::Coff => {}
607                object::BinaryFormat::MachO => match self.object.symbol_flags_mut(symbol) {
608                    SymbolFlags::MachO { n_desc } => *n_desc |= object::macho::N_NO_DEAD_STRIP,
609                    _ => unreachable!(),
610                },
611                _ => unreachable!(),
612            }
613        }
614
615        let align = std::cmp::max(align.unwrap_or(1), self.isa.symbol_alignment());
616        let offset = match *init {
617            Init::Uninitialized => {
618                panic!("data is not initialized yet");
619            }
620            Init::Zeros { size } => self
621                .object
622                .add_symbol_bss(symbol, section, size as u64, align),
623            Init::Bytes { ref contents } => self
624                .object
625                .add_symbol_data(symbol, section, &contents, align),
626        };
627        if !relocs.is_empty() {
628            self.relocs.push(SymbolRelocs {
629                section,
630                offset,
631                relocs,
632            });
633        }
634        Ok(())
635    }
636}
637
638impl ObjectModule {
639    fn define_function_inner(
640        &mut self,
641        func_id: FuncId,
642        alignment: u64,
643        bytes: &[u8],
644        relocs: Vec<ObjectRelocRecord>,
645    ) -> Result<(), ModuleError> {
646        info!("defining function {func_id} with bytes");
647        let decl = self.declarations.get_function_decl(func_id);
648        let decl_name = decl.linkage_name(func_id);
649        if !decl.linkage.is_definable() {
650            return Err(ModuleError::InvalidImportDefinition(decl_name.into_owned()));
651        }
652
653        let &mut (symbol, ref mut defined) = self.functions[func_id].as_mut().unwrap();
654        if *defined {
655            return Err(ModuleError::DuplicateDefinition(decl_name.into_owned()));
656        }
657        *defined = true;
658
659        let align = alignment.max(self.isa.symbol_alignment());
660        let section = if self.per_function_section {
661            // FIXME pass empty symbol name once add_subsection produces `.text` as section name
662            // instead of `.text.` when passed an empty symbol name. (object#748) Until then pass
663            // `subsection` to produce `.text.subsection` as section name to reduce confusion.
664            self.object
665                .add_subsection(StandardSection::Text, b"subsection")
666        } else {
667            self.object.section_id(StandardSection::Text)
668        };
669        let offset = self.object.add_symbol_data(symbol, section, bytes, align);
670
671        if !relocs.is_empty() {
672            self.relocs.push(SymbolRelocs {
673                section,
674                offset,
675                relocs,
676            });
677        }
678
679        Ok(())
680    }
681
682    /// Finalize all relocations and output an object.
683    pub fn finish(mut self) -> ObjectProduct {
684        if cfg!(debug_assertions) {
685            for (func_id, decl) in self.declarations.get_functions() {
686                if !decl.linkage.requires_definition() {
687                    continue;
688                }
689
690                assert!(
691                    self.functions[func_id].unwrap().1,
692                    "function \"{}\" with linkage {:?} must be defined but is not",
693                    decl.linkage_name(func_id),
694                    decl.linkage,
695                );
696            }
697
698            for (data_id, decl) in self.declarations.get_data_objects() {
699                if !decl.linkage.requires_definition() {
700                    continue;
701                }
702
703                assert!(
704                    self.data_objects[data_id].unwrap().1,
705                    "data object \"{}\" with linkage {:?} must be defined but is not",
706                    decl.linkage_name(data_id),
707                    decl.linkage,
708                );
709            }
710        }
711
712        let symbol_relocs = mem::take(&mut self.relocs);
713        for symbol in symbol_relocs {
714            for &ObjectRelocRecord {
715                offset,
716                ref name,
717                flags,
718                addend,
719            } in &symbol.relocs
720            {
721                let target_symbol = self.get_symbol(name);
722                self.object
723                    .add_relocation(
724                        symbol.section,
725                        Relocation {
726                            offset: symbol.offset + u64::from(offset),
727                            flags,
728                            symbol: target_symbol,
729                            addend,
730                        },
731                    )
732                    .unwrap();
733            }
734        }
735
736        // Indicate that this object has a non-executable stack.
737        if self.object.format() == object::BinaryFormat::Elf {
738            self.object.add_section(
739                vec![],
740                ".note.GNU-stack".as_bytes().to_vec(),
741                SectionKind::Linker,
742            );
743        }
744
745        #[cfg(feature = "unwind")]
746        if let Some(unwind) = self.unwind.take() {
747            unwind
748                .finish(&mut self.object, &*self.isa)
749                .expect("failed to emit .eh_frame section");
750        }
751
752        ObjectProduct {
753            object: self.object,
754            functions: self.functions,
755            data_objects: self.data_objects,
756        }
757    }
758
759    /// This should only be called during finish because it creates
760    /// symbols for missing libcalls.
761    fn get_symbol(&mut self, name: &ModuleRelocTarget) -> SymbolId {
762        match *name {
763            ModuleRelocTarget::User { .. } => {
764                if ModuleDeclarations::is_function(name) {
765                    let id = FuncId::from_name(name);
766                    self.functions[id].unwrap().0
767                } else {
768                    let id = DataId::from_name(name);
769                    self.data_objects[id].unwrap().0
770                }
771            }
772            ModuleRelocTarget::LibCall(ref libcall) => {
773                let name = (self.libcall_names)(*libcall);
774                if let Some(symbol) = self.object.symbol_id(name.as_bytes()) {
775                    symbol
776                } else if let Some(symbol) = self.libcalls.get(libcall) {
777                    *symbol
778                } else {
779                    let symbol = self.object.add_symbol(Symbol {
780                        name: name.as_bytes().to_vec(),
781                        value: 0,
782                        size: 0,
783                        kind: SymbolKind::Text,
784                        scope: SymbolScope::Unknown,
785                        weak: false,
786                        section: SymbolSection::Undefined,
787                        flags: SymbolFlags::None,
788                    });
789                    self.libcalls.insert(*libcall, symbol);
790                    symbol
791                }
792            }
793            // These are "magic" names well-known to the linker.
794            // They require special treatment.
795            ModuleRelocTarget::KnownSymbol(ref known_symbol) => {
796                if let Some(symbol) = self.known_symbols.get(known_symbol) {
797                    *symbol
798                } else {
799                    let symbol = self.object.add_symbol(match known_symbol {
800                        ir::KnownSymbol::ElfGlobalOffsetTable => Symbol {
801                            name: b"_GLOBAL_OFFSET_TABLE_".to_vec(),
802                            value: 0,
803                            size: 0,
804                            kind: SymbolKind::Data,
805                            scope: SymbolScope::Unknown,
806                            weak: false,
807                            section: SymbolSection::Undefined,
808                            flags: SymbolFlags::None,
809                        },
810                        ir::KnownSymbol::CoffTlsIndex => Symbol {
811                            name: b"_tls_index".to_vec(),
812                            value: 0,
813                            size: 32,
814                            kind: SymbolKind::Tls,
815                            scope: SymbolScope::Unknown,
816                            weak: false,
817                            section: SymbolSection::Undefined,
818                            flags: SymbolFlags::None,
819                        },
820                    });
821                    self.known_symbols.insert(*known_symbol, symbol);
822                    symbol
823                }
824            }
825
826            ModuleRelocTarget::FunctionOffset(func_id, offset) => {
827                match self.known_labels.entry((func_id, offset)) {
828                    Entry::Occupied(o) => *o.get(),
829                    Entry::Vacant(v) => {
830                        let func_symbol_id = self.functions[func_id].unwrap().0;
831                        let func_symbol = self.object.symbol(func_symbol_id);
832
833                        let name = format!(".L{}_{}", func_id.as_u32(), offset);
834                        let symbol_id = self.object.add_symbol(Symbol {
835                            name: name.as_bytes().to_vec(),
836                            value: func_symbol.value + offset as u64,
837                            size: 0,
838                            kind: SymbolKind::Label,
839                            scope: SymbolScope::Compilation,
840                            weak: false,
841                            section: SymbolSection::Section(func_symbol.section.id().unwrap()),
842                            flags: SymbolFlags::None,
843                        });
844
845                        v.insert(symbol_id);
846                        symbol_id
847                    }
848                }
849            }
850        }
851    }
852
853    fn process_reloc(&self, record: &ModuleReloc) -> ObjectRelocRecord {
854        let flags = match record.kind {
855            Reloc::Abs4 => RelocationFlags::Generic {
856                kind: RelocationKind::Absolute,
857                encoding: RelocationEncoding::Generic,
858                size: 32,
859            },
860            Reloc::Abs8 => RelocationFlags::Generic {
861                kind: RelocationKind::Absolute,
862                encoding: RelocationEncoding::Generic,
863                size: 64,
864            },
865            Reloc::X86PCRel4 => RelocationFlags::Generic {
866                kind: RelocationKind::Relative,
867                encoding: RelocationEncoding::Generic,
868                size: 32,
869            },
870            Reloc::X86CallPCRel4 => RelocationFlags::Generic {
871                kind: RelocationKind::Relative,
872                encoding: RelocationEncoding::X86Branch,
873                size: 32,
874            },
875            // TODO: Get Cranelift to tell us when we can use
876            // R_X86_64_GOTPCRELX/R_X86_64_REX_GOTPCRELX.
877            Reloc::X86CallPLTRel4 => RelocationFlags::Generic {
878                kind: RelocationKind::PltRelative,
879                encoding: RelocationEncoding::X86Branch,
880                size: 32,
881            },
882            Reloc::X86SecRel => RelocationFlags::Generic {
883                kind: RelocationKind::SectionOffset,
884                encoding: RelocationEncoding::Generic,
885                size: 32,
886            },
887            Reloc::X86GOTPCRel4 => RelocationFlags::Generic {
888                kind: RelocationKind::GotRelative,
889                encoding: RelocationEncoding::Generic,
890                size: 32,
891            },
892            Reloc::Arm64Call => RelocationFlags::Generic {
893                kind: RelocationKind::Relative,
894                encoding: RelocationEncoding::AArch64Call,
895                size: 26,
896            },
897            Reloc::ElfX86_64TlsGd => {
898                assert_eq!(
899                    self.object.format(),
900                    object::BinaryFormat::Elf,
901                    "ElfX86_64TlsGd is not supported for this file format"
902                );
903                RelocationFlags::Elf {
904                    r_type: object::elf::R_X86_64_TLSGD,
905                }
906            }
907            Reloc::MachOX86_64Tlv => {
908                assert_eq!(
909                    self.object.format(),
910                    object::BinaryFormat::MachO,
911                    "MachOX86_64Tlv is not supported for this file format"
912                );
913                RelocationFlags::MachO {
914                    r_type: object::macho::X86_64_RELOC_TLV,
915                    r_pcrel: true,
916                    r_length: 2,
917                }
918            }
919            Reloc::MachOAarch64TlsAdrPage21 => {
920                assert_eq!(
921                    self.object.format(),
922                    object::BinaryFormat::MachO,
923                    "MachOAarch64TlsAdrPage21 is not supported for this file format"
924                );
925                RelocationFlags::MachO {
926                    r_type: object::macho::ARM64_RELOC_TLVP_LOAD_PAGE21,
927                    r_pcrel: true,
928                    r_length: 2,
929                }
930            }
931            Reloc::MachOAarch64TlsAdrPageOff12 => {
932                assert_eq!(
933                    self.object.format(),
934                    object::BinaryFormat::MachO,
935                    "MachOAarch64TlsAdrPageOff12 is not supported for this file format"
936                );
937                RelocationFlags::MachO {
938                    r_type: object::macho::ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
939                    r_pcrel: false,
940                    r_length: 2,
941                }
942            }
943            Reloc::Aarch64TlsDescAdrPage21 => {
944                assert_eq!(
945                    self.object.format(),
946                    object::BinaryFormat::Elf,
947                    "Aarch64TlsDescAdrPage21 is not supported for this file format"
948                );
949                RelocationFlags::Elf {
950                    r_type: object::elf::R_AARCH64_TLSDESC_ADR_PAGE21,
951                }
952            }
953            Reloc::Aarch64TlsDescLd64Lo12 => {
954                assert_eq!(
955                    self.object.format(),
956                    object::BinaryFormat::Elf,
957                    "Aarch64TlsDescLd64Lo12 is not supported for this file format"
958                );
959                RelocationFlags::Elf {
960                    r_type: object::elf::R_AARCH64_TLSDESC_LD64_LO12,
961                }
962            }
963            Reloc::Aarch64TlsDescAddLo12 => {
964                assert_eq!(
965                    self.object.format(),
966                    object::BinaryFormat::Elf,
967                    "Aarch64TlsDescAddLo12 is not supported for this file format"
968                );
969                RelocationFlags::Elf {
970                    r_type: object::elf::R_AARCH64_TLSDESC_ADD_LO12,
971                }
972            }
973            Reloc::Aarch64TlsDescCall => {
974                assert_eq!(
975                    self.object.format(),
976                    object::BinaryFormat::Elf,
977                    "Aarch64TlsDescCall is not supported for this file format"
978                );
979                RelocationFlags::Elf {
980                    r_type: object::elf::R_AARCH64_TLSDESC_CALL,
981                }
982            }
983
984            Reloc::Aarch64AdrGotPage21 => match self.object.format() {
985                object::BinaryFormat::Elf => RelocationFlags::Elf {
986                    r_type: object::elf::R_AARCH64_ADR_GOT_PAGE,
987                },
988                object::BinaryFormat::MachO => RelocationFlags::MachO {
989                    r_type: object::macho::ARM64_RELOC_GOT_LOAD_PAGE21,
990                    r_pcrel: true,
991                    r_length: 2,
992                },
993                _ => unimplemented!("Aarch64AdrGotPage21 is not supported for this file format"),
994            },
995            Reloc::Aarch64Ld64GotLo12Nc => match self.object.format() {
996                object::BinaryFormat::Elf => RelocationFlags::Elf {
997                    r_type: object::elf::R_AARCH64_LD64_GOT_LO12_NC,
998                },
999                object::BinaryFormat::MachO => RelocationFlags::MachO {
1000                    r_type: object::macho::ARM64_RELOC_GOT_LOAD_PAGEOFF12,
1001                    r_pcrel: false,
1002                    r_length: 2,
1003                },
1004                _ => unimplemented!("Aarch64Ld64GotLo12Nc is not supported for this file format"),
1005            },
1006            Reloc::Aarch64AdrPrelPgHi21 => match self.object.format() {
1007                object::BinaryFormat::Elf => RelocationFlags::Elf {
1008                    r_type: object::elf::R_AARCH64_ADR_PREL_PG_HI21,
1009                },
1010                object::BinaryFormat::MachO => RelocationFlags::MachO {
1011                    r_type: object::macho::ARM64_RELOC_PAGE21,
1012                    r_pcrel: true,
1013                    r_length: 2,
1014                },
1015                _ => unimplemented!("Aarch64AdrPrelPgHi21 is not supported for this file format"),
1016            },
1017            Reloc::Aarch64AddAbsLo12Nc => match self.object.format() {
1018                object::BinaryFormat::Elf => RelocationFlags::Elf {
1019                    r_type: object::elf::R_AARCH64_ADD_ABS_LO12_NC,
1020                },
1021                object::BinaryFormat::MachO => RelocationFlags::MachO {
1022                    r_type: object::macho::ARM64_RELOC_PAGEOFF12,
1023                    r_pcrel: false,
1024                    r_length: 2,
1025                },
1026                _ => unimplemented!("Aarch64AddAbsLo12Nc is not supported for this file format"),
1027            },
1028            Reloc::S390xPCRel32Dbl => RelocationFlags::Generic {
1029                kind: RelocationKind::Relative,
1030                encoding: RelocationEncoding::S390xDbl,
1031                size: 32,
1032            },
1033            Reloc::S390xPLTRel32Dbl => RelocationFlags::Generic {
1034                kind: RelocationKind::PltRelative,
1035                encoding: RelocationEncoding::S390xDbl,
1036                size: 32,
1037            },
1038            Reloc::S390xTlsGd64 => {
1039                assert_eq!(
1040                    self.object.format(),
1041                    object::BinaryFormat::Elf,
1042                    "S390xTlsGd64 is not supported for this file format"
1043                );
1044                RelocationFlags::Elf {
1045                    r_type: object::elf::R_390_TLS_GD64,
1046                }
1047            }
1048            Reloc::S390xTlsGdCall => {
1049                assert_eq!(
1050                    self.object.format(),
1051                    object::BinaryFormat::Elf,
1052                    "S390xTlsGdCall is not supported for this file format"
1053                );
1054                RelocationFlags::Elf {
1055                    r_type: object::elf::R_390_TLS_GDCALL,
1056                }
1057            }
1058            Reloc::RiscvCallPlt => {
1059                assert_eq!(
1060                    self.object.format(),
1061                    object::BinaryFormat::Elf,
1062                    "RiscvCallPlt is not supported for this file format"
1063                );
1064                RelocationFlags::Elf {
1065                    r_type: object::elf::R_RISCV_CALL_PLT,
1066                }
1067            }
1068            Reloc::RiscvTlsGdHi20 => {
1069                assert_eq!(
1070                    self.object.format(),
1071                    object::BinaryFormat::Elf,
1072                    "RiscvTlsGdHi20 is not supported for this file format"
1073                );
1074                RelocationFlags::Elf {
1075                    r_type: object::elf::R_RISCV_TLS_GD_HI20,
1076                }
1077            }
1078            Reloc::RiscvPCRelLo12I => {
1079                assert_eq!(
1080                    self.object.format(),
1081                    object::BinaryFormat::Elf,
1082                    "RiscvPCRelLo12I is not supported for this file format"
1083                );
1084                RelocationFlags::Elf {
1085                    r_type: object::elf::R_RISCV_PCREL_LO12_I,
1086                }
1087            }
1088            Reloc::RiscvGotHi20 => {
1089                assert_eq!(
1090                    self.object.format(),
1091                    object::BinaryFormat::Elf,
1092                    "RiscvGotHi20 is not supported for this file format"
1093                );
1094                RelocationFlags::Elf {
1095                    r_type: object::elf::R_RISCV_GOT_HI20,
1096                }
1097            }
1098            Reloc::RiscvPCRelHi20 => {
1099                assert_eq!(
1100                    self.object.format(),
1101                    object::BinaryFormat::Elf,
1102                    "RiscvPCRelHi20 is not supported for this file format"
1103                );
1104                RelocationFlags::Elf {
1105                    r_type: object::elf::R_RISCV_PCREL_HI20,
1106                }
1107            }
1108            // FIXME
1109            reloc => unimplemented!("{:?}", reloc),
1110        };
1111
1112        ObjectRelocRecord {
1113            offset: record.offset,
1114            name: record.name.clone(),
1115            flags,
1116            addend: record.addend,
1117        }
1118    }
1119}
1120
1121fn translate_linkage(linkage: Linkage) -> (SymbolScope, bool) {
1122    let scope = match linkage {
1123        Linkage::Import => SymbolScope::Unknown,
1124        Linkage::Local => SymbolScope::Compilation,
1125        Linkage::Hidden => SymbolScope::Linkage,
1126        Linkage::Export | Linkage::Preemptible => SymbolScope::Dynamic,
1127    };
1128    // TODO: this matches rustc_codegen_cranelift, but may be wrong.
1129    let weak = linkage == Linkage::Preemptible;
1130    (scope, weak)
1131}
1132
1133/// This is the output of `ObjectModule`'s
1134/// [`finish`](../struct.ObjectModule.html#method.finish) function.
1135/// It contains the generated `Object` and other information produced during
1136/// compilation.
1137pub struct ObjectProduct {
1138    /// Object artifact with all functions and data from the module defined.
1139    pub object: Object<'static>,
1140    /// Symbol IDs for functions (both declared and defined).
1141    pub functions: SecondaryMap<FuncId, Option<(SymbolId, bool)>>,
1142    /// Symbol IDs for data objects (both declared and defined).
1143    pub data_objects: SecondaryMap<DataId, Option<(SymbolId, bool)>>,
1144}
1145
1146impl ObjectProduct {
1147    /// Return the `SymbolId` for the given function.
1148    #[inline]
1149    pub fn function_symbol(&self, id: FuncId) -> SymbolId {
1150        self.functions[id].unwrap().0
1151    }
1152
1153    /// Return the `SymbolId` for the given data object.
1154    #[inline]
1155    pub fn data_symbol(&self, id: DataId) -> SymbolId {
1156        self.data_objects[id].unwrap().0
1157    }
1158
1159    /// Write the object bytes in memory.
1160    #[inline]
1161    pub fn emit(self) -> Result<Vec<u8>, object::write::Error> {
1162        self.object.write()
1163    }
1164}
1165
1166#[derive(Clone)]
1167struct SymbolRelocs {
1168    section: SectionId,
1169    offset: u64,
1170    relocs: Vec<ObjectRelocRecord>,
1171}
1172
1173#[derive(Clone)]
1174struct ObjectRelocRecord {
1175    offset: CodeOffset,
1176    name: ModuleRelocTarget,
1177    flags: RelocationFlags,
1178    addend: Addend,
1179}
1180
1181fn parse_section(
1182    section: &str,
1183    binary_format: BinaryFormat,
1184) -> Result<(&str, &str, u32), anyhow::Error> {
1185    match binary_format {
1186        // See https://github.com/llvm/llvm-project/blob/main/llvm/lib/MC/MCSectionMachO.cpp
1187        BinaryFormat::MachO => {
1188            let mut parts = section.split(',');
1189
1190            let section_err = |msg| {
1191                Err(anyhow!(
1192                    "section `{section}` is not valid for Mach-O target: {msg}"
1193                ))
1194            };
1195
1196            let segment_name = parts.next().unwrap();
1197            if segment_name.len() > 16 {
1198                return section_err("segment name larger than 16 bytes");
1199            }
1200
1201            let Some(section_name) = parts.next() else {
1202                return section_err("must be segment and section separated by comma");
1203            };
1204            if section_name.len() > 16 {
1205                return section_err("section name larger than 16 bytes");
1206            }
1207
1208            let section_type = parts.next().unwrap_or("regular");
1209
1210            // The custom Mach-O section flags. This is the section type
1211            // (8 bits) packed together with the attributes (24 bits).
1212            let mut macho_flags = if let Some((_, val)) = MACHO_SECTION_TYPES
1213                .iter()
1214                .find(|(name, _)| *name == section_type)
1215            {
1216                *val
1217            } else {
1218                let types = list_valid_values(MACHO_SECTION_TYPES);
1219                return section_err(&format!(
1220                    "unsupported section type `{section_type}`, valid values are {types}"
1221                ));
1222            };
1223
1224            if let Some(section_attributes) = parts.next() {
1225                for attr in section_attributes.split('+') {
1226                    macho_flags |= if let Some((_, val)) = MACHO_SECTION_ATTRIBUTES
1227                        .iter()
1228                        .find(|(name, _)| *name == attr)
1229                    {
1230                        *val
1231                    } else {
1232                        let attributes = list_valid_values(MACHO_SECTION_ATTRIBUTES);
1233                        return section_err(&format!(
1234                            "unsupported section attribute `{attr}`, valid values are {attributes}"
1235                        ));
1236                    };
1237                }
1238            }
1239
1240            if parts.next().is_some() {
1241                return section_err("too many components");
1242            }
1243
1244            Ok((segment_name, section_name, macho_flags))
1245        }
1246        // Otherwise, assume no segment and flags.
1247        _ => Ok(("", section, 0)),
1248    }
1249}
1250
1251// We support the same custom section type / attrs naming as LLVM:
1252// <https://github.com/llvm/llvm-project/blob/llvmorg-22.1.3/llvm/lib/MC/MCSectionMachO.cpp#L23-L91>
1253// <https://github.com/llvm/llvm-project/blob/llvmorg-22.1.3/llvm/include/llvm/BinaryFormat/MachO.h#L120-L223>
1254//
1255// See also the Mac OS X Assembler Reference:
1256// <https://leopard-adc.pepas.com/documentation/DeveloperTools/Reference/Assembler/040-Assembler_Directives/asm_directives.html#//apple_ref/doc/uid/TP30000823-TPXREF102>
1257#[rustfmt::skip]
1258const MACHO_SECTION_TYPES: &[(&str, u32)] = {
1259    use object::macho::*;
1260    &[
1261        ("regular", S_REGULAR),
1262        ("zerofill", S_ZEROFILL),
1263        ("cstring_literals", S_CSTRING_LITERALS),
1264        ("4byte_literals", S_4BYTE_LITERALS),
1265        ("8byte_literals", S_8BYTE_LITERALS),
1266        ("literal_pointers", S_LITERAL_POINTERS),
1267        ("non_lazy_symbol_pointers", S_NON_LAZY_SYMBOL_POINTERS),
1268        ("lazy_symbol_pointers", S_LAZY_SYMBOL_POINTERS),
1269        // ("symbol_stubs", S_SYMBOL_STUBS) (requires extra param stub size)
1270        ("mod_init_funcs", S_MOD_INIT_FUNC_POINTERS),
1271        ("mod_term_funcs", S_MOD_TERM_FUNC_POINTERS),
1272        ("coalesced", S_COALESCED),
1273        // S_GB_ZEROFILL (not supported by LLVM)
1274        ("interposing", S_INTERPOSING),
1275        ("16byte_literals", S_16BYTE_LITERALS),
1276        // S_DTRACE_DOF (not supported by LLVM)
1277        // S_LAZY_DYLIB_SYMBOL_POINTERS (not supported by LLVM)
1278        ("thread_local_regular", S_THREAD_LOCAL_REGULAR),
1279        ("thread_local_zerofill", S_THREAD_LOCAL_ZEROFILL),
1280        ("thread_local_variables", S_THREAD_LOCAL_VARIABLES),
1281        ("thread_local_variable_pointers", S_THREAD_LOCAL_VARIABLE_POINTERS),
1282        ("thread_local_init_function_pointers", S_THREAD_LOCAL_INIT_FUNCTION_POINTERS),
1283        // S_INIT_FUNC_OFFSETS (not supported by LLVM)
1284    ]
1285};
1286
1287const MACHO_SECTION_ATTRIBUTES: &[(&str, u32)] = {
1288    use object::macho::*;
1289    &[
1290        ("pure_instructions", S_ATTR_PURE_INSTRUCTIONS),
1291        ("no_toc", S_ATTR_NO_TOC),
1292        ("strip_static_syms", S_ATTR_STRIP_STATIC_SYMS),
1293        ("no_dead_strip", S_ATTR_NO_DEAD_STRIP),
1294        ("live_support", S_ATTR_LIVE_SUPPORT),
1295        ("self_modifying_code", S_ATTR_SELF_MODIFYING_CODE),
1296        ("debug", S_ATTR_DEBUG),
1297        // System settable attributes are not supported by LLVM:
1298        // S_ATTR_SOME_INSTRUCTIONS
1299        // S_ATTR_EXT_RELOC
1300        // S_ATTR_LOC_RELOC
1301    ]
1302};
1303
1304fn list_valid_values(items: &[(&str, u32)]) -> String {
1305    let mut items = items.iter().peekable();
1306    let mut result = String::new();
1307    if let Some((item, _)) = items.next() {
1308        write!(&mut result, "`{item}`").unwrap();
1309    }
1310    while let Some((item, _)) = items.next() {
1311        if items.peek().is_none() {
1312            write!(&mut result, " and `{item}`").unwrap();
1313        } else {
1314            write!(&mut result, ", `{item}`").unwrap();
1315        }
1316    }
1317    result
1318}
1319
1320#[cfg(test)]
1321mod tests {
1322    use super::*;
1323    use object::macho::*;
1324
1325    #[test]
1326    fn section() {
1327        assert_eq!(
1328            parse_section("__DATA,__mod_init_func,mod_init_funcs", BinaryFormat::MachO).unwrap(),
1329            ("__DATA", "__mod_init_func", S_MOD_INIT_FUNC_POINTERS),
1330        );
1331        assert_eq!(
1332            parse_section(
1333                "__OBJC,__module_info,regular,no_dead_strip",
1334                BinaryFormat::MachO,
1335            )
1336            .unwrap(),
1337            ("__OBJC", "__module_info", S_REGULAR | S_ATTR_NO_DEAD_STRIP),
1338        );
1339
1340        assert_eq!(
1341            parse_section("__TEXT,__text", BinaryFormat::MachO).unwrap(),
1342            ("__TEXT", "__text", S_REGULAR),
1343        );
1344        assert_eq!(
1345            parse_section("__TEXT,__text,regular", BinaryFormat::MachO).unwrap(),
1346            ("__TEXT", "__text", S_REGULAR),
1347        );
1348        assert_eq!(
1349            parse_section(
1350                "foo,bar,literal_pointers,no_toc+no_dead_strip",
1351                BinaryFormat::MachO
1352            )
1353            .unwrap(),
1354            (
1355                "foo",
1356                "bar",
1357                S_LITERAL_POINTERS | S_ATTR_NO_TOC | S_ATTR_NO_DEAD_STRIP
1358            ),
1359        );
1360
1361        assert!(parse_section("foo", BinaryFormat::MachO).is_err());
1362        assert!(parse_section("12345678901234567,bar", BinaryFormat::MachO).is_err());
1363        assert!(parse_section("foo,12345678901234567", BinaryFormat::MachO).is_err());
1364        assert!(parse_section("foo,bar,unknown", BinaryFormat::MachO).is_err());
1365        assert!(parse_section("foo,bar,regular,unknown", BinaryFormat::MachO).is_err());
1366        assert!(
1367            parse_section("foo,bar,regular,no_dead_strip+unknown", BinaryFormat::MachO).is_err()
1368        );
1369        assert!(
1370            parse_section("foo,bar,regular,no_dead_strip,unknown", BinaryFormat::MachO).is_err()
1371        );
1372    }
1373}