Skip to content

Commit 957cb42

Browse files
committed
rollup merge of #24439: alexcrichton/fix-archive-assembler
When linking an archive statically to an rlib, the compiler will extract all contents of the archive and add them all to the rlib being generated. The current method of extraction is to run `ar x`, dumping all files into a temporary directory. Object archives, however, are allowed to have multiple entries with the same file name, so there is no method for them to extract their contents into a directory in a lossless fashion. This commit adds iterator support to the `ArchiveRO` structure which hooks into LLVM's support for reading object archives. This iterator is then used to inspect each object in turn and extract it to a unique location for later assembly.
2 parents 2fc2e12 + 9ab0475 commit 957cb42

File tree

12 files changed

+264
-84
lines changed

12 files changed

+264
-84
lines changed

src/librustc/metadata/loader.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -692,11 +692,16 @@ pub fn note_crate_name(diag: &SpanHandler, name: &str) {
692692

693693
impl ArchiveMetadata {
694694
fn new(ar: ArchiveRO) -> Option<ArchiveMetadata> {
695-
let data = match ar.read(METADATA_FILENAME) {
696-
Some(data) => data as *const [u8],
697-
None => {
698-
debug!("didn't find '{}' in the archive", METADATA_FILENAME);
699-
return None;
695+
let data = {
696+
let section = ar.iter().find(|sect| {
697+
sect.name() == Some(METADATA_FILENAME)
698+
});
699+
match section {
700+
Some(s) => s.data() as *const [u8],
701+
None => {
702+
debug!("didn't find '{}' in the archive", METADATA_FILENAME);
703+
return None;
704+
}
700705
}
701706
};
702707

src/librustc_back/archive.rs

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@
1111
//! A helper class for dealing with static archives
1212
1313
use std::env;
14-
use std::fs;
14+
use std::fs::{self, File};
1515
use std::io::prelude::*;
1616
use std::io;
1717
use std::path::{Path, PathBuf};
1818
use std::process::{Command, Output, Stdio};
1919
use std::str;
2020
use syntax::diagnostic::Handler as ErrorHandler;
21+
use rustc_llvm::archive_ro::ArchiveRO;
2122

2223
use tempdir::TempDir;
2324

@@ -282,45 +283,61 @@ impl<'a> ArchiveBuilder<'a> {
282283
mut skip: F) -> io::Result<()>
283284
where F: FnMut(&str) -> bool,
284285
{
285-
let loc = TempDir::new("rsar").unwrap();
286-
287-
// First, extract the contents of the archive to a temporary directory.
288-
// We don't unpack directly into `self.work_dir` due to the possibility
289-
// of filename collisions.
290-
let archive = env::current_dir().unwrap().join(archive);
291-
run_ar(self.archive.handler, &self.archive.maybe_ar_prog,
292-
"x", Some(loc.path()), &[&archive]);
286+
let archive = match ArchiveRO::open(archive) {
287+
Some(ar) => ar,
288+
None => return Err(io::Error::new(io::ErrorKind::Other,
289+
"failed to open archive")),
290+
};
293291

294292
// Next, we must rename all of the inputs to "guaranteed unique names".
295-
// We move each file into `self.work_dir` under its new unique name.
293+
// We write each file into `self.work_dir` under its new unique name.
296294
// The reason for this renaming is that archives are keyed off the name
297295
// of the files, so if two files have the same name they will override
298296
// one another in the archive (bad).
299297
//
300298
// We skip any files explicitly desired for skipping, and we also skip
301299
// all SYMDEF files as these are just magical placeholders which get
302300
// re-created when we make a new archive anyway.
303-
let files = try!(fs::read_dir(loc.path()));
304-
for file in files {
305-
let file = try!(file).path();
306-
let filename = file.file_name().unwrap().to_str().unwrap();
307-
if skip(filename) { continue }
301+
for file in archive.iter() {
302+
let filename = match file.name() {
303+
Some(s) => s,
304+
None => continue,
305+
};
308306
if filename.contains(".SYMDEF") { continue }
307+
if skip(filename) { continue }
309308

310-
let filename = format!("r-{}-{}", name, filename);
311-
// LLDB (as mentioned in back::link) crashes on filenames of exactly
312-
// 16 bytes in length. If we're including an object file with
313-
// exactly 16-bytes of characters, give it some prefix so that it's
314-
// not 16 bytes.
315-
let filename = if filename.len() == 16 {
316-
format!("lldb-fix-{}", filename)
317-
} else {
318-
filename
319-
};
320-
let new_filename = self.work_dir.path().join(&filename[..]);
321-
try!(fs::rename(&file, &new_filename));
322-
self.members.push(PathBuf::from(filename));
309+
// An archive can contain files of the same name multiple times, so
310+
// we need to be sure to not have them overwrite one another when we
311+
// extract them. Consequently we need to find a truly unique file
312+
// name for us!
313+
let mut new_filename = String::new();
314+
for n in 0.. {
315+
let n = if n == 0 {String::new()} else {format!("-{}", n)};
316+
new_filename = format!("r{}-{}-{}", n, name, filename);
317+
318+
// LLDB (as mentioned in back::link) crashes on filenames of
319+
// exactly
320+
// 16 bytes in length. If we're including an object file with
321+
// exactly 16-bytes of characters, give it some prefix so
322+
// that it's not 16 bytes.
323+
new_filename = if new_filename.len() == 16 {
324+
format!("lldb-fix-{}", new_filename)
325+
} else {
326+
new_filename
327+
};
328+
329+
let present = self.members.iter().filter_map(|p| {
330+
p.file_name().and_then(|f| f.to_str())
331+
}).any(|s| s == new_filename);
332+
if !present {
333+
break
334+
}
335+
}
336+
let dst = self.work_dir.path().join(&new_filename);
337+
try!(try!(File::create(&dst)).write_all(file.data()));
338+
self.members.push(PathBuf::from(new_filename));
323339
}
340+
324341
Ok(())
325342
}
326343
}

src/librustc_back/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
extern crate syntax;
4747
extern crate libc;
4848
extern crate serialize;
49+
extern crate rustc_llvm;
4950
#[macro_use] extern crate log;
5051

5152
pub mod abi;

src/librustc_llvm/archive_ro.rs

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,23 @@
1010

1111
//! A wrapper around LLVM's archive (.a) code
1212
13-
use libc;
1413
use ArchiveRef;
1514

1615
use std::ffi::CString;
17-
use std::slice;
1816
use std::path::Path;
17+
use std::slice;
18+
use std::str;
1919

20-
pub struct ArchiveRO {
21-
ptr: ArchiveRef,
20+
pub struct ArchiveRO { ptr: ArchiveRef }
21+
22+
pub struct Iter<'a> {
23+
archive: &'a ArchiveRO,
24+
ptr: ::ArchiveIteratorRef,
25+
}
26+
27+
pub struct Child<'a> {
28+
name: Option<&'a str>,
29+
data: &'a [u8],
2230
}
2331

2432
impl ArchiveRO {
@@ -52,18 +60,9 @@ impl ArchiveRO {
5260
}
5361
}
5462

55-
/// Reads a file in the archive
56-
pub fn read<'a>(&'a self, file: &str) -> Option<&'a [u8]> {
63+
pub fn iter(&self) -> Iter {
5764
unsafe {
58-
let mut size = 0 as libc::size_t;
59-
let file = CString::new(file).unwrap();
60-
let ptr = ::LLVMRustArchiveReadSection(self.ptr, file.as_ptr(),
61-
&mut size);
62-
if ptr.is_null() {
63-
None
64-
} else {
65-
Some(slice::from_raw_parts(ptr as *const u8, size as usize))
66-
}
65+
Iter { ptr: ::LLVMRustArchiveIteratorNew(self.ptr), archive: self }
6766
}
6867
}
6968
}
@@ -75,3 +74,47 @@ impl Drop for ArchiveRO {
7574
}
7675
}
7776
}
77+
78+
impl<'a> Iterator for Iter<'a> {
79+
type Item = Child<'a>;
80+
81+
fn next(&mut self) -> Option<Child<'a>> {
82+
unsafe {
83+
let ptr = ::LLVMRustArchiveIteratorCurrent(self.ptr);
84+
if ptr.is_null() {
85+
return None
86+
}
87+
let mut name_len = 0;
88+
let name_ptr = ::LLVMRustArchiveChildName(ptr, &mut name_len);
89+
let mut data_len = 0;
90+
let data_ptr = ::LLVMRustArchiveChildData(ptr, &mut data_len);
91+
let child = Child {
92+
name: if name_ptr.is_null() {
93+
None
94+
} else {
95+
let name = slice::from_raw_parts(name_ptr as *const u8,
96+
name_len as usize);
97+
str::from_utf8(name).ok().map(|s| s.trim())
98+
},
99+
data: slice::from_raw_parts(data_ptr as *const u8,
100+
data_len as usize),
101+
};
102+
::LLVMRustArchiveIteratorNext(self.ptr);
103+
Some(child)
104+
}
105+
}
106+
}
107+
108+
#[unsafe_destructor]
109+
impl<'a> Drop for Iter<'a> {
110+
fn drop(&mut self) {
111+
unsafe {
112+
::LLVMRustArchiveIteratorFree(self.ptr);
113+
}
114+
}
115+
}
116+
117+
impl<'a> Child<'a> {
118+
pub fn name(&self) -> Option<&'a str> { self.name }
119+
pub fn data(&self) -> &'a [u8] { self.data }
120+
}

