Skip to content

Conversation

@sfourdrinier
Copy link

@sfourdrinier sfourdrinier commented Oct 4, 2025

Summary

This PR fixes two bugs discovered during Android builds with 32-bit ARM (armeabi-v7a):

  1. RustBuffer ABI mismatch causing crashes on 32-bit ARM (Bug: RustBuffer uses size_t instead of uint64_t, causing crashes on 32-bit ARM #314)
  2. Non-canonical CMake paths causing compatibility issues (Bug: Generated CMakeLists.txt uses non-canonical relative paths (dir/.. instead of ../dir) #315)

Bug 1: RustBuffer 32-bit ARM Crash (Critical)

Problem

  • Generated RustBuffer struct uses size_t for capacity/len fields
  • UniFFI's C ABI uses uint64_t (8 bytes) on ALL platforms
  • On 32-bit ARM, size_t is 4 bytes → ABI mismatch → stack corruption → crash

Root Cause

The crash occurred in constructor marshalling on Amazon Fire TV (armeabi-v7a) before any Rust code ran. The C++ JSI wrapper called uniffi_metric_engine_wasm_fn_constructor_metricengine_new with a RustBuffer parameter, but the field size mismatch caused stack corruption.

Impact

  • Crashes: 32-bit ARM devices (armeabi-v7a) - confirmed on Amazon Fire TV with Expo SDK 54
  • Should work: 64-bit platforms where size_t == uint64_t (not explicitly tested in this PR)

Fix

Changed RustBuffer struct definition in cpp/includes/RustBuffer.h to use uint64_t explicitly for capacity/len fields, matching UniFFI's documented C ABI specification.

File changed: cpp/includes/RustBuffer.h

// Before (wrong):
struct RustBuffer {
  size_t capacity;
  size_t len;
  uint8_t *data;
};

// After (correct):
struct RustBuffer {
  uint64_t capacity;
  uint64_t len;
  uint8_t *data;
};

Bug 2: Non-Canonical CMake Paths

Problem

While testing the RustBuffer fix, discovered that generated android/CMakeLists.txt uses non-canonical paths like cpp/../file.cpp instead of ../cpp/file.cpp.

Impact

  • Some CMake configurations may fail with non-canonical paths and it was failing on my side
  • Less readable generated files
  • Not following CMake best practices

Fix

Added path normalization to relative_to() function in crates/ubrn_cli/src/codegen/mod.rs that lexically simplifies paths by eliminating dir/.. patterns.

File changed: crates/ubrn_cli/src/codegen/mod.rs

Testing

  • ✅ Amazon Fire TV (armeabi-v7a, Expo SDK 54): Crash fixed - app now launches successfully and MetricEngine constructor works

References

RustBuffer must use uint64_t (not size_t) for capacity/len fields
to match UniFFI's C ABI on all platforms. Using size_t causes
stack corruption on 32-bit ARM (armeabi-v7a) where size_t is 4 bytes
but Rust expects 8-byte u64.

Fixes crash in constructor marshalling on Amazon Fire TV.

References:
- https://mozilla.github.io/uniffi-rs/0.27/internals/api/uniffi/struct.RustBuffer.html
- mozilla/uniffi-rs#2681
…ility

The pathdiff crate generates 'cpp/..' when calculating relative paths
from android/ to cpp/, but CMake and build tools prefer the canonical
'../cpp' form.

This fix applies a simple string replacement to normalize the paths,
matching the behavior of the existing workaround script.

Fixes: jhugman#315
@sfourdrinier sfourdrinier force-pushed the fix-32bit-rustbuffer-abi branch from adfb68d to 20b48c5 Compare October 4, 2025 20:53
@sfourdrinier
Copy link
Author

Update: Simplified CMake path fix after testing

Initial implementation used complex path component normalization logic, but testing revealed a simpler approach works better and matches existing community workaround patterns.

Changes in latest commit:

  • Replaced complex component-based normalization with direct string replacement
  • Now simply replaces cpp/.. with ../cpp to match canonical form
  • Tested successfully on Amazon Fire TV build (armeabi-v7a)
  • Verified generated CMakeLists.txt produces correct paths without additional post-processing

The simpler approach is more maintainable and aligns with how existing users have been working around this issue in their build scripts.

Copy link
Owner

@jhugman jhugman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two bug fixes here, so I'd like to see two PRs. The first with RufferBuffer is an instant approve. Thanks.

The second, I'm really not sure about.

If you could drop commit 20b48c5 from this we can deal with the two fixes at different speeds.

Comment on lines -14 to +15
size_t capacity;
size_t len;
uint64_t capacity;
uint64_t len;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wow, this is an excellent catch. Thank you.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Yeah this crashed on me running in 32 bit. Required quite some digging to find!

// This happens when calculating paths between sibling directories (e.g., android/ to cpp/)
// CMake and other tools prefer the "../dir" form over "dir/.." even though both work
let path_str = path.as_str().replace("cpp/..", "../cpp");
Utf8PathBuf::from(path_str)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I'm not sure if this works accidentally, or if this doesn't work.

cpp/.. decends down into a directory called cpp and then comes back to where we started. It fails if there isn't a child directory of the pwd called cpp.
../cpp ascends to the parent of the pwd, and iff there is a sibling directory called cpp, then descends into it. If the pwd is called cpp, then we're back where we started.

Then, I'm also not sure where you're getting the cpp from?

@sfourdrinier
Copy link
Author

There are two bug fixes here, so I'd like to see two PRs. The first with RufferBuffer is an instant approve. Thanks.

The second, I'm really not sure about.

If you could drop commit 20b48c5 from this we can deal with the two fixes at different speeds.

Would you be able to cherry pick?

Or let me see what I can do but it might take me a few days because I'm using locally a fork of this for this to work with my platform. I'm quite busy.

I know that without this I couldn't use it and it was writing in the wrong place. I spent over a day trying to figure out how to make it work. At least for me it's cleaner and now it works consistently.

@ibarrick
Copy link

I am having the same issue with the paths. I am having to manually change the paths in the CMakeLists.txt file every time I generate code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants