Skip to content

Commit 9dc7101

Browse files
committed
rustc: Preserve reachable extern fns with LTO
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 rust-lang#14500
1 parent 8a41485 commit 9dc7101

File tree

11 files changed

+128
-14
lines changed

11 files changed

+128
-14
lines changed

src/librustc/metadata/common.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ pub static tag_dylib_dependency_formats: uint = 0x67;
209209
pub static tag_method_argument_names: uint = 0x8e;
210210
pub static tag_method_argument_name: uint = 0x8f;
211211

212+
pub static tag_reachable_extern_fns: uint = 0x90;
213+
pub static tag_reachable_extern_fn_id: uint = 0x91;
214+
212215
#[deriving(Clone, Show)]
213216
pub struct LinkMeta {
214217
pub crateid: CrateId,

src/librustc/metadata/csearch.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,3 +314,10 @@ pub fn get_method_arg_names(cstore: &cstore::CStore, did: ast::DefId)
314314
let cdata = cstore.get_crate_data(did.krate);
315315
decoder::get_method_arg_names(&*cdata, did.node)
316316
}
317+
318+
pub fn get_reachable_extern_fns(cstore: &cstore::CStore, cnum: ast::CrateNum)
319+
-> Vec<ast::DefId>
320+
{
321+
let cdata = cstore.get_crate_data(cnum);
322+
decoder::get_reachable_extern_fns(&*cdata)
323+
}

src/librustc/metadata/decoder.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,3 +1324,17 @@ pub fn get_method_arg_names(cdata: Cmd, id: ast::NodeId) -> Vec<String> {
13241324
}
13251325
return ret;
13261326
}
1327+
1328+
pub fn get_reachable_extern_fns(cdata: Cmd) -> Vec<ast::DefId> {
1329+
let mut ret = Vec::new();
1330+
let items = reader::get_doc(ebml::Doc::new(cdata.data()),
1331+
tag_reachable_extern_fns);
1332+
reader::tagged_docs(items, tag_reachable_extern_fn_id, |doc| {
1333+
ret.push(ast::DefId {
1334+
krate: cdata.cnum,
1335+
node: reader::doc_as_u32(doc),
1336+
});
1337+
true
1338+
});
1339+
return ret;
1340+
}

src/librustc/metadata/encoder.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ pub struct EncodeParams<'a> {
7575
pub link_meta: &'a LinkMeta,
7676
pub cstore: &'a cstore::CStore,
7777
pub encode_inlined_item: EncodeInlinedItem<'a>,
78+
pub reachable: &'a NodeSet,
7879
}
7980

8081
pub struct EncodeContext<'a> {
@@ -87,6 +88,7 @@ pub struct EncodeContext<'a> {
8788
pub cstore: &'a cstore::CStore,
8889
pub encode_inlined_item: RefCell<EncodeInlinedItem<'a>>,
8990
pub type_abbrevs: tyencode::abbrev_map,
91+
pub reachable: &'a NodeSet,
9092
}
9193

9294
fn encode_name(ebml_w: &mut Encoder, name: Name) {
@@ -1700,6 +1702,26 @@ fn encode_misc_info(ecx: &EncodeContext,
17001702
ebml_w.end_tag();
17011703
}
17021704

1705+
fn encode_reachable_extern_fns(ecx: &EncodeContext, ebml_w: &mut Encoder) {
1706+
ebml_w.start_tag(tag_reachable_extern_fns);
1707+
1708+
for id in ecx.reachable.iter() {
1709+
match ecx.tcx.map.find(*id) {
1710+
Some(ast_map::NodeItem(i)) => {
1711+
match i.node {
1712+
ast::ItemFn(_, _, abi, _, _) if abi != abi::Rust => {
1713+
ebml_w.wr_tagged_u32(tag_reachable_extern_fn_id, *id);
1714+
}
1715+
_ => {}
1716+
}
1717+
}
1718+
_ => {}
1719+
}
1720+
}
1721+
1722+
ebml_w.end_tag();
1723+
}
1724+
17031725
fn encode_crate_dep(ebml_w: &mut Encoder,
17041726
dep: decoder::CrateDep) {
17051727
ebml_w.start_tag(tag_crate_dep);
@@ -1799,6 +1821,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate)
17991821
encode_inlined_item,
18001822
link_meta,
18011823
non_inlineable_statics,
1824+
reachable,
18021825
..
18031826
} = parms;
18041827
let ecx = EncodeContext {
@@ -1811,6 +1834,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate)
18111834
cstore: cstore,
18121835
encode_inlined_item: RefCell::new(encode_inlined_item),
18131836
type_abbrevs: RefCell::new(HashMap::new()),
1837+
reachable: reachable,
18141838
};
18151839