src/librustc_llvm/lib.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#![feature(libc)]
3131
#![feature(link_args)]
3232
#![feature(staged_api)]
33+
#![feature(unsafe_destructor)]
3334

3435
extern crate libc;
3536
#[macro_use] #[no_link] extern crate rustc_bitflags;
@@ -488,9 +489,12 @@ pub type PassRef = *mut Pass_opaque;
488489
#[allow(missing_copy_implementations)]
489490
pub enum TargetMachine_opaque {}
490491
pub type TargetMachineRef = *mut TargetMachine_opaque;
491-
#[allow(missing_copy_implementations)]
492492
pub enum Archive_opaque {}
493493
pub type ArchiveRef = *mut Archive_opaque;
494+
pub enum ArchiveIterator_opaque {}
495+
pub type ArchiveIteratorRef = *mut ArchiveIterator_opaque;
496+
pub enum ArchiveChild_opaque {}
497+
pub type ArchiveChildRef = *mut ArchiveChild_opaque;
494498
#[allow(missing_copy_implementations)]
495499
pub enum Twine_opaque {}
496500
pub type TwineRef = *mut Twine_opaque;
@@ -2051,8 +2055,14 @@ extern {
20512055
pub fn LLVMRustMarkAllFunctionsNounwind(M: ModuleRef);
20522056

20532057
pub fn LLVMRustOpenArchive(path: *const c_char) -> ArchiveRef;
2054-
pub fn LLVMRustArchiveReadSection(AR: ArchiveRef, name: *const c_char,
2055-
out_len: *mut size_t) -> *const c_char;
2058+
pub fn LLVMRustArchiveIteratorNew(AR: ArchiveRef) -> ArchiveIteratorRef;
2059+
pub fn LLVMRustArchiveIteratorNext(AIR: ArchiveIteratorRef);
2060+
pub fn LLVMRustArchiveIteratorCurrent(AIR: ArchiveIteratorRef) -> ArchiveChildRef;
2061+
pub fn LLVMRustArchiveChildName(ACR: ArchiveChildRef,
2062+
size: *mut size_t) -> *const c_char;
2063+
pub fn LLVMRustArchiveChildData(ACR: ArchiveChildRef,
2064+
size: *mut size_t) -> *const c_char;
2065+
pub fn LLVMRustArchiveIteratorFree(AIR: ArchiveIteratorRef);
20562066
pub fn LLVMRustDestroyArchive(AR: ArchiveRef);
20572067

20582068
pub fn LLVMRustSetDLLExportStorageClass(V: ValueRef);

src/librustc_trans/back/lto.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,13 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
6363
let file = &file[3..file.len() - 5]; // chop off lib/.rlib
6464
debug!("reading {}", file);
6565
for i in 0.. {
66-
let bc_encoded = time(sess.time_passes(),
67-
&format!("check for {}.{}.bytecode.deflate", name, i),
68-
(),
69-
|_| {
70-
archive.read(&format!("{}.{}.bytecode.deflate",
71-
file, i))
72-
});
66+
let filename = format!("{}.{}.bytecode.deflate", file, i);
67+
let msg = format!("check for {}", filename);
68+
let bc_encoded = time(sess.time_passes(), &msg, (), |_| {
69+
archive.iter().find(|section| {
70+
section.name() == Some(&filename[..])
71+
})
72+
});
7373
let bc_encoded = match bc_encoded {
7474
Some(data) => data,
7575
None => {
@@ -79,9 +79,10 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
7979
path.display()));
8080
}
8181
// No more bitcode files to read.
82-
break;
83-
},
82+
break
83+
}
8484
};
85+
let bc_encoded = bc_encoded.data();
8586

