Repository navigation
Support the PE TLS callback in Windows ARM64X images - #421
Conversation
|
Hi Lastique, Could you take a look at this PR, or request for another reviewer? I don't seem to have permission to request reviewers myself. This is a blocker for our team right now. We're stuck on an older Boost version with a custom patch, which is keeping us from upgrading our toolchain. As a result, we're missing out on new amazing features and critical bug fixes that other teams already get to use. It’s also creating a headache for managing our dev environments, since we have to maintain our patched Boost side-by-side with the up-to-date version everyone else is on. If the code quality isn't up to your standards for merging, feel free to edit it. All we care about is getting the TLS callback bug resolved, not how it's implemented. BR |
An ARM64X image contains native ARM64 and ARM64EC code and can be loaded by both ARM64 and x64 processes. It has a single TLS directory whose callback array the linker builds from the native .CRT$XL* entries, so the callback registered by tss_pe.cpp (a function with internal linkage) is the native ARM64 one in both views: x64 processes loading an ARM64X image linked with the static library call native ARM64 code from the loader's TLS callback dispatch. In practice this breaks e.g. attaching a debugger to such x64 processes. When BOOST_THREAD_ARM64X is defined, register an extern "C" forwarder instead and mark it /ARM64XSAMEADDRESS from the ARM64EC object file, as the MSVC CRT does for __dyn_tls_init. The linker then emits a thunk that dispatches to the ARM64 forwarder in the native view and to the ARM64EC forwarder in the x64 view. The directive makes the ARM64EC object reference the ARM64 symbol, so the ARM64EC library has to be linked together with the ARM64 one (e.g. as an ARM64X library containing both, like the MSVC CRT libraries), also for ARM64EC-only images. This is why the behaviour is opt-in; without the macro the generated code is unchanged on all targets.
88cf207 to
3f0b8e8
Compare
|
Thanks. |
Support the PE TLS callback in Windows ARM64X images
Problem
An [ARM64X](https://learn.microsoft.com/windows/arm/arm64x-pe) image (linked with
/MACHINE:ARM64X) containsnative ARM64 code and ARM64EC code and can be loaded by both ARM64 and x64 processes. It has a single
TLS directory; the linker builds its callback array from the native
.CRT$XL*contributions.src/win32/tss_pe.cppregisterson_tls_callback(internal linkage) in.CRT$XLC. In an ARM64X imagethat links the static Boost.Thread library (ARM64 + ARM64EC variants), the callback array therefore
points at the native ARM64
on_tls_callbackin both views, i.e. x64 processes that load the imagecall native ARM64 code from the loader's TLS callback dispatch (
DLL_THREAD_ATTACH/DETACH, …).We hit this with a driver DLL: attaching WinDbg to x64 processes that had loaded it failed.
The MSVC CRT has the same situation for its own TLS callback and solves it in
tlsdyn.obj: the ARM64ECobject carries
/ARM64XSAMEADDRESS:__dyn_tls_init, which makes the linker emit a thunk that dispatchesto the ARM64 function in the native view and to the ARM64EC function in the x64 view.
Change
When
BOOST_THREAD_ARM64Xis defined (ARM64 / ARM64EC targets only), register anextern "C"forwarder
boost_thread_arm64x_on_tls_callbackand emit/arm64xsameaddressfor it from the ARM64ECobject, exactly like the CRT.
Opt-in because the directive makes the ARM64EC object reference the native symbol: the ARM64EC library
must then be linked together with the ARM64 one (for example both merged into one ARM64X library with
lib /machine:arm64x, as the CRT libraries are), also for ARM64EC-only images. Linking the ARM64EClibrary alone fails with
LNK2001: unresolved external symbol boost_thread_arm64x_on_tls_callback (Arm64 Native Symbol). Enabling it by default would therefore break existing ARM64EC-only builds thatuse the plain b2/CMake output; making it the default would need the build to produce ARM64X archives
for ARM64EC, which could be a follow-up.
Without the macro the compiled code is unchanged on every target (object files for x86, x64, ARM64
and ARM64EC built from the old and new
tss_pe.cppdisassemble identically, modulo theanonymous-namespace hash that derives from the file path).
The
.CRT$XIU/XCU/XTUinitializers are not affected: the CRT runs the native and the ARM64ECinitializer tables in their respective views.
Validation
builds the four Win32 sources of Boost. Thread as ARM64 and ARM64EC, merges them into an ARM64X
library, links a small ARM64X DLL that uses
boost::thread, and prints the TLS callbacks of both views(applying the ARM64X dynamic relocations of the image), resolving thunk targets via the PDB.
MSVC 14.50 (VS 2026), WDK 10.0.28000:
Also checked with MSVC 14.44 (VS 2022 17.14): ARM64X, ARM64EC-only (with the ARM64X library) and ARM64
images link; ARM64EC-only with only the ARM64EC library fails with the documented
LNK2001.In a large production DLL the patched library has been in use for ARM64, ARM64EC, ARM64X and the
ARM64X "pure forwarder" layout.