Supported way for an arm64 process to map below the 4 GB __PAGEZERO floor?

Hi Quinn — following up from DTS case 22070584.

I'm working on a Windows compatibility runtime (Wine plus a CPU translator) that runs natively on Apple Silicon. 64-bit x86 Windows programs work fine. 32-bit ones don't, because they need address space in the low 4 GB: guest pointers are 32-bit, and some Windows structures sit at fixed addresses like 0x7ffe0000 that programs read directly.

On arm64 I can't get anything down there:

task_info(TASK_VM_INFO) -> min_address 0x100ea0000 mmap(0x7ffe0000, MAP_FIXED) -> ENOMEM mach_vm_allocate(0x7ffe0000, VM_FLAGS_FIXED) -> KERN_INVALID_ADDRESS

There's also nothing below 4 GB to remove: mach_vm_region finds no entry there at all, and mach_vm_deallocate(0, 4 GB) returns KERN_SUCCESS without changing anything. Building with a smaller __PAGEZERO doesn't help either — every size I tried (0x1000, 0x4000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x80000000) gets SIGKILLed before main, with no crash report. Ad-hoc signing, the hardened runtime and -no_pie made no difference.

I did notice /usr/libexec/rosetta/runtime is arm64 with no __PAGEZERO segment at all and __TEXT at vmaddr 0, so the kernel can clearly do this, at least for platform binaries.

Is there a supported way for a third-party arm64 process to map below 4 GB — an entitlement, a spawn attribute, something I've missed? If the answer is no, that's fine, I'd just like to know so I can stop looking and plan around it.

I have two small test programs that print all of the above if they'd be useful.

Answered by DTS Engineer in 906401022

I sat down to play with this today and I want to share some results.

Using Xcode 27.0 on macOS 26.6.1, I created a new app target and then gutted all the app bits, à la Signing a daemon with a restricted entitlement.

I then added a main.swift with this code:

import Darwin

func main() {
    print("Hello Cruel World!")
    var addr: vm_address_t = 0x7ffe0000
    // Annoyingly, using `vm_page_size` makes Swift concurrency unhappy, so
    // let’s get this from BSD.
    let pageSize = vm_size_t(bitPattern: Int(getpagesize())) 
    print("page size: 0x\(String(pageSize, radix: 16))")
    let kr = vm_allocate(mach_task_self_, &addr, pageSize, VM_FLAGS_FIXED)
    if kr == KERN_SUCCESS {
        print("did allocate, addr: 0x\(String(addr, radix: 16))")
    } else {
        print("did not allocate, kr: \(kr)")
    }
    if false {
        print("will pause, pid: \(getpid())")
        pause()
        print("did pause")
    }
}

main()

On running that, I saw this:

Hello Cruel World!
page size: 0x4000
did not allocate, kr: 1

where 1 is KERN_INVALID_ADDRESS. So far, so expected.

To get this to work I had to change two things. First, in Signing & Capabilities I added the Cross-architecture Compatibility Framework capability [1]. This added the com.apple.developer.cross-architecture-support entitlement. Xcode’s automatic code signing kicked in and generated a provisioning profile that authorises my app to claim that entitlement.

When I ran that version the allocation failed with error 3, or KERN_NO_SPACE. Which brings me to the second change: adding an explicit zero page segment. Specifically, I edited the Other Linker Flags build setting to look like this:

-Xlinker
-pagezero_size
-Xlinker
0x4000
$(inherited)

And with change, the allocation works:

Hello Cruel World!
page size: 0x4000
did allocate, addr: 0x7ffe0000

Yay!

I think that’s all you need, but lemme know if you hit any snags.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] No docs for this yet, alas (r. 188189100).

I don't have an answer, however the latest CrossOver beta can use FEX to run 32-bit Windows programs too. Maybe check what it's doing.

Accepted Answer

Thanks for bringing this to the forums.

I need to do research this before I can give you a definitive answer. I’ll reply here when I know more.

ps For those following along at home, note that this came up in other thread but we never drove it to a conclusion there, so I’m gonna use this thread to do that.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I’m making good progress on this. I need to do some due diligence at my end before I say anything definitive, but there is one bit of info that’s clearly public and thus I can pass along…

If you look in the Signing & Capabilities editor in Xcode 27.0 you’ll see that it has a new Cross-architecture Compatibility Framework capability. If you add that to your app, Xcode updates your project to add a claim for the com.apple.developer.cross-architecture-support entitlement and approval to use that entitlement in your provisioning profile.

AFAICT we don’t have any official docs about that yet, hence the caution I opened this response with, but if you search the ’net you’ll find other folks talking about the entitlement.

I hope to have another update here early next week.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

WIll wait, but this was helpful too.

I sat down to play with this today and I want to share some results.

Using Xcode 27.0 on macOS 26.6.1, I created a new app target and then gutted all the app bits, à la Signing a daemon with a restricted entitlement.

I then added a main.swift with this code:

import Darwin

func main() {
    print("Hello Cruel World!")
    var addr: vm_address_t = 0x7ffe0000
    // Annoyingly, using `vm_page_size` makes Swift concurrency unhappy, so
    // let’s get this from BSD.
    let pageSize = vm_size_t(bitPattern: Int(getpagesize())) 
    print("page size: 0x\(String(pageSize, radix: 16))")
    let kr = vm_allocate(mach_task_self_, &addr, pageSize, VM_FLAGS_FIXED)
    if kr == KERN_SUCCESS {
        print("did allocate, addr: 0x\(String(addr, radix: 16))")
    } else {
        print("did not allocate, kr: \(kr)")
    }
    if false {
        print("will pause, pid: \(getpid())")
        pause()
        print("did pause")
    }
}

