From 699b33d060c5c9cc580d50cf3fe39536ae7a059a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 25 Feb 2014 16:15:10 -0800 Subject: [PATCH] rustc: Support various flavors of linkages 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 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. --- src/librustc/front/feature_gate.rs | 14 ++++ src/librustc/middle/lint.rs | 2 +- src/librustc/middle/trans/base.rs | 37 +--------- src/librustc/middle/trans/foreign.rs | 101 ++++++++++++++++++++++++++- src/libstd/lib.rs | 5 +- src/libstd/rt/thread.rs | 63 +++++++---------- src/test/auxiliary/linkage1.rs | 12 ++++ src/test/compile-fail/linkage1.rs | 14 ++++ src/test/compile-fail/linkage2.rs | 20 ++++++ src/test/compile-fail/linkage3.rs | 21 ++++++ src/test/run-pass/linkage1.rs | 32 +++++++++ 11 files changed, 243 insertions(+), 78 deletions(-) create mode 100644 src/test/auxiliary/linkage1.rs create mode 100644 src/test/compile-fail/linkage1.rs create mode 100644 src/test/compile-fail/linkage2.rs create mode 100644 src/test/compile-fail/linkage3.rs create mode 100644 src/test/run-pass/linkage1.rs 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()); +}