Skip to content

linking problem on extern "C" fn called via trait method (on type parameter) in other crate #10543

Closed
@pnkfelix

Description

@pnkfelix
Member

While spending some time playing with a ncurses wrapper library (one of many), I encountered the following odd linking problem. I have one crate which attempts to link to the system ncurses library via #[link_args="-lncurses"], and then a main program that pulls in the first crate.

However, the crate that links to the system ncurses library is also exposing functionality via type-parametric methods, and something seems to go wrong with the linkage due to this, AFAICT.

Here is the example:

main crate (call it ncurses.rs):

#[link(name="ncurses",vers="5.7")];

use std::libc::c_int;
use ncurses_core::{A_NORMAL, A_STANDOUT, attrset};

mod ncurses_core {
    use std::libc::c_int;

    #[link_args = "-lncurses"]
    extern { pub fn attrset (_:c_int) -> c_int; }

    pub static A_NORMAL: c_int     = 0;
    pub static A_STANDOUT: c_int   = (1u << 16) as c_int;
}

pub struct Context;

pub enum attr { normal = A_NORMAL, standout = A_STANDOUT }

#[cfg(not(show_bug))]
impl Context {
    pub fn attrset2(&mut self, attrs: attrv) {
        let i = attrs.encode_direct();
        unsafe { attrset(i); }
    }
}

#[cfg(show_bug)]
impl Context {
    pub fn attrset2<A:EncodesAttrs>(&mut self, attrs: A) {
        let i = attrs.encode_via_trait();
        unsafe { attrset(i); }
    }
}

pub trait EncodesAttrs { fn encode_via_trait(&self) -> c_int; }

impl<'a> EncodesAttrs for attrv<'a> {
    fn encode_via_trait(&self) -> c_int { encode_attrs(self.contents) }
}

pub struct attrv<'a> { contents: &'a [attr] }
pub fn attrv<'a>(av: &'a [attr]) -> attrv<'a> { attrv{ contents: av } }

impl<'a> attrv<'a> {
    pub fn encode_direct(&self) -> c_int { encode_attrs(self.contents) }
}

fn encode_attrs(_attrs: &[attr]) -> c_int { 0 }

main program (ncurses-intro.rs):

extern mod ncurses;

fn main() {
    let mut context = ncurses::Context;
    context.attrset2(ncurses::attrv(&[ncurses::standout]));
}

Invocation that works (by sidestepping type-parametricity):

% rustc --lib ncurses.rs && rustc -L. ncurses-intro.rs
warning: missing crate link meta `package_id`, using `ncurses` as default

Invocation that illustrates the bug (by using the desired type parametric form):

% rustc --cfg show_bug --lib ncurses.rs && rustc -L. ncurses-intro.rs
warning: missing crate link meta `package_id`, using `ncurses` as default
error: linking with `cc` failed: exit code: 1
note: cc arguments: -L/Users/fklock/opt/rust-dbg/lib/rustc/x86_64-apple-darwin/lib -m64 -o ncurses-intro ncurses-intro.o -L/Users/fklock/opt/rust-dbg/lib/rustc/x86_64-apple-darwin/lib -lstd-6425b930ca146ae9-0.9-pre -L/Users/fklock/opt/rust-dbg/lib/rustc/x86_64-apple-darwin/lib -lrustuv-a13edc95d75df17-0.9-pre -L. -lncurses-dd4798ddf43ebf9-5.7 -L. -L/Users/fklock/Dev/Rust/rust-curses/.rust -L/Users/fklock/Dev/Rust/rust-curses -lmorestack -lrustrt -Wl,-rpath,@loader_path/../../../opt/rust-dbg/lib/rustc/x86_64-apple-darwin/lib -Wl,-rpath,@loader_path/. -Wl,-rpath,/Users/fklock/opt/rust-dbg/lib/rustc/x86_64-apple-darwin/lib -Wl,-rpath,/Users/fklock/Dev/Rust/rust-curses
note: ld: warning: directory not found for option '-L/Users/fklock/Dev/Rust/rust-curses/.rust'
Undefined symbols for architecture x86_64:
  "_attrset", referenced from:
      Context::attrset2::h3513932d42d079afxaM::v0.0 in ncurses-intro.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

error: aborting due to previous error
task 'rustc' failed at 'explicit failure', /Users/fklock/Dev/Mozilla/rust.git/src/libsyntax/diagnostic.rs:101
task '<main>' failed at 'explicit failure', /Users/fklock/Dev/Mozilla/rust.git/src/librustc/lib.rs:396

Activity

alexcrichton

alexcrichton commented on Nov 18, 2013

@alexcrichton
Member

The reason that this is happening is that generic code is instantiated in the client crate, and the client crate is not linking to ncurses.

This was an explicit decision as part of 2b9c774 to stop propagating link arguments across crates. The reason for doing so is that if we unconditionally propagate linker arguments there is no way to have a build dependency that is not distributed alongside a rust library.

I believe that this can be revisited after #10528 lands because that already adds back propagation to a certain degree (one which I am much more comfortable with).

pnkfelix

pnkfelix commented on Nov 18, 2013

@pnkfelix
MemberAuthor

Okay, I'll assign this to myself as a reminder to double-check on it (and hopefully close it after we've landed #10528, assuming that enables reasonable propagation).

alexcrichton

alexcrichton commented on Nov 18, 2013

@alexcrichton
Member

The static linking pull request will not close this in its current incarnation. I'd want to talk about this more broadly to make sure that we want to continue to propagate dependencies, but I believe that we'll be much more amenable to it this time around rather than before.

alexcrichton

alexcrichton commented on Dec 1, 2013

@alexcrichton
Member

Closing as a dupe of #10743

removed their assignment
on Jun 16, 2014
added a commit that references this issue on Apr 6, 2023

Auto merge of rust-lang#10543 - blyxyas:tests_outside_test_module, r=…

added a commit that references this issue on Apr 23, 2023

Auto merge of rust-lang#10578 - blyxyas:items_after_test_module, r=dswij

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-linkageArea: linking into static, shared libraries and binaries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @alexcrichton@pnkfelix

        Issue actions

          linking problem on extern "C" fn called via trait method (on type parameter) in other crate · Issue #10543 · rust-lang/rust