Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 23 additions & 8 deletions third_party/darwin_xnu_macho/METADATA
Original file line number Diff line number Diff line change
@@ -1,23 +1,38 @@
name: "Darwin XNU Mach-O headers"
description:
"A few header files from the Darwin XNU project. These define the Mach-O format."
"Header files from Apple's cctools and Darwin XNU projects."
"Mach-O file format definitions from the cctools project."
"Mach kernel type definitions from the XNU project."
"They only contain struct and constant definitions (no code)."

third_party {
url {
type: HOMEPAGE
value: "https://opensource.apple.com/source/xnu/"
type: GIT
value: "https://github.com/apple-oss-distributions/xnu"
}
version: "f6217f891ac0bb64f3d375211650a4c1ff8ca1ea"
last_upgrade_date {
year: 2025
month: 11
day: 13
}
local_modifications:
"Mach kernel type headers (machine.h, vm_prot.h) from XNU osfmk/mach/"
"Added __has_include() guards and fallback typedefs for cross-platform compatibility."
}

third_party {
url {
type: GIT
value: "https://github.com/apple/darwin-xnu"
value: "https://github.com/apple-oss-distributions/cctools"
}
version: "0a798f6738bc1db01281fc08ae024145e84df927"
version: "920a2b45080fb9badf31bf675f03b19973f0dd4f"
last_upgrade_date {
year: 2017
year: 2025
month: 11
day: 11
day: 13
}
local_modifications:
"Removed some includes and definitions not related to the Mach-O file format."
"Mach-O file format headers (fat.h, loader.h, nlist.h, reloc.h) from cctools include/mach-o/"
"Modified loader.h to use bundled vm_prot.h instead of system header for cross-platform compatibility."
}
78 changes: 78 additions & 0 deletions third_party/darwin_xnu_macho/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Darwin XNU and Mach-O Headers

This directory contains header files from Apple's open source XNU kernel and cctools projects.
These headers define the Mach-O binary format and related kernel types used by bloaty to parse Mach-O binaries.

## Source Repositories

The headers are obtained from:

- **XNU (mach/ headers)**: https://github.com/apple-oss-distributions/xnu
- `mach/machine.h` - CPU type definitions
- `mach/vm_prot.h` - Virtual memory protection flags

- **cctools (mach-o/ headers)**: https://github.com/apple-oss-distributions/cctools
- `mach-o/fat.h` - Universal binary format
- `mach-o/loader.h` - Mach-O file structure
- `mach-o/nlist.h` - Symbol table format

The specific versions are documented in the `METADATA` file.

## Local Modifications

To make these headers work cross-platform, we apply minimal local modifications:

1. **Cross-platform includes**: Add `__has_include()` guards for system headers
2. **Fallback definitions**: Provide fallback typedefs when system headers aren't available
3. **Bundled headers**: Reference bundled copies instead of system headers

All local modifications are documented as patch files in the `patches/` directory.

## Reproducibility

The header update process is fully reproducible using the provided Python scripts:

### Updating Headers

To update to newer versions of the headers:

1. Edit `METADATA` to specify new git commit hashes
2. Run the update script:
```bash
python3 update_headers.py
```

This will:
- Fetch the specified headers from GitHub
- Apply local modifications from `patches/`
- Install the modified headers

### Regenerating Patches

If you need to add new local modifications:

1. Manually edit the header files as needed
2. Regenerate the patches:
```bash
python3 generate_patches.py
```

This will:
- Fetch the original upstream headers
- Compare them to your modified versions
- Generate/update patch files in `patches/`

### Dry Run

To see what would happen without making changes:

```bash
python3 update_headers.py --dry-run
```

## Patch Files

Current patches:

- **`mach_machine.h.patch`**: Adds `__has_include()` guards and fallback `integer_t` typedef for cross-platform compatibility
- **`mach-o_loader.h.patch`**: Changes `#include <mach/vm_prot.h>` to `#include "../mach/vm_prot.h"` to use bundled header
204 changes: 204 additions & 0 deletions third_party/darwin_xnu_macho/generate_patches.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
#!/usr/bin/env python3
"""
Script to generate patch files documenting local modifications to headers.

This script should be run after manually editing headers to add cross-platform
compatibility fixes. It will generate patch files that can be applied by
update_headers.py.

Usage: python3 generate_patches.py

The script will:
1. Fetch the original upstream headers
2. Compare them to the current headers
3. Generate patch files
"""

import os
import subprocess
import sys
from pathlib import Path
from update_headers import (
MetadataParser,
fetch_file_from_github,
XNU_HEADERS,
CCTOOLS_HEADERS,
)


def generate_patch(original_content: bytes, modified_file: Path, output_patch: Path) -> bool:
"""
Generate a patch file comparing original content to modified file.

Args:
original_content: Original file content from upstream
modified_file: Path to the modified file
output_patch: Path where patch should be written

Returns:
True if a patch was generated, False otherwise
"""
# Write original content to a temporary file
temp_original = modified_file.parent / (modified_file.name + ".orig")
with open(temp_original, 'wb') as f:
f.write(original_content)

try:
# Generate diff
result = subprocess.run(
["diff", "-u", str(temp_original), str(modified_file)],
capture_output=True,
text=True
)

if result.returncode == 0:
return False
elif result.returncode == 1:
# Files differ, generate new patch content
# Adjust paths in diff output to be relative to script directory
new_patch_content = result.stdout
new_patch_content = new_patch_content.replace(
str(temp_original), str(modified_file.name + ".orig")
)
new_patch_content = new_patch_content.replace(
str(modified_file), str(modified_file.name)
)

