]> git.lizzy.rs Git - rust.git/commitdiff
trans: Link rlibs to dylibs with --whole-archive
authorAlex Crichton <alex@alexcrichton.com>
Wed, 8 Jul 2015 04:33:44 +0000 (21:33 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Wed, 8 Jul 2015 22:24:23 +0000 (15:24 -0700)
This commit starts passing the `--whole-archive` flag (`-force_load` on OSX) to
the linker when linking rlibs into dylibs. The primary purpose of this commit is
to ensure that the linker doesn't strip out objects from an archive when
creating a dynamic library. Information on how this can go wrong can be found in
issues #14344 and #25185.

The unfortunate part about passing this flag to the linker is that we have to
preprocess the rlib to remove the metadata and compressed bytecode found within.
This means that creating a dylib will now take longer to link as we've got to
copy around the input rlibs to a temporary location, modify them, and then
invoke the linker. This isn't done for executables, however, so the "hello
world" compile time is not affected.

This fix was instigated because of the previous commit where rlibs may not
contain multiple object files instead of one due to codegen units being greater
than one. That change prevented the main distribution from being compiled with
more than one codegen-unit and this commit fixes that.

Closes #14344
Closes #25185

16 files changed:
src/liballoc/lib.rs
src/libcollections/lib.rs
src/liblibc/lib.rs
src/librustc/metadata/encoder.rs
src/librustc_driver/driver.rs
src/librustc_driver/lib.rs
src/librustc_trans/back/link.rs
src/librustc_trans/back/linker.rs
src/librustc_trans/back/lto.rs
src/test/auxiliary/issue-14344-1.rs [new file with mode: 0644]
src/test/auxiliary/issue-14344-2.rs [new file with mode: 0644]
src/test/auxiliary/issue-25185-1.rs [new file with mode: 0644]
src/test/auxiliary/issue-25185-2.rs [new file with mode: 0644]
src/test/run-make/extern-fn-reachable/Makefile
src/test/run-pass/issue-14344.rs [new file with mode: 0644]
src/test/run-pass/issue-25185.rs [new file with mode: 0644]

index 7dcf7a76da083ba3dc5143a78200add26ebaae10..905012bbb64d851e8deda1eeb03fee3641024311 100644 (file)
@@ -148,4 +148,5 @@ pub fn oom() -> ! {
 //                optimize it out).
 #[doc(hidden)]
 #[unstable(feature = "issue_14344_fixme")]
+#[cfg(stage0)]
 pub fn fixme_14344_be_sure_to_link_to_collections() {}
index 42adbe10e505166718bff8b0dca31b7653d2c307..3c90a2c54e128fc4bb552cfd2ab3ecf5810afe07 100644 (file)
@@ -138,6 +138,7 @@ pub mod btree_set {
 // FIXME(#14344) this shouldn't be necessary
 #[doc(hidden)]
 #[unstable(feature = "issue_14344_fixme")]
+#[cfg(stage0)]
 pub fn fixme_14344_be_sure_to_link_to_collections() {}
 
 #[cfg(not(test))]
index 2c5ebc25f6bde3ca47c715e34997a5f8eedf1bd3..102894bec133ed46ecc163f3e08610cea895c70a 100644 (file)
@@ -6431,6 +6431,7 @@ pub mod winsock {
 }
 
 #[doc(hidden)]
+#[cfg(stage0)]
 pub fn issue_14344_workaround() {} // FIXME #14344 force linkage to happen correctly
 
 #[test] fn work_on_windows() { } // FIXME #10872 needed for a happy windows
index b677e7b85703b2cdca52c156df0309f4f0e60c99..a9e9f17bdce75e31170e6b392c4097cf21f3a110 100644 (file)
@@ -2136,11 +2136,7 @@ struct Stats {
     let mut rbml_w = Encoder::new(wr);
 
     encode_crate_name(&mut rbml_w, &ecx.link_meta.crate_name);
-    encode_crate_triple(&mut rbml_w,
-                        &tcx.sess
-                           .opts
-                           .target_triple
-                           );
+    encode_crate_triple(&mut rbml_w, &tcx.sess.opts.target_triple);
     encode_hash(&mut rbml_w, &ecx.link_meta.crate_hash);
     encode_dylib_dependency_formats(&mut rbml_w, &ecx);
 
index ae6136a049adeb36c81e4d99b91776537e472d79..9541076df82be575eb3e80b12858558ca25bc949 100644 (file)
@@ -804,8 +804,8 @@ fn write_out_deps(sess: &Session,
         match *output_type {
             config::OutputTypeExe => {
                 for output in sess.crate_types.borrow().iter() {
-                    let p = link::filename_for_input(sess, *output,
-                                                     id, &file);
+                    let p = link::filename_for_input(sess, *output, id,
+                                                     outputs);
                     out_filenames.push(p);
                 }
             }
index 36438ccc784f876e81eba04595c90d5aa80bd4e0..282971daa28369d6df9143274ac4d8f15c73733a 100644 (file)
@@ -452,10 +452,8 @@ fn print_crate_info(sess: &Session,
                     let metadata = driver::collect_crate_metadata(sess, attrs);
                     *sess.crate_metadata.borrow_mut() = metadata;
                     for &style in &crate_types {
-                        let fname = link::filename_for_input(sess,
-                                                             style,
-                                                             &id,
-                                                             &t_outputs.with_extension(""));
+                        let fname = link::filename_for_input(sess, style, &id,
+                                                             &t_outputs);
                         println!("{}", fname.file_name().unwrap()
                                             .to_string_lossy());
                     }
index 9d5dcdd855d71475b748ddc51fc802467dc52841..21bc61593c9cdbf451fadcb726ba0277e753be5d 100644 (file)
@@ -464,26 +464,25 @@ fn is_writeable(p: &Path) -> bool {
 
 pub fn filename_for_input(sess: &Session,
                           crate_type: config::CrateType,
-                          name: &str,
-                          out_filename: &Path) -> PathBuf {
-    let libname = format!("{}{}", name, sess.opts.cg.extra_filename);
+                          crate_name: &str,
+                          outputs: &OutputFilenames) -> PathBuf {
+    let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename);
     match crate_type {
         config::CrateTypeRlib => {
-            out_filename.with_file_name(&format!("lib{}.rlib", libname))
+            outputs.out_directory.join(&format!("lib{}.rlib", libname))
         }
         config::CrateTypeDylib => {
             let (prefix, suffix) = (&sess.target.target.options.dll_prefix,
                                     &sess.target.target.options.dll_suffix);
-            out_filename.with_file_name(&format!("{}{}{}",
-                                                  prefix,
-                                                 libname,
-                                                 suffix))
+            outputs.out_directory.join(&format!("{}{}{}", prefix, libname,
+                                                suffix))
         }
         config::CrateTypeStaticlib => {
-            out_filename.with_file_name(&format!("lib{}.a", libname))
+            outputs.out_directory.join(&format!("lib{}.a", libname))
         }
         config::CrateTypeExecutable => {
             let suffix = &sess.target.target.options.exe_suffix;
+            let out_filename = outputs.path(OutputTypeExe);
             if suffix.is_empty() {
                 out_filename.to_path_buf()
             } else {
@@ -501,10 +500,7 @@ fn link_binary_output(sess: &Session,
     let objects = object_filenames(sess, outputs);
     let out_filename = match outputs.single_output_file {
         Some(ref file) => file.clone(),
-        None => {
-            let out_filename = outputs.path(OutputTypeExe);
-            filename_for_input(sess, crate_type, crate_name, &out_filename)
-        }
+        None => filename_for_input(sess, crate_type, crate_name, outputs),
     };
 
     // Make sure files are writeable.  Mac, FreeBSD, and Windows system linkers
@@ -551,6 +547,19 @@ fn archive_search_paths(sess: &Session) -> Vec<PathBuf> {
     return search;
 }
 
+fn archive_config<'a>(sess: &'a Session,
+                      output: &Path) -> ArchiveConfig<'a> {
+    ArchiveConfig {
+        handler: &sess.diagnostic().handler,
+        dst: output.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),
+    }
+}
+
 // Create an 'rlib'
 //
 // An rlib in its current incarnation is essentially a renamed .a file. The
@@ -562,17 +571,7 @@ fn link_rlib<'a>(sess: &'a Session,
                  objects: &[PathBuf],
                  out_filename: &Path) -> ArchiveBuilder<'a> {
     info!("preparing rlib from {:?} to {:?}", objects, out_filename);
-    let handler = &sess.diagnostic().handler;
-    let config = ArchiveConfig {
-        handler: handler,
-        dst: out_filename.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),
-    };
-    let mut ab = ArchiveBuilder::create(config);
+    let mut ab = ArchiveBuilder::create(archive_config(sess, out_filename));
     for obj in objects {
         ab.add_file(obj).unwrap();
     }
@@ -1131,7 +1130,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session,
                 add_dynamic_crate(cmd, sess, &src.dylib.unwrap().0)
             }
             cstore::RequireStatic => {
-                add_static_crate(cmd, sess, tmpdir, &src.rlib.unwrap().0)
+                add_static_crate(cmd, sess, tmpdir, dylib, &src.rlib.unwrap().0)
             }
         }
 
@@ -1147,71 +1146,80 @@ fn unlib<'a>(config: &config::Config, stem: &'a str) -> &'a str {
     }
 
     // Adds the static "rlib" versions of all crates to the command line.
+    // There's a bit of magic which happens here specifically related to LTO and
+    // dynamic libraries. Specifically:
+    //
+    // * For LTO, we remove upstream object files.
+    // * For dylibs we remove metadata and bytecode from upstream rlibs
+    //
+    // When performing LTO, all of the bytecode from the upstream libraries has
+    // already been included in our object file output. As a result we need to
+    // remove the object files in the upstream libraries so the linker doesn't
+    // try to include them twice (or whine about duplicate symbols). We must
+    // continue to include the rest of the rlib, however, as it may contain
+    // static native libraries which must be linked in.
+    //
+    // When making a dynamic library, linkers by default don't include any
+    // object files in an archive if they're not necessary to resolve the link.
+    // We basically want to convert the archive (rlib) to a dylib, though, so we
+    // *do* want everything included in the output, regardless of whether the
+    // linker thinks it's needed or not. As a result we must use the
+    // --whole-archive option (or the platform equivalent). When using this
+    // option the linker will fail if there are non-objects in the archive (such
+    // as our own metadata and/or bytecode). All in all, for rlibs to be
+    // entirely included in dylibs, we need to remove all non-object files.
+    //
+    // Note, however, that if we're not doing LTO or we're not producing a dylib
+    // (aka we're making an executable), we can just pass the rlib blindly to
+    // the linker (fast) because it's fine if it's not actually included as
+    // we're at the end of the dependency chain.
     fn add_static_crate(cmd: &mut Linker, sess: &Session, tmpdir: &Path,
-                        cratepath: &Path) {
-        // When performing LTO on an executable output, all of the
-        // bytecode from the upstream libraries has already been
-        // included in our object file output. We need to modify all of
-        // the upstream archives to remove their corresponding object
-        // file to make sure we don't pull the same code in twice.
-        //
-        // We must continue to link to the upstream archives to be sure
-        // to pull in native static dependencies. As the final caveat,
-        // on Linux it is apparently illegal to link to a blank archive,
-        // so if an archive no longer has any object files in it after
-        // we remove `lib.o`, then don't link against it at all.
-        //
-        // If we're not doing LTO, then our job is simply to just link
-        // against the archive.
-        if sess.lto() {
-            let name = cratepath.file_name().unwrap().to_str().unwrap();
-            let name = &name[3..name.len() - 5]; // chop off lib/.rlib
-            time(sess.time_passes(),
-                 &format!("altering {}.rlib", name),
-                 (), |()| {
-                let dst = tmpdir.join(cratepath.file_name().unwrap());
-                match fs::copy(&cratepath, &dst) {
-                    Ok(..) => {}
-                    Err(e) => {
-                        sess.fatal(&format!("failed to copy {} to {}: {}",
-                                            cratepath.display(),
-                                            dst.display(), e));
-                    }
+                        dylib: bool, cratepath: &Path) {
+        if !sess.lto() && !dylib {
+            cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath));
+            return
+        }
+
+        let dst = tmpdir.join(cratepath.file_name().unwrap());
+        let name = cratepath.file_name().unwrap().to_str().unwrap();
+        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));
+            archive.remove_file(METADATA_FILENAME);
+
+            let mut any_objects = false;
+            for f in archive.files() {
+                if f.ends_with("bytecode.deflate") {
+                    archive.remove_file(&f);
+                    continue
                 }
-                // Fix up permissions of the copy, as fs::copy() preserves
-                // permissions, but the original file may have been installed
-                // by a package manager and may be read-only.
-                match fs::metadata(&dst).and_then(|m| {
-                    let mut perms = m.permissions();
-                    perms.set_readonly(false);
-                    fs::set_permissions(&dst, perms)
-                }) {
-                    Ok(..) => {}
-                    Err(e) => {
-                        sess.fatal(&format!("failed to chmod {} when preparing \
-                                             for LTO: {}", dst.display(), e));
+                let canonical = f.replace("-", "_");
+                let canonical_name = name.replace("-", "_");
+                if sess.lto() && canonical.starts_with(&canonical_name) &&
+                   canonical.ends_with(".o") {
+                    let num = &f[name.len()..f.len() - 2];
+                    if num.len() > 0 && num[1..].parse::<u32>().is_ok() {
+                        archive.remove_file(&f);
+                        continue
                     }
                 }
-                let handler = &sess.diagnostic().handler;
-                let config = ArchiveConfig {
-                    handler: handler,
-                    dst: dst.clone(),
-                    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),
-                };
-                let mut archive = Archive::open(config);
-                archive.remove_file(&format!("{}.o", name));
-                let files = archive.files();
-                if files.iter().any(|s| s.ends_with(".o")) {
-                    cmd.link_rlib(&dst);
-                }
-            });
-        } else {
-            cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath));
-        }
+                any_objects = true;
+            }
+
+            if any_objects {
+                cmd.link_whole_rlib(&fix_windows_verbatim_for_gcc(&dst));
+            }
+        });
     }
 
     // Same thing as above, but for dynamic crates instead of static crates.
