diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index 6246000074d..36f6cedb4f1 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -53,6 +53,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ ("simd", Active), ("default_type_params", Active), ("quote", Active), + ("linkage", Active), // These are used to test this portion of the compiler, they don't actually // mean anything @@ -238,6 +239,19 @@ impl Visitor<()> for Context { } } + fn visit_foreign_item(&mut self, i: &ast::ForeignItem, _: ()) { + match i.node { + ast::ForeignItemFn(..) | ast::ForeignItemStatic(..) => { + if attr::contains_name(i.attrs.as_slice(), "linkage") { + self.gate_feature("linkage", i.span, + "the `linkage` attribute is experimental \ + and not portable across platforms") + } + } + } + visit::walk_foreign_item(self, i, ()) + } + fn visit_ty(&mut self, t: &ast::Ty, _: ()) { match t.node { ast::TyClosure(closure) if closure.onceness == ast::Once && diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs index ddf75d1f0b6..1351e87c7f6 100644 --- a/src/librustc/middle/lint.rs +++ b/src/librustc/middle/lint.rs @@ -983,7 +983,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/base.rs b/src/librustc/middle/trans/base.rs index 362f5fbacf0..156a4f914a9 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2107,7 +2107,6 @@ pub fn get_item_val(ccx: @CrateContext, id: ast::NodeId) -> ValueRef { } ast_map::NodeForeignItem(ni) => { - let ty = ty::node_id_to_type(ccx.tcx, ni.id); foreign = true; match ni.node { @@ -2116,41 +2115,7 @@ pub fn get_item_val(ccx: @CrateContext, id: ast::NodeId) -> ValueRef { foreign::register_foreign_item_fn(ccx, abis, ni) } ast::ForeignItemStatic(..) => { - // Treat the crate map static specially in order to - // a weak-linkage-like functionality where it's - // dynamically resolved at runtime. If we're - // building a library, then we declare the static - // with weak linkage, but if we're building a - // library then we've already declared the crate map - // so use that instead. - if attr::contains_name(ni.attrs.as_slice(), - "crate_map") { - if ccx.sess.building_library.get() { - let s = "_rust_crate_map_toplevel"; - let g = unsafe { - s.with_c_str(|buf| { - let ty = type_of(ccx, ty); - llvm::LLVMAddGlobal(ccx.llmod, - ty.to_ref(), - buf) - }) - }; - lib::llvm::SetLinkage(g, - lib::llvm::ExternalWeakLinkage); - g - } else { - ccx.crate_map - } - } else { - let ident = foreign::link_name(ni); - unsafe { - ident.get().with_c_str(|buf| { - let ty = type_of(ccx, ty); - llvm::LLVMAddGlobal(ccx.llmod, - ty.to_ref(), buf) - }) - } - } + foreign::register_static(ccx, ni) } } } diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index 374c85ffa6a..f37d4b9859d 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, StructRetAttribute, Linkage}; use lib; use middle::trans::base::push_ctxt; use middle::trans::base; @@ -105,6 +105,105 @@ 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_static(ccx: @CrateContext, + foreign_item: @ast::ForeignItem) -> ValueRef { + let ty = ty::node_id_to_type(ccx.tcx, foreign_item.id); + let llty = type_of::type_of(ccx, ty); + + // Treat the crate map static specially in order to + // a weak-linkage-like functionality where it's + // dynamically resolved at runtime. If we're + // building a library, then we declare the static + // with weak linkage, but if we're building a + // library then we've already declared the crate map + // so use that instead. + if attr::contains_name(foreign_item.attrs.as_slice(), "crate_map") { + return if ccx.sess.building_library.get() { + let s = "_rust_crate_map_toplevel"; + let g = unsafe { + s.with_c_str(|buf| { + llvm::LLVMAddGlobal(ccx.llmod, llty.to_ref(), buf) + }) + }; + lib::llvm::SetLinkage(g, lib::llvm::ExternalWeakLinkage); + g + } else { + ccx.crate_map + } + } + + let ident = link_name(foreign_item); + match attr::first_attr_value_str_by_name(foreign_item.attrs.as_slice(), + "linkage") { + // If this is a static with a linkage specified, then we need to handle + // it a little specially. The typesystem prevents things like &T and + // extern "C" fn() from being non-null, so we can't just declare a + // static and call it a day. Some linkages (like weak) will make it such + // that the static actually has a null value. + Some(name) => { + let linkage = match llvm_linkage_by_name(name.get()) { + Some(linkage) => linkage, + None => { + ccx.sess.span_fatal(foreign_item.span, + "invalid linkage specified"); + } + }; + let llty2 = match ty::get(ty).sty { + ty::ty_ptr(ref mt) => type_of::type_of(ccx, mt.ty), + _ => { + ccx.sess.span_fatal(foreign_item.span, + "must have type `*T` or `*mut T`"); + } + }; + unsafe { + let g1 = ident.get().with_c_str(|buf| { + llvm::LLVMAddGlobal(ccx.llmod, llty2.to_ref(), buf) + }); + lib::llvm::SetLinkage(g1, linkage); + + let real_name = "_rust_extern_with_linkage_" + ident.get(); + let g2 = real_name.with_c_str(|buf| { + llvm::LLVMAddGlobal(ccx.llmod, llty.to_ref(), buf) + }); + lib::llvm::SetLinkage(g2, lib::llvm::InternalLinkage); + llvm::LLVMSetInitializer(g2, g1); + g2 + } + } + None => unsafe { + ident.get().with_c_str(|buf| { + llvm::LLVMAddGlobal(ccx.llmod, llty.to_ref(), buf) + }) + } + } +} pub fn register_foreign_item_fn(ccx: @CrateContext, abis: AbiSet, foreign_item: @ast::ForeignItem) -> ValueRef { diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index bdc4fc387ca..4d3d1641bd0 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -52,10 +52,9 @@ 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(macro_rules, globs, asm, managed_boxes, thread_local, link_args, + simd, linkage, default_type_params)]; -// Turn on default type parameters. -#[feature(default_type_params)]; // NOTE remove the following two attributes after the next snapshot. #[allow(unrecognized_lint)]; #[allow(default_type_param_usage)]; diff --git a/src/libstd/rt/thread.rs b/src/libstd/rt/thread.rs index 7b24c94b518..e27698d7dd0 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,49 +261,37 @@ 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. - #[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), - } - } + // 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", not(stage0))] + fn min_stack_size(attr: *libc::pthread_attr_t) -> libc::size_t { + use ptr::RawPtr; + type F = extern "C" unsafe fn(*libc::pthread_attr_t) -> libc::size_t; + extern { + #[linkage = "extern_weak"] + static __pthread_get_minstack: *(); } + if __pthread_get_minstack.is_null() { + PTHREAD_STACK_MIN + } else { + unsafe { cast::transmute::<*(), F>(__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"))] + #[cfg(stage0)] + fn min_stack_size(_: *libc::pthread_attr_t) -> libc::size_t { + PTHREAD_STACK_MIN } extern { @@ -347,3 +335,4 @@ mod tests { assert_eq!(42, Thread::start_stack(1, proc () 42).join()); } } + diff --git a/src/test/auxiliary/linkage1.rs b/src/test/auxiliary/linkage1.rs new file mode 100644 index 00000000000..9017ee88363 --- /dev/null +++ b/src/test/auxiliary/linkage1.rs @@ -0,0 +1,12 @@ +// 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. + +#[no_mangle] +pub static foo: int = 3; diff --git a/src/test/compile-fail/linkage1.rs b/src/test/compile-fail/linkage1.rs new file mode 100644 index 00000000000..a045b838d51 --- /dev/null +++ b/src/test/compile-fail/linkage1.rs @@ -0,0 +1,14 @@ +// 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. + +extern { + #[linkage = "extern_weak"] static foo: int; + //~^ ERROR: the `linkage` attribute is experimental and not portable +} diff --git a/src/test/compile-fail/linkage2.rs b/src/test/compile-fail/linkage2.rs new file mode 100644 index 00000000000..524324fa1f1 --- /dev/null +++ b/src/test/compile-fail/linkage2.rs @@ -0,0 +1,20 @@ +// 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)]; + +extern { + #[linkage = "extern_weak"] static foo: i32; + //~^ ERROR: must have type `*T` +} + +fn main() { + println!("{}", foo); +} diff --git a/src/test/compile-fail/linkage3.rs b/src/test/compile-fail/linkage3.rs new file mode 100644 index 00000000000..2da800bcb1c --- /dev/null +++ b/src/test/compile-fail/linkage3.rs @@ -0,0 +1,21 @@ +// 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)]; + +extern { + #[linkage = "foo"] static foo: *i32; + //~^ ERROR: invalid linkage specified +} + +fn main() { + println!("{}", foo); +} + diff --git a/src/test/run-pass/linkage1.rs b/src/test/run-pass/linkage1.rs new file mode 100644 index 00000000000..8f8b0cfecb2 --- /dev/null +++ b/src/test/run-pass/linkage1.rs @@ -0,0 +1,32 @@ +// 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. + +// ignore-win32 +// ignore-fast +// ignore-android +// ignore-macos +// aux-build:linkage1.rs + +#[feature(linkage)]; + +extern crate other = "linkage1"; + +extern { + #[linkage = "extern_weak"] + static foo: *int; + #[linkage = "extern_weak"] + static something_that_should_never_exist: *mut int; +} + +fn main() { + assert!(!foo.is_null()); + assert_eq!(unsafe { *foo }, 3); + assert!(something_that_should_never_exist.is_null()); +}