Skip to content

Weak external symbol support #11978

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/librustc/front/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
("non_ascii_idents", Active),
("thread_local", Active),
("link_args", Active),
("linkage", Active),
("phase", Active),
("macro_registrar", Active),
("log_syntax", Active),
Expand Down Expand Up @@ -188,6 +189,18 @@ impl Visitor<()> for Context {
visit::walk_item(self, i, ());
}

fn visit_foreign_item(&mut self, i: &ast::ForeignItem, _: ()) {
match i.node {
ast::ForeignItemFn(..) | ast::ForeignItemStatic(..) => {
if attr::contains_name(i.attrs, "linkage") {
self.gate_feature("linkage", i.span,
"the `linkage` attribute is experimental \
and not portable across platforms")
}
},
}
}

fn visit_mac(&mut self, macro: &ast::Mac, _: ()) {
let ast::MacInvocTT(ref path, _, _) = macro.node;
let id = path.segments.last().unwrap().identifier;
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -974,7 +974,7 @@ static other_attrs: &'static [&'static str] = &[

// fn-level
"test", "bench", "should_fail", "ignore", "inline", "lang", "main", "start",
"no_split_stack", "cold", "macro_registrar",
"no_split_stack", "cold", "macro_registrar", "linkage",

// internal attribute: bypass privacy inside items
"!resolve_unexported",
Expand Down
42 changes: 41 additions & 1 deletion src/librustc/middle/trans/foreign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

use back::{link};
use lib::llvm::llvm;
use lib::llvm::{ValueRef, CallConv, StructRetAttribute};
use lib::llvm::{ValueRef, CallConv, Linkage, StructRetAttribute};
use lib;
use middle::trans::base::push_ctxt;
use middle::trans::base;
Expand Down Expand Up @@ -106,6 +106,34 @@ pub fn llvm_calling_convention(ccx: &CrateContext,
}


pub fn llvm_linkage_by_name(name: &str) -> Option<Linkage> {
// Use the names from src/llvm/docs/LangRef.rst here. Most types are only
// applicable to variable declarations and may not really make sense for
// Rust code in the first place but whitelist them anyway and trust that
// the user knows what s/he's doing. Who knows, unanticipated use cases
// may pop up in the future.
//
// ghost, dllimport, dllexport and linkonce_odr_autohide are not supported
// and don't have to be, LLVM treats them as no-ops.
match name {
"appending" => Some(lib::llvm::AppendingLinkage),
"available_externally" => Some(lib::llvm::AvailableExternallyLinkage),
"common" => Some(lib::llvm::CommonLinkage),
"extern_weak" => Some(lib::llvm::ExternalWeakLinkage),
"external" => Some(lib::llvm::ExternalLinkage),
"internal" => Some(lib::llvm::InternalLinkage),
"linker_private" => Some(lib::llvm::LinkerPrivateLinkage),
"linker_private_weak" => Some(lib::llvm::LinkerPrivateWeakLinkage),
"linkonce" => Some(lib::llvm::LinkOnceAnyLinkage),
"linkonce_odr" => Some(lib::llvm::LinkOnceODRLinkage),
"private" => Some(lib::llvm::PrivateLinkage),
"weak" => Some(lib::llvm::WeakAnyLinkage),
"weak_odr" => Some(lib::llvm::WeakODRLinkage),
_ => None,
}
}


pub fn register_foreign_item_fn(ccx: @CrateContext,
abis: AbiSet,
path: &ast_map::Path,
Expand Down Expand Up @@ -158,6 +186,18 @@ pub fn register_foreign_item_fn(ccx: @CrateContext,
llfn_ty,
tys.fn_sig.output);
};

match attr::first_attr_value_str_by_name(foreign_item.attrs, "linkage") {
Some(name) => {
match llvm_linkage_by_name(name.get()) {
Some(linkage) => lib::llvm::SetLinkage(llfn, linkage),
None => ccx.sess.span_fatal(foreign_item.span,
format!("Bad linkage `{}`", name)),
}
},
None => {}, // Default "external" linkage.
}

add_argument_attributes(&tys, llfn);

return llfn;
Expand Down
9 changes: 8 additions & 1 deletion src/libstd/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,14 @@
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
html_root_url = "http://static.rust-lang.org/doc/master")];

