Skip to content

Commit 9f3ebd8

Browse files
committed
auto merge of #12556 : alexcrichton/rust/weak-linkage, r=brson
It is often convenient to have forms of weak linkage or other various types of linkage. Sadly, just using these flavors of linkage are not compatible with Rust's typesystem and how it considers some pointers to be non-null. As a compromise, this commit adds support for weak linkage to external symbols, but it requires that this is only placed on extern statics of type `*T`. Codegen-wise, we get translations like: ```rust // rust code extern { #[linkage = "extern_weak"] static foo: *i32; } // generated IR @foo = extern_weak global i32 @_some_internal_symbol = internal global *i32 @foo ``` All references to the rust value of `foo` then reference `_some_internal_symbol` instead of the symbol `_foo` itself. This allows us to guarantee that the address of `foo` will never be null while the value may sometimes be null. An example was implemented in `std::rt::thread` to determine if `__pthread_get_minstack()` is available at runtime, and a test is checked in to use it for a static value as well. Function pointers a little odd because you still need to transmute the pointer value to a function pointer, but it's thankfully better than not having this capability at all. Thanks to @bnoordhuis for the original patch, most of this work is still his!
2 parents a0f20f0 + 699b33d commit 9f3ebd8

File tree

11 files changed

+243
-78
lines changed

11 files changed

+243
-78
lines changed

src/librustc/front/feature_gate.rs

+14
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
5353
("simd", Active),
5454
("default_type_params", Active),
5555
("quote", Active),
56+
("linkage", Active),
5657

5758
// These are used to test this portion of the compiler, they don't actually
5859
// mean anything
@@ -238,6 +239,19 @@ impl Visitor<()> for Context {
238239
}
239240
}
240241

242+
fn visit_foreign_item(&mut self, i: &ast::ForeignItem, _: ()) {
243+
match i.node {
244+
ast::ForeignItemFn(..) | ast::ForeignItemStatic(..) => {
245+
if attr::contains_name(i.attrs.as_slice(), "linkage") {
246+
self.gate_feature("linkage", i.span,
247+
"the `linkage` attribute is experimental \
248+
and not portable across platforms")
249+
}
250+
}
251+
}
252+
visit::walk_foreign_item(self, i, ())
253+
}
254+
241255
fn visit_ty(&mut self, t: &ast::Ty, _: ()) {
242256
match t.node {
243257
ast::TyClosure(closure) if closure.onceness == ast::Once &&

src/librustc/middle/lint.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -983,7 +983,7 @@ static other_attrs: &'static [&'static str] = &[
983983

984984
// fn-level
985985
"test", "bench", "should_fail", "ignore", "inline", "lang", "main", "start",
986-
"no_split_stack", "cold", "macro_registrar",
986+
"no_split_stack", "cold", "macro_registrar", "linkage",
987987

988988
// internal attribute: bypass privacy inside items
989989
"!resolve_unexported",

src/librustc/middle/trans/base.rs

+1-36
Original file line numberDiff line numberDiff line change
@@ -2107,7 +2107,6 @@ pub fn get_item_val(ccx: @CrateContext, id: ast::NodeId) -> ValueRef {
21072107
}
21082108

21092109
ast_map::NodeForeignItem(ni) => {
2110-
let ty = ty::node_id_to_type(ccx.tcx, ni.id);
21112110
foreign = true;
21122111

21132112
match ni.node {
@@ -2116,41 +2115,7 @@ pub fn get_item_val(ccx: @CrateContext, id: ast::NodeId) -> ValueRef {
21162115
foreign::register_foreign_item_fn(ccx, abis, ni)
21172116
}
21182117
ast::ForeignItemStatic(..) => {
2119-
// Treat the crate map static specially in order to
2120-
// a weak-linkage-like functionality where it's
2121-
// dynamically resolved at runtime. If we're
2122-
// building a library, then we declare the static
2123-
// with weak linkage, but if we're building a
2124-
// library then we've already declared the crate map
2125-
// so use that instead.
2126-
if attr::contains_name(ni.attrs.as_slice(),
2127-
"crate_map") {
2128-
if ccx.sess.building_library.get() {
2129-
let s = "_rust_crate_map_toplevel";
2130-
let g = unsafe {
2131-
s.with_c_str(|buf| {
2132-
let ty = type_of(ccx, ty);
2133-
llvm::LLVMAddGlobal(ccx.llmod,
2134-
ty.to_ref(),
2135-
buf)
2136-
})
2137-
};
2138-
lib::llvm::SetLinkage(g,
2139-
lib::llvm::ExternalWeakLinkage);
2140-
g
2141-
} else {
2142-
ccx.crate_map
2143-
}
2144-
} else {
2145-
let ident = foreign::link_name(ni);
2146-
unsafe {
2147-
ident.get().with_c_str(|buf| {
2148-
let ty = type_of(ccx, ty);
2149-
llvm::LLVMAddGlobal(ccx.llmod,
2150-
ty.to_ref(), buf)
2151-
})
2152-
}
2153-
}
2118+
foreign::register_static(ccx, ni)
21542119
}
21552120
}
21562121
}

src/librustc/middle/trans/foreign.rs

+100-1
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, StructRetAttribute, Linkage};
1515
use lib;
1616
use middle::trans::base::push_ctxt;
1717
use middle::trans::base;
@@ -105,6 +105,105 @@ pub fn llvm_calling_convention(ccx: &CrateContext,
105105
})
106106
}
107107

