Skip to content

Commit

Permalink
std: Add a helper for symbols that may not exist
Browse files Browse the repository at this point in the history
Right now we only attempt to call one symbol which my not exist everywhere,
__pthread_get_minstack, but this pattern will come up more often as we start to
bind newer functionality of systems like Linux.

Take a similar strategy as the Windows implementation where we use `dlopen` to
lookup whether a symbol exists or not.
  • Loading branch information
alexcrichton committed Feb 6, 2016
1 parent 1bd2d20 commit 1a31e1c
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 28 deletions.
2 changes: 1 addition & 1 deletion src/liblibc
3 changes: 3 additions & 0 deletions src/libstd/sys/unix/mod.rs
Expand Up @@ -27,6 +27,9 @@ use ops::Neg;
#[cfg(target_os = "openbsd")] pub use os::openbsd as platform;
#[cfg(target_os = "solaris")] pub use os::solaris as platform;

#[macro_use]
pub mod weak;

pub mod backtrace;
pub mod condvar;
pub mod ext;
Expand Down
29 changes: 2 additions & 27 deletions src/libstd/sys/unix/thread.rs
Expand Up @@ -317,37 +317,12 @@ pub mod guard {
// 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.
//
// Use dlsym to get the symbol value at runtime, both for
// compatibility with older versions of glibc, and to avoid creating
// dependencies on GLIBC_PRIVATE symbols. Assumes that we've been
// dynamically linked to libpthread but that is currently always the
// case. We previously used weak linkage (under the same assumption),
// but that caused Debian to detect an unnecessarily strict versioned
// dependency on libc6 (#23628).
#[cfg(target_os = "linux")]
#[allow(deprecated)]
fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize {
use dynamic_lib::DynamicLibrary;
use sync::Once;

type F = unsafe extern "C" fn(*const libc::pthread_attr_t) -> libc::size_t;
static INIT: Once = Once::new();
static mut __pthread_get_minstack: Option<F> = None;

INIT.call_once(|| {
let lib = match DynamicLibrary::open(None) {
Ok(l) => l,
Err(..) => return,
};
unsafe {
if let Ok(f) = lib.symbol("__pthread_get_minstack") {
__pthread_get_minstack = Some(mem::transmute::<*const (), F>(f));
}
}
});
weak!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t);

match unsafe { __pthread_get_minstack } {
match __pthread_get_minstack.get() {
None => libc::PTHREAD_STACK_MIN as usize,
Some(f) => unsafe { f(attr) as usize },
}
Expand Down
81 changes: 81 additions & 0 deletions src/libstd/sys/unix/weak.rs
@@ -0,0 +1,81 @@
// Copyright 2016 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.

//! Support for "weak linkage" to symbols on Unix
//!
//! Some I/O operations we do in libstd require newer versions of OSes but we
//! need to maintain binary compatibility with older releases for now. In order
//! to use the new functionality when available we use this module for
//! detection.
//!
//! One option to use here is weak linkage, but that is unfortunately only
//! really workable on Linux. Hence, use dlsym to get the symbol value at
//! runtime. This is also done for compatibility with older versions of glibc,
//! and to avoid creating dependencies on GLIBC_PRIVATE symbols. It assumes that
//! we've been dynamically linked to the library the symbol comes from, but that
//! is currently always the case for things like libpthread/libc.
//!
//! A long time ago this used weak linkage for the __pthread_get_minstack
//! symbol, but that caused Debian to detect an unnecessarily strict versioned
//! dependency on libc6 (#23628).

use libc;

use ffi::CString;
use marker;
use mem;
use sync::atomic::{AtomicUsize, Ordering};

macro_rules! weak {
(fn $name:ident($($t:ty),*) -> $ret:ty) => (
static $name: ::sys::weak::Weak<unsafe extern fn($($t),*) -> $ret> =
::sys::weak::Weak::new(stringify!($name));
)
}

pub struct Weak<F> {
name: &'static str,
addr: AtomicUsize,
_marker: marker::PhantomData<F>,
}

impl<F> Weak<F> {
pub const fn new(name: &'static str) -> Weak<F> {
Weak {
name: name,
addr: AtomicUsize::new(1),
_marker: marker::PhantomData,
}
}

pub fn get(&self) -> Option<&F> {
assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
unsafe {
if self.addr.load(Ordering::SeqCst) == 1 {
self.addr.store(fetch(self.name), Ordering::SeqCst);
}
mem::transmute::<&AtomicUsize, Option<&F>>(&self.addr)
}
}
}

unsafe fn fetch(name: &str) -> usize {
let name = match CString::new(name) {
Ok(cstr) => cstr,
Err(..) => return 0,
};
let lib = libc::dlopen(0 as *const _, libc::RTLD_LAZY);
if lib.is_null() {
return 0
}
let ret = libc::dlsym(lib, name.as_ptr()) as usize;
libc::dlclose(lib);
return ret
}

0 comments on commit 1a31e1c

Please sign in to comment.