Skip to content

Commit d4dec47

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 #14500
1 parent cb12e7a commit d4dec47

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
@@ -1325,3 +1325,17 @@ pub fn get_method_arg_names(cdata: Cmd, id: ast::NodeId) -> Vec<String> {
13251325
}
13261326
return ret;
13271327
}
1328+
1329+
pub fn get_reachable_extern_fns(cdata: Cmd) -> Vec<ast::DefId> {
1330+
let mut ret = Vec::new();
1331+
let items = reader::get_doc(ebml::Doc::new(cdata.data()),
1332+
tag_reachable_extern_fns);
1333+
reader::tagged_docs(items, tag_reachable_extern_fn_id, |doc| {
1334+
ret.push(ast::DefId {
1335+
krate: cdata.cnum,
1336+
node: reader::doc_as_u32(doc),
1337+
});
1338+
true
1339+
});
1340+
return ret;
1341+
}

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) {
@@ -1702,6 +1704,26 @@ fn encode_misc_info(ecx: &EncodeContext,
17021704
ebml_w.end_tag();
17031705
}
17041706

1707+
fn encode_reachable_extern_fns(ecx: &EncodeContext, ebml_w: &mut Encoder) {
1708+
ebml_w.start_tag(tag_reachable_extern_fns);
1709+
1710+
for id in ecx.reachable.iter() {
1711+
match ecx.tcx.map.find(*id) {
1712+
Some(ast_map::NodeItem(i)) => {
1713+
match i.node {
1714+
ast::ItemFn(_, _, abi, _, _) if abi != abi::Rust => {
1715+
ebml_w.wr_tagged_u32(tag_reachable_extern_fn_id, *id);
1716+
}
1717+
_ => {}
1718+
}
1719+
}
1720+
_ => {}
1721+
}
1722+
}
1723+
1724+
ebml_w.end_tag();
1725+
}
1726+
17051727
fn encode_crate_dep(ebml_w: &mut Encoder,
17061728
dep: decoder::CrateDep) {
17071729
ebml_w.start_tag(tag_crate_dep);
@@ -1801,6 +1823,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate)
18011823
encode_inlined_item,
18021824
link_meta,
18031825
non_inlineable_statics,
1826+
reachable,
18041827
..
18051828
} = parms;
18061829
let ecx = EncodeContext {
@@ -1813,6 +1836,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate)
18131836
cstore: cstore,
18141837
encode_inlined_item: RefCell::new(encode_inlined_item),
18151838
type_abbrevs: RefCell::new(HashMap::new()),
1839+
reachable: reachable,
18161840
};
18171841

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

18691894
// 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
@@ -2238,6 +2238,7 @@ pub fn crate_ctxt_to_encode_parms<'r>(cx: &'r CrateContext, ie: encoder::EncodeI
22382238
link_meta: &cx.link_meta,
22392239
cstore: &cx.sess().cstore,
22402240
encode_inlined_item: ie,
2241+
reachable: &cx.reachable,
22412242
}
22422243
}
22432244

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

2378+
// For the purposes of LTO, we add to the reachable set all of the upstream
2379+
// reachable extern fns. These functions are all part of the public ABI of
2380+
// the final product, so LTO needs to preserve them.
2381+
ccx.sess().cstore.iter_crate_data(|cnum, _| {
2382+
let syms = csearch::get_reachable_extern_fns(&ccx.sess().cstore, cnum);
2383+
reachable.extend(syms.move_iter().map(|did| {
2384+
csearch::get_symbol(&ccx.sess().cstore, did)
2385+
}));
2386+
});
2387+
23772388
// Make sure that some other crucial symbols are not eliminated from the
23782389
// module. This includes the main function, the crate map (used for debug
23792390
// 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)