Skip to content

Commit

Permalink
rustc: Preserve reachable extern fns with LTO
Browse files Browse the repository at this point in the history
All rust functions are internal implementation details with respect to the ABI
exposed by crates, but extern fns are public components of the ABI and shouldn't
be stripped. This commit serializes reachable extern fns to metadata, so when
LTO is performed all of their symbols are not stripped.

Closes #14500
  • Loading branch information
alexcrichton committed Jun 7, 2014
1 parent cb12e7a commit d4dec47
Show file tree
Hide file tree
Showing 11 changed files with 128 additions and 14 deletions.
3 changes: 3 additions & 0 deletions src/librustc/metadata/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ pub static tag_dylib_dependency_formats: uint = 0x67;
pub static tag_method_argument_names: uint = 0x8e;
pub static tag_method_argument_name: uint = 0x8f;

pub static tag_reachable_extern_fns: uint = 0x90;
pub static tag_reachable_extern_fn_id: uint = 0x91;

#[deriving(Clone, Show)]
pub struct LinkMeta {
pub crateid: CrateId,
Expand Down
7 changes: 7 additions & 0 deletions src/librustc/metadata/csearch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,10 @@ pub fn get_method_arg_names(cstore: &cstore::CStore, did: ast::DefId)
let cdata = cstore.get_crate_data(did.krate);
decoder::get_method_arg_names(&*cdata, did.node)
}

pub fn get_reachable_extern_fns(cstore: &cstore::CStore, cnum: ast::CrateNum)
-> Vec<ast::DefId>
{
let cdata = cstore.get_crate_data(cnum);
decoder::get_reachable_extern_fns(&*cdata)
}
14 changes: 14 additions & 0 deletions src/librustc/metadata/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1325,3 +1325,17 @@ pub fn get_method_arg_names(cdata: Cmd, id: ast::NodeId) -> Vec<String> {
}
return ret;
}

pub fn get_reachable_extern_fns(cdata: Cmd) -> Vec<ast::DefId> {
let mut ret = Vec::new();
let items = reader::get_doc(ebml::Doc::new(cdata.data()),
tag_reachable_extern_fns);
reader::tagged_docs(items, tag_reachable_extern_fn_id, |doc| {
ret.push(ast::DefId {
krate: cdata.cnum,
node: reader::doc_as_u32(doc),
});
true
});
return ret;
}
25 changes: 25 additions & 0 deletions src/librustc/metadata/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ pub struct EncodeParams<'a> {
pub link_meta: &'a LinkMeta,
pub cstore: &'a cstore::CStore,
pub encode_inlined_item: EncodeInlinedItem<'a>,
pub reachable: &'a NodeSet,
}

pub struct EncodeContext<'a> {
Expand All @@ -87,6 +88,7 @@ pub struct EncodeContext<'a> {
pub cstore: &'a cstore::CStore,
pub encode_inlined_item: RefCell<EncodeInlinedItem<'a>>,
pub type_abbrevs: tyencode::abbrev_map,
pub reachable: &'a NodeSet,
}

fn encode_name(ebml_w: &mut Encoder, name: Name) {
Expand Down Expand Up @@ -1702,6 +1704,26 @@ fn encode_misc_info(ecx: &EncodeContext,
ebml_w.end_tag();
}

fn encode_reachable_extern_fns(ecx: &EncodeContext, ebml_w: &mut Encoder) {
ebml_w.start_tag(tag_reachable_extern_fns);

for id in ecx.reachable.iter() {
match ecx.tcx.map.find(*id) {
Some(ast_map::NodeItem(i)) => {
match i.node {
ast::ItemFn(_, _, abi, _, _) if abi != abi::Rust => {
ebml_w.wr_tagged_u32(tag_reachable_extern_fn_id, *id);
}
_ => {}
}
}
_ => {}
}
}

ebml_w.end_tag();
}

fn encode_crate_dep(ebml_w: &mut Encoder,
dep: decoder::CrateDep) {
ebml_w.start_tag(tag_crate_dep);
Expand Down Expand Up @@ -1801,6 +1823,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate)
encode_inlined_item,
link_meta,
non_inlineable_statics,
reachable,
..
} = parms;
let ecx = EncodeContext {
Expand All @@ -1813,6 +1836,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate)
cstore: cstore,
encode_inlined_item: RefCell::new(encode_inlined_item),
type_abbrevs: RefCell::new(HashMap::new()),
reachable: reachable,
};