8687
let bc_decoded = if is_versioned_bytecode_format(bc_encoded) {
8788
time(sess.time_passes(), &format!("decode {}.{}.bc", file, i), (), |_| {

src/rustllvm/RustWrapper.cpp

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -770,37 +770,68 @@ LLVMRustOpenArchive(char *path) {
770770
return ret;
771771
}
772772

773-
extern "C" const char*
774773
#if LLVM_VERSION_MINOR >= 6
775-
LLVMRustArchiveReadSection(OwningBinary<Archive> *ob, char *name, size_t *size) {
776-
777-
Archive *ar = ob->getBinary();
774+
typedef OwningBinary<Archive> RustArchive;
775+
#define GET_ARCHIVE(a) ((a)->getBinary())
778776
#else
779-
LLVMRustArchiveReadSection(Archive *ar, char *name, size_t *size) {
777+
typedef Archive RustArchive;
778+
#define GET_ARCHIVE(a) (a)
780779
#endif
781780

782-
Archive::child_iterator child = ar->child_begin(),
783-
end = ar->child_end();
784-
for (; child != end; ++child) {
785-
ErrorOr<StringRef> name_or_err = child->getName();
786-
if (name_or_err.getError()) continue;
787-
StringRef sect_name = name_or_err.get();
788-
if (sect_name.trim(" ") == name) {
789-
StringRef buf = child->getBuffer();
790-
*size = buf.size();
791-
return buf.data();
792-
}
793-
}
794-
return NULL;
781+
extern "C" void
782+
LLVMRustDestroyArchive(RustArchive *ar) {
783+
delete ar;
784+
}
785+
786+
struct RustArchiveIterator {
787+
Archive::child_iterator cur;
788+
Archive::child_iterator end;
789+
};
790+
791+
extern "C" RustArchiveIterator*
792+
LLVMRustArchiveIteratorNew(RustArchive *ra) {
793+
Archive *ar = GET_ARCHIVE(ra);
794+
RustArchiveIterator *rai = new RustArchiveIterator();
795+
rai->cur = ar->child_begin();
796+
rai->end = ar->child_end();
797+
return rai;
798+
}
799+
800+
extern "C" const Archive::Child*
801+
LLVMRustArchiveIteratorCurrent(RustArchiveIterator *rai) {
802+
if (rai->cur == rai->end)
803+
return NULL;
804+
const Archive::Child &ret = *rai->cur;
805+
return &ret;
795806
}
796807

797808
extern "C" void
798-
#if LLVM_VERSION_MINOR >= 6
799-
LLVMRustDestroyArchive(OwningBinary<Archive> *ar) {
800-
#else
801-
LLVMRustDestroyArchive(Archive *ar) {
802-
#endif
803-
delete ar;
809+
LLVMRustArchiveIteratorNext(RustArchiveIterator *rai) {
810+
if (rai->cur == rai->end)
811+
return;
812+
++rai->cur;
813+
}
814+
815+
extern "C" void
816+
LLVMRustArchiveIteratorFree(RustArchiveIterator *rai) {
817+
delete rai;
818+
}
819+
820+
extern "C" const char*
821+
LLVMRustArchiveChildName(const Archive::Child *child, size_t *size) {
822+
ErrorOr<StringRef> name_or_err = child->getName();
823+
if (name_or_err.getError())
824+
return NULL;
825+
StringRef name = name_or_err.get();
826+
*size = name.size();
827+
return name.data();
828+
}
829+
830+
extern "C" const char*
831+
LLVMRustArchiveChildData(Archive::Child *child, size_t *size) {
832+
StringRef buf = child->getBuffer();
833+
*size = buf.size();
834+
return buf.data();
804835
}
805836

806837
extern "C" void

0 commit comments

Comments
 (0)