From 9539b95848a1df305739126811a870e97cbf269d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 7 Apr 2014 15:40:58 -0700 Subject: [PATCH 1/4] rustc: Remove absolute rpaths Concerns have been raised about using absolute rpaths in #11746, and this is the first step towards not relying on rpaths at all. The only current use case for an absolute rpath is when a non-installed rust builds an executable that then moves from is built location. The relative rpath back to libstd and absolute rpath to the installation directory still remain (CFG_PREFIX). Closes #11746 Rebasing of #12754 --- mk/main.mk | 4 ---- src/librustc/back/rpath.rs | 30 +----------------------------- 2 files changed, 1 insertion(+), 33 deletions(-) diff --git a/mk/main.mk b/mk/main.mk index fa19a4b380ec6..f2bcdbd4bd539 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -358,7 +358,6 @@ CFGFLAG$(1)_T_$(2)_H_$(3) = stage1 endif endif -ifdef CFG_DISABLE_RPATH ifeq ($$(OSTYPE_$(3)),apple-darwin) RPATH_VAR$(1)_T_$(2)_H_$(3) := \ DYLD_LIBRARY_PATH="$$$$DYLD_LIBRARY_PATH:$$(CURDIR)/$$(HLIB$(1)_H_$(3))" @@ -366,9 +365,6 @@ else RPATH_VAR$(1)_T_$(2)_H_$(3) := \ LD_LIBRARY_PATH="$$$$LD_LIBRARY_PATH:$$(CURDIR)/$$(HLIB$(1)_H_$(3))" endif -else - RPATH_VAR$(1)_T_$(2)_H_$(3) := -endif STAGE$(1)_T_$(2)_H_$(3) := \ $$(Q)$$(RPATH_VAR$(1)_T_$(2)_H_$(3)) \ diff --git a/src/librustc/back/rpath.rs b/src/librustc/back/rpath.rs index c47f9893cd2ef..73ccc8871caea 100644 --- a/src/librustc/back/rpath.rs +++ b/src/librustc/back/rpath.rs @@ -87,10 +87,6 @@ fn get_rpaths(os: abi::Os, // crates they depend on. let rel_rpaths = get_rpaths_relative_to_output(os, output, libs); - // Make backup absolute paths to the libraries. Binaries can - // be moved as long as the crates they link against don't move. - let abs_rpaths = get_absolute_rpaths(libs); - // And a final backup rpath to the global library location. let fallback_rpaths = vec!(get_install_prefix_rpath(sysroot, target_triple)); @@ -102,11 +98,9 @@ fn get_rpaths(os: abi::Os, } log_rpaths("relative", rel_rpaths.as_slice()); - log_rpaths("absolute", abs_rpaths.as_slice()); log_rpaths("fallback", fallback_rpaths.as_slice()); let mut rpaths = rel_rpaths; - rpaths.push_all(abs_rpaths.as_slice()); rpaths.push_all(fallback_rpaths.as_slice()); // Remove duplicates @@ -146,17 +140,6 @@ pub fn get_rpath_relative_to_output(os: abi::Os, prefix+"/"+relative.as_str().expect("non-utf8 component in path") } -fn get_absolute_rpaths(libs: &[Path]) -> Vec<~str> { - libs.iter().map(|a| get_absolute_rpath(a)).collect() -} - -pub fn get_absolute_rpath(lib: &Path) -> ~str { - let mut p = os::make_absolute(lib); - p.pop(); - // FIXME (#9639): This needs to handle non-utf8 paths - p.as_str().expect("non-utf8 component in rpath").to_owned() -} - pub fn get_install_prefix_rpath(sysroot: &Path, target_triple: &str) -> ~str { let install_prefix = env!("CFG_PREFIX"); @@ -183,7 +166,7 @@ pub fn minimize_rpaths(rpaths: &[~str]) -> Vec<~str> { mod test { use std::os; - use back::rpath::{get_absolute_rpath, get_install_prefix_rpath}; + use back::rpath::get_install_prefix_rpath; use back::rpath::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output}; use syntax::abi; use metadata::filesearch; @@ -258,15 +241,4 @@ mod test { &Path::new("lib/libstd.so")); assert_eq!(res.as_slice(), "@loader_path/../lib"); } - - #[test] - fn test_get_absolute_rpath() { - let res = get_absolute_rpath(&Path::new("lib/libstd.so")); - let lib = os::make_absolute(&Path::new("lib")); - debug!("test_get_absolute_rpath: {} vs. {}", - res.to_str(), lib.display()); - - // FIXME (#9639): This needs to handle non-utf8 paths - assert_eq!(res.as_slice(), lib.as_str().expect("non-utf8 component in path")); - } } From 83ae17da30e24a55eb21323a720b3bb2dc8cc7b1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 8 Apr 2014 10:05:18 -0700 Subject: [PATCH 2/4] rustc: Don't rpath to librustrt.dylib This library no longer exists, there's no reason for this rpath to exist any more. --- src/librustc/back/rpath.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/librustc/back/rpath.rs b/src/librustc/back/rpath.rs index 73ccc8871caea..4b54b1f600885 100644 --- a/src/librustc/back/rpath.rs +++ b/src/librustc/back/rpath.rs @@ -42,10 +42,9 @@ pub fn get_rpath_flags(sess: &Session, out_filename: &Path) -> Vec<~str> { let sysroot = sess.filesearch().sysroot; let output = out_filename; let libs = sess.cstore.get_used_crates(cstore::RequireDynamic); - let libs = libs.move_iter().filter_map(|(_, l)| l.map(|p| p.clone())).collect(); - // We don't currently rpath extern libraries, but we know - // where rustrt is and we know every rust program needs it - let libs = slice::append_one(libs, get_sysroot_absolute_rt_lib(sess)); + let libs = libs.move_iter().filter_map(|(_, l)| { + l.map(|p| p.clone()) + }).collect::<~[_]>(); let rpaths = get_rpaths(os, sysroot, output, libs, sess.opts.target_triple); @@ -53,14 +52,6 @@ pub fn get_rpath_flags(sess: &Session, out_filename: &Path) -> Vec<~str> { flags } -fn get_sysroot_absolute_rt_lib(sess: &Session) -> Path { - let sysroot = sess.filesearch().sysroot; - let r = filesearch::relative_target_lib_path(sysroot, sess.opts.target_triple); - let mut p = sysroot.join(&r); - p.push(os::dll_filename("rustrt")); - p -} - pub fn rpaths_to_flags(rpaths: &[~str]) -> Vec<~str> { let mut ret = Vec::new(); for rpath in rpaths.iter() { From 3690dc0802f0f665a98647cfa3fbb53af12f3695 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 8 Apr 2014 10:06:11 -0700 Subject: [PATCH 3/4] rustc: Add a realpath utility function This is required in rustc to resolve symlinks for utilities such as the sysroot and the rpath values which are encoded into binaries. --- src/librustc/lib.rs | 1 + src/librustc/util/fs.rs | 103 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 src/librustc/util/fs.rs diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 95627cd1039af..f8b701ee5b018 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -127,6 +127,7 @@ pub mod util { pub mod ppaux; pub mod sha2; pub mod nodemap; + pub mod fs; } pub mod lib { diff --git a/src/librustc/util/fs.rs b/src/librustc/util/fs.rs new file mode 100644 index 0000000000000..c051b8e60cd80 --- /dev/null +++ b/src/librustc/util/fs.rs @@ -0,0 +1,103 @@ +// Copyright 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. + +use std::io; +use std::io::fs; +use std::os; + +/// Returns an absolute path in the filesystem that `path` points to. The +/// returned path does not contain any symlinks in its hierarchy. +pub fn realpath(original: &Path) -> io::IoResult { + static MAX_LINKS_FOLLOWED: uint = 256; + let original = os::make_absolute(original); + + // Right now lstat on windows doesn't work quite well + if cfg!(windows) { + return Ok(original) + } + + let result = original.root_path(); + let mut result = result.expect("make_absolute has no root_path"); + let mut followed = 0; + + for part in original.components() { + result.push(part); + + loop { + if followed == MAX_LINKS_FOLLOWED { + return Err(io::standard_error(io::InvalidInput)) + } + + match fs::lstat(&result) { + Err(..) => break, + Ok(ref stat) if stat.kind != io::TypeSymlink => break, + Ok(..) => { + followed += 1; + let path = try!(fs::readlink(&result)); + result.pop(); + result.push(path); + } + } + } + } + + return Ok(result); +} + +#[cfg(not(windows), test)] +mod test { + use std::io; + use std::io::fs::{File, symlink, mkdir, mkdir_recursive}; + use super::realpath; + use std::io::TempDir; + + #[test] + fn realpath_works() { + let tmpdir = TempDir::new("rustc-fs").unwrap(); + let tmpdir = realpath(tmpdir.path()).unwrap(); + let file = tmpdir.join("test"); + let dir = tmpdir.join("test2"); + let link = dir.join("link"); + let linkdir = tmpdir.join("test3"); + + File::create(&file).unwrap(); + mkdir(&dir, io::UserRWX).unwrap(); + symlink(&file, &link).unwrap(); + symlink(&dir, &linkdir).unwrap(); + + assert!(realpath(&tmpdir).unwrap() == tmpdir); + assert!(realpath(&file).unwrap() == file); + assert!(realpath(&link).unwrap() == file); + assert!(realpath(&linkdir).unwrap() == dir); + assert!(realpath(&linkdir.join("link")).unwrap() == file); + } + + #[test] + fn realpath_works_tricky() { + let tmpdir = TempDir::new("rustc-fs").unwrap(); + let tmpdir = realpath(tmpdir.path()).unwrap(); + + let a = tmpdir.join("a"); + let b = a.join("b"); + let c = b.join("c"); + let d = a.join("d"); + let e = d.join("e"); + let f = a.join("f"); + + mkdir_recursive(&b, io::UserRWX).unwrap(); + mkdir_recursive(&d, io::UserRWX).unwrap(); + File::create(&f).unwrap(); + symlink(&Path::new("../d/e"), &c).unwrap(); + symlink(&Path::new("../f"), &e).unwrap(); + + assert!(realpath(&c).unwrap() == f); + assert!(realpath(&e).unwrap() == f); + } +} From cf70ffc7945141e08f9c2ce249df8d741a04947c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 8 Apr 2014 10:15:46 -0700 Subject: [PATCH 4/4] rustc: Use realpath() for sysroot/rpath When calculating the sysroot, it's more accurate to use realpath() rather than just one readlink() to account for any intermediate symlinks that the rustc binary resolves itself to. For rpath, realpath() is necessary because the rpath must dictate a relative rpath from the destination back to the originally linked library, which works more robustly if there are no symlinks involved. Concretely, any binary generated on OSX into $TMPDIR requires an absolute rpath because the temporary directory is behind a symlink with one layer of indirection. This symlink causes all relative rpaths to fail to resolve. cc #11734 cc #11857 --- src/librustc/back/rpath.rs | 7 ++++--- src/librustc/metadata/filesearch.rs | 17 ++++++----------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/librustc/back/rpath.rs b/src/librustc/back/rpath.rs index 4b54b1f600885..e455c4ad23ce0 100644 --- a/src/librustc/back/rpath.rs +++ b/src/librustc/back/rpath.rs @@ -12,9 +12,10 @@ use driver::session::Session; use metadata::cstore; use metadata::filesearch; +use util::fs; use collections::HashSet; -use std::{os, slice}; +use std::os; use syntax::abi; fn not_win32(os: abi::Os) -> bool { @@ -121,9 +122,9 @@ pub fn get_rpath_relative_to_output(os: abi::Os, abi::OsWin32 => unreachable!() }; - let mut lib = os::make_absolute(lib); + let mut lib = fs::realpath(&os::make_absolute(lib)).unwrap(); lib.pop(); - let mut output = os::make_absolute(output); + let mut output = fs::realpath(&os::make_absolute(output)).unwrap(); output.pop(); let relative = lib.path_relative_from(&output); let relative = relative.expect("could not create rpath relative to output"); diff --git a/src/librustc/metadata/filesearch.rs b/src/librustc/metadata/filesearch.rs index b83f42da0a056..a752a3e1444bb 100644 --- a/src/librustc/metadata/filesearch.rs +++ b/src/librustc/metadata/filesearch.rs @@ -15,6 +15,8 @@ use std::os; use std::io::fs; use collections::HashSet; +use myfs = util::fs; + pub enum FileMatch { FileMatches, FileDoesntMatch } // A module for searching for libraries @@ -156,17 +158,10 @@ fn make_rustpkg_target_lib_path(sysroot: &Path, pub fn get_or_default_sysroot() -> Path { // Follow symlinks. If the resolved path is relative, make it absolute. fn canonicalize(path: Option) -> Option { - path.and_then(|mut path| - match fs::readlink(&path) { - Ok(canon) => { - if canon.is_absolute() { - Some(canon) - } else { - path.pop(); - Some(path.join(canon)) - } - }, - Err(..) => Some(path), + path.and_then(|path| + match myfs::realpath(&path) { + Ok(canon) => Some(canon), + Err(e) => fail!("failed to get realpath: {}", e), }) }