#[feature(macro_rules, globs, asm, managed_boxes, thread_local, link_args, simd)];
#[feature(asm,
globs,
link_args,
linkage,
macro_rules,
managed_boxes,
simd,
thread_local)];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm getting the feeling that libstd essentially has #[feature(*)]...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't have quote or non_ascii_idents ;P


// Don't link to std. We are std.
#[no_std];
Expand Down
59 changes: 24 additions & 35 deletions src/libstd/rt/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ mod imp {
PTHREAD_CREATE_JOINABLE), 0);

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

#[cfg(not(target_os = "linux"))]
unsafe fn __pthread_get_minstack(_: *libc::pthread_attr_t) -> libc::size_t {
libc::PTHREAD_STACK_MIN
}

// glibc >= 2.15 has a __pthread_get_minstack() function that returns
// PTHREAD_STACK_MIN plus however many bytes are needed for thread-local
// storage. We need that information to avoid blowing up when a small stack
// is created in an application with big thread-local storage requirements.
// See #6233 for rationale and details.
//
// Dynamically resolve the symbol for compatibility with older versions
// of glibc. Assumes that we've been dynamically linked to libpthread
// but that is currently always the case. Note that this means we take
// a dlopen/dlsym/dlclose hit for every new thread. Mitigating that by
// caching the symbol or the function's return value has its drawbacks:
//
// * Caching the symbol breaks when libpthread.so is reloaded because
// its address changes.
//
// * Caching the return value assumes that it's a fixed quantity.
// Not very future-proof and untrue in the presence of guard pages
// The reason __pthread_get_minstack() takes a *libc::pthread_attr_t
// as its argument is because it takes pthread_attr_setguardsize() into
// account.
//
// A better solution is to define __pthread_get_minstack() as a weak symbol
// but there is currently no way to express that in Rust code.
// Link weakly to the symbol for compatibility with older versions of glibc.
// Assumes that we've been dynamically linked to libpthread but that is
// currently always the case. Note that you need to check that the symbol
// is non-null before calling it!
#[cfg(target_os = "linux")]
unsafe fn __pthread_get_minstack(attr: *libc::pthread_attr_t) -> libc::size_t {
use option::None;
use result::{Err, Ok};
use unstable::dynamic_lib;
match dynamic_lib::DynamicLibrary::open(None) {
Err(err) => fail!("DynamicLibrary::open(): {}", err),
Ok(handle) => {
match handle.symbol::<extern "C" fn(*libc::pthread_attr_t) ->
libc::size_t>("__pthread_get_minstack") {
Err(_) => libc::PTHREAD_STACK_MIN,
Ok(__pthread_get_minstack) => __pthread_get_minstack(attr),
}
fn min_stack_size(attr: *libc::pthread_attr_t) -> libc::size_t {
extern {
#[linkage = "extern_weak"]
fn __pthread_get_minstack(_: *libc::pthread_attr_t) -> libc::size_t;
}
unsafe fn is_null<T>(thing: T) -> bool {
cast::transmute::<T, *u8>(thing) == ptr::null()
}
unsafe {
match is_null(__pthread_get_minstack) {
true => PTHREAD_STACK_MIN,
false => __pthread_get_minstack(attr),
}
}
}

// __pthread_get_minstack() is marked as weak but extern_weak linkage is
// not supported on OS X, hence this kludge...
#[cfg(not(target_os = "linux"))]
fn min_stack_size(_: *libc::pthread_attr_t) -> libc::size_t {
PTHREAD_STACK_MIN
}

extern {
fn pthread_create(native: *mut libc::pthread_t,
attr: *libc::pthread_attr_t,
Expand Down
43 changes: 43 additions & 0 deletions src/test/run-pass/linkage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// 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.

#[feature(linkage)];

use std::cast;
use std::ptr;

fn external() {
extern {
#[linkage = "external"]
fn abort();
}
let ptr: *u8 = unsafe { cast::transmute(abort) };
assert!(ptr != ptr::null());
}

#[cfg(target_os = "linux")]
fn extern_weak() {
extern {
#[linkage = "extern_weak"]
fn frobnitz();
}
let ptr: *u8 = unsafe { cast::transmute(frobnitz) };
assert!(ptr == ptr::null());
}

#[cfg(not(target_os = "linux"))]
fn extern_weak() {
// Not supported.
}

fn main() {
external();
extern_weak();
}