diff --git a/Cargo.toml b/Cargo.toml index e07bc77..5c38b51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "faerie" -version = "0.7.1" +version = "0.8.0" authors = ["m4b ", "Dan Gohman ", "Pat Hickey ", "Philip Craig "] readme = "README.md" keywords = ["elf", "mach-o", "binary", "object", "compiler"] @@ -17,7 +17,7 @@ name = "prototype" path = "src/bin/main.rs" [dependencies] -goblin = "0.0.19" +goblin = "0.0.21" scroll = "0.9" log = "0.4" env_logger = "0.5" diff --git a/README.md b/README.md index 09f862e..13820ea 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,11 @@ let mut obj = ArtifactBuilder::new(triple!("x86_64-unknown-unknown-unknown-elf") // it is a runtime error to define a symbol _without_ declaring it first obj.declarations( [ - ("deadbeef", Decl::Function { global: false }), - ("main", Decl::Function { global: true }), - ("str.1", Decl::CString { global: false }), - ("DEADBEEF", Decl::DataImport), - ("printf", Decl::FunctionImport), + ("deadbeef", Decl::function().into()), + ("main", Decl::function().global().into()), + ("str.1", Decl::cstring().into()), + ("DEADBEEF", Decl::data_import()), + ("printf", Decl::function_import()), ].into_iter().cloned() )?; @@ -89,7 +89,7 @@ e_phoff: 0x0 e_shoff: 0x2a20 SHT_NULL 0x0 0x0 0x0 0x0 0x0 1 .strtab SHT_STRTAB 0x8c 0x0 0xc6 0x0 0x1 2 .symtab SHT_SYMTAB 0x152 0x0 0xf0 .strtab(1) 0x18 0x8 - 3 .data.str.1 SHT_PROGBITS ALLOC MERGE STRINGS 0x40 0x0 0x10 0x1 0x1 + 3 .rodata.str.1 SHT_PROGBITS ALLOC MERGE STRINGS 0x40 0x0 0x10 0x1 0x1 4 .text.deadbeef SHT_PROGBITS ALLOC EXECINSTR 0x50 0x0 0x14 0x0 0x10 5 .text.main SHT_PROGBITS ALLOC EXECINSTR 0x64 0x0 0x28 0x0 0x10 6 .reloc.main SHT_RELA 0x242 0x0 0x48 .symtab(2) 0x18 0x8 @@ -100,10 +100,10 @@ e_phoff: 0x0 e_shoff: 0x2a2 Addr Bind Type Symbol Size Section Other 0 LOCAL NOTYPE 0x0 0x0 0 LOCAL FILE test.o 0x0 ABS 0x0 - 0 LOCAL SECTION 0x0 .data.str.1(3) 0x0 + 0 LOCAL SECTION 0x0 .rodata.str.1(3) 0x0 0 LOCAL SECTION 0x0 .text.deadbeef(4) 0x0 0 LOCAL SECTION 0x0 .text.main(5) 0x0 - 0 LOCAL OBJECT str.1 0x10 .data.str.1(3) 0x0 + 0 LOCAL OBJECT str.1 0x10 .rodata.str.1(3) 0x0 0 LOCAL FUNC deadbeef 0x14 .text.deadbeef(4) 0x0 0 GLOBAL FUNC main 0x28 .text.main(5) 0x0 0 GLOBAL NOTYPE DEADBEEF 0x0 0x0 @@ -111,7 +111,7 @@ e_phoff: 0x0 e_shoff: 0x2a2Shdr Relocations(4): .text.main(3) - 13 X86_64_PC32 .data.str.1 + 13 X86_64_PC32 .rodata.str.1 1d X86_64_PLT32 printf+-4 a X86_64_PLT32 .text.deadbeef+-4 diff --git a/src/artifact.rs b/src/artifact.rs index b3293cf..9e026af 100644 --- a/src/artifact.rs +++ b/src/artifact.rs @@ -12,7 +12,7 @@ use std::io::Write; use crate::{elf, mach}; pub(crate) mod decl; -pub use decl::{Decl, DefinedDecl, ImportKind}; +pub use crate::artifact::decl::{DataType, Decl, DefinedDecl, ImportKind, Scope, Visibility}; /// A blob of binary bytes, representing a function body, or data object pub type Data = Vec; @@ -282,7 +282,11 @@ impl Artifact { } /// Declare a new symbolic reference, with the given `decl`. /// **Note**: All declarations _must_ precede their definitions. - pub fn declare, D: Into>(&mut self, name: T, decl: D) -> Result<(), Error> { + pub fn declare, D: Into>( + &mut self, + name: T, + decl: D, + ) -> Result<(), ArtifactError> { let decl = decl.into(); let decl_name = self.strings.get_or_intern(name.as_ref()); let previous_was_import; diff --git a/src/artifact/decl.rs b/src/artifact/decl.rs index 90a5a5d..ea81028 100644 --- a/src/artifact/decl.rs +++ b/src/artifact/decl.rs @@ -1,5 +1,4 @@ use crate::artifact::ArtifactError; -use failure::Error; /// The kind of declaration this is #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -29,6 +28,140 @@ impl ImportKind { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +/// Linker binding scope of a definition +pub enum Scope { + /// Available to all components + Global, + /// Available only inside the defining component + Local, + /// Available to all modules, but only selected if a Global + /// definition is not found. No conflict if there are multiple + /// weak symbols. + Weak, +} + +macro_rules! scope_methods { + () => { + /// Set scope to global + pub fn global(self) -> Self { + self.with_scope(Scope::Global) + } + /// Set scope to local + pub fn local(self) -> Self { + self.with_scope(Scope::Local) + } + /// Set scope to weak + pub fn weak(self) -> Self { + self.with_scope(Scope::Weak) + } + /// Builder for scope + pub fn with_scope(mut self, scope: Scope) -> Self { + self.scope = scope; + self + } + /// Gst scope + pub fn get_scope(&self) -> Scope { + self.scope + } + /// Set scope + pub fn set_scope(&mut self, scope: Scope) { + self.scope = scope; + } + /// Check if scope is `Scope::Global`. False if set to Local or Weak. + pub fn is_global(&self) -> bool { + self.scope == Scope::Global + } +}} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +/// Linker visibility of a definition +pub enum Visibility { + /// Visibility determined by the symbol's `Scope`. + Default, + /// Visible in other components, but cannot be preempted. References to the symbol must be + /// resolved to this definition in that component, even if another definition would interpose + /// by the default rules. + Protected, + /// Not visible to other components, plus the constraints provided by `Protected`. + Hidden, +} + +macro_rules! visibility_methods { + () => { + /// Set visibility to default + pub fn default_visibility(self) -> Self { + self.with_visibility(Visibility::Default) + } + /// Set visibility to protected + pub fn protected(self) -> Self { + self.with_visibility(Visibility::Protected) + } + /// Set visibility to hidden + pub fn hidden(self) -> Self { + self.with_visibility(Visibility::Hidden) + } + /// Builder for visibility + pub fn with_visibility(mut self, visibility: Visibility) -> Self { + self.visibility =visibility; + self + } + /// Get visibility + pub fn get_visibility(&self) -> Visibility { + self.visibility + } + /// Set visibility + pub fn set_visibility(&mut self, visibility: Visibility) { + self.visibility = visibility; + } +}} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +/// Type of data declared +pub enum DataType { + /// Ordinary raw bytes + Bytes, + /// 0-terminated C-style string. + String, +} + +macro_rules! datatype_methods { + () => { + /// Build datatype + pub fn with_datatype(mut self, datatype: DataType) -> Self { + self.datatype = datatype; + self + } + /// Set datatype + pub fn set_datatype(&mut self, datatype: DataType) { + self.datatype = datatype; + } + /// Get datatype + pub fn get_datatype(&self) -> DataType { + self.datatype + } + } +} + +macro_rules! align_methods { + () => { + /// Build alignment. Size is in bytes. If None, a default is chosen + /// in the backend. + pub fn with_align(mut self, align: Option) -> Self { + self.align = align; + self + } + /// Set alignment + pub fn set_align(&mut self, align: Option) { + self.align = align; + } + /// Get alignment + pub fn get_align(&self) -> Option { + self.align + } + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] /// A declaration that is defined inside this artifact pub enum DefinedDecl { @@ -36,8 +169,6 @@ pub enum DefinedDecl { Function(FunctionDecl), /// A data object defined in this artifact Data(DataDecl), - /// A null-terminated string object defined in this artifact - CString(CStringDecl), /// A DWARF debug section defined in this artifact DebugSection(DebugSectionDecl), } @@ -59,14 +190,6 @@ impl DefinedDecl { } } - /// Accessor to determine whether variant is CString - pub fn is_cstring(&self) -> bool { - match self { - DefinedDecl::CString { .. } => true, - _ => false, - } - } - /// Accessor to determine whether variant is DebugSection pub fn is_debug_section(&self) -> bool { match self { @@ -80,7 +203,6 @@ impl DefinedDecl { match self { DefinedDecl::Function(a) => a.is_global(), DefinedDecl::Data(a) => a.is_global(), - DefinedDecl::CString(a) => a.is_global(), DefinedDecl::DebugSection(a) => a.is_global(), } } @@ -89,9 +211,7 @@ impl DefinedDecl { pub fn is_writable(&self) -> bool { match self { DefinedDecl::Data(a) => a.is_writable(), - DefinedDecl::Function(_) | DefinedDecl::CString(_) | DefinedDecl::DebugSection(_) => { - false - } + DefinedDecl::Function(_) | DefinedDecl::DebugSection(_) => false, } } } @@ -114,8 +234,8 @@ impl Decl { DataDecl::default() } /// A null-terminated string object defined in this artifact - pub fn cstring() -> CStringDecl { - CStringDecl::default() + pub fn cstring() -> DataDecl { + DataDecl::default().with_datatype(DataType::String) } /// A DWARF debug section defined in this artifact pub fn debug_section() -> DebugSectionDecl { @@ -135,7 +255,7 @@ impl Decl { /// 4. Anything else is a [IncompatibleDeclaration](enum.ArtifactError.html#variant.IncompatibleDeclaration) error! // ref https://github.com/m4b/faerie/issues/24 // ref https://github.com/m4b/faerie/issues/18 - pub fn absorb(&mut self, other: Self) -> Result<(), Error> { + pub fn absorb(&mut self, other: Self) -> Result<(), ArtifactError> { // FIXME: i can't think of a way offhand to not clone here, without unusual contortions match self.clone() { Decl::Import(ImportKind::Data) => { @@ -264,30 +384,25 @@ impl Into for DataImportDecl { #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] /// Builder for function declarations pub struct FunctionDecl { - global: bool, + scope: Scope, + visibility: Visibility, + align: Option, } impl Default for FunctionDecl { fn default() -> Self { - FunctionDecl { global: false } + FunctionDecl { + scope: Scope::Local, + visibility: Visibility::Default, + align: None, + } } } impl FunctionDecl { - /// Set binding to global - pub fn global(mut self) -> Self { - self.global = true; - self - } - /// Set binding to local - pub fn local(mut self) -> Self { - self.global = false; - self - } - /// Accessor for binding - pub fn is_global(&self) -> bool { - self.global - } + scope_methods!(); + visibility_methods!(); + align_methods!(); } impl Into for FunctionDecl { @@ -299,43 +414,46 @@ impl Into for FunctionDecl { #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] /// Builder for data declarations pub struct DataDecl { - global: bool, + scope: Scope, + visibility: Visibility, writable: bool, + datatype: DataType, + align: Option, } impl Default for DataDecl { fn default() -> Self { DataDecl { - global: false, + scope: Scope::Local, + visibility: Visibility::Default, writable: false, + datatype: DataType::Bytes, + align: None, } } } impl DataDecl { - /// Set binding to global - pub fn global(mut self) -> Self { - self.global = true; + scope_methods!(); + visibility_methods!(); + datatype_methods!(); + align_methods!(); + /// Builder for writability + pub fn with_writable(mut self, writable: bool) -> Self { + self.writable = writable; self } - /// Set binding to local - pub fn local(mut self) -> Self { - self.global = false; - self - } - /// Accessor for binding - pub fn is_global(&self) -> bool { - self.global - } /// Set mutability to writable - pub fn writable(mut self) -> Self { - self.writable = true; - self + pub fn writable(self) -> Self { + self.with_writable(true) } /// Set mutability to read-only - pub fn read_only(mut self) -> Self { - self.writable = false; - self + pub fn read_only(self) -> Self { + self.with_writable(false) + } + /// Setter for mutability + pub fn set_writable(&mut self, writable: bool) { + self.writable = writable; } /// Accessor for mutability pub fn is_writable(&self) -> bool { @@ -349,46 +467,16 @@ impl Into for DataDecl { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -/// Builder for a CString (0-terminated character sequence) declaration -pub struct CStringDecl { - global: bool, -} - -impl Default for CStringDecl { - fn default() -> Self { - CStringDecl { global: false } - } -} - -impl CStringDecl { - /// Set binding to global - pub fn global(mut self) -> Self { - self.global = true; - self - } - /// Set binding to local - pub fn local(mut self) -> Self { - self.global = false; - self - } - /// Accessor for binding - pub fn is_global(&self) -> bool { - self.global - } -} - -impl Into for CStringDecl { - fn into(self) -> Decl { - Decl::Defined(DefinedDecl::CString(self)) - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] /// Builder for a debug section declaration -pub struct DebugSectionDecl {} +pub struct DebugSectionDecl { + datatype: DataType, + align: Option, +} impl DebugSectionDecl { + datatype_methods!(); + align_methods!(); /// Debug sections are never global, but we have an accessor /// for symmetry with other section declarations pub fn is_global(&self) -> bool { @@ -398,7 +486,10 @@ impl DebugSectionDecl { impl Default for DebugSectionDecl { fn default() -> Self { - DebugSectionDecl {} + DebugSectionDecl { + datatype: DataType::Bytes, + align: None, + } } } diff --git a/src/elf.rs b/src/elf.rs index 433ec7c..f765bb3 100644 --- a/src/elf.rs +++ b/src/elf.rs @@ -6,7 +6,10 @@ #![allow(dead_code)] use crate::{ - artifact::{self, Artifact, Decl, DefinedDecl, ImportKind, LinkAndDecl, Reloc}, + artifact::{ + self, Artifact, DataType, Decl, DefinedDecl, ImportKind, LinkAndDecl, Reloc, Scope, + Visibility, + }, target::make_ctx, Ctx, }; @@ -61,12 +64,10 @@ impl From for MachineTag { } /// The kind of symbol this is; used in [SymbolBuilder](struct.SymbolBuilder.html) -enum SymbolType { - /// A function - Function, - /// A data object - Object, - /// An impor +enum SymbolType<'a> { + /// From a definition + Decl(&'a DefinedDecl), + /// An import Import, /// A section reference Section, @@ -77,59 +78,88 @@ enum SymbolType { } /// A builder for creating a 32/64 bit ELF symbol -struct SymbolBuilder { +struct SymbolBuilder<'a> { name_offset: usize, - global: bool, size: u64, - typ: SymbolType, + typ: SymbolType<'a>, + shndx: usize, } -impl SymbolBuilder { +impl<'a> SymbolBuilder<'a> { /// Create a new symbol with `typ` - pub fn new(typ: SymbolType) -> Self { + pub fn new(typ: SymbolType<'a>) -> Self { SymbolBuilder { - global: false, name_offset: 0, typ, size: 0, + shndx: 0, } } + pub fn from_decl(decl: &'a DefinedDecl) -> Self { + SymbolBuilder::new(SymbolType::Decl(decl)) + } /// Set the size of this symbol; for functions, it should be the routines size in bytes pub fn size(mut self, size: usize) -> Self { self.size = size as u64; self } - /// Is this symbol local in scope? - pub fn local(mut self, local: bool) -> Self { - self.global = !local; - self - } /// Set the symbol name as a byte offset into the corresponding strtab pub fn name_offset(mut self, name_offset: usize) -> Self { self.name_offset = name_offset; self } + /// Set the section index + pub fn section_index(mut self, shndx: usize) -> Self { + // Underlying representation is only 16 bits. Catch this early! + debug_assert!(shndx < u16::max_value() as usize); + self.shndx = shndx; + self + } /// Finalize and create the symbol pub fn create(self) -> Symbol { use goblin::elf::section_header::SHN_ABS; use goblin::elf::sym::{ - STB_GLOBAL, STB_LOCAL, STT_FILE, STT_FUNC, STT_NOTYPE, STT_OBJECT, STT_SECTION, + STB_GLOBAL, STB_LOCAL, STB_WEAK, STT_FILE, STT_FUNC, STT_NOTYPE, STT_OBJECT, + STT_SECTION, STV_DEFAULT, STV_HIDDEN, STV_PROTECTED, }; - let mut st_shndx = 0; + let mut st_shndx = self.shndx; let mut st_info = 0; + let mut st_other = 0; let st_value = 0; + + fn scope_stb_flags(s: Scope) -> u8 { + let flag = match s { + Scope::Local => STB_LOCAL, + Scope::Global => STB_GLOBAL, + Scope::Weak => STB_WEAK, + }; + flag << 4 + } + + fn vis_stother_flags(v: Visibility) -> u8 { + match v { + Visibility::Default => STV_DEFAULT, + Visibility::Hidden => STV_HIDDEN, + Visibility::Protected => STV_PROTECTED, + } + } + match self.typ { - SymbolType::Function => { + SymbolType::Decl(DefinedDecl::Function(d)) => { st_info |= STT_FUNC; + st_info |= scope_stb_flags(d.get_scope()); + st_other |= vis_stother_flags(d.get_visibility()); } - SymbolType::Object => { + SymbolType::Decl(DefinedDecl::Data(d)) => { st_info |= STT_OBJECT; + st_info |= scope_stb_flags(d.get_scope()); + st_other |= vis_stother_flags(d.get_visibility()); } SymbolType::Import => { st_info = STT_NOTYPE; st_info |= STB_GLOBAL << 4; } - SymbolType::Section => { + SymbolType::Decl(DefinedDecl::DebugSection(_)) | SymbolType::Section => { st_info |= STT_SECTION; st_info |= STB_LOCAL << 4; } @@ -140,14 +170,9 @@ impl SymbolBuilder { } SymbolType::None => st_info = STT_NOTYPE, } - if self.global { - st_info |= STB_GLOBAL << 4; - } else { - st_info |= STB_LOCAL << 4; - } Symbol { st_name: self.name_offset, - st_other: 0, + st_other, st_size: self.size, st_info, st_shndx, @@ -175,6 +200,7 @@ struct SectionBuilder { alloc: bool, size: u64, name_offset: usize, + align: Option, } impl SectionBuilder { @@ -187,6 +213,7 @@ impl SectionBuilder { alloc: false, name_offset: 0, size, + align: None, } } /// Make this section executable @@ -204,6 +231,11 @@ impl SectionBuilder { self.write = writable; self } + /// Specify section alignment + pub fn align(mut self, align: Option) -> Self { + self.align = align; + self + } /// Set the byte offset of this section's name in the corresponding strtab pub fn name_offset(mut self, name_offset: usize) -> Self { @@ -231,36 +263,29 @@ impl SectionBuilder { if self.alloc { shdr.sh_flags |= SHF_ALLOC as u64 } + + let align = if let Some(align) = self.align { + align as u64 + } else if self.exec { + 0x10 + } else if self.write { + 0x8 + } else { + 1 + }; + match self.typ { SectionType::Bits => { - shdr.sh_addralign = if self.exec { - 0x10 - } else if self.write { - 0x8 - } else { - 1 - }; - shdr.sh_type = SHT_PROGBITS + shdr.sh_addralign = align; + shdr.sh_type = SHT_PROGBITS; } SectionType::String => { - shdr.sh_addralign = if self.exec { - 0x10 - } else if self.write { - 0x8 - } else { - 1 - }; + shdr.sh_addralign = align; shdr.sh_type = SHT_PROGBITS; shdr.sh_flags |= (SHF_MERGE | SHF_STRINGS) as u64; } SectionType::Data => { - shdr.sh_addralign = if self.exec { - 0x10 - } else if self.write { - 0x8 - } else { - 1 - }; + shdr.sh_addralign = align; shdr.sh_type = SHT_PROGBITS; } SectionType::StrTab => { @@ -444,66 +469,75 @@ impl<'a> Elf<'a> { } } pub fn add_definition(&mut self, name: &str, data: &'a [u8], decl: &artifact::DefinedDecl) { - // we need this because sh_info requires nsections + nlocals to add as delimiter; see the associated FunFact + // sh_info requires nsections + nlocals to add as delimiter; see the associated FunFact if !decl.is_global() { self.nlocals += 1; } - // FIXME: this is kind of hacky? - let segment_name = if let DefinedDecl::Function { .. } = decl { - "text" - } else { - // we'd add ro here once the prop supports that - "data" + let def_size = data.len(); + + let section_name = match decl { + DefinedDecl::Function(_) => format!(".text.{}", name), + DefinedDecl::Data(d) => format!( + ".{}.{}", + if d.is_writable() { "data" } else { "rodata" }, + name + ), + DefinedDecl::DebugSection(_) => name.to_owned(), }; - let section_name = format!(".{}.{}", segment_name, name); - // store the size of this code - let size = data.len(); - // now we build the section a la LLVM "function sections" - // FIXME: probably add padding alignment - let section = { - let stype = match decl { - DefinedDecl::Function { .. } => SectionType::Bits, - DefinedDecl::CString { .. } => SectionType::String, - _ => SectionType::Data, - }; - - SectionBuilder::new(size as u64) - .section_type(stype) + let section = match decl { + DefinedDecl::Function(d) => SectionBuilder::new(def_size as u64) + .section_type(SectionType::Bits) + .alloc() + .writable(false) + .exec(true) + .align(d.get_align()), + DefinedDecl::Data(d) => SectionBuilder::new(def_size as u64) + .section_type(match d.get_datatype() { + DataType::Bytes => SectionType::Data, + DataType::String => SectionType::String, + }) .alloc() - .writable(decl.is_writable()) - .exec(decl.is_function()) + .writable(d.is_writable()) + .exec(false) + .align(d.get_align()), + DefinedDecl::DebugSection(d) => SectionBuilder::new(def_size as u64) + .section_type( + // TODO: this behavior should be deprecated, but we need to warn users! + if name == ".debug_str" || name == ".debug_line_str" { + SectionType::String + } else { + match d.get_datatype() { + DataType::Bytes => SectionType::Bits, + DataType::String => SectionType::String, + } + }, + ) + .align(d.get_align()), }; + let shndx = self.add_progbits(section_name, section, data); - // can do prefix optimization here actually, because .text.* - let (idx, offset) = self.new_string(name.to_string()); - debug!( - "idx: {:?} @ {:#x} - new strtab offset: {:#x}", - idx, offset, self.sizeof_strtab - ); - // build symbol based on this _and_ the properties of the definition - let mut symbol = SymbolBuilder::new(if let DefinedDecl::Function { .. } = decl { - SymbolType::Function - } else { - SymbolType::Object - }) - .size(size) - .name_offset(offset) - .local(!decl.is_global()) - .create(); - symbol.st_shndx = shndx; - // insert it into our symbol table - self.symbols.insert(idx, symbol); - } - pub fn add_section(&mut self, name: &str, data: &'a [u8], _decl: &artifact::DefinedDecl) { - let stype = if name == ".debug_str" || name == ".debug_line_str" { - SectionType::String - } else { - SectionType::Bits - }; - let section = SectionBuilder::new(data.len() as u64).section_type(stype); - self.add_progbits(name.to_string(), section, data); + match decl { + DefinedDecl::Function(_) | DefinedDecl::Data(_) => { + let (idx, offset) = self.new_string(name.to_string()); + debug!( + "idx: {:?} @ {:#x} - new strtab offset: {:#x}", + idx, offset, self.sizeof_strtab + ); + // build symbol based on this _and_ the properties of the definition + let symbol = SymbolBuilder::from_decl(decl) + .size(def_size) + .name_offset(offset) + .section_index(shndx) + .create(); + // insert it into our symbol table + self.symbols.insert(idx, symbol); + } + DefinedDecl::DebugSection(_) => { + // No symbols in debug sections, yet... + } + } } /// Create a progbits section (and its section symbol), and return the section index. fn add_progbits(&mut self, name: String, section: SectionBuilder, data: &'a [u8]) -> usize { @@ -516,8 +550,9 @@ impl<'a> Elf<'a> { let size = data.len(); // the symbols section reference/index will be the current number of sections let shndx = self.sections.len() + 3; // null + strtab + symtab - let mut section_symbol = SymbolBuilder::new(SymbolType::Section).create(); - section_symbol.st_shndx = shndx; + let section_symbol = SymbolBuilder::new(SymbolType::Section) + .section_index(shndx) + .create(); let mut section = section.name_offset(offset).create(&self.ctx); // the offset is the head of how many program bits we've added @@ -601,9 +636,6 @@ impl<'a> Elf<'a> { Decl::Defined(DefinedDecl::Function { .. }) | Decl::Import(ImportKind::Function) => (reloc::R_X86_64_PLT32, -4), Decl::Defined(DefinedDecl::Data { .. }) => (reloc::R_X86_64_PC32, -4), - Decl::Defined(DefinedDecl::CString { .. }) => { - (reloc::R_X86_64_PC32, -4) - } Decl::Import(ImportKind::Data) => (reloc::R_X86_64_GOTPCREL, -4), _ => panic!("unsupported relocation {:?}", l), } @@ -851,11 +883,7 @@ pub fn to_bytes(artifact: &Artifact) -> Result, Error> { let mut elf = Elf::new(&artifact); for def in artifact.definitions() { debug!("Def: {:?}", def); - if let DefinedDecl::DebugSection { .. } = def.decl { - elf.add_section(def.name, def.data, def.decl); - } else { - elf.add_definition(def.name, def.data, def.decl); - } + elf.add_definition(def.name, def.data, def.decl); } for (ref import, ref kind) in artifact.imports() { debug!("Import: {:?} -> {:?}", import, kind); diff --git a/src/lib.rs b/src/lib.rs index 0fd08e7..a870fa4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,8 +22,8 @@ mod target; pub mod artifact; pub use crate::artifact::{ decl::{ - CStringDecl, DataDecl, DataImportDecl, DebugSectionDecl, Decl, FunctionDecl, - FunctionImportDecl, + DataDecl, DataImportDecl, DataType, DebugSectionDecl, Decl, FunctionDecl, + FunctionImportDecl, Scope, Visibility, }, - Artifact, ArtifactBuilder, ImportKind, Link, Reloc, + Artifact, ArtifactBuilder, ArtifactError, ImportKind, Link, Reloc, }; diff --git a/src/mach.rs b/src/mach.rs index 7d2610f..fb25223 100644 --- a/src/mach.rs +++ b/src/mach.rs @@ -1,6 +1,6 @@ //! The Mach 32/64 bit backend for transforming an artifact to a valid, mach-o object file. -use crate::artifact::{Decl, DefinedDecl, Definition, ImportKind, Reloc}; +use crate::artifact::{DataType, Decl, DefinedDecl, Definition, ImportKind, Reloc}; use crate::target::make_ctx; use crate::{Artifact, Ctx}; @@ -574,11 +574,12 @@ impl<'a> Mach<'a> { DefinedDecl::Function { .. } => { code.push(def); } - DefinedDecl::Data { .. } => { - data.push(def); - } - DefinedDecl::CString { .. } => { - cstrings.push(def); + DefinedDecl::Data(d) => { + if d.get_datatype() == DataType::String { + cstrings.push(def); + } else { + data.push(def); + } } DefinedDecl::DebugSection { .. } => { debug.push(def); @@ -813,8 +814,6 @@ fn build_relocations(segment: &mut SegmentBuilder, artifact: &Artifact, symtab: Decl::Defined(DefinedDecl::Data { .. }), ) => (true, X86_64_RELOC_UNSIGNED), (_, Decl::Defined(DefinedDecl::Data { .. })) => (false, X86_64_RELOC_SIGNED), - // TODO: we will also need to specify relocations from Data to Cstrings, e.g., char * STR = "a global static string"; - (_, Decl::Defined(DefinedDecl::CString { .. })) => (false, X86_64_RELOC_SIGNED), (_, Decl::Import(ImportKind::Function)) => (false, X86_64_RELOC_BRANCH), (_, Decl::Import(ImportKind::Data)) => (false, X86_64_RELOC_GOT_LOAD), } diff --git a/tests/elf.rs b/tests/elf.rs index c60be99..fa91495 100644 --- a/tests/elf.rs +++ b/tests/elf.rs @@ -3,11 +3,13 @@ extern crate goblin; extern crate scroll; #[macro_use] extern crate target_lexicon; - -use std::str::FromStr; +#[macro_use] +extern crate failure; use faerie::{Artifact, Decl, Link}; +use failure::Error; use goblin::elf::*; +use std::str::FromStr; #[test] // This test is for a known bug (issue #31). @@ -72,3 +74,222 @@ fn link_symbol_pair_panic_issue_30() { // and return an error describing them: assert!(obj.emit().is_err()); } + +#[test] +fn decl_attributes() { + decl_tests(vec![ + DeclTestCase::new("weak_func", Decl::function().weak(), |sym, sect| { + ensure!(sym.is_function(), "symbol is function"); + ensure!(sym.st_bind() == sym::STB_WEAK, "symbol is weak"); + ensure!( + sym.st_visibility() == sym::STV_DEFAULT, + "symbol is default vis" + ); + ensure!(sect.is_executable(), "executable"); + ensure!(!sect.is_writable(), "immutable"); + Ok(()) + }), + DeclTestCase::new("weak_data", Decl::data().weak(), |sym, sect| { + ensure!(sym.st_type() == sym::STT_OBJECT, "symbol is object"); + ensure!(sym.st_bind() == sym::STB_WEAK, "symbol is weak"); + ensure!( + sym.st_visibility() == sym::STV_DEFAULT, + "symbol is default vis" + ); + ensure!(!sect.is_executable(), "not executable"); + ensure!(!sect.is_writable(), "immutable"); + Ok(()) + }), + DeclTestCase::new( + "weak_data_writable", + Decl::data().weak().writable(), + |sym, sect| { + ensure!(sym.st_type() == sym::STT_OBJECT, "symbol is object"); + ensure!(sym.st_bind() == sym::STB_WEAK, "symbol is weak"); + ensure!( + sym.st_visibility() == sym::STV_DEFAULT, + "symbol is default vis" + ); + ensure!(!sect.is_executable(), "not executable"); + ensure!(sect.is_writable(), "mutable"); + Ok(()) + }, + ), + DeclTestCase::new("weak_cstring", Decl::cstring().weak(), |sym, sect| { + ensure!(sym.st_type() == sym::STT_OBJECT, "symbol is object"); + ensure!(sym.st_bind() == sym::STB_WEAK, "symbol is weak"); + ensure!( + sym.st_visibility() == sym::STV_DEFAULT, + "symbol is default vis" + ); + ensure!(!sect.is_executable(), "not executable"); + ensure!(!sect.is_writable(), "immutable"); + Ok(()) + }), + DeclTestCase::new("hidden_func", Decl::function().hidden(), |sym, sect| { + ensure!(sym.is_function(), "symbol is func"); + ensure!(sym.st_bind() == sym::STB_LOCAL, "symbol is local"); + ensure!(sym.st_visibility() == sym::STV_HIDDEN, "symbol is hidden"); + ensure!(sect.is_executable(), "executable"); + ensure!(!sect.is_writable(), "immutable"); + Ok(()) + }), + DeclTestCase::new("hidden_data", Decl::data().hidden(), |sym, sect| { + ensure!(sym.st_type() == sym::STT_OBJECT, "symbol is object"); + ensure!(sym.st_bind() == sym::STB_LOCAL, "symbol is local"); + ensure!(sym.st_visibility() == sym::STV_HIDDEN, "symbol is hidden"); + ensure!(!sect.is_executable(), "not executable"); + ensure!(!sect.is_writable(), "immutable"); + Ok(()) + }), + DeclTestCase::new("hidden_cstring", Decl::cstring().hidden(), |sym, sect| { + ensure!(sym.st_type() == sym::STT_OBJECT, "symbol is object"); + ensure!(sym.st_bind() == sym::STB_LOCAL, "symbol is weak"); + ensure!(sym.st_visibility() == sym::STV_HIDDEN, "symbol is hidden"); + ensure!(!sect.is_executable(), "not executable"); + ensure!(!sect.is_writable(), "immutable"); + Ok(()) + }), + DeclTestCase::new( + "protected_func", + Decl::function().protected(), + |sym, sect| { + ensure!(sym.is_function(), "symbol is func"); + ensure!(sym.st_bind() == sym::STB_LOCAL, "symbol is local"); + ensure!( + sym.st_visibility() == sym::STV_PROTECTED, + "symbol is protected" + ); + ensure!(sect.is_executable(), "executable"); + ensure!(!sect.is_writable(), "immutable"); + Ok(()) + }, + ), + DeclTestCase::new("protected_data", Decl::data().protected(), |sym, sect| { + ensure!(sym.st_type() == sym::STT_OBJECT, "symbol is object"); + ensure!(sym.st_bind() == sym::STB_LOCAL, "symbol is local"); + ensure!( + sym.st_visibility() == sym::STV_PROTECTED, + "symbol is protected" + ); + ensure!(!sect.is_executable(), "not executable"); + ensure!(!sect.is_writable(), "immutable"); + Ok(()) + }), + DeclTestCase::new( + "protected_cstring", + Decl::cstring().protected(), + |sym, sect| { + ensure!(sym.st_type() == sym::STT_OBJECT, "symbol is object"); + ensure!(sym.st_bind() == sym::STB_LOCAL, "symbol is weak"); + ensure!( + sym.st_visibility() == sym::STV_PROTECTED, + "symbol is protected" + ); + ensure!(!sect.is_executable(), "not executable"); + ensure!(!sect.is_writable(), "immutable"); + Ok(()) + }, + ), + DeclTestCase::new("ordinary_func", Decl::function(), |sym, sect| { + ensure!(sym.is_function(), "symbol is function"); + ensure!(sym.st_bind() == sym::STB_LOCAL, "symbol is local"); + ensure!( + sym.st_visibility() == sym::STV_DEFAULT, + "symbol is default vis" + ); + ensure!(sect.is_executable(), "executable"); + ensure!(!sect.is_writable(), "immutable"); + ensure!(sect.sh_addralign == 16, "aligned to 16"); + Ok(()) + }), + DeclTestCase::new( + "custom_align_func", + Decl::function().with_align(Some(64)), + |_sym, sect| { + ensure!( + sect.sh_addralign == 64, + "expected aligned to 64, got {}", + sect.sh_addralign + ); + Ok(()) + }, + ), + DeclTestCase::new( + "custom_align_data", + Decl::data().with_align(Some(128)), + |_sym, sect| { + ensure!( + sect.sh_addralign == 128, + "expected aligned to 128, got {}", + sect.sh_addralign + ); + Ok(()) + }, + ), + ]); +} + +/* test scaffolding: */ + +fn decl_tests(tests: Vec) { + let mut obj = Artifact::new(triple!("x86_64-unknown-unknown-unknown-elf"), "a".into()); + for t in tests.iter() { + t.define(&mut obj); + } + + println!("\n{:#?}", obj); + let bytes = obj.emit().expect("can emit elf file"); + let bytes = bytes.as_slice(); + println!("{:?}", bytes); + + let elf = goblin::Object::parse(&bytes).expect("can parse elf file"); + + match elf { + goblin::Object::Elf(elf) => { + for t in tests { + t.check(&elf) + } + } + _ => { + panic!("Elf file not parsed as elf file"); + } + } +} + +struct DeclTestCase { + name: String, + decl: Decl, + pred: Box Result<(), Error>>, +} +impl DeclTestCase { + fn new(name: &str, decl: D, pred: F) -> Self + where + D: Into, + F: Fn(&Sym, &SectionHeader) -> Result<(), Error> + 'static, + { + Self { + name: name.to_owned(), + decl: decl.into(), + pred: Box::new(pred), + } + } + fn define(&self, art: &mut Artifact) { + art.declare(&self.name, self.decl) + .expect(&format!("declare {}", self.name)); + art.define(&self.name, vec![1, 2, 3, 4]) + .expect(&format!("define {}", self.name)); + } + fn check(&self, elf: &goblin::elf::Elf) { + let sym = elf + .syms + .iter() + .find(|sym| &elf.strtab[sym.st_name] == self.name) + .expect("symbol should exist"); + let sectheader = elf + .section_headers + .get(sym.st_shndx) + .expect("section header should exist"); + (self.pred)(&sym, sectheader).expect(&format!("check {}", self.name)) + } +}