diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 624748f352e..94aa3d6b513 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -646,7 +646,10 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log2(self) -> f32 { - unsafe { intrinsics::log2f32(self) } + #[cfg(target_os = "android")] + return ::sys::android::log2f32(self); + #[cfg(not(target_os = "android"))] + return unsafe { intrinsics::log2f32(self) }; } /// Returns the base 10 logarithm of the number. diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index 6515301aefd..2beffb64d3d 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -546,7 +546,12 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log2(self) -> f64 { - self.log_wrapper(|n| { unsafe { intrinsics::log2f64(n) } }) + self.log_wrapper(|n| { + #[cfg(target_os = "android")] + return ::sys::android::log2f64(n); + #[cfg(not(target_os = "android"))] + return unsafe { intrinsics::log2f64(n) }; + }) } /// Returns the base 10 logarithm of the number. diff --git a/src/libstd/sys/unix/android.rs b/src/libstd/sys/unix/android.rs new file mode 100644 index 00000000000..abbe3fc1846 --- /dev/null +++ b/src/libstd/sys/unix/android.rs @@ -0,0 +1,119 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Android ABI-compatibility module +//! +//! The ABI of Android has changed quite a bit over time, and libstd attempts to +//! be both forwards and backwards compatible as much as possible. We want to +//! always work with the most recent version of Android, but we also want to +//! work with older versions of Android for whenever projects need to. +//! +//! Our current minimum supported Android version is `android-9`, e.g. Android +//! with API level 9. We then in theory want to work on that and all future +//! versions of Android! +//! +//! Some of the detection here is done at runtime via `dlopen` and +//! introspection. Other times no detection is performed at all and we just +//! provide a fallback implementation as some versions of Android we support +//! don't have the function. +//! +//! You'll find more details below about why each compatibility shim is needed. + +#![cfg(target_os = "android")] + +use libc::{c_int, sighandler_t}; + +use io; +use sys::cvt_r; + +// The `log2` and `log2f` functions apparently appeared in android-18, or at +// least you can see they're not present in the android-17 header [1] and they +// are present in android-18 [2]. +// +// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms +// /android-17/arch-arm/usr/include/math.h +// [2]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms +// /android-18/arch-arm/usr/include/math.h +// +// Note that these shims are likely less precise than directly calling `log2`, +// but hopefully that should be enough for now... +// +// Note that mathematically, for any arbitrary `y`: +// +// log_2(x) = log_y(x) / log_y(2) +// = log_y(x) / (1 / log_2(y)) +// = log_y(x) * log_2(y) +// +// Hence because `ln` (log_e) is available on all Android we just choose `y = e` +// and get: +// +// log_2(x) = ln(x) * log_2(e) + +#[cfg(not(test))] +pub fn log2f32(f: f32) -> f32 { + f.ln() * ::f32::consts::LOG2_E +} + +#[cfg(not(test))] +pub fn log2f64(f: f64) -> f64 { + f.ln() * ::f64::consts::LOG2_E +} + +// Back in the day [1] the `signal` function was just an inline wrapper +// around `bsd_signal`, but starting in API level android-20 the `signal` +// symbols was introduced [2]. Finally, in android-21 the API `bsd_signal` was +// removed [3]. +// +// Basically this means that if we want to be binary compatible with multiple +// Android releases (oldest being 9 and newest being 21) then we need to check +// for both symbols and not actually link against either. +// +// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms +// /android-18/arch-arm/usr/include/signal.h +// [2]: https://chromium.googlesource.com/android_tools/+/fbd420/ndk_experimental +// /platforms/android-20/arch-arm +// /usr/include/signal.h +// [3]: https://chromium.googlesource.com/android_tools/+/20ee6d/ndk/platforms +// /android-21/arch-arm/usr/include/signal.h +pub unsafe fn signal(signum: c_int, handler: sighandler_t) -> sighandler_t { + weak!(fn signal(c_int, sighandler_t) -> sighandler_t); + weak!(fn bsd_signal(c_int, sighandler_t) -> sighandler_t); + + let f = signal.get().or_else(|| bsd_signal.get()); + let f = f.expect("neither `signal` nor `bsd_signal` symbols found"); + f(signum, handler) +} + +// The `ftruncate64` symbol apparently appeared in android-12, so we do some +// dynamic detection to see if we can figure out whether `ftruncate64` exists. +// +// If it doesn't we just fall back to `ftruncate`, generating an error for +// too-large values. +pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> { + weak!(fn ftruncate64(c_int, i64) -> c_int); + + extern { + fn ftruncate(fd: c_int, off: i32) -> c_int; + } + + unsafe { + match ftruncate64.get() { + Some(f) => cvt_r(|| f(fd, size as i64)).map(|_| ()), + None => { + if size > i32::max_value() as u64 { + Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot truncate >2GB")) + } else { + cvt_r(|| ftruncate(fd, size as i32)).map(|_| ()) + } + } + } + } +} diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index d5d17e7ee12..0969a59ea43 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -27,7 +27,7 @@ use sys_common::{AsInner, FromInner}; #[cfg(any(target_os = "linux", target_os = "emscripten"))] use libc::{stat64, fstat64, lstat64, off64_t, ftruncate64, lseek64, dirent64, readdir64_r, open64}; #[cfg(target_os = "android")] -use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, off64_t, ftruncate64, lseek64, +use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, off64_t, lseek64, dirent as dirent64, open as open64}; #[cfg(not(any(target_os = "linux", target_os = "emscripten", @@ -475,10 +475,13 @@ impl File { } pub fn truncate(&self, size: u64) -> io::Result<()> { - cvt_r(|| unsafe { + #[cfg(target_os = "android")] + return ::sys::android::ftruncate64(self.0.raw(), size); + + #[cfg(not(target_os = "android"))] + return cvt_r(|| unsafe { ftruncate64(self.0.raw(), size as off64_t) - })?; - Ok(()) + }).map(|_| ()); } pub fn read(&self, buf: &mut [u8]) -> io::Result { diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index f8b2d4dd232..12a877f7478 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -31,6 +31,7 @@ use ops::Neg; #[macro_use] pub mod weak; +pub mod android; pub mod backtrace; pub mod condvar; pub mod ext; @@ -91,37 +92,8 @@ pub fn init() { unsafe fn reset_sigpipe() {} } -// Currently the minimum supported Android version of the standard library is -// API level 18 (android-18). Back in those days [1] the `signal` function was -// just an inline wrapper around `bsd_signal`, but starting in API level -// android-20 the `signal` symbols was introduced [2]. Finally, in android-21 -// the API `bsd_signal` was removed [3]. -// -// Basically this means that if we want to be binary compatible with multiple -// Android releases (oldest being 18 and newest being 21) then we need to check -// for both symbols and not actually link against either. -// -// Note that if we're not on android we just link against the `android` symbol -// itself. -// -// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms -// /android-18/arch-arm/usr/include/signal.h -// [2]: https://chromium.googlesource.com/android_tools/+/fbd420/ndk_experimental -// /platforms/android-20/arch-arm -// /usr/include/signal.h -// [3]: https://chromium.googlesource.com/android_tools/+/20ee6d/ndk/platforms -// /android-21/arch-arm/usr/include/signal.h #[cfg(target_os = "android")] -unsafe fn signal(signum: libc::c_int, - handler: libc::sighandler_t) -> libc::sighandler_t { - weak!(fn signal(libc::c_int, libc::sighandler_t) -> libc::sighandler_t); - weak!(fn bsd_signal(libc::c_int, libc::sighandler_t) -> libc::sighandler_t); - - let f = signal.get().or_else(|| bsd_signal.get()); - let f = f.expect("neither `signal` nor `bsd_signal` symbols found"); - f(signum, handler) -} - +pub use sys::android::signal; #[cfg(not(target_os = "android"))] pub use libc::signal;