Odd issue returning a pointer from rust to C

I have some C code I'm trying to migrate to rust, and have run into an odd issue. Code is up at GitHub - martindemello/arcccc at riir and the relevant snippets are:

rust code:

#[repr(C)] 
pub struct LetterVar {
  letter_counts: [[i32; 256]; 2], // support for each letter from the across=0 or down=1 words
  letters_allowed: [i32; 256], // letters that can appear in this word
  num_letters_allowed: i32,
  constraints: [*mut OverlapConstraint; 2],
  stack: *mut glib_sys::GArray, // for backtracking
  name: *mut glib_sys::GString,
  pos: *mut u8
}

impl LetterVar {
  #[no_mangle]
  pub unsafe extern "C" fn new(chr: u8, p: *mut u8) -> LetterVar {
    let f = if chr == 46 { 1 } else { 0 };
    let mut out = LetterVar {
        letter_counts: [[0; 256]; 2],
        letters_allowed: [f; 256],
        num_letters_allowed: 0,
        constraints: [std::ptr::null_mut(); 2],
        stack: glib_sys::g_array_new(
            0, 0, mem::size_of::<LetterVar>() as u32),
        name: std::ptr::null_mut(),
        pos: p
    };
    out.letters_allowed[chr as usize] = 1;
    out
  }
}

#[no_mangle]
pub unsafe extern "C" fn make_lettervar(chr: u8, p: *mut u8) -> *mut LetterVar {
    let l = Box::into_raw(Box::new(LetterVar::new(chr, p)));
    l
}

and C code calling it:

  LetterVar *l;
  // create new lettervar
  *p = grid[row][col];
  l = make_lettervar(grid[row][col], p++);

When I step through it in gdb, in the rust code I get different values for the pointer in the rust and C side:

Breakpoint 1, make_lettervar (chr=46, p=0x5555559b2980 ".\000") at src/lettervar.rs:41
41	    l
(gdb) print l
$2 = (arcccc::lettervar::LetterVar *) 0x5555559b8b60
(gdb) print *l
$3 = arcccc::lettervar::LetterVar {letter_counts: [[0 <repeats 256 times>], [0 <repeats 256 times>]], letters_allowed: [
    1 <repeats 256 times>], num_letters_allowed: 0, constraints: [0x0, 0x0], stack: 0x5555559b7c00, name: 0x0, pos: 0x5555559b2980 ".\000"}
(gdb) next
42	}
(gdb) next
read_grid (filename=0x7fffffffe78c "examples/4.grid", wordlist=0x7fffffffe2e8, letterlist=0x7fffffffe2f0, constraintlist=0x7fffffffe2f8)
    at /home/mdemello/github/arcccc/src/lib/read_grid.c:64
64	      *letterlist = g_slist_prepend(*letterlist, l);
(gdb) print l
$4 = (LetterVar *) 0x559b8b60
(gdb) print *l
Cannot access memory at address 0x559b8b60
(gdb) print *((LetterVar *) 0x5555559b8b60)
$6 = {letter_counts = {{0 <repeats 256 times>}, {0 <repeats 256 times>}}, letters_allowed = {1 <repeats 256 times>}, num_letters_allowed = 0, constraints = {0x0, 0x0}, 
  stack = 0x5555559b7c00, name = 0x0, pos = 0x5555559b2980 "."}

I looked at the repo, and you don't appear to actually define make_lettervar anywhere in the C code. You just call it.

Which reminds me that C allows you to call a function that doesn't exist. It guesses the types. The default return type is int, which on most platforms is 32-bit.

That pointer looks suspiciously like it's been truncated to 32 bits.

 

You might want to try with a prototype.

3 Likes

that was, of course, the issue :slight_smile: thanks!