Skip to content

Commit 1b4757c

Browse files
authored
On macOS, add 64 bytes of headerpad to accommodate code signatures (#780)
Closes #749.
1 parent 4ec8e29 commit 1b4757c

File tree

2 files changed

+42
-2
lines changed

2 files changed

+42
-2
lines changed

cpython-unix/build-cpython.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,16 @@ if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" && "${TARGET_TRIPLE}" = "ppc64le
365365
LDFLAGS="${LDFLAGS} -Wl,--no-tls-get-addr-optimize"
366366
fi
367367

368+
# We're calling install_name_tool -add_rpath on extension modules, which
369+
# eats up 0x20 bytes of space in the Mach-O header, and we need to make
370+
# sure there's still enough room to add a code signature (0x10 bytes) on
371+
# non-arm64 where there's no automatic ad-hoc signature. We are somehow
372+
# on a toolchain that doesn't make sure there's enough space by default
373+
# so give it plenty of space.
374+
if [[ "${PYBUILD_PLATFORM}" = macos* ]]; then
375+
LDFLAGS="${LDFLAGS} -Wl,-headerpad,40"
376+
fi
377+
368378
CPPFLAGS=$CFLAGS
369379

370380
CONFIGURE_FLAGS="

src/validation.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ use {
1212
FileHeader32, FileHeader64, ET_DYN, ET_EXEC, SHN_UNDEF, STB_GLOBAL, STB_WEAK, STV_DEFAULT,
1313
STV_HIDDEN,
1414
},
15-
macho::{MachHeader32, MachHeader64, MH_OBJECT, MH_TWOLEVEL},
15+
macho::{LC_CODE_SIGNATURE, MH_OBJECT, MH_TWOLEVEL, MachHeader32, MachHeader64},
1616
read::{
1717
elf::{Dyn, FileHeader, SectionHeader, Sym},
18-
macho::{LoadCommandVariant, MachHeader, Nlist},
18+
macho::{LoadCommandVariant, MachHeader, Nlist, Section, Segment},
1919
pe::{ImageNtHeaders, PeFile, PeFile32, PeFile64},
2020
},
2121
Architecture, Endianness, FileKind, Object, SectionIndex, SymbolScope,
@@ -1264,6 +1264,8 @@ fn validate_macho<Mach: MachHeader<Endian = Endianness>>(
12641264
let mut undefined_symbols = vec![];
12651265
let mut target_version = None;
12661266
let mut sdk_version = None;
1267+
let mut has_code_signature = false;
1268+
let mut lowest_file_offset = u64::MAX;
12671269

12681270
while let Some(load_command) = load_commands.next()? {
12691271
match load_command.variant()? {
@@ -1386,10 +1388,38 @@ fn validate_macho<Mach: MachHeader<Endian = Endianness>>(
13861388
}
13871389
}
13881390
}
1391+
LoadCommandVariant::Segment32(segment, segment_data) => {
1392+
for section in segment.sections(endian, segment_data)? {
1393+
if let Some((offset, _)) = section.file_range(endian) {
1394+
lowest_file_offset = lowest_file_offset.min(offset);
1395+
}
1396+
}
1397+
}
1398+
LoadCommandVariant::Segment64(segment, segment_data) => {
1399+
for section in segment.sections(endian, segment_data)? {
1400+
if let Some((offset, _)) = section.file_range(endian) {
1401+
lowest_file_offset = lowest_file_offset.min(offset);
1402+
}
1403+
}
1404+
}
1405+
LoadCommandVariant::LinkeditData(c) if c.cmd.get(endian) == LC_CODE_SIGNATURE => {
1406+
has_code_signature = true;
1407+
}
13891408
_ => {}
13901409
}
13911410
}
13921411

1412+
let end_of_load_commands =
1413+
std::mem::size_of_val(header) as u64 + header.sizeofcmds(endian) as u64;
1414+
if header.filetype(endian) != MH_OBJECT
1415+
&& end_of_load_commands + if has_code_signature { 0 } else { 16 } > lowest_file_offset
1416+
{
1417+
context.errors.push(format!(
1418+
"{}: Insufficient headerpad between end of load commands {end_of_load_commands:#x} and beginning of code {lowest_file_offset:#x}",
1419+
path.display(),
1420+
));
1421+
}
1422+
13931423
if let Some(actual_target_version) = target_version {
13941424
if actual_target_version != advertised_target_version {
13951425
context.errors.push(format!(

0 commit comments

Comments
 (0)