Skip to content

Commit 57d65da

Browse files
committed
Support weak/common/private/etc. symbol linkage.
This commit adds a feature-gated #[linkage = "foo"] attribute that can be applied to extern functions and instructs the linker on how to resolve the symbol. "extern_weak" linkage is the most immediately useful one because it lets us link to library symbols that may or may not exist at run-time. Note that non-default linkage types require toolchain and (sometimes) dynamic linker support. For example, "extern_weak" is accepted but not actually supported by OS X's dyld. I decided not to warn for known bad platform/linkage combinations because clang and gcc don't do so either and because you don't rightly know whether the user is actually using the problematic toolchain or dynamic linker. For all we know, they're implementing their own ELF loader.
1 parent c13a929 commit 57d65da

File tree

4 files changed

+98
-2
lines changed

4 files changed

+98
-2
lines changed

src/librustc/front/feature_gate.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
4343
("non_ascii_idents", Active),
4444
("thread_local", Active),
4545
("link_args", Active),
46+
("linkage", Active),
4647
("phase", Active),
4748
("macro_registrar", Active),
4849
("log_syntax", Active),
@@ -188,6 +189,18 @@ impl Visitor<()> for Context {
188189
visit::walk_item(self, i, ());
189190
}
190191

192+
fn visit_foreign_item(&mut self, i: &ast::ForeignItem, _: ()) {
193+
match i.node {
194+
ast::ForeignItemFn(..) | ast::ForeignItemStatic(..) => {
195+
if attr::contains_name(i.attrs, "linkage") {
196+
self.gate_feature("linkage", i.span,
197+
"the `linkage` attribute is experimental \
198+
and not portable across platforms")
199+
}
200+
},
201+
}
202+
}
203+
191204
fn visit_mac(&mut self, macro: &ast::Mac, _: ()) {
192205
let ast::MacInvocTT(ref path, _, _) = macro.node;
193206
let id = path.segments.last().unwrap().identifier;

src/librustc/middle/lint.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -974,7 +974,7 @@ static other_attrs: &'static [&'static str] = &[
974974

975975
// fn-level
976976
"test", "bench", "should_fail", "ignore", "inline", "lang", "main", "start",
977-
"no_split_stack", "cold", "macro_registrar",
977+
"no_split_stack", "cold", "macro_registrar", "linkage",
978978

979979
// internal attribute: bypass privacy inside items
980980
"!resolve_unexported",

src/librustc/middle/trans/foreign.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
use back::{link};
1313
use lib::llvm::llvm;
14-
use lib::llvm::{ValueRef, CallConv, StructRetAttribute};
14+
use lib::llvm::{ValueRef, CallConv, Linkage, StructRetAttribute};
1515
use lib;
1616
use middle::trans::base::push_ctxt;
1717
use middle::trans::base;
@@ -106,6 +106,34 @@ pub fn llvm_calling_convention(ccx: &CrateContext,
106106
}
107107

108108

109+
pub fn llvm_linkage_by_name(name: &str) -> Option<Linkage> {
110+
// Use the names from src/llvm/docs/LangRef.rst here. Most types are only
111+
// applicable to variable declarations and may not really make sense for
112+
// Rust code in the first place but whitelist them anyway and trust that
113+
// the user knows what s/he's doing. Who knows, unanticipated use cases
114+
// may pop up in the future.
115+
//
116+
// ghost, dllimport, dllexport and linkonce_odr_autohide are not supported
117+
// and don't have to be, LLVM treats them as no-ops.
118+
match name {
119+
"appending" => Some(lib::llvm::AppendingLinkage),
120+
"available_externally" => Some(lib::llvm::AvailableExternallyLinkage),
121+
"common" => Some(lib::llvm::CommonLinkage),
122+
"extern_weak" => Some(lib::llvm::ExternalWeakLinkage),
123+
"external" => Some(lib::llvm::ExternalLinkage),
124+
"internal" => Some(lib::llvm::InternalLinkage),
125+
"linker_private" => Some(lib::llvm::LinkerPrivateLinkage),
126+
"linker_private_weak" => Some(lib::llvm::LinkerPrivateWeakLinkage),
127+
"linkonce" => Some(lib::llvm::LinkOnceAnyLinkage),
128+
"linkonce_odr" => Some(lib::llvm::LinkOnceODRLinkage),
129+
"private" => Some(lib::llvm::PrivateLinkage),
130+
"weak" => Some(lib::llvm::WeakAnyLinkage),
131+
"weak_odr" => Some(lib::llvm::WeakODRLinkage),
132+
_ => None,
133+
}
134+
}
135+
136+
109137
pub fn register_foreign_item_fn(ccx: @CrateContext,
110138
abis: AbiSet,
111139
path: &ast_map::Path,
@@ -158,6 +186,18 @@ pub fn register_foreign_item_fn(ccx: @CrateContext,
158186
llfn_ty,
159187
tys.fn_sig.output);
160188
};
189+
190+
match attr::first_attr_value_str_by_name(foreign_item.attrs, "linkage") {
191+
Some(name) => {
192+
match llvm_linkage_by_name(name.get()) {
193+
Some(linkage) => lib::llvm::SetLinkage(llfn, linkage),
194+
None => ccx.sess.span_fatal(foreign_item.span,
195+
format!("Bad linkage `{}`", name)),
196+
}
197+
},
198+
None => {}, // Default "external" linkage.
199+
}
200+
161201
add_argument_attributes(&tys, llfn);
162202

163203
return llfn;

src/test/run-pass/linkage.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
#[feature(linkage)];
12+
13+
use std::cast;
14+
use std::ptr;
15+
16+
fn external() {
17+
extern {
18+
#[linkage = "external"]
19+
fn abort();
20+
}
21+
let ptr: *u8 = unsafe { cast::transmute(abort) };
22+
assert!(ptr != ptr::null());
23+
}
24+
25+
#[cfg(target_os = "linux")]
26+
fn extern_weak() {
27+
extern {
28+
#[linkage = "extern_weak"]
29+
fn frobnitz();
30+
}
31+
let ptr: *u8 = unsafe { cast::transmute(frobnitz) };
32+
assert!(ptr == ptr::null());
33+
}
34+
35+
#[cfg(not(target_os = "linux"))]
36+
fn extern_weak() {
37+
// Not supported.
38+
}
39+
40+
fn main() {
41+
external();
42+
extern_weak();
43+
}

0 commit comments

Comments
 (0)