index 7253334d6998989c95104d671e630869565a5cee..518a6c248407a995e38a6edc990b791c8fd18ad9 100644 (file)
@@ -30,6 +30,7 @@ pub trait Linker {
     fn link_framework(&mut self, framework: &str);
     fn link_staticlib(&mut self, lib: &str);
     fn link_rlib(&mut self, lib: &Path);
+    fn link_whole_rlib(&mut self, lib: &Path);
     fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]);
     fn include_path(&mut self, path: &Path);
     fn framework_path(&mut self, path: &Path);
@@ -96,6 +97,17 @@ fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]) {
         }
     }
 
+    fn link_whole_rlib(&mut self, lib: &Path) {
+        if self.sess.target.target.options.is_like_osx {
+            let mut v = OsString::from("-Wl,-force_load,");
+            v.push(lib);
+            self.cmd.arg(&v);
+        } else {
+            self.cmd.arg("-Wl,--whole-archive").arg(lib)
+                    .arg("-Wl,--no-whole-archive");
+        }
+    }
+
     fn gc_sections(&mut self, is_dylib: bool) {
         // The dead_strip option to the linker specifies that functions and data
         // unreachable by the entry point will be removed. This is quite useful
@@ -250,6 +262,10 @@ fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) {
         // not supported?
         self.link_staticlib(lib);
     }
