about summary refs log tree commit diff
path: root/src/librustc_back
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-07-30 07:44:20 -0700
committerAlex Crichton <alex@alexcrichton.com>2014-08-04 11:02:26 -0700
commit1ae1461fbf59db8db5dd2fe11bbe22c6adeb1aed (patch)
treee79679953309797f1b392fda875b33031bd4ee81 /src/librustc_back
parent31590bd34900403a18079bf4623cd35f9da0c100 (diff)
downloadrust-1ae1461fbf59db8db5dd2fe11bbe22c6adeb1aed.tar.gz
rust-1ae1461fbf59db8db5dd2fe11bbe22c6adeb1aed.zip
rustc: Link entire archives of native libraries
As discovered in #15460, a particular #[link(kind = "static", ...)] line is not
actually guaranteed to link the library at all. The reason for this is that if
the external library doesn't have any referenced symbols in the object generated
by rustc, the entire library is dropped by the linker.

For dynamic native libraries, this is solved by passing -lfoo for all downstream
compilations unconditionally. For static libraries in rlibs this is solved
because the entire archive is bundled in the rlib. The only situation in which
this was a problem was when a static native library was linked to a rust dynamic
library.

This commit brings the behavior of dylibs in line with rlibs by passing the
--whole-archive flag to the linker when linking native libraries. On OSX, this
uses the -force_load flag. This flag ensures that the entire archive is
considered candidate for being linked into the final dynamic library.

This is a breaking change because if any static library is included twice in the
same compilation unit then the linker will start emitting errors about duplicate
definitions now. The fix for this would involve only statically linking to a
library once.

Closes #15460
[breaking-change]
Diffstat (limited to 'src/librustc_back')
-rw-r--r--src/librustc_back/archive.rs51
1 files changed, 27 insertions, 24 deletions
diff --git a/src/librustc_back/archive.rs b/src/librustc_back/archive.rs
index e2cadf817d5..85e0f2f10d8 100644
--- a/src/librustc_back/archive.rs
+++ b/src/librustc_back/archive.rs
@@ -95,6 +95,30 @@ fn run_ar(handler: &ErrorHandler, maybe_ar_prog: &Option<String>,
     }
 }
 
+pub fn find_library(name: &str, os: abi::Os, search_paths: &[Path],
+                    handler: &ErrorHandler) -> Path {
+    let (osprefix, osext) = match os {
+        abi::OsWin32 => ("", "lib"), _ => ("lib", "a"),
+    };
+    // On Windows, static libraries sometimes show up as libfoo.a and other
+    // times show up as foo.lib
+    let oslibname = format!("{}{}.{}", osprefix, name, osext);
+    let unixlibname = format!("lib{}.a", name);
+
+    for path in search_paths.iter() {
+        debug!("looking for {} inside {}", name, path.display());
+        let test = path.join(oslibname.as_slice());
+        if test.exists() { return test }
+        if oslibname != unixlibname {
+            let test = path.join(unixlibname.as_slice());
+            if test.exists() { return test }
+        }
+    }
+    handler.fatal(format!("could not find native static library `{}`, \
+                           perhaps an -L flag is missing?",
+                          name).as_slice());
+}
+
 impl<'a> Archive<'a> {
     fn new(config: ArchiveConfig<'a>) -> Archive<'a> {
         let ArchiveConfig { handler, dst, lib_search_paths, os, maybe_ar_prog } = config;
@@ -153,7 +177,9 @@ impl<'a> ArchiveBuilder<'a> {
     /// 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::IoResult<()> {
-        let location = self.find_library(name);
+        let location = find_library(name, self.archive.os,
+                                    self.archive.lib_search_paths.as_slice(),
+                                    self.archive.handler);
         self.add_archive(&location, name, [])
     }
 
@@ -285,28 +311,5 @@ impl<'a> ArchiveBuilder<'a> {
         }
         Ok(())
     }
-
-    fn find_library(&self, name: &str) -> Path {
-        let (osprefix, osext) = match self.archive.os {
-            abi::OsWin32 => ("", "lib"), _ => ("lib", "a"),
-        };
-        // On Windows, static libraries sometimes show up as libfoo.a and other
-        // times show up as foo.lib
-        let oslibname = format!("{}{}.{}", osprefix, name, osext);
-        let unixlibname = format!("lib{}.a", name);
-
-        for path in self.archive.lib_search_paths.iter() {
-            debug!("looking for {} inside {}", name, path.display());
-            let test = path.join(oslibname.as_slice());
-            if test.exists() { return test }
-            if oslibname != unixlibname {
-                let test = path.join(unixlibname.as_slice());
-                if test.exists() { return test }
-            }
-        }
-        self.archive.handler.fatal(format!("could not find native static library `{}`, \
-                                            perhaps an -L flag is missing?",
-                                           name).as_slice());
-    }
 }