diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index 15056d9d2d842..6873d9b68e7a5 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -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), @@ -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; diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs index e674af6b3b3e3..3c700558c60b9 100644 --- a/src/librustc/middle/lint.rs +++ b/src/librustc/middle/lint.rs @@ -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", diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index bc9dd767ec670..3630dd6e89fe5 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -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; @@ -106,6 +106,34 @@ pub fn llvm_calling_convention(ccx: &CrateContext, } +pub fn llvm_linkage_by_name(name: &str) -> Option { + // 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, @@ -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; diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index adce11fed2dad..7828cd5202f7a 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -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)]; // Don't link to std. We are std. #[no_std]; diff --git a/src/libstd/rt/thread.rs b/src/libstd/rt/thread.rs index b762c1173f56b..70411054a13ae 100644 --- a/src/libstd/rt/thread.rs +++ b/src/libstd/rt/thread.rs @@ -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 => { }, @@ -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:: - 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(thing: T) -> bool { + cast::transmute::(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, diff --git a/src/test/run-pass/linkage.rs b/src/test/run-pass/linkage.rs new file mode 100644 index 0000000000000..170d97bad9a4e --- /dev/null +++ b/src/test/run-pass/linkage.rs @@ -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 or the MIT license +// , 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(); +}