Skip to content

Linker improvements #6105

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 98 additions & 77 deletions src/librustc/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,71 @@ pub fn link_binary(sess: Session,
obj_filename: &Path,
out_filename: &Path,
lm: LinkMeta) {
// In the future, FreeBSD will use clang as default compiler.
// It would be flexible to use cc (system's default C compiler)
// instead of hard-coded gcc.
// For win32, there is no cc command,
// so we add a condition to make it use gcc.
let cc_prog: ~str = if sess.targ_cfg.os == session::os_android {
match &sess.opts.android_cross_path {
&Some(copy path) => {
fmt!("%s/bin/arm-linux-androideabi-gcc", path)
}
&None => {
sess.fatal(~"need Android NDK path for linking \
(--android-cross-path)")
}
}
} else if sess.targ_cfg.os == session::os_win32 { ~"gcc" }
else { ~"cc" };
// The invocations of cc share some flags across platforms


let output = if *sess.building_library {
let long_libname = output_dll_filename(sess.targ_cfg.os, lm);
debug!("link_meta.name: %s", lm.name);
debug!("long_libname: %s", long_libname);
debug!("out_filename: %s", out_filename.to_str());
debug!("dirname(out_filename): %s", out_filename.dir_path().to_str());

out_filename.dir_path().push(long_libname)
} else {
/*bad*/copy *out_filename
};

debug!("output: %s", output.to_str());
let mut cc_args = link_args(sess, obj_filename, out_filename, lm);
debug!("%s link args: %s", cc_prog, str::connect(cc_args, ~" "));
// We run 'cc' here
let prog = run::program_output(cc_prog, cc_args);
if 0 != prog.status {
sess.err(fmt!("linking with `%s` failed with code %d",
cc_prog, prog.status));
sess.note(fmt!("%s arguments: %s",
cc_prog, str::connect(cc_args, ~" ")));
sess.note(prog.err + prog.out);
sess.abort_if_errors();
}

// Clean up on Darwin
if sess.targ_cfg.os == session::os_macos {
run::run_program(~"dsymutil", ~[output.to_str()]);
}

// Remove the temporary object file if we aren't saving temps
if !sess.opts.save_temps {
if ! os::remove_file(obj_filename) {
sess.warn(fmt!("failed to delete object file `%s`",
obj_filename.to_str()));
}
}
}