18161840
let mut ebml_w = writer::Encoder::new(wr);
@@ -1862,6 +1886,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate)
18621886
// Encode miscellaneous info.
18631887
i = ebml_w.writer.tell().unwrap();
18641888
encode_misc_info(&ecx, krate, &mut ebml_w);
1889+
encode_reachable_extern_fns(&ecx, &mut ebml_w);
18651890
stats.misc_bytes = ebml_w.writer.tell().unwrap() - i;
18661891

18671892
// Encode and index the items.

src/librustc/middle/trans/base.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2194,6 +2194,7 @@ pub fn crate_ctxt_to_encode_parms<'r>(cx: &'r CrateContext, ie: encoder::EncodeI
21942194
link_meta: &cx.link_meta,
21952195
cstore: &cx.sess().cstore,
21962196
encode_inlined_item: ie,
2197+
reachable: &cx.reachable,
21972198
}
21982199
}
21992200

@@ -2330,6 +2331,16 @@ pub fn trans_crate(krate: ast::Crate,
23302331
ccx.item_symbols.borrow().find(id).map(|s| s.to_string())
23312332
}).collect();
23322333

2334+
// For the purposes of LTO, we add to the reachable set all of the upstream
2335+
// reachable extern fns. These functions are all part of the public ABI of
2336+
// the final product, so LTO needs to preserve them.
2337+
ccx.sess().cstore.iter_crate_data(|cnum, _| {
2338+
let syms = csearch::get_reachable_extern_fns(&ccx.sess().cstore, cnum);
2339+
reachable.extend(syms.move_iter().map(|did| {
2340+
csearch::get_symbol(&ccx.sess().cstore, did)
2341+
}));
2342+
});
2343+
23332344
// Make sure that some other crucial symbols are not eliminated from the
23342345
// module. This includes the main function, the crate map (used for debug
23352346
// log settings and I/O), and finally the curious rust_stack_exhausted
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
-include ../tools.mk
2+
3+
# Test to make sure that reachable extern fns are always available in final
4+
# productcs, including when LTO is used. In this test, the `foo` crate has a
5+
# reahable symbol, and is a dependency of the `bar` crate. When the `bar` crate
6+
# is compiled with LTO, it shouldn't strip the symbol from `foo`, and that's the
7+
# only way that `foo.c` will successfully compile.
8+
9+
all:
10+
$(RUSTC) foo.rs --crate-type=rlib
11+
$(RUSTC) bar.rs --crate-type=staticlib -Zlto -L. -o $(TMPDIR)/libbar.a
12+
$(CC) foo.c -lbar -o $(call RUN_BINFILE,foo) $(EXTRACFLAGS)
13+
$(call RUN,foo)
14+

src/test/run-make/issue-14500/bar.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
extern crate foo;

src/test/run-make/issue-14500/foo.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
extern void foo();
12+
13+
int main() {
14+
foo();
15+
return 0;
16+
}

src/test/run-make/issue-14500/foo.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#[no_mangle]
12+
pub extern fn foo() {}
Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,10 @@
11
-include ../tools.mk
22

3-
ifdef IS_WINDOWS
4-
EXTRAFLAGS :=
5-
else
6-
ifeq ($(shell uname),Darwin)
7-
else
8-
ifeq ($(shell uname),FreeBSD)
9-
EXTRAFLAGS := -lm -lpthread -lgcc_s
10-
else
11-
EXTRAFLAGS := -lm -lrt -ldl -lpthread
12-
endif
13-
endif
14-
endif
15-
163
# Apparently older versions of GCC segfault if -g is passed...
174
CC := $(CC:-g=)
185

196
all:
207
$(RUSTC) foo.rs -Z lto
218
ln -s $(call STATICLIB,foo-*) $(call STATICLIB,foo)
22-
$(CC) bar.c -lfoo -o $(call RUN_BINFILE,bar) $(EXTRAFLAGS) -lstdc++
9+
$(CC) bar.c -lfoo -o $(call RUN_BINFILE,bar) $(EXTRACFLAGS) -lstdc++
2310
$(call RUN,bar)

src/test/run-make/tools.mk

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@ RPATH_LINK_SEARCH = -Wl,-rpath-link=$(1)
5353
endif
5454
endif
5555

56+
# Extra flags needed to compile a working executable with the standard library
57+
ifdef IS_WINDOWS
58+
EXTRACFLAGS :=
59+
else
60+
ifeq ($(shell uname),Darwin)
61+
else
62+
ifeq ($(shell uname),FreeBSD)
63+
EXTRACFLAGS := -lm -lpthread -lgcc_s
64+
else
65+
EXTRACFLAGS := -lm -lrt -ldl -lpthread
66+
endif
67+
endif
68+
endif
69+
5670
REMOVE_DYLIBS = rm $(TMPDIR)/$(call DYLIB_GLOB,$(1))
5771
REMOVE_RLIBS = rm $(TMPDIR)/$(call RLIB_GLOB,$(1))
5872

0 commit comments

Comments
 (0)