+    fn link_whole_rlib(&mut self, path: &Path) {
+        // not supported?
+        self.link_rlib(path);
+    }
     fn optimize(&mut self) {
         // Needs more investigation of `/OPT` arguments
     }
index e13a5e97f7573c8207cfd21d09e88b7f1b656891..dfeb866c5b3f00a2fd7b69e8079bad0a50edb58d 100644 (file)
@@ -56,33 +56,14 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
         };
 
         let archive = ArchiveRO::open(&path).expect("wanted an rlib");
-        let file = path.file_name().unwrap().to_str().unwrap();
-        let file = &file[3..file.len() - 5]; // chop off lib/.rlib
-        debug!("reading {}", file);
-        for i in 0.. {
-            let filename = format!("{}.{}.bytecode.deflate", file, i);
-            let msg = format!("check for {}", filename);
-            let bc_encoded = time(sess.time_passes(), &msg, (), |_| {
-                archive.iter().find(|section| {
-                    section.name() == Some(&filename[..])
-                })
-            });
-            let bc_encoded = match bc_encoded {
-                Some(data) => data,
-                None => {
-                    if i == 0 {
-                        // No bitcode was found at all.
-                        sess.fatal(&format!("missing compressed bytecode in {}",
-                                           path.display()));
-                    }
-                    // No more bitcode files to read.
-                    break
-                }
-            };
-            let bc_encoded = bc_encoded.data();
+        let bytecodes = archive.iter().filter_map(|child| {
+            child.name().map(|name| (name, child))
+        }).filter(|&(name, _)| name.ends_with("bytecode.deflate"));
+        for (name, data) in bytecodes {
+            let bc_encoded = data.data();
 
             let bc_decoded = if is_versioned_bytecode_format(bc_encoded) {
-                time(sess.time_passes(), &format!("decode {}.{}.bc", file, i), (), |_| {
+                time(sess.time_passes(), &format!("decode {}", name), (), |_| {
                     // Read the version
                     let version = extract_bytecode_format_version(bc_encoded);
 
@@ -106,7 +87,7 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
                     }
                 })
             } else {
-                time(sess.time_passes(), &format!("decode {}.{}.bc", file, i), (), |_| {
+                time(sess.time_passes(), &format!("decode {}", name), (), |_| {
                 // the object must be in the old, pre-versioning format, so simply
                 // inflate everything and let LLVM decide if it can make sense of it
                     match flate::inflate_bytes(bc_encoded) {
@@ -120,10 +101,8 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
             };
 
             let ptr = bc_decoded.as_ptr();
-            debug!("linking {}, part {}", name, i);
-            time(sess.time_passes(),
-                 &format!("ll link {}.{}", name, i),
-                 (),
+            debug!("linking {}", name);
+            time(sess.time_passes(), &format!("ll link {}", name), (),
                  |()| unsafe {
                 if !llvm::LLVMRustLinkInExternalBitcode(llmod,
                                                         ptr as *const libc::c_char,
diff --git a/src/test/auxiliary/issue-14344-1.rs b/src/test/auxiliary/issue-14344-1.rs
new file mode 100644 (file)
index 0000000..78c03ba
--- /dev/null
@@ -0,0 +1,15 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+#![crate_type = "rlib"]
+
+pub fn foo() {}
diff --git a/src/test/auxiliary/issue-14344-2.rs b/src/test/auxiliary/issue-14344-2.rs
new file mode 100644 (file)
index 0000000..9df35e5
--- /dev/null
@@ -0,0 +1,13 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate issue_14344_1;
+
+pub fn bar() {}
diff --git a/src/test/auxiliary/issue-25185-1.rs b/src/test/auxiliary/issue-25185-1.rs
new file mode 100644 (file)
index 0000000..b9da39c
--- /dev/null
@@ -0,0 +1,18 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+#![crate_type = "rlib"]
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern {
+    pub fn rust_dbg_extern_identity_u32(u: u32) -> u32;
+}
diff --git a/src/test/auxiliary/issue-25185-2.rs b/src/test/auxiliary/issue-25185-2.rs
new file mode 100644 (file)
index 0000000..00b5277
--- /dev/null
@@ -0,0 +1,13 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate issue_25185_1;
+
+pub use issue_25185_1::rust_dbg_extern_identity_u32;
index 56748b1eb9b007dc75f4bd08a6fdb86b4ec01581..79a9a3c640f03b62cc294be160d9e82f48372ab2 100644 (file)
@@ -4,6 +4,6 @@
 TARGET_RPATH_DIR:=$(TARGET_RPATH_DIR):$(TMPDIR)
 
 all:
-       $(RUSTC) dylib.rs -o $(TMPDIR)/libdylib.so
-       $(RUSTC) main.rs
+       $(RUSTC) dylib.rs -o $(TMPDIR)/libdylib.so -C prefer-dynamic
+       $(RUSTC) main.rs -C prefer-dynamic
        $(call RUN,main)
diff --git a/src/test/run-pass/issue-14344.rs b/src/test/run-pass/issue-14344.rs
new file mode 100644 (file)
index 0000000..06b8f44
--- /dev/null
@@ -0,0 +1,20 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:issue-14344-1.rs
+// aux-build:issue-14344-2.rs
+
+extern crate issue_14344_1;
+extern crate issue_14344_2;
+
+fn main() {
+    issue_14344_1::foo();
+    issue_14344_2::bar();
+}
diff --git a/src/test/run-pass/issue-25185.rs b/src/test/run-pass/issue-25185.rs
new file mode 100644 (file)
index 0000000..d8d2d50
--- /dev/null
@@ -0,0 +1,21 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:issue-25185-1.rs
+// aux-build:issue-25185-2.rs
+
+extern crate issue_25185_2;
+
+fn main() {
+    let x = unsafe {
+        issue_25185_2::rust_dbg_extern_identity_u32(1)
+    };
+    assert_eq!(x, 1);
+}