diff --git a/mk/rustllvm.mk b/mk/rustllvm.mk index 50d99370142..6adffda7d1b 100644 --- a/mk/rustllvm.mk +++ b/mk/rustllvm.mk @@ -24,7 +24,8 @@ LLVM_EXTRA_INCDIRS_$(1)= $$(call CFG_CC_INCLUDE_$(1),$(S)src/llvm/include) \ endif RUSTLLVM_OBJS_CS_$(1) := $$(addprefix rustllvm/, \ - ExecutionEngineWrapper.cpp RustWrapper.cpp PassWrapper.cpp) + ExecutionEngineWrapper.cpp RustWrapper.cpp PassWrapper.cpp \ + ArchiveWrapper.cpp) RUSTLLVM_INCS_$(1) = $$(LLVM_EXTRA_INCDIRS_$(1)) \ $$(call CFG_CC_INCLUDE_$(1),$$(LLVM_INCDIR_$(1))) \ diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index e4e7459f8c6..732b77a626e 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -99,7 +99,6 @@ pub mod back { pub use rustc_back::abi; - pub use rustc_back::archive; pub use rustc_back::arm; pub use rustc_back::mips; pub use rustc_back::mipsel; diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index 2af3c393868..81acaf66e08 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -212,7 +212,6 @@ //! no means all of the necessary details. Take a look at the rest of //! metadata::loader or metadata::creader for all the juicy details! -use back::archive::METADATA_FILENAME; use back::svh::Svh; use session::Session; use session::search_paths::PathKind; @@ -280,6 +279,8 @@ pub struct CratePaths { pub rlib: Option } +pub const METADATA_FILENAME: &'static str = "rust.metadata.bin"; + impl CratePaths { fn paths(&self) -> Vec { match (&self.dylib, &self.rlib) { diff --git a/src/librustc_back/archive.rs b/src/librustc_back/archive.rs deleted file mode 100644 index c7968db4733..00000000000 --- a/src/librustc_back/archive.rs +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A helper class for dealing with static archives - -use std::env; -use std::ffi::OsString; -use std::fs::{self, File}; -use std::io::prelude::*; -use std::io; -use std::path::{Path, PathBuf}; -use std::process::{Command, Output, Stdio}; -use std::str; -use syntax::diagnostic::Handler as ErrorHandler; -use rustc_llvm::archive_ro::ArchiveRO; - -use tempdir::TempDir; - -pub const METADATA_FILENAME: &'static str = "rust.metadata.bin"; - -pub struct ArchiveConfig<'a> { - pub handler: &'a ErrorHandler, - pub dst: PathBuf, - pub lib_search_paths: Vec, - pub slib_prefix: String, - pub slib_suffix: String, - pub ar_prog: String, - pub command_path: OsString, -} - -pub struct Archive<'a> { - config: ArchiveConfig<'a>, -} - -/// Helper for adding many files to an archive with a single invocation of -/// `ar`. -#[must_use = "must call build() to finish building the archive"] -pub struct ArchiveBuilder<'a> { - archive: Archive<'a>, - work_dir: TempDir, - /// Filename of each member that should be added to the archive. - members: Vec, - should_update_symbols: bool, -} - -enum Action<'a> { - Remove(&'a Path), - AddObjects(&'a [&'a PathBuf], bool), - UpdateSymbols, -} - -pub fn find_library(name: &str, osprefix: &str, ossuffix: &str, - search_paths: &[PathBuf], - handler: &ErrorHandler) -> PathBuf { - // On Windows, static libraries sometimes show up as libfoo.a and other - // times show up as foo.lib - let oslibname = format!("{}{}{}", osprefix, name, ossuffix); - let unixlibname = format!("lib{}.a", name); - - for path in search_paths { - debug!("looking for {} inside {:?}", name, path); - let test = path.join(&oslibname[..]); - if test.exists() { return test } - if oslibname != unixlibname { - let test = path.join(&unixlibname[..]); - if test.exists() { return test } - } - } - handler.fatal(&format!("could not find native static library `{}`, \ - perhaps an -L flag is missing?", - name)); -} - -impl<'a> Archive<'a> { - fn new(config: ArchiveConfig<'a>) -> Archive<'a> { - Archive { config: config } - } - - /// Opens an existing static archive - pub fn open(config: ArchiveConfig<'a>) -> Archive<'a> { - let archive = Archive::new(config); - assert!(archive.config.dst.exists()); - archive - } - - /// Removes a file from this archive - pub fn remove_file(&mut self, file: &str) { - self.run(None, Action::Remove(Path::new(file))); - } - - /// Lists all files in an archive - pub fn files(&self) -> Vec { - let archive = match ArchiveRO::open(&self.config.dst) { - Some(ar) => ar, - None => return Vec::new(), - }; - let ret = archive.iter().filter_map(|child| child.name()) - .map(|name| name.to_string()) - .collect(); - return ret; - } - - /// Creates an `ArchiveBuilder` for adding files to this archive. - pub fn extend(self) -> ArchiveBuilder<'a> { - ArchiveBuilder::new(self) - } - - fn run(&self, cwd: Option<&Path>, action: Action) -> Output { - let abs_dst = env::current_dir().unwrap().join(&self.config.dst); - let ar = &self.config.ar_prog; - let mut cmd = Command::new(ar); - cmd.env("PATH", &self.config.command_path); - cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); - self.prepare_ar_action(&mut cmd, &abs_dst, action); - info!("{:?}", cmd); - - if let Some(p) = cwd { - cmd.current_dir(p); - info!("inside {:?}", p.display()); - } - - let handler = &self.config.handler; - match cmd.spawn() { - Ok(prog) => { - let o = prog.wait_with_output().unwrap(); - if !o.status.success() { - handler.err(&format!("{:?} failed with: {}", cmd, o.status)); - handler.note(&format!("stdout ---\n{}", - str::from_utf8(&o.stdout).unwrap())); - handler.note(&format!("stderr ---\n{}", - str::from_utf8(&o.stderr).unwrap())); - handler.abort_if_errors(); - } - o - }, - Err(e) => { - handler.err(&format!("could not exec `{}`: {}", - self.config.ar_prog, e)); - handler.abort_if_errors(); - panic!("rustc::back::archive::run() should not reach this point"); - } - } - } - - fn prepare_ar_action(&self, cmd: &mut Command, dst: &Path, action: Action) { - match action { - Action::Remove(file) => { - cmd.arg("d").arg(dst).arg(file); - } - Action::AddObjects(objs, update_symbols) => { - cmd.arg(if update_symbols {"crs"} else {"crS"}) - .arg(dst) - .args(objs); - } - Action::UpdateSymbols => { - cmd.arg("s").arg(dst); - } - } - } -} - -impl<'a> ArchiveBuilder<'a> { - fn new(archive: Archive<'a>) -> ArchiveBuilder<'a> { - ArchiveBuilder { - archive: archive, - work_dir: TempDir::new("rsar").unwrap(), - members: vec![], - should_update_symbols: false, - } - } - - /// Create a new static archive, ready for adding files. - pub fn create(config: ArchiveConfig<'a>) -> ArchiveBuilder<'a> { - let archive = Archive::new(config); - ArchiveBuilder::new(archive) - } - - /// Adds all of the contents of a native library to this archive. This will - /// search in the relevant locations for a library named `name`. - pub fn add_native_library(&mut self, name: &str) -> io::Result<()> { - let location = find_library(name, - &self.archive.config.slib_prefix, - &self.archive.config.slib_suffix, - &self.archive.config.lib_search_paths, - self.archive.config.handler); - self.add_archive(&location, name, |_| false) - } - - /// Adds all of the contents of the rlib at the specified path to this - /// archive. - /// - /// This ignores adding the bytecode from the rlib, and if LTO is enabled - /// then the object file also isn't added. - pub fn add_rlib(&mut self, rlib: &Path, name: &str, - lto: bool) -> io::Result<()> { - // Ignoring obj file starting with the crate name - // as simple comparison is not enough - there - // might be also an extra name suffix - let obj_start = format!("{}", name); - let obj_start = &obj_start[..]; - // Ignoring all bytecode files, no matter of - // name - let bc_ext = ".bytecode.deflate"; - - self.add_archive(rlib, &name[..], |fname: &str| { - let skip_obj = lto && fname.starts_with(obj_start) - && fname.ends_with(".o"); - skip_obj || fname.ends_with(bc_ext) || fname == METADATA_FILENAME - }) - } - - /// Adds an arbitrary file to this archive - pub fn add_file(&mut self, file: &Path) -> io::Result<()> { - let filename = Path::new(file.file_name().unwrap()); - let new_file = self.work_dir.path().join(&filename); - try!(fs::copy(file, &new_file)); - self.members.push(filename.to_path_buf()); - Ok(()) - } - - /// Indicate that the next call to `build` should updates all symbols in - /// the archive (run 'ar s' over it). - pub fn update_symbols(&mut self) { - self.should_update_symbols = true; - } - - /// Combine the provided files, rlibs, and native libraries into a single - /// `Archive`. - pub fn build(self) -> Archive<'a> { - // Get an absolute path to the destination, so `ar` will work even - // though we run it from `self.work_dir`. - let mut objects = Vec::new(); - let mut total_len = self.archive.config.dst.to_string_lossy().len(); - - if self.members.is_empty() { - if self.should_update_symbols { - self.archive.run(Some(self.work_dir.path()), - Action::UpdateSymbols); - } - return self.archive; - } - - // Don't allow the total size of `args` to grow beyond 32,000 bytes. - // Windows will raise an error if the argument string is longer than - // 32,768, and we leave a bit of extra space for the program name. - const ARG_LENGTH_LIMIT: usize = 32_000; - - for member_name in &self.members { - let len = member_name.to_string_lossy().len(); - - // `len + 1` to account for the space that's inserted before each - // argument. (Windows passes command-line arguments as a single - // string, not an array of strings.) - if total_len + len + 1 > ARG_LENGTH_LIMIT { - // Add the archive members seen so far, without updating the - // symbol table. - self.archive.run(Some(self.work_dir.path()), - Action::AddObjects(&objects, false)); - - objects.clear(); - total_len = self.archive.config.dst.to_string_lossy().len(); - } - - objects.push(member_name); - total_len += len + 1; - } - - // Add the remaining archive members, and update the symbol table if - // necessary. - self.archive.run(Some(self.work_dir.path()), - Action::AddObjects(&objects, self.should_update_symbols)); - - self.archive - } - - fn add_archive(&mut self, archive: &Path, name: &str, - mut skip: F) -> io::Result<()> - where F: FnMut(&str) -> bool, - { - let archive = match ArchiveRO::open(archive) { - Some(ar) => ar, - None => return Err(io::Error::new(io::ErrorKind::Other, - "failed to open archive")), - }; - - // Next, we must rename all of the inputs to "guaranteed unique names". - // We write each file into `self.work_dir` under its new unique name. - // The reason for this renaming is that archives are keyed off the name - // of the files, so if two files have the same name they will override - // one another in the archive (bad). - // - // We skip any files explicitly desired for skipping, and we also skip - // all SYMDEF files as these are just magical placeholders which get - // re-created when we make a new archive anyway. - for file in archive.iter() { - let filename = match file.name() { - Some(s) => s, - None => continue, - }; - if filename.contains(".SYMDEF") { continue } - if skip(filename) { continue } - let filename = Path::new(filename).file_name().unwrap() - .to_str().unwrap(); - - // Archives on unix systems typically do not have slashes in - // filenames as the `ar` utility generally only uses the last - // component of a path for the filename list in the archive. On - // Windows, however, archives assembled with `lib.exe` will preserve - // the full path to the file that was placed in the archive, - // including path separators. - // - // The code below is munging paths so it'll go wrong pretty quickly - // if there's some unexpected slashes in the filename, so here we - // just chop off everything but the filename component. Note that - // this can cause duplicate filenames, but that's also handled below - // as well. - let filename = Path::new(filename).file_name().unwrap() - .to_str().unwrap(); - - // An archive can contain files of the same name multiple times, so - // we need to be sure to not have them overwrite one another when we - // extract them. Consequently we need to find a truly unique file - // name for us! - let mut new_filename = String::new(); - for n in 0.. { - let n = if n == 0 {String::new()} else {format!("-{}", n)}; - new_filename = format!("r{}-{}-{}", n, name, filename); - - // LLDB (as mentioned in back::link) crashes on filenames of - // exactly - // 16 bytes in length. If we're including an object file with - // exactly 16-bytes of characters, give it some prefix so - // that it's not 16 bytes. - new_filename = if new_filename.len() == 16 { - format!("lldb-fix-{}", new_filename) - } else { - new_filename - }; - - let present = self.members.iter().filter_map(|p| { - p.file_name().and_then(|f| f.to_str()) - }).any(|s| s == new_filename); - if !present { - break - } - } - let dst = self.work_dir.path().join(&new_filename); - try!(try!(File::create(&dst)).write_all(file.data())); - self.members.push(PathBuf::from(new_filename)); - } - - Ok(()) - } -} diff --git a/src/librustc_back/lib.rs b/src/librustc_back/lib.rs index 297041a9907..ef5ba625e11 100644 --- a/src/librustc_back/lib.rs +++ b/src/librustc_back/lib.rs @@ -51,7 +51,6 @@ #[macro_use] extern crate log; pub mod abi; -pub mod archive; pub mod tempdir; pub mod arm; pub mod mips; diff --git a/src/librustc_back/target/linux_base.rs b/src/librustc_back/target/linux_base.rs index 3ae70ca854b..b2dcd5aae21 100644 --- a/src/librustc_back/target/linux_base.rs +++ b/src/librustc_back/target/linux_base.rs @@ -29,6 +29,7 @@ pub fn opts() -> TargetOptions { "-Wl,--as-needed".to_string(), ], position_independent_executables: true, + archive_format: "gnu".to_string(), .. Default::default() } } diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index bc5f306cd35..8ecee616219 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -166,6 +166,11 @@ pub struct TargetOptions { /// the functions in the executable are not randomized and can be used /// during an exploit of a vulnerability in any code. pub position_independent_executables: bool, + /// Format that archives should be emitted in. This affects whether we use + /// LLVM to assemble an archive or fall back to the system linker, and + /// currently only "gnu" is used to fall into LLVM. Unknown strings cause + /// the system linker to be used. + pub archive_format: String, } impl Default for TargetOptions { @@ -202,6 +207,7 @@ fn default() -> TargetOptions { position_independent_executables: false, pre_link_objects: Vec::new(), post_link_objects: Vec::new(), + archive_format: String::new(), } } } diff --git a/src/librustc_back/target/windows_base.rs b/src/librustc_back/target/windows_base.rs index 148be8cab76..fd29fe89271 100644 --- a/src/librustc_back/target/windows_base.rs +++ b/src/librustc_back/target/windows_base.rs @@ -25,6 +25,7 @@ pub fn opts() -> TargetOptions { staticlib_suffix: ".lib".to_string(), morestack: false, is_like_windows: true, + archive_format: "gnu".to_string(), pre_link_args: vec!( // And here, we see obscure linker flags #45. On windows, it has been // found to be necessary to have this flag to compile liblibc. diff --git a/src/librustc_llvm/archive_ro.rs b/src/librustc_llvm/archive_ro.rs index b591e37f893..2c6022bc614 100644 --- a/src/librustc_llvm/archive_ro.rs +++ b/src/librustc_llvm/archive_ro.rs @@ -13,6 +13,7 @@ use ArchiveRef; use std::ffi::CString; +use std::marker; use std::path::Path; use std::slice; use std::str; @@ -25,8 +26,8 @@ pub struct Iter<'a> { } pub struct Child<'a> { - name: Option<&'a str>, - data: &'a [u8], + ptr: ::ArchiveChildRef, + _data: marker::PhantomData<&'a ArchiveRO>, } impl ArchiveRO { @@ -60,6 +61,8 @@ fn path2cstr(p: &Path) -> CString { } } + pub fn raw(&self) -> ArchiveRef { self.ptr } + pub fn iter(&self) -> Iter { unsafe { Iter { ptr: ::LLVMRustArchiveIteratorNew(self.ptr), archive: self } @@ -79,28 +82,11 @@ impl<'a> Iterator for Iter<'a> { type Item = Child<'a>; fn next(&mut self) -> Option> { - unsafe { - let ptr = ::LLVMRustArchiveIteratorCurrent(self.ptr); - if ptr.is_null() { - return None - } - let mut name_len = 0; - let name_ptr = ::LLVMRustArchiveChildName(ptr, &mut name_len); - let mut data_len = 0; - let data_ptr = ::LLVMRustArchiveChildData(ptr, &mut data_len); - let child = Child { - name: if name_ptr.is_null() { - None - } else { - let name = slice::from_raw_parts(name_ptr as *const u8, - name_len as usize); - str::from_utf8(name).ok().map(|s| s.trim()) - }, - data: slice::from_raw_parts(data_ptr as *const u8, - data_len as usize), - }; - ::LLVMRustArchiveIteratorNext(self.ptr); - Some(child) + let ptr = unsafe { ::LLVMRustArchiveIteratorNext(self.ptr) }; + if ptr.is_null() { + None + } else { + Some(Child { ptr: ptr, _data: marker::PhantomData }) } } } @@ -114,6 +100,33 @@ fn drop(&mut self) { } impl<'a> Child<'a> { - pub fn name(&self) -> Option<&'a str> { self.name } - pub fn data(&self) -> &'a [u8] { self.data } + pub fn name(&self) -> Option<&'a str> { + unsafe { + let mut name_len = 0; + let name_ptr = ::LLVMRustArchiveChildName(self.ptr, &mut name_len); + if name_ptr.is_null() { + None + } else { + let name = slice::from_raw_parts(name_ptr as *const u8, + name_len as usize); + str::from_utf8(name).ok().map(|s| s.trim()) + } + } + } + + pub fn data(&self) -> &'a [u8] { + unsafe { + let mut data_len = 0; + let data_ptr = ::LLVMRustArchiveChildData(self.ptr, &mut data_len); + slice::from_raw_parts(data_ptr as *const u8, data_len as usize) + } + } + + pub fn raw(&self) -> ::ArchiveChildRef { self.ptr } +} + +impl<'a> Drop for Child<'a> { + fn drop(&mut self) { + unsafe { ::LLVMRustArchiveChildFree(self.ptr); } + } } diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index c4960c8d6a3..7afcb54cce4 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -522,6 +522,9 @@ pub enum DebugLoc_opaque {} #[allow(missing_copy_implementations)] pub enum SMDiagnostic_opaque {} pub type SMDiagnosticRef = *mut SMDiagnostic_opaque; +#[allow(missing_copy_implementations)] +pub enum RustArchiveMember_opaque {} +pub type RustArchiveMemberRef = *mut RustArchiveMember_opaque; pub type DiagnosticHandler = unsafe extern "C" fn(DiagnosticInfoRef, *mut c_void); pub type InlineAsmDiagHandler = unsafe extern "C" fn(SMDiagnosticRef, *const c_void, c_uint); @@ -2069,12 +2072,12 @@ pub fn LLVMRustRunRestrictionPass(M: ModuleRef, pub fn LLVMRustOpenArchive(path: *const c_char) -> ArchiveRef; pub fn LLVMRustArchiveIteratorNew(AR: ArchiveRef) -> ArchiveIteratorRef; - pub fn LLVMRustArchiveIteratorNext(AIR: ArchiveIteratorRef); - pub fn LLVMRustArchiveIteratorCurrent(AIR: ArchiveIteratorRef) -> ArchiveChildRef; + pub fn LLVMRustArchiveIteratorNext(AIR: ArchiveIteratorRef) -> ArchiveChildRef; pub fn LLVMRustArchiveChildName(ACR: ArchiveChildRef, size: *mut size_t) -> *const c_char; pub fn LLVMRustArchiveChildData(ACR: ArchiveChildRef, size: *mut size_t) -> *const c_char; + pub fn LLVMRustArchiveChildFree(ACR: ArchiveChildRef); pub fn LLVMRustArchiveIteratorFree(AIR: ArchiveIteratorRef); pub fn LLVMRustDestroyArchive(AR: ArchiveRef); @@ -2111,6 +2114,15 @@ pub fn LLVMSetInlineAsmDiagnosticHandler(C: ContextRef, CX: *mut c_void); pub fn LLVMWriteSMDiagnosticToString(d: SMDiagnosticRef, s: RustStringRef); + + pub fn LLVMRustWriteArchive(Dst: *const c_char, + NumMembers: size_t, + Members: *const RustArchiveMemberRef, + WriteSymbtab: bool) -> c_int; + pub fn LLVMRustArchiveMemberNew(Filename: *const c_char, + Name: *const c_char, + Child: ArchiveChildRef) -> RustArchiveMemberRef; + pub fn LLVMRustArchiveMemberFree(Member: RustArchiveMemberRef); } // LLVM requires symbols from this library, but apparently they're not printed diff --git a/src/librustc_trans/back/archive.rs b/src/librustc_trans/back/archive.rs new file mode 100644 index 00000000000..cc3d1d842fa --- /dev/null +++ b/src/librustc_trans/back/archive.rs @@ -0,0 +1,504 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A helper class for dealing with static archives + +use std::env; +use std::ffi::{CString, CStr, OsString}; +use std::fs::{self, File}; +use std::io::prelude::*; +use std::io; +use std::mem; +use std::path::{Path, PathBuf}; +use std::process::{Command, Output, Stdio}; +use std::str; + +use libc; +use llvm::archive_ro::{ArchiveRO, Child}; +use llvm; +use rustc::metadata::loader::METADATA_FILENAME; +use rustc::session::Session; +use rustc_back::tempdir::TempDir; + +pub struct ArchiveConfig<'a> { + pub sess: &'a Session, + pub dst: PathBuf, + pub src: Option, + pub lib_search_paths: Vec, + pub ar_prog: String, + pub command_path: OsString, +} + +/// Helper for adding many files to an archive with a single invocation of +/// `ar`. +#[must_use = "must call build() to finish building the archive"] +pub struct ArchiveBuilder<'a> { + config: ArchiveConfig<'a>, + work_dir: TempDir, + removals: Vec, + additions: Vec, + should_update_symbols: bool, + src_archive: Option>, +} + +enum Addition { + File { + path: PathBuf, + name_in_archive: String, + }, + Archive { + archive: ArchiveRO, + archive_name: String, + skip: Box bool>, + }, +} + +enum Action<'a> { + Remove(&'a [String]), + AddObjects(&'a [&'a PathBuf], bool), + UpdateSymbols, +} + +pub fn find_library(name: &str, search_paths: &[PathBuf], sess: &Session) + -> PathBuf { + // On Windows, static libraries sometimes show up as libfoo.a and other + // times show up as foo.lib + let oslibname = format!("{}{}{}", + sess.target.target.options.staticlib_prefix, + name, + sess.target.target.options.staticlib_suffix); + let unixlibname = format!("lib{}.a", name); + + for path in search_paths { + debug!("looking for {} inside {:?}", name, path); + let test = path.join(&oslibname[..]); + if test.exists() { return test } + if oslibname != unixlibname { + let test = path.join(&unixlibname[..]); + if test.exists() { return test } + } + } + sess.fatal(&format!("could not find native static library `{}`, \ + perhaps an -L flag is missing?", name)); +} + +fn is_relevant_child(c: &Child) -> bool { + match c.name() { + Some(name) => !name.contains("SYMDEF"), + None => false, + } +} + +impl<'a> ArchiveBuilder<'a> { + /// Create a new static archive, ready for modifying the archive specified + /// by `config`. + pub fn new(config: ArchiveConfig<'a>) -> ArchiveBuilder<'a> { + ArchiveBuilder { + config: config, + work_dir: TempDir::new("rsar").unwrap(), + removals: Vec::new(), + additions: Vec::new(), + should_update_symbols: false, + src_archive: None, + } + } + + /// Removes a file from this archive + pub fn remove_file(&mut self, file: &str) { + self.removals.push(file.to_string()); + } + + /// Lists all files in an archive + pub fn src_files(&mut self) -> Vec { + if self.src_archive().is_none() { + return Vec::new() + } + let archive = self.src_archive.as_ref().unwrap().as_ref().unwrap(); + let ret = archive.iter() + .filter(is_relevant_child) + .filter_map(|child| child.name()) + .filter(|name| !self.removals.iter().any(|x| x == name)) + .map(|name| name.to_string()) + .collect(); + return ret; + } + + fn src_archive(&mut self) -> Option<&ArchiveRO> { + if let Some(ref a) = self.src_archive { + return a.as_ref() + } + let src = match self.config.src { + Some(ref src) => src, + None => return None, + }; + self.src_archive = Some(ArchiveRO::open(src)); + self.src_archive.as_ref().unwrap().as_ref() + } + + /// Adds all of the contents of a native library to this archive. This will + /// search in the relevant locations for a library named `name`. + pub fn add_native_library(&mut self, name: &str) -> io::Result<()> { + let location = find_library(name, &self.config.lib_search_paths, + self.config.sess); + self.add_archive(&location, name, |_| false) + } + + /// Adds all of the contents of the rlib at the specified path to this + /// archive. + /// + /// This ignores adding the bytecode from the rlib, and if LTO is enabled + /// then the object file also isn't added. + pub fn add_rlib(&mut self, rlib: &Path, name: &str, lto: bool) + -> io::Result<()> { + // Ignoring obj file starting with the crate name + // as simple comparison is not enough - there + // might be also an extra name suffix + let obj_start = format!("{}", name); + + // Ignoring all bytecode files, no matter of + // name + let bc_ext = ".bytecode.deflate"; + + self.add_archive(rlib, &name[..], move |fname: &str| { + let skip_obj = lto && fname.starts_with(&obj_start) + && fname.ends_with(".o"); + skip_obj || fname.ends_with(bc_ext) || fname == METADATA_FILENAME + }) + } + + fn add_archive(&mut self, archive: &Path, name: &str, skip: F) + -> io::Result<()> + where F: FnMut(&str) -> bool + 'static + { + let archive = match ArchiveRO::open(archive) { + Some(ar) => ar, + None => return Err(io::Error::new(io::ErrorKind::Other, + "failed to open archive")), + }; + self.additions.push(Addition::Archive { + archive: archive, + archive_name: name.to_string(), + skip: Box::new(skip), + }); + Ok(()) + } + + /// Adds an arbitrary file to this archive + pub fn add_file(&mut self, file: &Path) { + let name = file.file_name().unwrap().to_str().unwrap(); + self.additions.push(Addition::File { + path: file.to_path_buf(), + name_in_archive: name.to_string(), + }); + } + + /// Indicate that the next call to `build` should updates all symbols in + /// the archive (run 'ar s' over it). + pub fn update_symbols(&mut self) { + self.should_update_symbols = true; + } + + /// Combine the provided files, rlibs, and native libraries into a single + /// `Archive`. + pub fn build(&mut self) { + let res = if self.using_llvm() { + self.build_with_llvm() + } else { + self.build_with_ar_cmd() + }; + if let Err(e) = res { + self.config.sess.fatal(&format!("failed to build archive: {}", e)); + } + } + + pub fn using_llvm(&self) -> bool { + if unsafe { llvm::LLVMVersionMinor() < 7 } { + return false + } + + // Currently LLVM only supports writing archives in the 'gnu' format. + match &self.config.sess.target.target.options.archive_format[..] { + "gnu" => true, + _ => false, + } + } + + fn build_with_ar_cmd(&mut self) -> io::Result<()> { + let removals = mem::replace(&mut self.removals, Vec::new()); + let additions = mem::replace(&mut self.additions, Vec::new()); + let should_update_symbols = mem::replace(&mut self.should_update_symbols, + false); + + // Don't use fs::copy because libs may be installed as read-only and we + // want to modify this archive, so we use `io::copy` to not preserve + // permission bits. + if let Some(ref s) = self.config.src { + try!(io::copy(&mut try!(File::open(s)), + &mut try!(File::create(&self.config.dst)))); + } + + if removals.len() > 0 { + self.run(None, Action::Remove(&removals)); + } + + let mut members = Vec::new(); + for addition in additions { + match addition { + Addition::File { path, name_in_archive } => { + let dst = self.work_dir.path().join(&name_in_archive); + try!(fs::copy(&path, &dst)); + members.push(PathBuf::from(name_in_archive)); + } + Addition::Archive { archive, archive_name, mut skip } => { + try!(self.add_archive_members(&mut members, archive, + &archive_name, &mut *skip)); + } + } + } + + // Get an absolute path to the destination, so `ar` will work even + // though we run it from `self.work_dir`. + let mut objects = Vec::new(); + let mut total_len = self.config.dst.to_string_lossy().len(); + + if members.is_empty() { + if should_update_symbols { + self.run(Some(self.work_dir.path()), Action::UpdateSymbols); + } + return Ok(()) + } + + // Don't allow the total size of `args` to grow beyond 32,000 bytes. + // Windows will raise an error if the argument string is longer than + // 32,768, and we leave a bit of extra space for the program name. + const ARG_LENGTH_LIMIT: usize = 32_000; + + for member_name in &members { + let len = member_name.to_string_lossy().len(); + + // `len + 1` to account for the space that's inserted before each + // argument. (Windows passes command-line arguments as a single + // string, not an array of strings.) + if total_len + len + 1 > ARG_LENGTH_LIMIT { + // Add the archive members seen so far, without updating the + // symbol table. + self.run(Some(self.work_dir.path()), + Action::AddObjects(&objects, false)); + + objects.clear(); + total_len = self.config.dst.to_string_lossy().len(); + } + + objects.push(member_name); + total_len += len + 1; + } + + // Add the remaining archive members, and update the symbol table if + // necessary. + self.run(Some(self.work_dir.path()), + Action::AddObjects(&objects, should_update_symbols)); + Ok(()) + } + + fn add_archive_members(&mut self, members: &mut Vec, + archive: ArchiveRO, name: &str, + skip: &mut FnMut(&str) -> bool) -> io::Result<()> { + // Next, we must rename all of the inputs to "guaranteed unique names". + // We write each file into `self.work_dir` under its new unique name. + // The reason for this renaming is that archives are keyed off the name + // of the files, so if two files have the same name they will override + // one another in the archive (bad). + // + // We skip any files explicitly desired for skipping, and we also skip + // all SYMDEF files as these are just magical placeholders which get + // re-created when we make a new archive anyway. + for file in archive.iter().filter(is_relevant_child) { + let filename = file.name().unwrap(); + if skip(filename) { continue } + let filename = Path::new(filename).file_name().unwrap() + .to_str().unwrap(); + + // Archives on unix systems typically do not have slashes in + // filenames as the `ar` utility generally only uses the last + // component of a path for the filename list in the archive. On + // Windows, however, archives assembled with `lib.exe` will preserve + // the full path to the file that was placed in the archive, + // including path separators. + // + // The code below is munging paths so it'll go wrong pretty quickly + // if there's some unexpected slashes in the filename, so here we + // just chop off everything but the filename component. Note that + // this can cause duplicate filenames, but that's also handled below + // as well. + let filename = Path::new(filename).file_name().unwrap() + .to_str().unwrap(); + + // An archive can contain files of the same name multiple times, so + // we need to be sure to not have them overwrite one another when we + // extract them. Consequently we need to find a truly unique file + // name for us! + let mut new_filename = String::new(); + for n in 0.. { + let n = if n == 0 {String::new()} else {format!("-{}", n)}; + new_filename = format!("r{}-{}-{}", n, name, filename); + + // LLDB (as mentioned in back::link) crashes on filenames of + // exactly + // 16 bytes in length. If we're including an object file with + // exactly 16-bytes of characters, give it some prefix so + // that it's not 16 bytes. + new_filename = if new_filename.len() == 16 { + format!("lldb-fix-{}", new_filename) + } else { + new_filename + }; + + let present = members.iter().filter_map(|p| { + p.file_name().and_then(|f| f.to_str()) + }).any(|s| s == new_filename); + if !present { + break + } + } + let dst = self.work_dir.path().join(&new_filename); + try!(try!(File::create(&dst)).write_all(file.data())); + members.push(PathBuf::from(new_filename)); + } + Ok(()) + } + + fn run(&self, cwd: Option<&Path>, action: Action) -> Output { + let abs_dst = env::current_dir().unwrap().join(&self.config.dst); + let ar = &self.config.ar_prog; + let mut cmd = Command::new(ar); + cmd.env("PATH", &self.config.command_path); + cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); + self.prepare_ar_action(&mut cmd, &abs_dst, action); + info!("{:?}", cmd); + + if let Some(p) = cwd { + cmd.current_dir(p); + info!("inside {:?}", p.display()); + } + + let sess = &self.config.sess; + match cmd.spawn() { + Ok(prog) => { + let o = prog.wait_with_output().unwrap(); + if !o.status.success() { + sess.err(&format!("{:?} failed with: {}", cmd, o.status)); + sess.note(&format!("stdout ---\n{}", + str::from_utf8(&o.stdout).unwrap())); + sess.note(&format!("stderr ---\n{}", + str::from_utf8(&o.stderr).unwrap())); + sess.abort_if_errors(); + } + o + }, + Err(e) => { + sess.fatal(&format!("could not exec `{}`: {}", + self.config.ar_prog, e)); + } + } + } + + fn prepare_ar_action(&self, cmd: &mut Command, dst: &Path, action: Action) { + match action { + Action::Remove(files) => { + cmd.arg("d").arg(dst).args(files); + } + Action::AddObjects(objs, update_symbols) => { + cmd.arg(if update_symbols {"crs"} else {"crS"}) + .arg(dst) + .args(objs); + } + Action::UpdateSymbols => { + cmd.arg("s").arg(dst); + } + } + } + + fn build_with_llvm(&mut self) -> io::Result<()> { + let mut archives = Vec::new(); + let mut strings = Vec::new(); + let mut members = Vec::new(); + let removals = mem::replace(&mut self.removals, Vec::new()); + + unsafe { + if let Some(archive) = self.src_archive() { + for child in archive.iter() { + let child_name = match child.name() { + Some(s) => s, + None => continue, + }; + if removals.iter().any(|r| r == child_name) { + continue + } + + let name = try!(CString::new(child_name)); + members.push(llvm::LLVMRustArchiveMemberNew(0 as *const _, + name.as_ptr(), + child.raw())); + strings.push(name); + } + } + for addition in mem::replace(&mut self.additions, Vec::new()) { + match addition { + Addition::File { path, name_in_archive } => { + let path = try!(CString::new(path.to_str().unwrap())); + let name = try!(CString::new(name_in_archive)); + members.push(llvm::LLVMRustArchiveMemberNew(path.as_ptr(), + name.as_ptr(), + 0 as *mut _)); + strings.push(path); + strings.push(name); + } + Addition::Archive { archive, archive_name: _, mut skip } => { + for child in archive.iter().filter(is_relevant_child) { + let child_name = child.name().unwrap(); + if skip(child_name) { continue } + + let name = try!(CString::new(child_name)); + let m = llvm::LLVMRustArchiveMemberNew(0 as *const _, + name.as_ptr(), + child.raw()); + members.push(m); + strings.push(name); + } + archives.push(archive); + } + } + } + + let dst = self.config.dst.to_str().unwrap().as_bytes(); + let dst = try!(CString::new(dst)); + let r = llvm::LLVMRustWriteArchive(dst.as_ptr(), + members.len() as libc::size_t, + members.as_ptr(), + self.should_update_symbols); + let ret = if r != 0 { + let err = llvm::LLVMRustGetLastError(); + let msg = if err.is_null() { + "failed to write archive".to_string() + } else { + String::from_utf8_lossy(CStr::from_ptr(err).to_bytes()) + .into_owned() + }; + Err(io::Error::new(io::ErrorKind::Other, msg)) + } else { + Ok(()) + }; + for member in members { + llvm::LLVMRustArchiveMemberFree(member); + } + return ret + } + } +} diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 21bc61593c9..d24d8369751 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::archive::{Archive, ArchiveBuilder, ArchiveConfig, METADATA_FILENAME}; +use super::archive::{ArchiveBuilder, ArchiveConfig}; use super::linker::{Linker, GnuLinker, MsvcLinker}; use super::rpath::RPathConfig; use super::rpath; @@ -20,8 +20,9 @@ use session::search_paths::PathKind; use session::Session; use metadata::common::LinkMeta; -use metadata::{encoder, cstore, filesearch, csearch, creader}; use metadata::filesearch::FileDoesntMatch; +use metadata::loader::METADATA_FILENAME; +use metadata::{encoder, cstore, filesearch, csearch, creader}; use middle::ty::{self, Ty}; use rustc::ast_map::{PathElem, PathElems, PathName}; use trans::{CrateContext, CrateTranslation, gensym_name}; @@ -513,18 +514,22 @@ fn link_binary_output(sess: &Session, } } + let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir"); match crate_type { config::CrateTypeRlib => { - link_rlib(sess, Some(trans), &objects, &out_filename).build(); + link_rlib(sess, Some(trans), &objects, &out_filename, + tmpdir.path()).build(); } config::CrateTypeStaticlib => { - link_staticlib(sess, &objects, &out_filename); + link_staticlib(sess, &objects, &out_filename, tmpdir.path()); } config::CrateTypeExecutable => { - link_natively(sess, trans, false, &objects, &out_filename, outputs); + link_natively(sess, trans, false, &objects, &out_filename, outputs, + tmpdir.path()); } config::CrateTypeDylib => { - link_natively(sess, trans, true, &objects, &out_filename, outputs); + link_natively(sess, trans, true, &objects, &out_filename, outputs, + tmpdir.path()); } } @@ -548,13 +553,13 @@ fn archive_search_paths(sess: &Session) -> Vec { } fn archive_config<'a>(sess: &'a Session, - output: &Path) -> ArchiveConfig<'a> { + output: &Path, + input: Option<&Path>) -> ArchiveConfig<'a> { ArchiveConfig { - handler: &sess.diagnostic().handler, + sess: sess, dst: output.to_path_buf(), + src: input.map(|p| p.to_path_buf()), lib_search_paths: archive_search_paths(sess), - slib_prefix: sess.target.target.options.staticlib_prefix.clone(), - slib_suffix: sess.target.target.options.staticlib_suffix.clone(), ar_prog: get_ar_prog(sess), command_path: command_path(sess), } @@ -569,11 +574,12 @@ fn archive_config<'a>(sess: &'a Session, fn link_rlib<'a>(sess: &'a Session, trans: Option<&CrateTranslation>, // None == no metadata/bytecode objects: &[PathBuf], - out_filename: &Path) -> ArchiveBuilder<'a> { + out_filename: &Path, + tmpdir: &Path) -> ArchiveBuilder<'a> { info!("preparing rlib from {:?} to {:?}", objects, out_filename); - let mut ab = ArchiveBuilder::create(archive_config(sess, out_filename)); + let mut ab = ArchiveBuilder::new(archive_config(sess, out_filename, None)); for obj in objects { - ab.add_file(obj).unwrap(); + ab.add_file(obj); } for &(ref l, kind) in sess.cstore.get_used_libraries().borrow().iter() { @@ -587,13 +593,12 @@ fn link_rlib<'a>(sess: &'a Session, // symbol table of the archive. ab.update_symbols(); - let mut ab = match sess.target.target.options.is_like_osx { - // For OSX/iOS, we must be careful to update symbols only when adding - // object files. We're about to start adding non-object files, so run - // `ar` now to process the object files. - true => ab.build().extend(), - false => ab, - }; + // For OSX/iOS, we must be careful to update symbols only when adding + // object files. We're about to start adding non-object files, so run + // `ar` now to process the object files. + if sess.target.target.options.is_like_osx && !ab.using_llvm() { + ab.build(); + } // Note that it is important that we add all of our non-object "magical // files" *after* all of the object files in the archive. The reason for @@ -622,8 +627,7 @@ fn link_rlib<'a>(sess: &'a Session, // contain the metadata in a separate file. We use a temp directory // here so concurrent builds in the same directory don't try to use // the same filename for metadata (stomping over one another) - let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir"); - let metadata = tmpdir.path().join(METADATA_FILENAME); + let metadata = tmpdir.join(METADATA_FILENAME); match fs::File::create(&metadata).and_then(|mut f| { f.write_all(&trans.metadata) }) { @@ -633,8 +637,7 @@ fn link_rlib<'a>(sess: &'a Session, metadata.display(), e)); } } - ab.add_file(&metadata).unwrap(); - remove(sess, &metadata); + ab.add_file(&metadata); // For LTO purposes, the bytecode of this library is also inserted // into the archive. If codegen_units > 1, we insert each of the @@ -646,7 +649,9 @@ fn link_rlib<'a>(sess: &'a Session, // would cause it to crash if the name of a file in an archive // was exactly 16 bytes. let bc_filename = obj.with_extension("bc"); - let bc_deflated_filename = obj.with_extension("bytecode.deflate"); + let bc_deflated_filename = tmpdir.join({ + obj.with_extension("bytecode.deflate").file_name().unwrap() + }); let mut bc_data = Vec::new(); match fs::File::open(&bc_filename).and_then(|mut f| { @@ -676,8 +681,7 @@ fn link_rlib<'a>(sess: &'a Session, } }; - ab.add_file(&bc_deflated_filename).unwrap(); - remove(sess, &bc_deflated_filename); + ab.add_file(&bc_deflated_filename); // See the bottom of back::write::run_passes for an explanation // of when we do and don't keep .0.bc files around. @@ -692,7 +696,7 @@ fn link_rlib<'a>(sess: &'a Session, // After adding all files to the archive, we need to update the // symbol table of the archive. This currently dies on OSX (see // #11162), and isn't necessary there anyway - if !sess.target.target.options.is_like_osx { + if !sess.target.target.options.is_like_osx || ab.using_llvm() { ab.update_symbols(); } } @@ -749,12 +753,12 @@ fn write_rlib_bytecode_object_v1(writer: &mut Write, // There's no need to include metadata in a static archive, so ensure to not // link in the metadata object file (and also don't prepare the archive with a // metadata file). -fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path) { - let ab = link_rlib(sess, None, objects, out_filename); - let mut ab = match sess.target.target.options.is_like_osx { - true => ab.build().extend(), - false => ab, - }; +fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path, + tempdir: &Path) { + let mut ab = link_rlib(sess, None, objects, out_filename, tempdir); + if sess.target.target.options.is_like_osx && !ab.using_llvm() { + ab.build(); + } if sess.target.target.options.morestack { ab.add_native_library("morestack").unwrap(); } @@ -781,7 +785,7 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path) { } ab.update_symbols(); - let _ = ab.build(); + ab.build(); if !all_native_libs.is_empty() { sess.note("link against the following native artifacts when linking against \ @@ -806,10 +810,10 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path) { // links to all upstream files as well. fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, objects: &[PathBuf], out_filename: &Path, - outputs: &OutputFilenames) { + outputs: &OutputFilenames, + tmpdir: &Path) { info!("preparing dylib? ({}) from {:?} to {:?}", dylib, objects, out_filename); - let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir"); // The invocations of cc share some flags across platforms let (pname, mut cmd) = get_linker(sess); @@ -827,7 +831,7 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, } else { Box::new(GnuLinker { cmd: &mut cmd, sess: &sess }) as Box }; - link_args(&mut *linker, sess, dylib, tmpdir.path(), + link_args(&mut *linker, sess, dylib, tmpdir, trans, objects, out_filename, outputs); if !sess.target.target.options.no_compiler_rt { linker.link_staticlib("compiler-rt"); @@ -1185,20 +1189,13 @@ fn add_static_crate(cmd: &mut Linker, sess: &Session, tmpdir: &Path, let name = &name[3..name.len() - 5]; // chop off lib/.rlib time(sess.time_passes(), &format!("altering {}.rlib", name), (), |()| { - let err = (|| { - io::copy(&mut try!(fs::File::open(&cratepath)), - &mut try!(fs::File::create(&dst))) - })(); - if let Err(e) = err { - sess.fatal(&format!("failed to copy {} to {}: {}", - cratepath.display(), dst.display(), e)); - } - - let mut archive = Archive::open(archive_config(sess, &dst)); + let cfg = archive_config(sess, &dst, Some(cratepath)); + let mut archive = ArchiveBuilder::new(cfg); archive.remove_file(METADATA_FILENAME); + archive.update_symbols(); let mut any_objects = false; - for f in archive.files() { + for f in archive.src_files() { if f.ends_with("bytecode.deflate") { archive.remove_file(&f); continue @@ -1217,6 +1214,7 @@ fn add_static_crate(cmd: &mut Linker, sess: &Session, tmpdir: &Path, } if any_objects { + archive.build(); cmd.link_whole_rlib(&fix_windows_verbatim_for_gcc(&dst)); } }); diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index 518a6c24840..a272e7c4c8f 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -13,7 +13,7 @@ use std::process::Command; use std::fs; -use rustc_back::archive; +use back::archive; use session::Session; use session::config; @@ -88,11 +88,7 @@ fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]) { // -force_load is the OSX equivalent of --whole-archive, but it // involves passing the full path to the library to link. let mut v = OsString::from("-Wl,-force_load,"); - v.push(&archive::find_library(lib, - &target.options.staticlib_prefix, - &target.options.staticlib_suffix, - search_path, - &self.sess.diagnostic().handler)); + v.push(&archive::find_library(lib, search_path, &self.sess)); self.cmd.arg(&v); } } diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index cd2e597db13..0e425e1fd99 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -53,8 +53,8 @@ extern crate libc; extern crate rustc; extern crate rustc_back; -extern crate serialize; extern crate rustc_llvm as llvm; +extern crate serialize; #[macro_use] extern crate log; #[macro_use] extern crate syntax; @@ -68,7 +68,6 @@ pub mod back { pub use rustc_back::abi; - pub use rustc_back::archive; pub use rustc_back::arm; pub use rustc_back::mips; pub use rustc_back::mipsel; @@ -78,6 +77,7 @@ pub mod back { pub use rustc_back::x86; pub use rustc_back::x86_64; + pub mod archive; pub mod linker; pub mod link; pub mod lto; diff --git a/src/rustllvm/ArchiveWrapper.cpp b/src/rustllvm/ArchiveWrapper.cpp new file mode 100644 index 00000000000..2e94c196935 --- /dev/null +++ b/src/rustllvm/ArchiveWrapper.cpp @@ -0,0 +1,168 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#include "rustllvm.h" + +#include "llvm/Object/Archive.h" + +#if LLVM_VERSION_MINOR >= 7 +#include "llvm/Object/ArchiveWriter.h" +#endif + +using namespace llvm; +using namespace llvm::object; + +struct LLVMRustArchiveMember { + const char *filename; + const char *name; + Archive::Child child; + + LLVMRustArchiveMember(): filename(NULL), name(NULL), child(NULL, NULL) {} + ~LLVMRustArchiveMember() {} +}; + +#if LLVM_VERSION_MINOR >= 6 +typedef OwningBinary RustArchive; +#define GET_ARCHIVE(a) ((a)->getBinary()) +#else +typedef Archive RustArchive; +#define GET_ARCHIVE(a) (a) +#endif + +extern "C" void* +LLVMRustOpenArchive(char *path) { + ErrorOr> buf_or = MemoryBuffer::getFile(path, + -1, + false); + if (!buf_or) { + LLVMRustSetLastError(buf_or.getError().message().c_str()); + return nullptr; + } + +#if LLVM_VERSION_MINOR >= 6 + ErrorOr> archive_or = + Archive::create(buf_or.get()->getMemBufferRef()); + + if (!archive_or) { + LLVMRustSetLastError(archive_or.getError().message().c_str()); + return nullptr; + } + + OwningBinary *ret = new OwningBinary( + std::move(archive_or.get()), std::move(buf_or.get())); +#else + std::error_code err; + Archive *ret = new Archive(std::move(buf_or.get()), err); + if (err) { + LLVMRustSetLastError(err.message().c_str()); + return nullptr; + } +#endif + + return ret; +} + +extern "C" void +LLVMRustDestroyArchive(RustArchive *ar) { + delete ar; +} + +struct RustArchiveIterator { + Archive::child_iterator cur; + Archive::child_iterator end; +}; + +extern "C" RustArchiveIterator* +LLVMRustArchiveIteratorNew(RustArchive *ra) { + Archive *ar = GET_ARCHIVE(ra); + RustArchiveIterator *rai = new RustArchiveIterator(); + rai->cur = ar->child_begin(); + rai->end = ar->child_end(); + return rai; +} + +extern "C" const Archive::Child* +LLVMRustArchiveIteratorNext(RustArchiveIterator *rai) { + if (rai->cur == rai->end) + return NULL; + const Archive::Child *cur = rai->cur.operator->(); + Archive::Child *ret = new Archive::Child(*cur); + ++rai->cur; + return ret; +} + +extern "C" void +LLVMRustArchiveChildFree(Archive::Child *child) { + delete child; +} + +extern "C" void +LLVMRustArchiveIteratorFree(RustArchiveIterator *rai) { + delete rai; +} + +extern "C" const char* +LLVMRustArchiveChildName(const Archive::Child *child, size_t *size) { + ErrorOr name_or_err = child->getName(); + if (name_or_err.getError()) + return NULL; + StringRef name = name_or_err.get(); + *size = name.size(); + return name.data(); +} + +extern "C" const char* +LLVMRustArchiveChildData(Archive::Child *child, size_t *size) { + StringRef buf = child->getBuffer(); + *size = buf.size(); + return buf.data(); +} + +extern "C" LLVMRustArchiveMember* +LLVMRustArchiveMemberNew(char *Filename, char *Name, Archive::Child *child) { + LLVMRustArchiveMember *Member = new LLVMRustArchiveMember; + Member->filename = Filename; + Member->name = Name; + if (child) + Member->child = *child; + return Member; +} + +extern "C" void +LLVMRustArchiveMemberFree(LLVMRustArchiveMember *Member) { + delete Member; +} + +extern "C" int +LLVMRustWriteArchive(char *Dst, + size_t NumMembers, + const LLVMRustArchiveMember **NewMembers, + bool WriteSymbtab) { +#if LLVM_VERSION_MINOR >= 7 + std::vector Members; + + for (size_t i = 0; i < NumMembers; i++) { + auto Member = NewMembers[i]; + assert(Member->name); + if (Member->filename) { + Members.push_back(NewArchiveIterator(Member->filename, Member->name)); + } else { + Members.push_back(NewArchiveIterator(Member->child, Member->name)); + } + } + auto pair = writeArchive(Dst, Members, WriteSymbtab); + if (!pair.second) + return 0; + LLVMRustSetLastError(pair.second.message().c_str()); +#else + LLVMRustSetLastError("writing archives not supported with this LLVM version"); +#endif + return -1; +} diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 70ef64afc43..6861ad43a3c 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -825,107 +825,6 @@ LLVMRustLinkInExternalBitcode(LLVMModuleRef dst, char *bc, size_t len) { return true; } -extern "C" void* -LLVMRustOpenArchive(char *path) { - ErrorOr> buf_or = MemoryBuffer::getFile(path, - -1, - false); - if (!buf_or) { - LLVMRustSetLastError(buf_or.getError().message().c_str()); - return nullptr; - } - -#if LLVM_VERSION_MINOR >= 6 - ErrorOr> archive_or = - Archive::create(buf_or.get()->getMemBufferRef()); - - if (!archive_or) { - LLVMRustSetLastError(archive_or.getError().message().c_str()); - return nullptr; - } - - OwningBinary *ret = new OwningBinary( - std::move(archive_or.get()), std::move(buf_or.get())); -#else - std::error_code err; - Archive *ret = new Archive(std::move(buf_or.get()), err); - if (err) { - LLVMRustSetLastError(err.message().c_str()); - return nullptr; - } -#endif - - return ret; -} - -#if LLVM_VERSION_MINOR >= 6 -typedef OwningBinary RustArchive; -#define GET_ARCHIVE(a) ((a)->getBinary()) -#else -typedef Archive RustArchive; -#define GET_ARCHIVE(a) (a) -#endif - -extern "C" void -LLVMRustDestroyArchive(RustArchive *ar) { - delete ar; -} - -struct RustArchiveIterator { - Archive::child_iterator cur; - Archive::child_iterator end; -}; - -extern "C" RustArchiveIterator* -LLVMRustArchiveIteratorNew(RustArchive *ra) { - Archive *ar = GET_ARCHIVE(ra); - RustArchiveIterator *rai = new RustArchiveIterator(); - rai->cur = ar->child_begin(); - rai->end = ar->child_end(); - return rai; -} - -extern "C" const Archive::Child* -LLVMRustArchiveIteratorCurrent(RustArchiveIterator *rai) { - if (rai->cur == rai->end) - return NULL; -#if LLVM_VERSION_MINOR >= 6 - const Archive::Child &ret = *rai->cur; - return &ret; -#else - return rai->cur.operator->(); -#endif -} - -extern "C" void -LLVMRustArchiveIteratorNext(RustArchiveIterator *rai) { - if (rai->cur == rai->end) - return; - ++rai->cur; -} - -extern "C" void -LLVMRustArchiveIteratorFree(RustArchiveIterator *rai) { - delete rai; -} - -extern "C" const char* -LLVMRustArchiveChildName(const Archive::Child *child, size_t *size) { - ErrorOr name_or_err = child->getName(); - if (name_or_err.getError()) - return NULL; - StringRef name = name_or_err.get(); - *size = name.size(); - return name.data(); -} - -extern "C" const char* -LLVMRustArchiveChildData(Archive::Child *child, size_t *size) { - StringRef buf = child->getBuffer(); - *size = buf.size(); - return buf.data(); -} - extern "C" void LLVMRustSetDLLStorageClass(LLVMValueRef Value, GlobalValue::DLLStorageClassTypes Class) {