From 57d65daecc130e0b2168ece018f16d0f04d4a415 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 1 Feb 2014 12:36:04 +0100 Subject: [PATCH 1/2] Support weak/common/private/etc. symbol linkage. This commit adds a feature-gated #[linkage = "foo"] attribute that can be applied to extern functions and instructs the linker on how to resolve the symbol. "extern_weak" linkage is the most immediately useful one because it lets us link to library symbols that may or may not exist at run-time. Note that non-default linkage types require toolchain and (sometimes) dynamic linker support. For example, "extern_weak" is accepted but not actually supported by OS X's dyld. I decided not to warn for known bad platform/linkage combinations because clang and gcc don't do so either and because you don't rightly know whether the user is actually using the problematic toolchain or dynamic linker. For all we know, they're implementing their own ELF loader. --- src/librustc/front/feature_gate.rs | 13 +++++++++ src/librustc/middle/lint.rs | 2 +- src/librustc/middle/trans/foreign.rs | 42 ++++++++++++++++++++++++++- src/test/run-pass/linkage.rs | 43 ++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 src/test/run-pass/linkage.rs 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/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(); +} From ffb7d35d66ebab251d1ddd41eba5a564bf537836 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 1 Feb 2014 12:59:40 +0100 Subject: [PATCH 2/2] Link weakly to __pthread_get_minstack(). Use the weak symbol support that was added in the previous commit to link weakly against __pthread_get_minstack(). Significantly reduces the thread creation overhead because Thread::imp::create() no longer goes through a dlopen/dlsym/dlcose cycle for each new thread. --- src/libstd/lib.rs | 9 ++++++- src/libstd/rt/thread.rs | 59 +++++++++++++++++------------------------ 2 files changed, 32 insertions(+), 36 deletions(-) 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,