about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/front/feature_gate.rs14
-rw-r--r--src/librustc/middle/lint.rs2
-rw-r--r--src/librustc/middle/trans/base.rs37
-rw-r--r--src/librustc/middle/trans/foreign.rs101
-rw-r--r--src/libstd/lib.rs5
-rw-r--r--src/libstd/rt/thread.rs63
-rw-r--r--src/test/auxiliary/linkage1.rs12
-rw-r--r--src/test/compile-fail/linkage1.rs14
-rw-r--r--src/test/compile-fail/linkage2.rs20
-rw-r--r--src/test/compile-fail/linkage3.rs21
-rw-r--r--src/test/run-pass/linkage1.rs32
11 files changed, 243 insertions, 78 deletions
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<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_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,51 +261,39 @@ 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::<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),
-                }
-            }
+    // 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 {
         fn pthread_create(native: *mut libc::pthread_t,
                           attr: *libc::pthread_attr_t,
@@ -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 <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.
+
+#[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 <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.
+
+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 <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)];
+
+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 <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)];
+
+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 <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.
+
+// 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());
+}