pub fn link_args(sess: Session,
obj_filename: &Path,
out_filename: &Path,
lm:LinkMeta) -> ~[~str] {

// Converts a library file-stem into a cc -l argument
fn unlib(config: @session::config, stem: ~str) -> ~str {
if stem.starts_with("lib") &&
Expand All @@ -757,48 +822,23 @@ pub fn link_binary(sess: Session,
}
}


let output = if *sess.building_library {
let long_libname = output_dll_filename(sess.targ_cfg.os, lm);
debug!("link_meta.name: %s", lm.name);
debug!("long_libname: %s", long_libname);
debug!("out_filename: %s", out_filename.to_str());
debug!("dirname(out_filename): %s", out_filename.dir_path().to_str());

out_filename.dir_path().push(long_libname)
} else {
/*bad*/copy *out_filename
};

debug!("output: %s", output.to_str());

// The default library location, we need this to find the runtime.
// The location of crates will be determined as needed.
let stage: ~str = ~"-L" + sess.filesearch.get_target_lib_path().to_str();

// In the future, FreeBSD will use clang as default compiler.
// It would be flexible to use cc (system's default C compiler)
// instead of hard-coded gcc.
// For win32, there is no cc command,
// so we add a condition to make it use gcc.
let cc_prog: ~str = if sess.targ_cfg.os == session::os_android {
match &sess.opts.android_cross_path {
&Some(copy path) => {
fmt!("%s/bin/arm-linux-androideabi-gcc", path)
}
&None => {
sess.fatal(~"need Android NDK path for linking \
(--android-cross-path)")
}
}
} else if sess.targ_cfg.os == session::os_win32 { ~"gcc" }
else { ~"cc" };
// The invocations of cc share some flags across platforms
let mut args = vec::append(~[stage], sess.targ_cfg.target_strs.cc_args);

let mut cc_args =
vec::append(~[stage], sess.targ_cfg.target_strs.cc_args);
cc_args.push(~"-o");
cc_args.push(output.to_str());
cc_args.push(obj_filename.to_str());
args.push(~"-o");
args.push(output.to_str());
args.push(obj_filename.to_str());

let lib_cmd;
let os = sess.targ_cfg.os;
Expand All @@ -813,23 +853,23 @@ pub fn link_binary(sess: Session,
let cstore = sess.cstore;
for cstore::get_used_crate_files(cstore).each |cratepath| {
if cratepath.filetype() == Some(~".rlib") {
cc_args.push(cratepath.to_str());
args.push(cratepath.to_str());
loop;
}
let dir = cratepath.dirname();
if dir != ~"" { cc_args.push(~"-L" + dir); }
if dir != ~"" { args.push(~"-L" + dir); }
let libarg = unlib(sess.targ_cfg, cratepath.filestem().get());
cc_args.push(~"-l" + libarg);
args.push(~"-l" + libarg);
}

let ula = cstore::get_used_link_args(cstore);
for ula.each |arg| { cc_args.push(/*bad*/copy *arg); }
for ula.each |arg| { args.push(/*bad*/copy *arg); }

// Add all the link args for external crates.
do cstore::iter_crate_data(cstore) |crate_num, _| {
let link_args = csearch::get_link_args_for_crate(cstore, crate_num);
do vec::consume(link_args) |_, link_arg| {
cc_args.push(link_arg);
args.push(link_arg);
}
}

Expand All @@ -842,93 +882,74 @@ pub fn link_binary(sess: Session,
// forces to make sure that library can be found at runtime.

for sess.opts.addl_lib_search_paths.each |path| {
cc_args.push(~"-L" + path.to_str());
args.push(~"-L" + path.to_str());
}

// The names of the extern libraries
let used_libs = cstore::get_used_libraries(cstore);
for used_libs.each |l| { cc_args.push(~"-l" + *l); }
for used_libs.each |l| { args.push(~"-l" + *l); }

if *sess.building_library {
cc_args.push(lib_cmd);
args.push(lib_cmd);

// On mac we need to tell the linker to let this library
// be rpathed
if sess.targ_cfg.os == session::os_macos {
cc_args.push(~"-Wl,-install_name,@rpath/"
args.push(~"-Wl,-install_name,@rpath/"
+ output.filename().get());
}
}

// On linux librt and libdl are an indirect dependencies via rustrt,
// and binutils 2.22+ won't add them automatically
if sess.targ_cfg.os == session::os_linux {
cc_args.push_all(~[~"-lrt", ~"-ldl"]);
args.push_all(~[~"-lrt", ~"-ldl"]);

// LLVM implements the `frem` instruction as a call to `fmod`,
// which lives in libm. Similar to above, on some linuxes we
// have to be explicit about linking to it. See #2510
cc_args.push(~"-lm");
args.push(~"-lm");
}
else if sess.targ_cfg.os == session::os_android {
cc_args.push_all(~[~"-ldl", ~"-llog", ~"-lsupc++",
args.push_all(~[~"-ldl", ~"-llog", ~"-lsupc++",
~"-lgnustl_shared"]);
cc_args.push(~"-lm");
args.push(~"-lm");
}

if sess.targ_cfg.os == session::os_freebsd {
cc_args.push_all(~[~"-pthread", ~"-lrt",
~"-L/usr/local/lib", ~"-lexecinfo",
~"-L/usr/local/lib/gcc46",
~"-L/usr/local/lib/gcc44", ~"-lstdc++",
~"-Wl,-z,origin",
~"-Wl,-rpath,/usr/local/lib/gcc46",
~"-Wl,-rpath,/usr/local/lib/gcc44"]);
args.push_all(~[~"-pthread", ~"-lrt",
~"-L/usr/local/lib", ~"-lexecinfo",
~"-L/usr/local/lib/gcc46",
~"-L/usr/local/lib/gcc44", ~"-lstdc++",
~"-Wl,-z,origin",
~"-Wl,-rpath,/usr/local/lib/gcc46",
~"-Wl,-rpath,/usr/local/lib/gcc44"]);
}

// OS X 10.6 introduced 'compact unwind info', which is produced by the
// linker from the dwarf unwind info. Unfortunately, it does not seem to
// understand how to unwind our __morestack frame, so we have to turn it
// off. This has impacted some other projects like GHC.
if sess.targ_cfg.os == session::os_macos {
cc_args.push(~"-Wl,-no_compact_unwind");
args.push(~"-Wl,-no_compact_unwind");
}

// Stack growth requires statically linking a __morestack function
cc_args.push(~"-lmorestack");
args.push(~"-lmorestack");

// Always want the runtime linked in
cc_args.push(~"-lrustrt");
args.push(~"-lrustrt");

// FIXME (#2397): At some point we want to rpath our guesses as to where
// extern libraries might live, based on the addl_lib_search_paths
cc_args.push_all(rpath::get_rpath_flags(sess, &output));
args.push_all(rpath::get_rpath_flags(sess, &output));

debug!("%s link args: %s", cc_prog, str::connect(cc_args, ~" "));
// We run 'cc' here
let prog = run::program_output(cc_prog, cc_args);
if 0 != prog.status {
sess.err(fmt!("linking with `%s` failed with code %d",
cc_prog, prog.status));
sess.note(fmt!("%s arguments: %s",
cc_prog, str::connect(cc_args, ~" ")));
sess.note(prog.err + prog.out);
sess.abort_if_errors();
}
// Finally add all the linker arguments provided on the command line
args.push_all(sess.opts.linker_args);

// Clean up on Darwin
if sess.targ_cfg.os == session::os_macos {
run::run_program(~"dsymutil", ~[output.to_str()]);
}

// Remove the temporary object file if we aren't saving temps
if !sess.opts.save_temps {
if ! os::remove_file(obj_filename) {
sess.warn(fmt!("failed to delete object file `%s`",
obj_filename.to_str()));
}
}
return args;
}

//
// Local Variables:
// mode: rust
Expand Down
Loading