Change:
I'm having trouble linking a Rust program to a non-position-independent C library. When I manually remove the flag from the ld step that rustc runs, everything works correctly. I think that should remove the flag for me, but it doesn't. Here's what I think is going on: When I run It runs ld with the flag: But when I read the rustc code , it seems like the intention is that should only be passed to ld when . That is, if we're not compiling a position independent executable, then we should not pass the position independent executable flag to ld. I think what may be happening here is that somehow the defaults from are overriding the command line options I'm setting. This happens with stable and also the nightly I tried:
and are two distinct things. is also unrelated to the option. Interestingly, I do not remember us having an option to disable PIE; we should have one.
I did a bit of reading and here's what I figured out, please correct me if any of this is wrong. Linkers are new to me! Rust builds position independent executables by default on platforms that support it. These platforms have a relocation model of pic in their target config. The is what sets that linker option, except if it's disallowed by . The correct solution to this is to add a codegen flag, or something like that. Does this sound right?
Hi, I am having the same issue. I am trying to link against non-position-independent C library, build process ends without error but results returned by Rust bindings are different (and incorrect) than those returned from clean C implementation. In order to overcome that issue currently I am setting RUSTFLAGS="-C relocation-model=dynamic-no-pic -C linker=./linker" where linker is bash script that wraps cc and filter -pie flags:
I'm also having this problem. I assumed that passing when doing a build would suppress on the linker command line. (cc:
I'm still seeing this issue. Not sure if it's a regression or I'm doing something wrong. I have a "hello world" program in , and when I run , the resulting call to includes . I'm using nightly rust on Ubuntu: .
So, the problem here isn't with rust, but with the linker. In Ubuntu 15.10 and later the default GCC toolchain installed has been compiled with which causes and to be passed to during code generation and to during linking. Now, the linking part is what is important here as rust on most platforms uses as the linker for most targets. So, though no PIC or PIE code may have been generated by , the linker will still try to link as with PIE in mind, breaking when trying to compile with and the like. To work around this will need to see that the linker has been compiled with PIE as the default and pass to the linker if using GCC or something similar for other linkers. Code to do this can be seen in for Tarpaulin though it is only meant for Linux in that state and can be improved greatly if in tree. EDIT: Don't know why Github isn't linking cross-project PRs correctly right now.
Can this issue be resolved by always passing , except when compiling with ?
Sadly, no. In testing I have seen that any build of GCC that was not built with rejects the option and fails to run. This is why in the PR that I linked the linker was queried to see if it was compiled with before tacking on .
So it's came out from the discussion on an issue related to this in tarpaulin that passing -no-pie before -shared (and maybe then -static if it exists for static linkages as well) resolves the issue. I'm going to test this tarpaulin side and I can report back but I feel it would be best if this could be solved rustc side as it can affect any user in a similar situation.

Discussion:
// extensions <ins> pub fn get_args(&self) -> &[OsString] { &self.args } </ins> pub fn take_args(&mut self) -> Vec<OsString> { mem::replace(&mut self.args, Vec::new()) }

prog = time(sess.time_passes(), "running linker", || { exec_linker(sess, &mut cmd, tmpdir) }); <del> if !retry_on_segfault || i > 3 { break } </del> let output = match prog { Ok(ref output) => output, Err(_) => break,

let mut out = output.stderr.clone(); out.extend(&output.stdout); let out = String::from_utf8_lossy(&out); <ins> // Check to see if the link failed with "unrecognized command line option: // '-no-pie'" for gcc or "unknown argument: '-no-pie'" for clang. If so, // reperform the link step without the -no-pie option. This is safe because // if the linker doesn't support -no-pie then it should not default to // linking executables as pie. Different versions of gcc seem to use // different quotes in the error message so don't check for them. if sess.target.target.options.linker_is_gnu && (out.contains("unrecognized command line option") || out.contains("unknown argument")) && out.contains("-no-pie") && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie") { info!("linker output: {:?}", out); warn!("Linker does not support -no-pie command line option. Retrying without."); for arg in cmd.take_args() { if arg.to_string_lossy() != "-no-pie" { cmd.arg(arg); } } info!("{:?}", &cmd); continue; } if !retry_on_segfault || i > 3 { break } </ins> let msg_segv = "clang: error: unable to execute command: Segmentation fault: 11"; let msg_bus  = "clang: error: unable to execute command: Bus error: 10"; if !(out.contains(msg_segv) || out.contains(msg_bus)) {

let used_link_args = &trans.crate_info.link_args; <del> if crate_type == config::CrateTypeExecutable && t.options.position_independent_executables { let empty_vec = Vec::new(); let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec); let more_args = &sess.opts.cg.link_arg; let mut args = args.iter().chain(more_args.iter()).chain(used_link_args.iter()); if get_reloc_model(sess) == llvm::RelocMode::PIC && !sess.crt_static() && !args.any(|x| *x == "-static") { </del> <ins> if crate_type == config::CrateTypeExecutable { let mut position_independent_executable = false; if t.options.position_independent_executables { let empty_vec = Vec::new(); let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec); let more_args = &sess.opts.cg.link_arg; let mut args = args.iter().chain(more_args.iter()).chain(used_link_args.iter()); if get_reloc_model(sess) == llvm::RelocMode::PIC && !sess.crt_static() && !args.any(|x| *x == "-static") { position_independent_executable = true; } } if position_independent_executable { </ins> cmd.position_independent_executable(); <ins> } else { // recent versions of gcc can be configured to generate position // independent executables by default. We have to pass -no-pie to // explicitly turn that off. if sess.target.target.options.linker_is_gnu { cmd.no_position_independent_executable(); } </ins> } }

fn add_object(&mut self, path: &Path); fn gc_sections(&mut self, keep_metadata: bool); fn position_independent_executable(&mut self); <ins> fn no_position_independent_executable(&mut self); </ins> fn partial_relro(&mut self); fn full_relro(&mut self); fn optimize(&mut self);

fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); } fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); } <ins> fn no_position_independent_executable(&mut self) { self.cmd.arg("-no-pie"); } </ins> fn partial_relro(&mut self) { self.linker_arg("-z,relro"); } fn full_relro(&mut self) { self.linker_arg("-z,relro,-z,now"); } fn build_static_executable(&mut self) { self.cmd.arg("-static"); }

// noop } <ins> fn no_position_independent_executable(&mut self) { // noop } </ins> fn partial_relro(&mut self) { // noop }