summaryrefslogtreecommitdiffstats
path: root/third_party/rust/object/src/write/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/object/src/write/mod.rs')
-rw-r--r--third_party/rust/object/src/write/mod.rs917
1 files changed, 917 insertions, 0 deletions
diff --git a/third_party/rust/object/src/write/mod.rs b/third_party/rust/object/src/write/mod.rs
new file mode 100644
index 0000000000..c50640ae70
--- /dev/null
+++ b/third_party/rust/object/src/write/mod.rs
@@ -0,0 +1,917 @@
+//! Interface for writing object files.
+
+use alloc::borrow::Cow;
+use alloc::string::String;
+use alloc::vec::Vec;
+use core::{fmt, result, str};
+#[cfg(not(feature = "std"))]
+use hashbrown::HashMap;
+#[cfg(feature = "std")]
+use std::{boxed::Box, collections::HashMap, error, io};
+
+use crate::endian::{Endianness, U32, U64};
+use crate::{
+ Architecture, BinaryFormat, ComdatKind, FileFlags, RelocationEncoding, RelocationKind,
+ SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope,
+};
+
+#[cfg(feature = "coff")]
+mod coff;
+#[cfg(feature = "coff")]
+pub use coff::CoffExportStyle;
+
+#[cfg(feature = "elf")]
+pub mod elf;
+
+#[cfg(feature = "macho")]
+mod macho;
+
+#[cfg(feature = "pe")]
+pub mod pe;
+
+mod string;
+pub use string::StringId;
+
+mod util;
+pub use util::*;
+
+/// The error type used within the write module.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Error(String);
+
+impl fmt::Display for Error {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(&self.0)
+ }
+}
+
+#[cfg(feature = "std")]
+impl error::Error for Error {}
+
+/// The result type used within the write module.
+pub type Result<T> = result::Result<T, Error>;
+
+/// A writable object file.
+#[derive(Debug)]
+pub struct Object<'a> {
+ format: BinaryFormat,
+ architecture: Architecture,
+ endian: Endianness,
+ sections: Vec<Section<'a>>,
+ standard_sections: HashMap<StandardSection, SectionId>,
+ symbols: Vec<Symbol>,
+ symbol_map: HashMap<Vec<u8>, SymbolId>,
+ stub_symbols: HashMap<SymbolId, SymbolId>,
+ comdats: Vec<Comdat>,
+ /// File flags that are specific to each file format.
+ pub flags: FileFlags,
+ /// The symbol name mangling scheme.
+ pub mangling: Mangling,
+ /// Mach-O "_tlv_bootstrap" symbol.
+ tlv_bootstrap: Option<SymbolId>,
+}
+
+impl<'a> Object<'a> {
+ /// Create an empty object file.
+ pub fn new(format: BinaryFormat, architecture: Architecture, endian: Endianness) -> Object<'a> {
+ Object {
+ format,
+ architecture,
+ endian,
+ sections: Vec::new(),
+ standard_sections: HashMap::new(),
+ symbols: Vec::new(),
+ symbol_map: HashMap::new(),
+ stub_symbols: HashMap::new(),
+ comdats: Vec::new(),
+ flags: FileFlags::None,
+ mangling: Mangling::default(format, architecture),
+ tlv_bootstrap: None,
+ }
+ }
+
+ /// Return the file format.
+ #[inline]
+ pub fn format(&self) -> BinaryFormat {
+ self.format
+ }
+
+ /// Return the architecture.
+ #[inline]
+ pub fn architecture(&self) -> Architecture {
+ self.architecture
+ }
+
+ /// Return the current mangling setting.
+ #[inline]
+ pub fn mangling(&self) -> Mangling {
+ self.mangling
+ }
+
+ /// Specify the mangling setting.
+ #[inline]
+ pub fn set_mangling(&mut self, mangling: Mangling) {
+ self.mangling = mangling;
+ }
+
+ /// Return the name for a standard segment.
+ ///
+ /// This will vary based on the file format.
+ #[allow(unused_variables)]
+ pub fn segment_name(&self, segment: StandardSegment) -> &'static [u8] {
+ match self.format {
+ #[cfg(feature = "coff")]
+ BinaryFormat::Coff => &[],
+ #[cfg(feature = "elf")]
+ BinaryFormat::Elf => &[],
+ #[cfg(feature = "macho")]
+ BinaryFormat::MachO => self.macho_segment_name(segment),
+ _ => unimplemented!(),
+ }
+ }
+
+ /// Get the section with the given `SectionId`.
+ #[inline]
+ pub fn section(&self, section: SectionId) -> &Section<'a> {
+ &self.sections[section.0]
+ }
+
+ /// Mutably get the section with the given `SectionId`.
+ #[inline]
+ pub fn section_mut(&mut self, section: SectionId) -> &mut Section<'a> {
+ &mut self.sections[section.0]
+ }
+
+ /// Set the data for an existing section.
+ ///
+ /// Must not be called for sections that already have data, or that contain uninitialized data.
+ pub fn set_section_data<T>(&mut self, section: SectionId, data: T, align: u64)
+ where
+ T: Into<Cow<'a, [u8]>>,
+ {
+ self.sections[section.0].set_data(data, align)
+ }
+
+ /// Append data to an existing section. Returns the section offset of the data.
+ pub fn append_section_data(&mut self, section: SectionId, data: &[u8], align: u64) -> u64 {
+ self.sections[section.0].append_data(data, align)
+ }
+
+ /// Append zero-initialized data to an existing section. Returns the section offset of the data.
+ pub fn append_section_bss(&mut self, section: SectionId, size: u64, align: u64) -> u64 {
+ self.sections[section.0].append_bss(size, align)
+ }
+
+ /// Return the `SectionId` of a standard section.
+ ///
+ /// If the section doesn't already exist then it is created.
+ pub fn section_id(&mut self, section: StandardSection) -> SectionId {
+ self.standard_sections
+ .get(&section)
+ .cloned()
+ .unwrap_or_else(|| {
+ let (segment, name, kind) = self.section_info(section);
+ self.add_section(segment.to_vec(), name.to_vec(), kind)
+ })
+ }
+
+ /// Add a new section and return its `SectionId`.
+ ///
+ /// This also creates a section symbol.
+ pub fn add_section(&mut self, segment: Vec<u8>, name: Vec<u8>, kind: SectionKind) -> SectionId {
+ let id = SectionId(self.sections.len());
+ self.sections.push(Section {
+ segment,
+ name,
+ kind,
+ size: 0,
+ align: 1,
+ data: Cow::Borrowed(&[]),
+ relocations: Vec::new(),
+ symbol: None,
+ flags: SectionFlags::None,
+ });
+
+ // Add to self.standard_sections if required. This may match multiple standard sections.
+ let section = &self.sections[id.0];
+ for standard_section in StandardSection::all() {
+ if !self.standard_sections.contains_key(standard_section) {
+ let (segment, name, kind) = self.section_info(*standard_section);
+ if segment == &*section.segment && name == &*section.name && kind == section.kind {
+ self.standard_sections.insert(*standard_section, id);
+ }
+ }
+ }
+
+ id
+ }
+
+ fn section_info(
+ &self,
+ section: StandardSection,
+ ) -> (&'static [u8], &'static [u8], SectionKind) {
+ match self.format {
+ #[cfg(feature = "coff")]
+ BinaryFormat::Coff => self.coff_section_info(section),
+ #[cfg(feature = "elf")]
+ BinaryFormat::Elf => self.elf_section_info(section),
+ #[cfg(feature = "macho")]
+ BinaryFormat::MachO => self.macho_section_info(section),
+ _ => unimplemented!(),
+ }
+ }
+
+ /// Add a subsection. Returns the `SectionId` and section offset of the data.
+ pub fn add_subsection(
+ &mut self,
+ section: StandardSection,
+ name: &[u8],
+ data: &[u8],
+ align: u64,
+ ) -> (SectionId, u64) {
+ let section_id = if self.has_subsections_via_symbols() {
+ self.set_subsections_via_symbols();
+ self.section_id(section)
+ } else {
+ let (segment, name, kind) = self.subsection_info(section, name);
+ self.add_section(segment.to_vec(), name, kind)
+ };
+ let offset = self.append_section_data(section_id, data, align);
+ (section_id, offset)
+ }
+
+ fn has_subsections_via_symbols(&self) -> bool {
+ match self.format {
+ BinaryFormat::Coff | BinaryFormat::Elf => false,
+ BinaryFormat::MachO => true,
+ _ => unimplemented!(),
+ }
+ }
+
+ fn set_subsections_via_symbols(&mut self) {
+ match self.format {
+ #[cfg(feature = "macho")]
+ BinaryFormat::MachO => self.macho_set_subsections_via_symbols(),
+ _ => unimplemented!(),
+ }
+ }
+
+ fn subsection_info(
+ &self,
+ section: StandardSection,
+ value: &[u8],
+ ) -> (&'static [u8], Vec<u8>, SectionKind) {
+ let (segment, section, kind) = self.section_info(section);
+ let name = self.subsection_name(section, value);
+ (segment, name, kind)
+ }
+
+ #[allow(unused_variables)]
+ fn subsection_name(&self, section: &[u8], value: &[u8]) -> Vec<u8> {
+ debug_assert!(!self.has_subsections_via_symbols());
+ match self.format {
+ #[cfg(feature = "coff")]
+ BinaryFormat::Coff => self.coff_subsection_name(section, value),
+ #[cfg(feature = "elf")]
+ BinaryFormat::Elf => self.elf_subsection_name(section, value),
+ _ => unimplemented!(),
+ }
+ }
+
+ /// Get the COMDAT section group with the given `ComdatId`.
+ #[inline]
+ pub fn comdat(&self, comdat: ComdatId) -> &Comdat {
+ &self.comdats[comdat.0]
+ }
+
+ /// Mutably get the COMDAT section group with the given `ComdatId`.
+ #[inline]
+ pub fn comdat_mut(&mut self, comdat: ComdatId) -> &mut Comdat {
+ &mut self.comdats[comdat.0]
+ }
+
+ /// Add a new COMDAT section group and return its `ComdatId`.
+ pub fn add_comdat(&mut self, comdat: Comdat) -> ComdatId {
+ let comdat_id = ComdatId(self.comdats.len());
+ self.comdats.push(comdat);
+ comdat_id
+ }
+
+ /// Get the `SymbolId` of the symbol with the given name.
+ pub fn symbol_id(&self, name: &[u8]) -> Option<SymbolId> {
+ self.symbol_map.get(name).cloned()
+ }
+
+ /// Get the symbol with the given `SymbolId`.
+ #[inline]
+ pub fn symbol(&self, symbol: SymbolId) -> &Symbol {
+ &self.symbols[symbol.0]
+ }
+
+ /// Mutably get the symbol with the given `SymbolId`.
+ #[inline]
+ pub fn symbol_mut(&mut self, symbol: SymbolId) -> &mut Symbol {
+ &mut self.symbols[symbol.0]
+ }
+
+ /// Add a new symbol and return its `SymbolId`.
+ pub fn add_symbol(&mut self, mut symbol: Symbol) -> SymbolId {
+ // Defined symbols must have a scope.
+ debug_assert!(symbol.is_undefined() || symbol.scope != SymbolScope::Unknown);
+ if symbol.kind == SymbolKind::Section {
+ // There can only be one section symbol, but update its flags, since
+ // the automatically generated section symbol will have none.
+ let symbol_id = self.section_symbol(symbol.section.id().unwrap());
+ if symbol.flags != SymbolFlags::None {
+ self.symbol_mut(symbol_id).flags = symbol.flags;
+ }
+ return symbol_id;
+ }
+ if !symbol.name.is_empty()
+ && (symbol.kind == SymbolKind::Text
+ || symbol.kind == SymbolKind::Data
+ || symbol.kind == SymbolKind::Tls)
+ {
+ let unmangled_name = symbol.name.clone();
+ if let Some(prefix) = self.mangling.global_prefix() {
+ symbol.name.insert(0, prefix);
+ }
+ let symbol_id = self.add_raw_symbol(symbol);
+ self.symbol_map.insert(unmangled_name, symbol_id);
+ symbol_id
+ } else {
+ self.add_raw_symbol(symbol)
+ }
+ }
+
+ fn add_raw_symbol(&mut self, symbol: Symbol) -> SymbolId {
+ let symbol_id = SymbolId(self.symbols.len());
+ self.symbols.push(symbol);
+ symbol_id
+ }
+
+ /// Return true if the file format supports `StandardSection::UninitializedTls`.
+ #[inline]
+ pub fn has_uninitialized_tls(&self) -> bool {
+ self.format != BinaryFormat::Coff
+ }
+
+ /// Return true if the file format supports `StandardSection::Common`.
+ #[inline]
+ pub fn has_common(&self) -> bool {
+ self.format == BinaryFormat::MachO
+ }
+
+ /// Add a new common symbol and return its `SymbolId`.
+ ///
+ /// For Mach-O, this appends the symbol to the `__common` section.
+ pub fn add_common_symbol(&mut self, mut symbol: Symbol, size: u64, align: u64) -> SymbolId {
+ if self.has_common() {
+ let symbol_id = self.add_symbol(symbol);
+ let section = self.section_id(StandardSection::Common);
+ self.add_symbol_bss(symbol_id, section, size, align);
+ symbol_id
+ } else {
+ symbol.section = SymbolSection::Common;
+ symbol.size = size;
+ self.add_symbol(symbol)
+ }
+ }
+
+ /// Add a new file symbol and return its `SymbolId`.
+ pub fn add_file_symbol(&mut self, name: Vec<u8>) -> SymbolId {
+ self.add_raw_symbol(Symbol {
+ name,
+ value: 0,
+ size: 0,
+ kind: SymbolKind::File,
+ scope: SymbolScope::Compilation,
+ weak: false,
+ section: SymbolSection::None,
+ flags: SymbolFlags::None,
+ })
+ }
+
+ /// Get the symbol for a section.
+ pub fn section_symbol(&mut self, section_id: SectionId) -> SymbolId {
+ let section = &mut self.sections[section_id.0];
+ if let Some(symbol) = section.symbol {
+ return symbol;
+ }
+ let name = if self.format == BinaryFormat::Coff {
+ section.name.clone()
+ } else {
+ Vec::new()
+ };
+ let symbol_id = SymbolId(self.symbols.len());
+ self.symbols.push(Symbol {
+ name,
+ value: 0,
+ size: 0,
+ kind: SymbolKind::Section,
+ scope: SymbolScope::Compilation,
+ weak: false,
+ section: SymbolSection::Section(section_id),
+ flags: SymbolFlags::None,
+ });
+ section.symbol = Some(symbol_id);
+ symbol_id
+ }
+
+ /// Append data to an existing section, and update a symbol to refer to it.
+ ///
+ /// For Mach-O, this also creates a `__thread_vars` entry for TLS symbols, and the
+ /// symbol will indirectly point to the added data via the `__thread_vars` entry.
+ ///
+ /// Returns the section offset of the data.
+ pub fn add_symbol_data(
+ &mut self,
+ symbol_id: SymbolId,
+ section: SectionId,
+ data: &[u8],
+ align: u64,
+ ) -> u64 {
+ let offset = self.append_section_data(section, data, align);
+ self.set_symbol_data(symbol_id, section, offset, data.len() as u64);
+ offset
+ }
+
+ /// Append zero-initialized data to an existing section, and update a symbol to refer to it.
+ ///
+ /// For Mach-O, this also creates a `__thread_vars` entry for TLS symbols, and the
+ /// symbol will indirectly point to the added data via the `__thread_vars` entry.
+ ///
+ /// Returns the section offset of the data.
+ pub fn add_symbol_bss(
+ &mut self,
+ symbol_id: SymbolId,
+ section: SectionId,
+ size: u64,
+ align: u64,
+ ) -> u64 {
+ let offset = self.append_section_bss(section, size, align);
+ self.set_symbol_data(symbol_id, section, offset, size);
+ offset
+ }
+
+ /// Update a symbol to refer to the given data within a section.
+ ///
+ /// For Mach-O, this also creates a `__thread_vars` entry for TLS symbols, and the
+ /// symbol will indirectly point to the data via the `__thread_vars` entry.
+ #[allow(unused_mut)]
+ pub fn set_symbol_data(
+ &mut self,
+ mut symbol_id: SymbolId,
+ section: SectionId,
+ offset: u64,
+ size: u64,
+ ) {
+ // Defined symbols must have a scope.
+ debug_assert!(self.symbol(symbol_id).scope != SymbolScope::Unknown);
+ match self.format {
+ #[cfg(feature = "macho")]
+ BinaryFormat::MachO => symbol_id = self.macho_add_thread_var(symbol_id),
+ _ => {}
+ }
+ let symbol = self.symbol_mut(symbol_id);
+ symbol.value = offset;
+ symbol.size = size;
+ symbol.section = SymbolSection::Section(section);
+ }
+
+ /// Convert a symbol to a section symbol and offset.
+ ///
+ /// Returns `None` if the symbol does not have a section.
+ pub fn symbol_section_and_offset(&mut self, symbol_id: SymbolId) -> Option<(SymbolId, u64)> {
+ let symbol = self.symbol(symbol_id);
+ if symbol.kind == SymbolKind::Section {
+ return Some((symbol_id, 0));
+ }
+ let symbol_offset = symbol.value;
+ let section = symbol.section.id()?;
+ let section_symbol = self.section_symbol(section);
+ Some((section_symbol, symbol_offset))
+ }
+
+ /// Add a relocation to a section.
+ ///
+ /// Relocations must only be added after the referenced symbols have been added
+ /// and defined (if applicable).
+ pub fn add_relocation(&mut self, section: SectionId, mut relocation: Relocation) -> Result<()> {
+ let addend = match self.format {
+ #[cfg(feature = "coff")]
+ BinaryFormat::Coff => self.coff_fixup_relocation(&mut relocation),
+ #[cfg(feature = "elf")]
+ BinaryFormat::Elf => self.elf_fixup_relocation(&mut relocation)?,
+ #[cfg(feature = "macho")]
+ BinaryFormat::MachO => self.macho_fixup_relocation(&mut relocation),
+ _ => unimplemented!(),
+ };
+ if addend != 0 {
+ self.write_relocation_addend(section, &relocation, addend)?;
+ }
+ self.sections[section.0].relocations.push(relocation);
+ Ok(())
+ }
+
+ fn write_relocation_addend(
+ &mut self,
+ section: SectionId,
+ relocation: &Relocation,
+ addend: i64,
+ ) -> Result<()> {
+ let data = self.sections[section.0].data_mut();
+ let offset = relocation.offset as usize;
+ match relocation.size {
+ 32 => data.write_at(offset, &U32::new(self.endian, addend as u32)),
+ 64 => data.write_at(offset, &U64::new(self.endian, addend as u64)),
+ _ => {
+ return Err(Error(format!(
+ "unimplemented relocation addend {:?}",
+ relocation
+ )));
+ }
+ }
+ .map_err(|_| {
+ Error(format!(
+ "invalid relocation offset {}+{} (max {})",
+ relocation.offset,
+ relocation.size,
+ data.len()
+ ))
+ })
+ }
+
+ /// Write the object to a `Vec`.
+ pub fn write(&self) -> Result<Vec<u8>> {
+ let mut buffer = Vec::new();
+ self.emit(&mut buffer)?;
+ Ok(buffer)
+ }
+
+ /// Write the object to a `Write` implementation.
+ ///
+ /// Also flushes the writer.
+ ///
+ /// It is advisable to use a buffered writer like [`BufWriter`](std::io::BufWriter)
+ /// instead of an unbuffered writer like [`File`](std::fs::File).
+ #[cfg(feature = "std")]
+ pub fn write_stream<W: io::Write>(&self, w: W) -> result::Result<(), Box<dyn error::Error>> {
+ let mut stream = StreamingBuffer::new(w);
+ self.emit(&mut stream)?;
+ stream.result()?;
+ stream.into_inner().flush()?;
+ Ok(())
+ }
+
+ /// Write the object to a `WritableBuffer`.
+ pub fn emit(&self, buffer: &mut dyn WritableBuffer) -> Result<()> {
+ match self.format {
+ #[cfg(feature = "coff")]
+ BinaryFormat::Coff => self.coff_write(buffer),
+ #[cfg(feature = "elf")]
+ BinaryFormat::Elf => self.elf_write(buffer),
+ #[cfg(feature = "macho")]
+ BinaryFormat::MachO => self.macho_write(buffer),
+ _ => unimplemented!(),
+ }
+ }
+}
+
+/// A standard segment kind.
+#[allow(missing_docs)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[non_exhaustive]
+pub enum StandardSegment {
+ Text,
+ Data,
+ Debug,
+}
+
+/// A standard section kind.
+#[allow(missing_docs)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[non_exhaustive]
+pub enum StandardSection {
+ Text,
+ Data,
+ ReadOnlyData,
+ ReadOnlyDataWithRel,
+ ReadOnlyString,
+ UninitializedData,
+ Tls,
+ /// Zero-fill TLS initializers. Unsupported for COFF.
+ UninitializedTls,
+ /// TLS variable structures. Only supported for Mach-O.
+ TlsVariables,
+ /// Common data. Only supported for Mach-O.
+ Common,
+}
+
+impl StandardSection {
+ /// Return the section kind of a standard section.
+ pub fn kind(self) -> SectionKind {
+ match self {
+ StandardSection::Text => SectionKind::Text,
+ StandardSection::Data => SectionKind::Data,
+ StandardSection::ReadOnlyData => SectionKind::ReadOnlyData,
+ StandardSection::ReadOnlyDataWithRel => SectionKind::ReadOnlyDataWithRel,
+ StandardSection::ReadOnlyString => SectionKind::ReadOnlyString,
+ StandardSection::UninitializedData => SectionKind::UninitializedData,
+ StandardSection::Tls => SectionKind::Tls,
+ StandardSection::UninitializedTls => SectionKind::UninitializedTls,
+ StandardSection::TlsVariables => SectionKind::TlsVariables,
+ StandardSection::Common => SectionKind::Common,
+ }
+ }
+
+ // TODO: remembering to update this is error-prone, can we do better?
+ fn all() -> &'static [StandardSection] {
+ &[
+ StandardSection::Text,
+ StandardSection::Data,
+ StandardSection::ReadOnlyData,
+ StandardSection::ReadOnlyDataWithRel,
+ StandardSection::ReadOnlyString,
+ StandardSection::UninitializedData,
+ StandardSection::Tls,
+ StandardSection::UninitializedTls,
+ StandardSection::TlsVariables,
+ StandardSection::Common,
+ ]
+ }
+}
+
+/// An identifier used to reference a section.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct SectionId(usize);
+
+/// A section in an object file.
+#[derive(Debug)]
+pub struct Section<'a> {
+ segment: Vec<u8>,
+ name: Vec<u8>,
+ kind: SectionKind,
+ size: u64,
+ align: u64,
+ data: Cow<'a, [u8]>,
+ relocations: Vec<Relocation>,
+ symbol: Option<SymbolId>,
+ /// Section flags that are specific to each file format.
+ pub flags: SectionFlags,
+}
+
+impl<'a> Section<'a> {
+ /// Try to convert the name to a utf8 string.
+ #[inline]
+ pub fn name(&self) -> Option<&str> {
+ str::from_utf8(&self.name).ok()
+ }
+
+ /// Try to convert the segment to a utf8 string.
+ #[inline]
+ pub fn segment(&self) -> Option<&str> {
+ str::from_utf8(&self.segment).ok()
+ }
+
+ /// Return true if this section contains zerofill data.
+ #[inline]
+ pub fn is_bss(&self) -> bool {
+ self.kind.is_bss()
+ }
+
+ /// Set the data for a section.
+ ///
+ /// Must not be called for sections that already have data, or that contain uninitialized data.
+ pub fn set_data<T>(&mut self, data: T, align: u64)
+ where
+ T: Into<Cow<'a, [u8]>>,
+ {
+ debug_assert!(!self.is_bss());
+ debug_assert_eq!(align & (align - 1), 0);
+ debug_assert!(self.data.is_empty());
+ self.data = data.into();
+ self.size = self.data.len() as u64;
+ self.align = align;
+ }
+
+ /// Append data to a section.
+ ///
+ /// Must not be called for sections that contain uninitialized data.
+ pub fn append_data(&mut self, append_data: &[u8], align: u64) -> u64 {
+ debug_assert!(!self.is_bss());
+ debug_assert_eq!(align & (align - 1), 0);
+ if self.align < align {
+ self.align = align;
+ }
+ let align = align as usize;
+ let data = self.data.to_mut();
+ let mut offset = data.len();
+ if offset & (align - 1) != 0 {
+ offset += align - (offset & (align - 1));
+ data.resize(offset, 0);
+ }
+ data.extend_from_slice(append_data);
+ self.size = data.len() as u64;
+ offset as u64
+ }
+
+ /// Append unitialized data to a section.
+ ///
+ /// Must not be called for sections that contain initialized data.
+ pub fn append_bss(&mut self, size: u64, align: u64) -> u64 {
+ debug_assert!(self.is_bss());
+ debug_assert_eq!(align & (align - 1), 0);
+ if self.align < align {
+ self.align = align;
+ }
+ let mut offset = self.size;
+ if offset & (align - 1) != 0 {
+ offset += align - (offset & (align - 1));
+ self.size = offset;
+ }
+ self.size += size;
+ offset as u64
+ }
+
+ /// Returns the section as-built so far.
+ ///
+ /// This requires that the section is not a bss section.
+ pub fn data(&self) -> &[u8] {
+ debug_assert!(!self.is_bss());
+ &self.data
+ }
+
+ /// Returns the section as-built so far.
+ ///
+ /// This requires that the section is not a bss section.
+ pub fn data_mut(&mut self) -> &mut [u8] {
+ debug_assert!(!self.is_bss());
+ self.data.to_mut()
+ }
+}
+
+/// The section where a symbol is defined.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[non_exhaustive]
+pub enum SymbolSection {
+ /// The section is not applicable for this symbol (such as file symbols).
+ None,
+ /// The symbol is undefined.
+ Undefined,
+ /// The symbol has an absolute value.
+ Absolute,
+ /// The symbol is a zero-initialized symbol that will be combined with duplicate definitions.
+ Common,
+ /// The symbol is defined in the given section.
+ Section(SectionId),
+}
+
+impl SymbolSection {
+ /// Returns the section id for the section where the symbol is defined.
+ ///
+ /// May return `None` if the symbol is not defined in a section.
+ #[inline]
+ pub fn id(self) -> Option<SectionId> {
+ if let SymbolSection::Section(id) = self {
+ Some(id)
+ } else {
+ None
+ }
+ }
+}
+
+/// An identifier used to reference a symbol.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct SymbolId(usize);
+
+/// A symbol in an object file.
+#[derive(Debug)]
+pub struct Symbol {
+ /// The name of the symbol.
+ pub name: Vec<u8>,
+ /// The value of the symbol.
+ ///
+ /// If the symbol defined in a section, then this is the section offset of the symbol.
+ pub value: u64,
+ /// The size of the symbol.
+ pub size: u64,
+ /// The kind of the symbol.
+ pub kind: SymbolKind,
+ /// The scope of the symbol.
+ pub scope: SymbolScope,
+ /// Whether the symbol has weak binding.
+ pub weak: bool,
+ /// The section containing the symbol.
+ pub section: SymbolSection,
+ /// Symbol flags that are specific to each file format.
+ pub flags: SymbolFlags<SectionId>,
+}
+
+impl Symbol {
+ /// Try to convert the name to a utf8 string.
+ #[inline]
+ pub fn name(&self) -> Option<&str> {
+ str::from_utf8(&self.name).ok()
+ }
+
+ /// Return true if the symbol is undefined.
+ #[inline]
+ pub fn is_undefined(&self) -> bool {
+ self.section == SymbolSection::Undefined
+ }
+
+ /// Return true if the symbol is common data.
+ ///
+ /// Note: does not check for `SymbolSection::Section` with `SectionKind::Common`.
+ #[inline]
+ pub fn is_common(&self) -> bool {
+ self.section == SymbolSection::Common
+ }
+
+ /// Return true if the symbol scope is local.
+ #[inline]
+ pub fn is_local(&self) -> bool {
+ self.scope == SymbolScope::Compilation
+ }
+}
+
+/// A relocation in an object file.
+#[derive(Debug)]
+pub struct Relocation {
+ /// The section offset of the place of the relocation.
+ pub offset: u64,
+ /// The size in bits of the place of relocation.
+ pub size: u8,
+ /// The operation used to calculate the result of the relocation.
+ pub kind: RelocationKind,
+ /// Information about how the result of the relocation operation is encoded in the place.
+ pub encoding: RelocationEncoding,
+ /// The symbol referred to by the relocation.
+ ///
+ /// This may be a section symbol.
+ pub symbol: SymbolId,
+ /// The addend to use in the relocation calculation.
+ ///
+ /// This may be in addition to an implicit addend stored at the place of the relocation.
+ pub addend: i64,
+}
+
+/// An identifier used to reference a COMDAT section group.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct ComdatId(usize);
+
+/// A COMDAT section group.
+#[derive(Debug)]
+pub struct Comdat {
+ /// The COMDAT selection kind.
+ ///
+ /// This determines the way in which the linker resolves multiple definitions of the COMDAT
+ /// sections.
+ pub kind: ComdatKind,
+ /// The COMDAT symbol.
+ ///
+ /// If this symbol is referenced, then all sections in the group will be included by the
+ /// linker.
+ pub symbol: SymbolId,
+ /// The sections in the group.
+ pub sections: Vec<SectionId>,
+}
+
+/// The symbol name mangling scheme.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[non_exhaustive]
+pub enum Mangling {
+ /// No symbol mangling.
+ None,
+ /// Windows COFF symbol mangling.
+ Coff,
+ /// Windows COFF i386 symbol mangling.
+ CoffI386,
+ /// ELF symbol mangling.
+ Elf,
+ /// Mach-O symbol mangling.
+ MachO,
+}
+
+impl Mangling {
+ /// Return the default symboling mangling for the given format and architecture.
+ pub fn default(format: BinaryFormat, architecture: Architecture) -> Self {
+ match (format, architecture) {
+ (BinaryFormat::Coff, Architecture::I386) => Mangling::CoffI386,
+ (BinaryFormat::Coff, _) => Mangling::Coff,
+ (BinaryFormat::Elf, _) => Mangling::Elf,
+ (BinaryFormat::MachO, _) => Mangling::MachO,
+ _ => Mangling::None,
+ }
+ }
+
+ /// Return the prefix to use for global symbols.
+ pub fn global_prefix(self) -> Option<u8> {
+ match self {
+ Mangling::None | Mangling::Elf | Mangling::Coff => None,
+ Mangling::CoffI386 | Mangling::MachO => Some(b'_'),
+ }
+ }
+}