Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2bcf948
feat: copy frameworks to Frameworks folder
jkelleyrtp May 22, 2025
95ecae8
add dylib linking to patching
jkelleyrtp May 22, 2025
e396f06
Merge remote-tracking branch 'origin/main' into jk/fix-dyn-link-subse…
jkelleyrtp May 22, 2025
f9b4347
remove logging
jkelleyrtp May 22, 2025
5d228bc
rename __aslr_offset
jkelleyrtp May 22, 2025
54fd293
fix android/linux
jkelleyrtp May 22, 2025
6bf78c7
support linker flavor
jkelleyrtp May 22, 2025
52e0ef4
support custom target dirs
jkelleyrtp May 22, 2025
926bc9f
custom cargo target dir support
jkelleyrtp May 22, 2025
c3e8f28
whoops ios
jkelleyrtp May 22, 2025
3ad0c5f
dlsym doesn't exist on windows
jkelleyrtp May 23, 2025
cdd1d69
look for the right main symbol per platform
jkelleyrtp May 23, 2025
d2b1ef3
distinction between mains
jkelleyrtp May 23, 2025
9c677eb
export main on windows
jkelleyrtp May 23, 2025
99a1de6
enbable rlib caching
jkelleyrtp May 23, 2025
7b8469b
add dx hash
jkelleyrtp May 23, 2025
180f817
adjust linking warnings
jkelleyrtp May 23, 2025
d7227bd
update tui with hotreload state
jkelleyrtp May 23, 2025
8466347
implement symbol flags
jkelleyrtp May 23, 2025
157ed97
add flags
jkelleyrtp May 23, 2025
6b899e3
only run ranlib on darwin
jkelleyrtp May 23, 2025
af0c118
enable lld
jkelleyrtp May 23, 2025
7053318
paths
jkelleyrtp May 23, 2025
9ed164c
add harness test for tls
jkelleyrtp May 23, 2025
5db8b95
wip: revert launch mobile
jkelleyrtp May 24, 2025
0b1a624
wip
jkelleyrtp May 24, 2025
d937b7f
fix fullstack mobile
jkelleyrtp May 24, 2025
f5c5b3e
fix a few bugs
jkelleyrtp May 24, 2025
77082dc
pass-thru fuse-ld args
jkelleyrtp May 24, 2025
8569cb0
fix nan in render, fix empty archives
jkelleyrtp May 24, 2025
2813db5
consider existing dep files if archive exists
jkelleyrtp May 24, 2025
e598cde
fmt
jkelleyrtp May 24, 2025
80200d8
drop msrv
jkelleyrtp May 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ members = [
# subsecond
"packages/subsecond/subsecond",
"packages/subsecond/subsecond-types",
"packages/subsecond/subsecond-tests/cross-tls-crate",
"packages/subsecond/subsecond-tests/cross-tls-crate-dylib",
"packages/subsecond/subsecond-tests/cross-tls-test",

# Full project examples
"example-projects/fullstack-hackernews",
Expand Down
1 change: 1 addition & 0 deletions packages/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ wasm-bindgen-externref-xform = "0.2.100"
pdb = "0.8.0"
self_update = { version = "0.42.0", features = ["archive-tar", "archive-zip", "compression-flate2", "compression-zip-deflate"] }
self-replace = "1.5.0"
cargo-config2 = { workspace = true }

[build-dependencies]
built = { version = "0.7.5", features = ["git2"] }
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/build/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,12 +208,12 @@ impl AppBuilder {
self.bundling_progress = 0.0;
}
BuildStage::Starting { crate_count, .. } => {
self.expected_crates = *crate_count;
self.expected_crates = *crate_count.max(&1);
}
BuildStage::InstallingTooling => {}
BuildStage::Compiling { current, total, .. } => {
self.compiled_crates = *current;
self.expected_crates = *total;
self.expected_crates = *total.max(&1);

if self.compile_start.is_none() {
self.compile_start = Some(Instant::now());
Expand Down
110 changes: 70 additions & 40 deletions packages/cli/src/build/patch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use itertools::Itertools;
use object::{
macho::{self},
read::File,
write::{MachOBuildVersion, StandardSection, Symbol, SymbolSection},
Endianness, Object, ObjectSymbol, SymbolKind, SymbolScope,
write::{MachOBuildVersion, SectionId, StandardSection, Symbol, SymbolId, SymbolSection},
Endianness, Object, ObjectSymbol, SymbolFlags, SymbolKind, SymbolScope,
};
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
use std::{
Expand Down Expand Up @@ -84,6 +84,7 @@ pub struct CachedSymbol {
pub is_undefined: bool,
pub is_weak: bool,
pub size: u64,
pub flags: SymbolFlags<SectionId, SymbolId>,
}

impl PartialEq for HotpatchModuleCache {
Expand Down Expand Up @@ -139,6 +140,7 @@ impl HotpatchModuleCache {
is_undefined,
is_weak: false,
size: 0,
flags: SymbolFlags::None,
},
);
}
Expand All @@ -158,6 +160,7 @@ impl HotpatchModuleCache {
is_undefined,
is_weak: false,
size: 0,
flags: SymbolFlags::None,
},
);
}
Expand Down Expand Up @@ -252,6 +255,15 @@ impl HotpatchModuleCache {
let symbol_table = obj
.symbols()
.filter_map(|s| {
let flags = match s.flags() {
SymbolFlags::None => SymbolFlags::None,
SymbolFlags::Elf { st_info, st_other } => {
SymbolFlags::Elf { st_info, st_other }
}
SymbolFlags::MachO { n_desc } => SymbolFlags::MachO { n_desc },
_ => SymbolFlags::None,
};

Some((
s.name().ok()?.to_string(),
CachedSymbol {
Expand All @@ -260,6 +272,7 @@ impl HotpatchModuleCache {
is_weak: s.is_weak(),
kind: s.kind(),
size: s.size(),
flags,
},
))
})
Expand Down Expand Up @@ -326,9 +339,9 @@ fn create_windows_jump_table(patch: &Path, cache: &HotpatchModuleCache) -> Resul
.context("failed to find 'main' symbol in patch")?;

let aslr_reference = old_name_to_addr
.get("__aslr_reference")
.get("main")
.map(|s| s.address)
.context("failed to find '_aslr_reference' symbol in original module")?;
.context("failed to find '_main' symbol in original module")?;

Ok(JumpTable {
lib: patch.to_path_buf(),
Expand Down Expand Up @@ -371,31 +384,15 @@ fn create_native_jump_table(
}
}

let new_base_address = match triple.operating_system {
// The symbol in the symtab is called "_main" but in the dysymtab it is called "main"
OperatingSystem::MacOSX(_) | OperatingSystem::Darwin(_) | OperatingSystem::IOS(_) => {
*new_name_to_addr
.get("_main")
.context("failed to find '_main' symbol in patch")?
}

// No distincation between the two on these platforms
OperatingSystem::Freebsd
| OperatingSystem::Openbsd
| OperatingSystem::Linux
| OperatingSystem::Windows => *new_name_to_addr
.get("main")
.context("failed to find 'main' symbol in patch")?,

// On wasm, it doesn't matter what the address is since the binary is PIC
_ => 0,
};

let sentinel = main_sentinel(triple);
let new_base_address = new_name_to_addr
.get(sentinel)
.cloned()
.context("failed to find 'main' symbol in base - are deubg symbols enabled?")?;
let aslr_reference = old_name_to_addr
.get("___aslr_reference")
.or_else(|| old_name_to_addr.get("__aslr_reference"))
.get(sentinel)
.map(|s| s.address)
.context("failed to find '___aslr_reference' symbol in original module")?;
.context("failed to find 'main' symbol in original module - are debug symbols enabled?")?;

Ok(JumpTable {
lib: patch.to_path_buf(),
Expand Down Expand Up @@ -831,22 +828,31 @@ pub fn create_undefined_symbol_stub(
_ => {}
}

let symbol_table = &cache.symbol_table;
// Get the offset from the main module and adjust the addresses by the slide;
let aslr_ref_address = cache
.symbol_table
.get(main_sentinel(triple))
.context("failed to find '_main' symbol in patch")?
.address;

if aslr_reference < aslr_ref_address {
return Err(PatchError::InvalidModule(
format!(
"ASLR reference is less than the main module's address - is there a `main`?. {:x} < {:x}", aslr_reference, aslr_ref_address )
));
}

// Get the offset from the main module and adjust the addresses by the slide
let aslr_ref_address = symbol_table
.get("___aslr_reference")
.or_else(|| symbol_table.get("__aslr_reference"))
.map(|s| s.address)
.context("Failed to find ___aslr_reference symbol")?;
let aslr_offset = aslr_reference - aslr_ref_address;

// we need to assemble a PLT/GOT so direct calls to the patch symbols work
// for each symbol we either write the address directly (as a symbol) or create a PLT/GOT entry
let text_section = obj.section_id(StandardSection::Text);
for name in undefined_symbols {
let Some(sym) = symbol_table.get(name.as_str().trim_start_matches("__imp_")) else {
tracing::error!("Symbol not found: {}", name);
let Some(sym) = cache
.symbol_table
.get(name.as_str().trim_start_matches("__imp_"))
else {
tracing::debug!("Symbol not found: {}", name);
continue;
};

Expand Down Expand Up @@ -899,7 +905,7 @@ pub fn create_undefined_symbol_stub(
kind: SymbolKind::Data, // Always Data for IAT entries
weak: false,
section: SymbolSection::Section(data_section),
flags: object::SymbolFlags::None,
flags: SymbolFlags::None,
});
}

Expand Down Expand Up @@ -1028,7 +1034,7 @@ pub fn create_undefined_symbol_stub(
kind: SymbolKind::Text,
weak: false,
section: SymbolSection::Section(text_section),
flags: object::SymbolFlags::None,
flags: SymbolFlags::None, // ignore for these stubs
});
}

Expand Down Expand Up @@ -1097,7 +1103,7 @@ pub fn create_undefined_symbol_stub(
kind: SymbolKind::Tls,
weak: false,
section: SymbolSection::Section(tls_section),
flags: object::SymbolFlags::None,
flags: SymbolFlags::None, // ignore for these stubs
});
}

Expand All @@ -1108,6 +1114,15 @@ pub fn create_undefined_symbol_stub(
SymbolKind::Unknown => SymbolKind::Data,
k => k,
};

// plain linux *wants* these flags, but android doesn't.
// unsure what's going on here, but this is special cased for now.
// I think the more advanced linkers don't want these flags, but the default linux linker (ld) does.
let flags = match triple.environment {
target_lexicon::Environment::Android => SymbolFlags::None,
_ => sym.flags,
};

obj.add_symbol(Symbol {
name: name.as_bytes()[name_offset..].to_vec(),
value: abs_addr,
Expand All @@ -1116,7 +1131,7 @@ pub fn create_undefined_symbol_stub(
kind,
weak: sym.is_weak,
section: SymbolSection::Absolute,
flags: object::SymbolFlags::None,
flags,
});
}
}
Expand Down Expand Up @@ -1439,3 +1454,18 @@ fn parse_module_with_ids(bindgened: &[u8]) -> Result<ParsedModule> {
symbols,
})
}

/// Get the main sentinel symbol for the given target triple
///
/// We need to special case darwin since `main` is the entrypoint but `_main` is the actual symbol.
/// The entrypoint ends up outside the text section, seemingly, and breaks our aslr detection.
fn main_sentinel(triple: &Triple) -> &'static str {
match triple.operating_system {
// The symbol in the symtab is called "_main" but in the dysymtab it is called "main"
OperatingSystem::MacOSX(_) | OperatingSystem::Darwin(_) | OperatingSystem::IOS(_) => {
"_main"
}

_ => "main",
}
}
Loading
Loading