feat: copy frameworks to Frameworks folder, detect linkers, custom target dirs (#4174)

* feat: copy frameworks to Frameworks folder

* add dylib linking to patching

* remove logging

* rename __aslr_offset

* fix android/linux

* support linker flavor

* support custom target dirs

* custom cargo target dir support

* whoops ios

* dlsym doesn't exist on windows

* look for the right main symbol per platform

* distinction between mains

* export main on windows

* enbable rlib caching

* add dx hash

* adjust linking warnings

* update tui with hotreload state

* implement symbol flags

* add flags

* only run ranlib on darwin

* enable lld

* paths

* add harness test for tls

* wip: revert launch mobile

* wip

* fix fullstack mobile

* fix a few bugs
- long paths?
- reverting back to original

* pass-thru fuse-ld args

* fix nan in render, fix empty archives

* consider existing dep files if archive exists

* fmt

* drop msrv
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2025-05-24 22:41:30 +01:00
1 parent 751f1920c3
commit 0982c1ff8b
25 files changed
+1044 -315

No files matched your search

@@ -0,0 +1,11 @@
[package]
name = "cross-tls-crate-dylib"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
# when testing, use a dylib
# crate-type = ["dylib"]
[dependencies]
@@ -0,0 +1,25 @@
pub use std::cell::Cell;
use std::{cell::RefCell, thread::LocalKey};
#[derive(Debug)]
pub struct StoredItem {
pub name: String,
pub value: f32,
pub items: Vec<String>,
}
thread_local! {
pub static BAZ: RefCell<Option<StoredItem>> = const { RefCell::new(None) };
}
pub fn get_baz() -> &'static LocalKey<RefCell<Option<StoredItem>>> {
if BAZ.with(|f| f.borrow().is_none()) {
BAZ.set(Some(StoredItem {
name: "BAR".to_string(),
value: 0.0,
items: vec!["item1".to_string(), "item2".to_string()],
}));
}
&BAZ
}
@@ -0,0 +1,7 @@
[package]
name = "cross-tls-crate"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
@@ -0,0 +1,25 @@
pub use std::cell::Cell;
use std::{cell::RefCell, thread::LocalKey};
#[derive(Debug)]
pub struct StoredItem {
pub name: String,
pub value: f32,
pub items: Vec<String>,
}
thread_local! {
pub static BAR: RefCell<Option<StoredItem>> = const { RefCell::new(None) };
}
pub fn get_bar() -> &'static LocalKey<RefCell<Option<StoredItem>>> {
if BAR.with(|f| f.borrow().is_none()) {
BAR.set(Some(StoredItem {
name: "BAR".to_string(),
value: 0.0,
items: vec!["item1".to_string(), "item2".to_string()],
}));
}
&BAR
}
@@ -0,0 +1,10 @@
[package]
name = "subsecond-tls-harness"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
dioxus-devtools = { workspace = true }
cross-tls-crate = { path = "../cross-tls-crate" }
cross-tls-crate-dylib = { path = "../cross-tls-crate-dylib" }
@@ -0,0 +1,13 @@
harness for testing cross-crate TLS imports.
TLS is the hardest to get right with binary patchinig since we need to rewrite initializers.
this crate relies on two crates - one as a dylib and the other as an rlib
the dylib should make it into our bundle and the rlib should just be linked.
both shouldn't cause access errors or segfaults during hotpatch.
```sh
cargo run --package dioxus-cli -- serve --hotpatch
```
@@ -0,0 +1,33 @@
use std::{cell::Cell, thread, time::Duration};
use cross_tls_crate::get_bar;
use cross_tls_crate_dylib::get_baz;
fn main() {
dioxus_devtools::connect_subsecond();
loop {
dioxus_devtools::subsecond::call(|| {
use cross_tls_crate::BAR;
use cross_tls_crate_dylib::BAZ;
thread_local! {
pub static FOO: Cell<f32> = const { Cell::new(2.0) };
}
println!("Hello 123s123123s: {}", FOO.get());
get_bar().with(|f| println!("Bar: {:?}", f.borrow()));
thread::sleep(Duration::from_secs(1));
FOO.set(2.0);
get_bar().with(|f| f.borrow_mut().as_mut().unwrap().value = 3.0);
get_baz().with(|f| f.borrow_mut().as_mut().unwrap().value = 4.0);
BAR.with_borrow(|f| {
println!("Bar: {:?}", f);
});
BAZ.with_borrow(|f| {
println!("Baz: {:?}", f);
});
});
}
}
+42 -16
View File
@@ -495,10 +495,9 @@ pub unsafe fn apply_patch(mut table: JumpTable) -> Result<(), PatchError> {
}
}));
// Use the `__aslr_reference` symbol as a sentinel for the current executable. This is basically a
// cross-platform version of `__mh_execute_header` on macOS that sets a reference point for the
// jump table.
let old_offset = __aslr_reference() - table.aslr_reference as usize;
// Use the `main` symbol as a sentinel for the current executable. This is basically a
// cross-platform version of `__mh_execute_header` on macOS that we can use to base the executable.
let old_offset = aslr_reference() - table.aslr_reference as usize;
// Use the `main` symbol as a sentinel for the loaded library. Might want to move away
// from this at some point, or make it configurable
@@ -672,22 +671,49 @@ pub enum PatchError {
AndroidMemfd(String),
}
/// This function returns its own address, providing a stable reference point for hot-patch engine
/// to hook onto. If you were to write an object file for this function, it would amount to:
///
/// ```asm
/// __aslr_reference:
/// mov rax, qword ptr [rip + __aslr_reference@GOTPCREL] // notice the @GOTPCREL relocation
/// ret
/// ```
/// This function returns the address of the main function in the current executable. This is used as
/// an anchor to reference the current executable's base address.
///
/// The point here being that we have a stable address both at runtime and compile time, making it
/// possible to calculate the ASLR offset from within the process to correct the jump table.
///
/// It should only be called from the main executable *first* and not from a shared library since it
/// self-initializes.
#[doc(hidden)]
#[inline(never)]
#[no_mangle]
pub extern "C" fn __aslr_reference() -> usize {
__aslr_reference as *const () as usize
pub fn aslr_reference() -> usize {
#[cfg(target_family = "wasm")]
return 0;
#[cfg(not(target_family = "wasm"))]
unsafe {
use std::ffi::c_void;
// The first call to this function should occur in the
static mut MAIN_PTR: *mut c_void = std::ptr::null_mut();
if MAIN_PTR.is_null() {
#[cfg(unix)]
{
MAIN_PTR = libc::dlsym(libc::RTLD_DEFAULT, c"main".as_ptr() as _);
}
#[cfg(windows)]
{
extern "system" {
fn GetModuleHandleA(lpModuleName: *const i8) -> *mut std::ffi::c_void;
fn GetProcAddress(
hModule: *mut std::ffi::c_void,
lpProcName: *const i8,
) -> *mut std::ffi::c_void;
}
MAIN_PTR =
GetProcAddress(GetModuleHandleA(std::ptr::null()), c"main".as_ptr() as _) as _;
}
}
MAIN_PTR as usize
}
}
/// On Android, we can't dlopen libraries that aren't placed inside /data/data/<package_name>/lib/