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