108+
pub fn llvm_linkage_by_name(name: &str) -> Option<Linkage> {
109+
// Use the names from src/llvm/docs/LangRef.rst here. Most types are only
110+
// applicable to variable declarations and may not really make sense for
111+
// Rust code in the first place but whitelist them anyway and trust that
112+
// the user knows what s/he's doing. Who knows, unanticipated use cases
113+
// may pop up in the future.
114+
//
115+
// ghost, dllimport, dllexport and linkonce_odr_autohide are not supported
116+
// and don't have to be, LLVM treats them as no-ops.
117+
match name {
118+
"appending" => Some(lib::llvm::AppendingLinkage),
119+
"available_externally" => Some(lib::llvm::AvailableExternallyLinkage),
120+
"common" => Some(lib::llvm::CommonLinkage),
121+
"extern_weak" => Some(lib::llvm::ExternalWeakLinkage),
122+
"external" => Some(lib::llvm::ExternalLinkage),
123+
"internal" => Some(lib::llvm::InternalLinkage),
124+
"linker_private" => Some(lib::llvm::LinkerPrivateLinkage),
125+
"linker_private_weak" => Some(lib::llvm::LinkerPrivateWeakLinkage),
126+
"linkonce" => Some(lib::llvm::LinkOnceAnyLinkage),
127+
"linkonce_odr" => Some(lib::llvm::LinkOnceODRLinkage),
128+
"private" => Some(lib::llvm::PrivateLinkage),
129+
"weak" => Some(lib::llvm::WeakAnyLinkage),
130+
"weak_odr" => Some(lib::llvm::WeakODRLinkage),
131+
_ => None,
132+
}
133+
}
134+
135+
pub fn register_static(ccx: @CrateContext,
136+
foreign_item: @ast::ForeignItem) -> ValueRef {
137+
let ty = ty::node_id_to_type(ccx.tcx, foreign_item.id);
138+
let llty = type_of::type_of(ccx, ty);
139+
140+
// Treat the crate map static specially in order to
141+
// a weak-linkage-like functionality where it's
142+
// dynamically resolved at runtime. If we're
143+
// building a library, then we declare the static
144+
// with weak linkage, but if we're building a
145+
// library then we've already declared the crate map
146+
// so use that instead.
147+
if attr::contains_name(foreign_item.attrs.as_slice(), "crate_map") {
148+
return if ccx.sess.building_library.get() {
149+
let s = "_rust_crate_map_toplevel";
150+
let g = unsafe {
151+
s.with_c_str(|buf| {
152+
llvm::LLVMAddGlobal(ccx.llmod, llty.to_ref(), buf)
153+
})
154+
};
155+
lib::llvm::SetLinkage(g, lib::llvm::ExternalWeakLinkage);
156+
g
157+
} else {
158+
ccx.crate_map
159+
}
160+
}
161+
162+
let ident = link_name(foreign_item);
163+
match attr::first_attr_value_str_by_name(foreign_item.attrs.as_slice(),
164+
"linkage") {
165+
// If this is a static with a linkage specified, then we need to handle
166+
// it a little specially. The typesystem prevents things like &T and
167+
// extern "C" fn() from being non-null, so we can't just declare a
168+
// static and call it a day. Some linkages (like weak) will make it such
169+
// that the static actually has a null value.
170+
Some(name) => {
171+
let linkage = match llvm_linkage_by_name(name.get()) {
172+
Some(linkage) => linkage,
173+
None => {
174+
ccx.sess.span_fatal(foreign_item.span,
175+
"invalid linkage specified");
176+
}
177+
};
178+
let llty2 = match ty::get(ty).sty {
179+
ty::ty_ptr(ref mt) => type_of::type_of(ccx, mt.ty),
180+
_ => {
181+
ccx.sess.span_fatal(foreign_item.span,
182+
"must have type `*T` or `*mut T`");
183+
}
184+
};
185+
unsafe {
186+
let g1 = ident.get().with_c_str(|buf| {
187+
llvm::LLVMAddGlobal(ccx.llmod, llty2.to_ref(), buf)
188+
});
189+
lib::llvm::SetLinkage(g1, linkage);
190+
191+
let real_name = "_rust_extern_with_linkage_" + ident.get();
192+
let g2 = real_name.with_c_str(|buf| {
193+
llvm::LLVMAddGlobal(ccx.llmod, llty.to_ref(), buf)
194+
});
195+
lib::llvm::SetLinkage(g2, lib::llvm::InternalLinkage);
196+
llvm::LLVMSetInitializer(g2, g1);
197+
g2
198+
}
199+
}
200+
None => unsafe {
201+
ident.get().with_c_str(|buf| {
202+
llvm::LLVMAddGlobal(ccx.llmod, llty.to_ref(), buf)
203+
})
204+
}
205+
}
206+
}
108207

