Skip to content

Commit c478370

Browse files
authored
Fix getting symbols from OSX universal binaries (#843)
When profiling on OSX, the python binary could be a universal binary containing both x86_64 and aarch64 code. We didn't account for this when loading, which caused us to read incorrect values for symbols - since we could be getting the address for the x86_64 PyRuntime symbol when we need the aarch64 PyRuntime address. Fix.
1 parent 344703d commit c478370

4 files changed

Lines changed: 39 additions & 13 deletions

File tree

.github/workflows/build.yml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,6 @@ jobs:
282282
python-version: 3.10.20
283283
- os: windows-latest
284284
python-version: 3.10.20
285-
- os: macos-latest
286-
python-version: 3.11.0
287285
- os: macos-latest
288286
python-version: 3.11.15
289287
- os: windows-latest
@@ -294,14 +292,6 @@ jobs:
294292
python-version: 3.12.13
295293
- os: windows-latest
296294
python-version: 3.12.13
297-
- os: macos-latest
298-
python-version: 3.14.0
299-
- os: macos-latest
300-
python-version: 3.14.1
301-
- os: macos-latest
302-
python-version: 3.14.2
303-
- os: macos-latest
304-
python-version: 3.14.3
305295

306296
steps:
307297
- uses: actions/checkout@v6

.github/workflows/release-drafter.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# draft release notes with https://github.com/release-drafter/release-drafter
22
name: Release Drafter
33
permissions:
4-
contents: read
4+
contents: write
55
pull-requests: write
66

77
on:

ci/update_python_test_versions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
_VERSIONS_URL = "https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json" # noqa
99

10-
_OSX_PYTHON_EXCLUSIONS = ["3.11.0", "3.12", "3.14.0", "3.14.1", "3.14.2", "3.14.3"]
10+
_OSX_PYTHON_EXCLUSIONS = ["3.12"]
1111

1212

1313
def parse_version(v):

src/binary_parser.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,38 @@ impl BinaryInfo {
2727
}
2828
}
2929

30+
#[cfg(target_os = "macos")]
31+
fn get_mach_cpu_type() -> goblin::mach::cputype::CpuType {
32+
let is_arm: i32 = 0;
33+
let size: usize = std::mem::size_of_val(&is_arm);
34+
unsafe {
35+
let name = std::ffi::CString::new("hw.optional.arm64").expect("CString::new failed");
36+
let ret = libc::sysctlbyname(
37+
name.as_ptr() as *const i8,
38+
&is_arm as *const _ as *mut _,
39+
&size as *const _ as *mut _,
40+
std::ptr::null_mut(),
41+
0,
42+
);
43+
if ret != 0 {
44+
// if the `hw.optional.arm64` key doesn't exist, its likely that we are running on an
45+
// older x86_64 based intel mac
46+
warn!("failed to call 'libc::sysctlbyname(\"hw.optional.arm64\",...' - assume running on x86_64 ");
47+
return goblin::mach::cputype::CPU_TYPE_X86_64;
48+
}
49+
}
50+
if is_arm == 1 {
51+
goblin::mach::cputype::CPU_TYPE_ARM64
52+
} else {
53+
goblin::mach::cputype::CPU_TYPE_X86_64
54+
}
55+
}
56+
57+
#[cfg(not(target_os = "macos"))]
58+
fn get_mach_cpu_type() -> goblin::mach::cputype::CpuType {
59+
goblin::mach::cputype::CPU_TYPE_ANY
60+
}
61+
3062
/// Uses goblin to parse a binary file, returns information on symbols/bss/adjusted offset etc
3163
pub fn parse_binary(filename: &Path, addr: u64, size: u64) -> Result<BinaryInfo, Error> {
3264
let offset = addr;
@@ -37,6 +69,10 @@ pub fn parse_binary(filename: &Path, addr: u64, size: u64) -> Result<BinaryInfo,
3769
let file = File::open(filename)?;
3870
let buffer = unsafe { Mmap::map(&file)? };
3971

72+
// for OSX, we could be running under rosetta (is compiled for x86_64, but running under
73+
// arm64). check the currently running cpu type rather than relying on compile time flags
74+
let mach_cputype = get_mach_cpu_type();
75+
4076
// Use goblin to parse the binary
4177
match Object::parse(&buffer)? {
4278
Object::Mach(mach) => {
@@ -47,7 +83,7 @@ pub fn parse_binary(filename: &Path, addr: u64, size: u64) -> Result<BinaryInfo,
4783
let arch = fat
4884
.iter_arches()
4985
.find(|arch| match arch {
50-
Ok(arch) => arch.is_64(),
86+
Ok(arch) => arch.is_64() && arch.cputype() == mach_cputype,
5187
Err(_) => false,
5288
})
5389
.ok_or_else(|| {

0 commit comments

Comments
 (0)