| //! 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, SubArchitecture, SymbolFlags, SymbolKind, SymbolScope, |
| }; |
| |
| #[cfg(feature = "coff")] |
| pub mod coff; |
| #[cfg(feature = "coff")] |
| pub use coff::CoffExportStyle; |
| |
| #[cfg(feature = "elf")] |
| pub mod elf; |
| |
| #[cfg(feature = "macho")] |
| mod macho; |
| #[cfg(feature = "macho")] |
| pub use macho::MachOBuildVersion; |
| |
| #[cfg(feature = "pe")] |
| pub mod pe; |
| |
| #[cfg(feature = "xcoff")] |
| mod xcoff; |
| |
| 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 relocatable object file. |
| #[derive(Debug)] |
| pub struct Object<'a> { |
| format: BinaryFormat, |
| architecture: Architecture, |
| sub_architecture: Option<SubArchitecture>, |
| 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>, |
| /// Mach-O CPU subtype. |
| #[cfg(feature = "macho")] |
| macho_cpu_subtype: Option<u32>, |
| #[cfg(feature = "macho")] |
| macho_build_version: Option<MachOBuildVersion>, |
| } |
| |
| impl<'a> Object<'a> { |
| /// Create an empty object file. |
| pub fn new(format: BinaryFormat, architecture: Architecture, endian: Endianness) -> Object<'a> { |
| Object { |
| format, |
| architecture, |
| sub_architecture: None, |
| 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, |
| #[cfg(feature = "macho")] |
| macho_cpu_subtype: None, |
| #[cfg(feature = "macho")] |
| macho_build_version: 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 sub-architecture. |
| #[inline] |
| pub fn sub_architecture(&self) -> Option<SubArchitecture> { |
| self.sub_architecture |
| } |
| |
| /// Specify the sub-architecture. |
| pub fn set_sub_architecture(&mut self, sub_architecture: Option<SubArchitecture>) { |
| self.sub_architecture = sub_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(§ion) |
| .cloned() |
| .unwrap_or_else(|| { |
| let (segment, name, kind, flags) = self.section_info(section); |
| let id = self.add_section(segment.to_vec(), name.to_vec(), kind); |
| self.section_mut(id).flags = flags; |
| id |
| }) |
| } |
| |
| /// 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, _flags) = 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, SectionFlags) { |
| 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), |
| #[cfg(feature = "xcoff")] |
| BinaryFormat::Xcoff => self.xcoff_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, flags) = self.subsection_info(section, name); |
| let id = self.add_section(segment.to_vec(), name, kind); |
| self.section_mut(id).flags = flags; |
| id |
| }; |
| 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 | BinaryFormat::Xcoff => 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, SectionFlags) { |
| let (segment, section, kind, flags) = self.section_info(section); |
| let name = self.subsection_name(section, value); |
| (segment, name, kind, flags) |
| } |
| |
| #[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), |
| #[cfg(feature = "xcoff")] |
| BinaryFormat::Xcoff => self.xcoff_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), |
| #[cfg(feature = "xcoff")] |
| BinaryFormat::Xcoff => self.xcoff_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, |
| /// Notes for GNU properties. Only supported for ELF. |
| GnuProperty, |
| } |
| |
| 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, |
| StandardSection::GnuProperty => SectionKind::Note, |
| } |
| } |
| |
| // 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, |
| StandardSection::GnuProperty, |
| ] |
| } |
| } |
| |
| /// 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 uninitialized 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 |
| } |
| |
| /// 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, SymbolId>, |
| } |
| |
| 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, |
| /// Xcoff symbol mangling. |
| Xcoff, |
| } |
| |
| 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, |
| (BinaryFormat::Xcoff, _) => Mangling::Xcoff, |
| _ => 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 | Mangling::Xcoff => None, |
| Mangling::CoffI386 | Mangling::MachO => Some(b'_'), |
| } |
| } |
| } |