# Check if existing patch has the same content (ignoring timestamp lines)
if output_patch.exists():
with open(output_patch, 'r') as f:
old_patch_content = f.read()

# Compare patches ignoring the first two lines (which contain timestamps)
old_lines = old_patch_content.split('\n')[2:] # Skip timestamp lines
new_lines = new_patch_content.split('\n')[2:] # Skip timestamp lines

if old_lines == new_lines:
# Patch content is identical, don't update
return False

with open(output_patch, 'w') as f:
f.write(new_patch_content)
return True
else:
raise RuntimeError(f"diff failed: {result.stderr}")

finally:
if temp_original.exists():
temp_original.unlink()


def process_headers(
repo_name: str,
repo_url: str,
version: str,
headers: list,
script_dir: Path,
patch_dir: Path
) -> list:
"""
Process headers from a repository and generate patches.

Args:
repo_name: Human-readable repository name (e.g., "XNU")
repo_url: Git repository URL
version: Git commit hash
headers: List of (source_path, dest_path) tuples
script_dir: Script directory Path
patch_dir: Patches directory Path

Returns:
List of patch filenames that were generated or unchanged

Raises:
Exception: If an error occurs processing a header
"""
patches_generated = []
print(f"=== Processing {repo_name} headers ===")

for source_path, dest_path in headers:
print(f" Checking: {dest_path}")
modified_file = script_dir / dest_path

if not modified_file.exists():
print(f" Warning: Modified file not found, skipping")
continue

original_content = fetch_file_from_github(repo_url, version, source_path)

patch_name = dest_path.replace("/", "_") + ".patch"
patch_file = patch_dir / patch_name

if generate_patch(original_content, modified_file, patch_file):
print(f" Generated patch: {patch_name}")
patches_generated.append(patch_name)
else:
if patch_file.exists():
print(f" Patch unchanged: {patch_name}")
patches_generated.append(patch_name)
else:
print(f" No modifications detected")

print()
return patches_generated


def main():
script_dir = Path(__file__).parent.resolve()
os.chdir(script_dir)

print("=== Darwin XNU/Mach-O Header Patch Generator ===")
print()

metadata_path = script_dir / "METADATA"
if not metadata_path.exists():
print(f"Error: METADATA file not found at {metadata_path}")
return 1

parser = MetadataParser(metadata_path)

try:
xnu_url, xnu_version = parser.get_repo_info("xnu")
cctools_url, cctools_version = parser.get_repo_info("cctools")
except ValueError as e:
print(f"Error parsing METADATA: {e}")
return 1

print(f"XNU repository: {xnu_url}")
print(f"XNU version: {xnu_version}")
print(f"cctools repository: {cctools_url}")
print(f"cctools version: {cctools_version}")
print()

patch_dir = script_dir / "patches"
patch_dir.mkdir(exist_ok=True)
print(f"Patches will be written to: {patch_dir}")
print()

patches_generated = []
try:
patches_generated.extend(
process_headers("XNU", xnu_url, xnu_version, XNU_HEADERS, script_dir, patch_dir)
)
patches_generated.extend(
process_headers("cctools", cctools_url, cctools_version, CCTOOLS_HEADERS, script_dir, patch_dir)
)
except Exception as e:
print(f"Error: {e}")
return 1

print("=== Patch Generation Complete ===")
if patches_generated:
print(f"Generated {len(patches_generated)} patch file(s):")
for patch in patches_generated:
print(f" - {patch}")
print()
print("These patches will be automatically applied by update_headers.py")
else:
print("No patches generated - files are identical to upstream")

return 0


if __name__ == "__main__":
sys.exit(main())
36 changes: 31 additions & 5 deletions third_party/darwin_xnu_macho/mach-o/fat.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
* Copyright (c) 2016 Apple, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
Expand Down Expand Up @@ -42,22 +42,48 @@
* and contains the constants for the possible values of these types.
*/
#include <stdint.h>
#include "third_party/darwin_xnu_macho/mach/machine.h"

#if __has_include(<mach/machine.h>)
#include <mach/machine.h>
#endif

#if __has_include(<architecture/byte_order.h>)
#include <architecture/byte_order.h>
#endif

#define FAT_MAGIC 0xcafebabe
#define FAT_CIGAM 0xbebafeca /* NXSwapLong(FAT_MAGIC) */

struct fat_header {
uint32_t magic; /* FAT_MAGIC */
uint32_t magic; /* FAT_MAGIC or FAT_MAGIC_64 */
uint32_t nfat_arch; /* number of structs that follow */
};

struct fat_arch {
cpu_type_t cputype; /* cpu specifier (int) */
cpu_subtype_t cpusubtype; /* machine specifier (int) */
int32_t cputype; /* cpu specifier (int) */
int32_t cpusubtype; /* machine specifier (int) */
uint32_t offset; /* file offset to this object file */
uint32_t size; /* size of this object file */
uint32_t align; /* alignment as a power of 2 */
};

/*
* The support for the 64-bit fat file format described here is a work in
* progress and not yet fully supported in all the Apple Developer Tools.
*
* When a slice is greater than 4mb or an offset to a slice is greater than 4mb
* then the 64-bit fat file format is used.
*/
#define FAT_MAGIC_64 0xcafebabf
#define FAT_CIGAM_64 0xbfbafeca /* NXSwapLong(FAT_MAGIC_64) */

struct fat_arch_64 {
int32_t cputype; /* cpu specifier (int) */
int32_t cpusubtype; /* machine specifier (int) */
uint64_t offset; /* file offset to this object file */
uint64_t size; /* size of this object file */
uint32_t align; /* alignment as a power of 2 */
uint32_t reserved; /* reserved */
};

#endif /* _MACH_O_FAT_H_ */
Loading