let mut ebml_w = writer::Encoder::new(wr);
Expand Down Expand Up @@ -1864,6 +1888,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate)
// Encode miscellaneous info.
i = ebml_w.writer.tell().unwrap();
encode_misc_info(&ecx, krate, &mut ebml_w);
encode_reachable_extern_fns(&ecx, &mut ebml_w);
stats.misc_bytes = ebml_w.writer.tell().unwrap() - i;

// Encode and index the items.
Expand Down
11 changes: 11 additions & 0 deletions src/librustc/middle/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2238,6 +2238,7 @@ pub fn crate_ctxt_to_encode_parms<'r>(cx: &'r CrateContext, ie: encoder::EncodeI
link_meta: &cx.link_meta,
cstore: &cx.sess().cstore,
encode_inlined_item: ie,
reachable: &cx.reachable,
}
}

Expand Down Expand Up @@ -2374,6 +2375,16 @@ pub fn trans_crate(krate: ast::Crate,
ccx.item_symbols.borrow().find(id).map(|s| s.to_string())
}).collect();

// For the purposes of LTO, we add to the reachable set all of the upstream
// reachable extern fns. These functions are all part of the public ABI of
// the final product, so LTO needs to preserve them.
ccx.sess().cstore.iter_crate_data(|cnum, _| {
let syms = csearch::get_reachable_extern_fns(&ccx.sess().cstore, cnum);
reachable.extend(syms.move_iter().map(|did| {
csearch::get_symbol(&ccx.sess().cstore, did)
}));
});

// Make sure that some other crucial symbols are not eliminated from the
// module. This includes the main function, the crate map (used for debug
// log settings and I/O), and finally the curious rust_stack_exhausted
Expand Down
14 changes: 14 additions & 0 deletions src/test/run-make/issue-14500/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-include ../tools.mk

# Test to make sure that reachable extern fns are always available in final
# productcs, including when LTO is used. In this test, the `foo` crate has a
# reahable symbol, and is a dependency of the `bar` crate. When the `bar` crate
# is compiled with LTO, it shouldn't strip the symbol from `foo`, and that's the
# only way that `foo.c` will successfully compile.

all:
$(RUSTC) foo.rs --crate-type=rlib
$(RUSTC) bar.rs --crate-type=staticlib -Zlto -L. -o $(TMPDIR)/libbar.a
$(CC) foo.c -lbar -o $(call RUN_BINFILE,foo) $(EXTRACFLAGS)
$(call RUN,foo)

11 changes: 11 additions & 0 deletions src/test/run-make/issue-14500/bar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate foo;
16 changes: 16 additions & 0 deletions src/test/run-make/issue-14500/foo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern void foo();

int main() {
foo();
return 0;
}
12 changes: 12 additions & 0 deletions src/test/run-make/issue-14500/foo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[no_mangle]
pub extern fn foo() {}
15 changes: 1 addition & 14 deletions src/test/run-make/lto-smoke-c/Makefile
Original file line number Diff line number Diff line change
@@ -1,23 +1,10 @@
-include ../tools.mk

ifdef IS_WINDOWS
EXTRAFLAGS :=
else
ifeq ($(shell uname),Darwin)
else
ifeq ($(shell uname),FreeBSD)
EXTRAFLAGS := -lm -lpthread -lgcc_s
else
EXTRAFLAGS := -lm -lrt -ldl -lpthread
endif
endif
endif

# Apparently older versions of GCC segfault if -g is passed...
CC := $(CC:-g=)

all:
$(RUSTC) foo.rs -Z lto
ln -s $(call STATICLIB,foo-*) $(call STATICLIB,foo)
$(CC) bar.c -lfoo -o $(call RUN_BINFILE,bar) $(EXTRAFLAGS) -lstdc++
$(CC) bar.c -lfoo -o $(call RUN_BINFILE,bar) $(EXTRACFLAGS) -lstdc++
$(call RUN,bar)
14 changes: 14 additions & 0 deletions src/test/run-make/tools.mk
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ RPATH_LINK_SEARCH = -Wl,-rpath-link=$(1)
endif
endif

# Extra flags needed to compile a working executable with the standard library
ifdef IS_WINDOWS
EXTRACFLAGS :=
else
ifeq ($(shell uname),Darwin)
else
ifeq ($(shell uname),FreeBSD)
EXTRACFLAGS := -lm -lpthread -lgcc_s
else
EXTRACFLAGS := -lm -lrt -ldl -lpthread
endif
endif
endif

REMOVE_DYLIBS = rm $(TMPDIR)/$(call DYLIB_GLOB,$(1))
REMOVE_RLIBS = rm $(TMPDIR)/$(call RLIB_GLOB,$(1))

Expand Down

0 comments on commit d4dec47

Please sign in to comment.