Skip to content

Instantly share code, notes, and snippets.

@mrnugget
Created December 21, 2022 05:53
Show Gist options
  • Save mrnugget/70ed30bf073c5752381c664284bfe569 to your computer and use it in GitHub Desktop.
Save mrnugget/70ed30bf073c5752381c664284bfe569 to your computer and use it in GitHub Desktop.
When I call my Rust function directly from C the top call frame disappears from the stack. Why?

Compile and run both files with (on Linux x86):

rustc --crate-type=staticlib gc_experiment4.rs && \
   gcc -O0 -g -o gc_experiment4 gc_experiment4.c -lgc_experiment4 -L. && \
  ./gc_experiment4

This will output the following:

&local_a_1: 0x7ffc6c344440 (11)
&local_a_2: 0x7ffc6c344448 (22)
&local_a_3: 0x7ffc6c344450 (33)
&local_c_1: 0x7ffc6c344400 (77)
&local_c_2: 0x7ffc6c344408 (88)
&local_c_3: 0x7ffc6c344410 (99)
call gc through trampoline
start mark phase. stack_top: 0x7ffc6c3443f0, stack_bottom: 0x7ffc6c344480, slots=18
stack_slot=0x7ffc6c3443f0 content=0x7ffc6c344420
stack_slot=0x7ffc6c3443f8 content=0x56075106c807
stack_slot=0x7ffc6c344400 content=0x4d
stack_slot=0x7ffc6c344408 content=0x58
stack_slot=0x7ffc6c344410 content=0x63
stack_slot=0x7ffc6c344418 content=0x2a1880e880ec4500
stack_slot=0x7ffc6c344420 content=0x7ffc6c344430
stack_slot=0x7ffc6c344428 content=0x56075106c849
stack_slot=0x7ffc6c344430 content=0x7ffc6c344460
stack_slot=0x7ffc6c344438 content=0x56075106c8e6
stack_slot=0x7ffc6c344440 content=0xb
stack_slot=0x7ffc6c344448 content=0x16
stack_slot=0x7ffc6c344450 content=0x21
stack_slot=0x7ffc6c344458 content=0x2a1880e880ec4500
stack_slot=0x7ffc6c344460 content=0x7ffc6c344480
stack_slot=0x7ffc6c344468 content=0x56075106c924
stack_slot=0x7ffc6c344470 content=0x7ffc6c344598
stack_slot=0x7ffc6c344478 content=0x15106c660
call gc WITHOUT trampoline
start mark phase. stack_top: 0x7ffc6c344420, stack_bottom: 0x7ffc6c344480, slots=12
stack_slot=0x7ffc6c344420 content=0x7ffc6c344430
stack_slot=0x7ffc6c344428 content=0x56075106c849
stack_slot=0x7ffc6c344430 content=0x7ffc6c344460
stack_slot=0x7ffc6c344438 content=0x56075106c8e6
stack_slot=0x7ffc6c344440 content=0xb
stack_slot=0x7ffc6c344448 content=0x16
stack_slot=0x7ffc6c344450 content=0x21
stack_slot=0x7ffc6c344458 content=0x2a1880e880ec4500
stack_slot=0x7ffc6c344460 content=0x7ffc6c344480
stack_slot=0x7ffc6c344468 content=0x56075106c924
stack_slot=0x7ffc6c344470 content=0x7ffc6c344598
stack_slot=0x7ffc6c344478 content=0x15106c660

Question: Where are the missing 4 stack slots when calling gc (Rust) directly from C without going through gc_trampoline (C)?

#include <stdio.h>
#include <stdlib.h>
// both are defined in Rust, see below
void gc_init();
void gc();
// QUESTION: Why do we need gc_trampoline?!. If we don't have it, the
// stack of the previous function is not visible to gc();
void gc_trampoline() { gc(); }
void function_c() {
int64_t local_c_1 = 77;
int64_t local_c_2 = 88;
int64_t local_c_3 = 99;
printf("&local_c_1: %p (%ld)\n", &local_c_1, local_c_1);
printf("&local_c_2: %p (%ld)\n", &local_c_2, local_c_2);
printf("&local_c_3: %p (%ld)\n", &local_c_3, local_c_3);
printf("call gc through trampoline\n");
gc_trampoline();
printf("call gc WITHOUT trampoline\n");
gc();
}
void function_b() { function_c(); }
void function_a() {
int64_t local_a_1 = 11;
int64_t local_a_2 = 22;
int64_t local_a_3 = 33;
printf("&local_a_1: %p (%ld)\n", &local_a_1, local_a_1);
printf("&local_a_2: %p (%ld)\n", &local_a_2, local_a_2);
printf("&local_a_3: %p (%ld)\n", &local_a_3, local_a_3);
function_b();
}
int main(int argc, char *argv[]) {
gc_init();
function_a();
}
#![allow(clippy::missing_safety_doc)]
use std::{arch::asm, ffi::c_void};
static mut STACK_BOTTOM: Option<*const *const c_void> = None;
#[no_mangle]
pub unsafe extern "C" fn gc_init() {
let stack_bottom: *const *const c_void;
asm!("mov %rbp, {0}", out(reg) stack_bottom, options(att_syntax));
STACK_BOTTOM.replace(stack_bottom);
}
#[no_mangle]
#[inline(never)]
pub unsafe extern "C" fn gc() {
mark_rs();
}
#[no_mangle]
#[inline(never)]
pub unsafe extern "C" fn mark_rs() {
let stack_top: *const *const c_void;
asm!("mov %rbp, {0}", out(reg) stack_top, options(att_syntax));
let stack_bottom =
STACK_BOTTOM.expect("GC data not initialized. Call gc_init at start of program.");
let slots = (stack_bottom as usize - stack_top as usize) / 8;
println!(
"start mark phase. stack_top: {:p}, stack_bottom: {:?}, slots={:?}",
stack_top, stack_bottom, slots
);
let mut stack_slot = stack_top;
while stack_slot < stack_bottom {
let ptr: *const c_void = *stack_slot;
println!(
"stack_slot={:p} content={:<18}",
stack_slot,
format!("{:p}", ptr),
);
stack_slot = stack_slot.add(1);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment