Trying to print the layout.size() inside the implemented alloc method to the terminal results in a 'cargo run' terminated by signal SIGSEGV (Address boundary error) error message.
This happens with any format! or eprintln! macro.
Using the println! macro makes cargo run hang forever.
Minimal example:
extern crate alloc;
use std::alloc::{GlobalAlloc, System, Layout};
use std::io::Write;
struct MyAllocator;
unsafe impl GlobalAlloc for MyAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = System.alloc(layout);
//let _t = format!("line"); <= 'cargo run' terminated by signal SIGSEGV (Address boundary error)
//eprintln!("in alloc"); <= 'cargo run' terminated by signal SIGSEGV (Address boundary error)
//println!("in alloc"); <= execution hangs forever
//without it runs fine
ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
System.dealloc(ptr, layout)
}
}
#[global_allocator]
static GLOBAL: MyAllocator = MyAllocator;
fn main() {
let mut v = Vec::new();
v.push(1);
}
I suspect what's happening is some of those formatting or print functions require allocation. In turn they will call the allocator and you get infinite recursion and stack overflow.
Maybe the compiler is detecting this and just inserting code to force a segfault? I'm not sure on that part.
It's worth stepping through the code in gdb to see what's happening
rust-gdb ./target/debug/global-alloc
break main
layout asm
start
ni
If you're implementing your own allocator and still want to be able to debug log, you would need to implement some logging functions which won't allocate. Here's some I wrote when I was doing something similar. I used libc's puts (put string) to output the nul terminated c string.
extern "C" {
fn puts(_: *const core::ffi::c_char);
}
fn put(s: &core::ffi::CStr) {
unsafe { puts(s.as_ptr()) };
}
#[inline]
fn to_hex(value: u8) -> u8 {
assert!(value < 16);
if value < 10 {
value + b'0'
} else {
value + b'a' - 10
}
}
// this assumes machine is little endian
fn hex_write_rev(buf: &mut [u8], mut value: usize) {
assert!(buf.len() > 16);
buf[16] = b'\0';
for i in 0..16 {
buf[17 - i] = to_hex((value & 0b1111) as u8);
value >>= 4;
}
buf[0] = b'0';
buf[1] = b'x';
}
fn put_hex(value: usize) {
let mut buf: [u8; 20] = [0; 20];
hex_write_rev(&mut buf, value);
unsafe { puts(buf.as_ptr() as *const i8) };
}
Yes, it seems to be the case here..
This makes me dive deeper than expected but that's a good thing I guess.
For simplicity I use the extern "C" printf for now:
extern "C" {
fn printf(format: *const c_char, ...) -> c_int;
}
.
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let start = Instant::now();
let ptr = System.alloc(layout);
let end = Instant::now();
let ellapsed = end - start;
let bytes_requested = layout.size();
let format: *const c_char = "bytes_requested: %d, ellapsed: %f \n".as_ptr().cast();
let _ = printf(format, bytes_requested, ellapsed.as_secs_f32() as c_double);
ptr
}
This works with my minimal example without any problems.
Using it in the more sophisticated example from the book I get a lot of non-crashing messages:
/rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/ptr/const_ptr.rsunsafe precondition(s) violated: slice::from_raw_parts_mut requires the pointer to be aligned and non-null, and the total size of the slice not to exceedisize::MAXis_nonoverlapping: size_of::() * countoverflows a usize/rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/iter/adapters/enumerate.rssrc/main.rs/home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/gfx-0.18.3/src/pso/buffer.rsUnsupportedBindOther
I am not sure where it comes from and why - to investigate another time.