Skip to main content

rt/
android.rs

1// Android runtime: Bionic allocation and logging for the native, host-driven UI.
2// New platform adapter; no assembly reference. Allocation never assumes a page size.
3
4use core::{alloc::{GlobalAlloc, Layout}, ffi::{c_int, c_void}, sync::atomic::{AtomicBool, Ordering}};
5
6#[link(name = "c")]
7unsafe extern "C" {
8    fn posix_memalign(out: *mut *mut c_void, align: usize, size: usize) -> c_int;
9    fn free(ptr: *mut c_void);
10    fn mmap(address: *mut c_void, size: usize, protection: c_int, flags: c_int, fd: c_int, offset: i64) -> *mut c_void;
11    fn munmap(address: *mut c_void, size: usize) -> c_int;
12    fn sched_yield() -> c_int;
13    fn _exit(code: c_int) -> !;
14    fn write(fd: c_int, bytes: *const c_void, count: usize) -> isize;
15}
16#[link(name = "log")]
17unsafe extern "C" {
18    fn __android_log_write(priority: c_int, tag: *const u8, text: *const u8) -> c_int;
19}
20
21struct AndroidAllocator;
22#[cfg_attr(not(feature = "std"), global_allocator)]
23#[cfg_attr(feature = "std", allow(dead_code))] // std's allocator: the same malloc
24static ALLOC: AndroidAllocator = AndroidAllocator;
25
26// SAFETY: Bionic supplies distinct, aligned allocations and accepts its own pointers on free.
27unsafe impl GlobalAlloc for AndroidAllocator {
28    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
29        let mut ptr = core::ptr::null_mut();
30        // SAFETY: out is writable; Layout alignment is a power of two, at least sizeof(void*).
31        let result = unsafe { posix_memalign(&mut ptr, layout.align().max(core::mem::size_of::<usize>()), layout.size().max(1)) };
32        if result == 0 { ptr.cast() } else { core::ptr::null_mut() }
33    }
34    unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
35        // SAFETY: GlobalAlloc's caller returns a live pointer from this allocator exactly once.
36        unsafe { free(ptr.cast()) };
37    }
38}
39
40/// Allocate zeroed, page-aligned memory on both 4 KiB and 16 KiB Android devices.
41/// # Safety
42/// The returned pointer must be released exactly once with pages_free.
43pub unsafe fn pages_alloc(size: usize) -> *mut u8 {
44    // SAFETY: anonymous private mapping, kernel-selected address; no supplied memory is read.
45    let ptr = unsafe { mmap(core::ptr::null_mut(), size.max(1), 3, 0x22, -1, 0) };
46    if ptr as usize == usize::MAX { core::ptr::null_mut() } else { ptr.cast() }
47}
48/// Release a runtime allocation.
49/// # Safety
50/// ptr must be null or an unreleased result of pages_alloc with the same size.
51pub unsafe fn pages_free(ptr: *mut u8, size: usize) {
52    if !ptr.is_null() {
53        // SAFETY: caller supplies the complete mapping and its original size.
54        unsafe { munmap(ptr.cast(), size.max(1)) };
55    }
56}
57/// Yield the calling thread.
58pub fn yield_now() {
59    // SAFETY: no arguments or borrowed memory.
60    unsafe { sched_yield() };
61}
62/// Terminate this app process on unrecoverable runtime failure.
63pub fn exit(code: i32) -> ! {
64    // SAFETY: terminates only the calling process.
65    unsafe { _exit(code) }
66}
67// a program without a UI, run from `adb shell` (android/exe_main.c): stdout and stderr
68static CONSOLE: AtomicBool = AtomicBool::new(false);
69/// (android/exe_main.c) the output goes to stdout and stderr, not to logcat
70#[unsafe(no_mangle)]
71pub extern "C" fn jfgui_console() {
72    CONSOLE.store(true, Ordering::Relaxed);
73}
74fn log(bytes: &[u8], priority: i32) {
75    if CONSOLE.load(Ordering::Relaxed) {
76        // SAFETY: bytes is readable for its length; only the descriptors of the process.
77        unsafe { write(if priority == 6 { 2 } else { 1 }, bytes.as_ptr().cast(), bytes.len()) };
78        return;
79    }
80    for chunk in bytes.chunks(1000) {
81        let mut text = [0u8; 1001];
82        text[..chunk.len()].copy_from_slice(chunk);
83        // SAFETY: tag and text are NUL-terminated and live for the synchronous logging call.
84        unsafe { __android_log_write(priority, c"jfgui".as_ptr().cast(), text.as_ptr()) };
85    }
86}
87/// Write diagnostics to Android logcat.
88pub fn write_stdout(bytes: &[u8]) { log(bytes, 4); }
89/// Write errors to Android logcat.
90pub fn write_stderr(bytes: &[u8]) { log(bytes, 6); }
91
92/// Unwinding is disabled; satisfy precompiled core's unused personality reference (std has its own).
93#[cfg(not(feature = "std"))]
94#[unsafe(no_mangle)]
95pub extern "C" fn rust_eh_personality() -> ! { exit(4) }