main()

On running that, I saw this:

Hello Cruel World!
page size: 0x4000
did not allocate, kr: 1

where 1 is KERN_INVALID_ADDRESS. So far, so expected.

To get this to work I had to change two things. First, in Signing & Capabilities I added the Cross-architecture Compatibility Framework capability [1]. This added the com.apple.developer.cross-architecture-support entitlement. Xcode’s automatic code signing kicked in and generated a provisioning profile that authorises my app to claim that entitlement.

When I ran that version the allocation failed with error 3, or KERN_NO_SPACE. Which brings me to the second change: adding an explicit zero page segment. Specifically, I edited the Other Linker Flags build setting to look like this:

-Xlinker
-pagezero_size
-Xlinker
0x4000
$(inherited)

And with change, the allocation works:

Hello Cruel World!
page size: 0x4000
did allocate, addr: 0x7ffe0000

Yay!

I think that’s all you need, but lemme know if you hit any snags.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] No docs for this yet, alas (r. 188189100).

Thanks Quinn, that worked for me too.

One thing I noticed while playing with it: the small page-zero isn't actually needed. With the entitlement, the space below the image becomes a reservation I can free myself. So I kept the default 4 GB page-zero, called vm_deallocate on it at startup, and could then map 0x10000, 0x7ffe0000 and 0xfffe0000 every time. With just the 16 KB page-zero, 0x7ffe0000 works but 0x10000 never does, because the slide gap is still reserved.

On packaging: a nested .app in Contents/Helpers that reuses the same bundle ID and profile gets the entitlement fine, and it notarized without issues. A bare helper binary gets killed at launch.

Two quick questions:

Is reusing the parent's bundle ID and profile for a nested helper app okay to rely on, or should each helper get its own App ID?

Is there any supported way to give the entitlement to a plain helper executable? Our loader isn't a bundle's main executable.

Thanks again, this helps a lot.

Glad to hear you’re making progress. And thanks for the vm_deallocate tip.

should each helper get its own App ID?

Yes.

A few years back I helped a developer debug a very weird TestFlight issue where things failed in that environment because their app and one of their helpers had the same App ID. Finding that was ‘fun’ (s. 1997177). Those scars are what guided the bundle ID advice in Creating distribution-signed code for macOS (search for pig-jato).

Is there any supported way to give the entitlement to a plain helper executable?

Not as such (r. 125850707). The standard workaround is described in Signing a daemon with a restricted entitlement. While that’s focused on daemons, the process work for helper tools and other unbundled executables.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks Quinn, that's really helpful. I'll give the helper its own App ID and wrap the loader in a small app bundle, following the daemon article. Appreciate you digging into this.

Hi Quinn,

Thank you for the concrete reproduction. I tried it with Xcode 27.0 (27A266a) on macOS 27.0.1 (26A434), using an Apple Silicon Mac and a free Personal Team.

I created an isolated macOS app target with its own bundle identifier and automatic Apple Development signing. Without the cross-architecture entitlement, the app builds, passes strict signature verification, and runs natively (sysctl.proc_translated = 0). getpagesize() returns 16384 and the fixed allocation at 0x7ffe0000 returns KERN_INVALID_ADDRESS, as expected for the control.

With this Personal Team selected, the capability picker has no matches for “Cross”. I also tried adding the exact entitlement from your answer:

com.apple.developer.cross-architecture-support = true

and setting -pagezero_size to 0x4000. Both the Xcode UI and xcodebuild automatic provisioning fail before producing a runnable entitled app:

“Entitlement com.apple.developer.cross-architecture-support not found and could not be included in profile. This likely is not a valid entitlement and should be removed from your entitlements file.”

Does Cross-architecture Compatibility Framework support free Personal Teams for local development? If so, is there another enrollment or capability-request step needed to make it appear and authorize the entitlement in the profile? If not, can an individual Apple Developer Program member enable it directly, or is separate approval required?

This is a standalone native memory-allocation probe, packaged as an app bundle, using the entitlement without the “-unmanaged” suffix.

Thank you.

Does Cross-architecture Compatibility Framework support free Personal Teams for local development?

Technically, the Cross-architecture Compatibility Framework capability maps to the com.apple.developer.cross-architecture-support entitlement which is only available to paid teams. This is not uncommon. The list of capabilities available to a Personal Team is quite short. See the Apple Developer column in Developer Account Help > Reference > Supported capabilities (macOS).

Note This table doesn’t list this capability because it’s not auto-generated from our back end, something I’d like to see change (r. 179941179).

The story with com.apple.developer.cross-architecture-support-unmanaged is a little muddy. It looks like it’s actually a thing — that is, the system treats it as equivalent to com.apple.developer.cross-architecture-support — but I can’t see any way to provision it. Note that it’s in the com.apple.developer. hierarchy, not com.apple.security., which makes it a restricted entitlement. Such entitlements must be authorised by a profile. The only way to create profiles for a Personal Team is with Xcode, and Xcode 27 doesn’t offer it as an option.

Note If this all sounds like gibberish to you, I recommend you have a read of TN3125 Inside Code Signing: Provisioning Profiles (-:

I’m gonna ask you to file a bug about this. Either the entitlement name needs to change to something in the com.apple.security. namespace, so that it becomes unrestricted on the Mac, or Xcode needs to gain a capability to provision it for free teams.

Once you’re done, reply here with the bug number and I’ll do some more digging at this end.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi Quinn, thank you for clarifying. I filed FB25187024 with the Personal Team reproduction steps, provisioning error, and minimal native probe source. Thank you for looking into this.

Supported way for an arm64 process to map below the 4 GB __PAGEZERO floor?
 
 
Q