109208
pub fn register_foreign_item_fn(ccx: @CrateContext, abis: AbiSet,
110209
foreign_item: @ast::ForeignItem) -> ValueRef {

src/libstd/lib.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,9 @@
5252
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
5353
html_root_url = "http://static.rust-lang.org/doc/master")];
5454

55-
#[feature(macro_rules, globs, asm, managed_boxes, thread_local, link_args, simd)];
55+
#[feature(macro_rules, globs, asm, managed_boxes, thread_local, link_args,
56+
simd, linkage, default_type_params)];
5657

57-
// Turn on default type parameters.
58-
#[feature(default_type_params)];
5958
// NOTE remove the following two attributes after the next snapshot.
6059
#[allow(unrecognized_lint)];
6160
#[allow(default_type_param_usage)];

src/libstd/rt/thread.rs

+26-37
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ mod imp {
221221
PTHREAD_CREATE_JOINABLE), 0);
222222

223223
// Reserve room for the red zone, the runtime's stack of last resort.
224-
let stack_size = cmp::max(stack, RED_ZONE + __pthread_get_minstack(&attr) as uint);
224+
let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr) as uint);
225225
match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) {
226226
0 => {
227227
},
@@ -261,51 +261,39 @@ mod imp {
261261
#[cfg(not(target_os = "macos"), not(target_os = "android"))]
262262
pub unsafe fn yield_now() { assert_eq!(pthread_yield(), 0); }
263263

264-
#[cfg(not(target_os = "linux"))]
265-
unsafe fn __pthread_get_minstack(_: *libc::pthread_attr_t) -> libc::size_t {
266-
libc::PTHREAD_STACK_MIN
267-
}
268-
269264
// glibc >= 2.15 has a __pthread_get_minstack() function that returns
270265
// PTHREAD_STACK_MIN plus however many bytes are needed for thread-local
271266
// storage. We need that information to avoid blowing up when a small stack
272267
// is created in an application with big thread-local storage requirements.
273268
// See #6233 for rationale and details.
274269
//
275-
// Dynamically resolve the symbol for compatibility with older versions
276-
// of glibc. Assumes that we've been dynamically linked to libpthread
277-
// but that is currently always the case. Note that this means we take
278-
// a dlopen/dlsym/dlclose hit for every new thread. Mitigating that by
279-
// caching the symbol or the function's return value has its drawbacks:
280-
//
281-
// * Caching the symbol breaks when libpthread.so is reloaded because
282-
// its address changes.
283-
//
284-
// * Caching the return value assumes that it's a fixed quantity.
285-
// Not very future-proof and untrue in the presence of guard pages
286-
// The reason __pthread_get_minstack() takes a *libc::pthread_attr_t
287-
// as its argument is because it takes pthread_attr_setguardsize() into
288-
// account.
289-
//
290-
// A better solution is to define __pthread_get_minstack() as a weak symbol
291-
// but there is currently no way to express that in Rust code.
292-
#[cfg(target_os = "linux")]
293-
unsafe fn __pthread_get_minstack(attr: *libc::pthread_attr_t) -> libc::size_t {
294-
use option::None;
295-
use result::{Err, Ok};
296-
use unstable::dynamic_lib;
297-
match dynamic_lib::DynamicLibrary::open(None) {
298-
Err(err) => fail!("DynamicLibrary::open(): {}", err),
299-
Ok(handle) => {
300-
match handle.symbol::<extern "C" fn(*libc::pthread_attr_t) ->
301-
libc::size_t>("__pthread_get_minstack") {
302-
Err(_) => libc::PTHREAD_STACK_MIN,
303-
Ok(__pthread_get_minstack) => __pthread_get_minstack(attr),
304-
}
305-
}
270+
// Link weakly to the symbol for compatibility with older versions of glibc.
271+
// Assumes that we've been dynamically linked to libpthread but that is
272+
// currently always the case. Note that you need to check that the symbol
273+
// is non-null before calling it!
274+
#[cfg(target_os = "linux", not(stage0))]
275+
fn min_stack_size(attr: *libc::pthread_attr_t) -> libc::size_t {
276+
use ptr::RawPtr;
277+
type F = extern "C" unsafe fn(*libc::pthread_attr_t) -> libc::size_t;
278+
extern {
279+
#[linkage = "extern_weak"]
280+
static __pthread_get_minstack: *();
281+
}
282+
if __pthread_get_minstack.is_null() {
283+
PTHREAD_STACK_MIN
284+
} else {
285+
unsafe { cast::transmute::<*(), F>(__pthread_get_minstack)(attr) }
306286
}
307287
}
308288

289+
// __pthread_get_minstack() is marked as weak but extern_weak linkage is
290+
// not supported on OS X, hence this kludge...
291+
#[cfg(not(target_os = "linux"))]
292+
#[cfg(stage0)]
293+
fn min_stack_size(_: *libc::pthread_attr_t) -> libc::size_t {
294+
PTHREAD_STACK_MIN
295+
}
296+
309297
extern {
310298
fn pthread_create(native: *mut libc::pthread_t,
311299
attr: *libc::pthread_attr_t,
@@ -347,3 +335,4 @@ mod tests {
347335
assert_eq!(42, Thread::start_stack(1, proc () 42).join());
348336
}
349337
}
338+

src/test/auxiliary/linkage1.rs

+12
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 static foo: int = 3;

src/test/compile-fail/linkage1.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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 {
12+
#[linkage = "extern_weak"] static foo: int;
13+
//~^ ERROR: the `linkage` attribute is experimental and not portable
14+
}

src/test/compile-fail/linkage2.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
extern {
14+
#[linkage = "extern_weak"] static foo: i32;
15+
//~^ ERROR: must have type `*T`
16+
}
17+
18+
fn main() {
19+
println!("{}", foo);
20+
}

src/test/compile-fail/linkage3.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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+
extern {
14+
#[linkage = "foo"] static foo: *i32;
15+
//~^ ERROR: invalid linkage specified
16+
}
17+
18+
fn main() {
19+
println!("{}", foo);
20+
}
21+

0 commit comments

Comments
 (0)