Debugger: support for binaries with chained fixup
16 Apr 2022 | fix debugger ios15 dyldXcode13.3 bring unaligned pointer problem during linking but also debugger launch was crashing with:
[ERROR] Couldn’t start application java.lang.IllegalArgumentException: there is no region for addr @10000101effaa0 at org.robovm.debugger.utils.bytebuffer.CompositeDataBuffer.setPosition(CompositeDataBuffer.java:37)
0x10000101effaa0
indeed looks weird for memory address, even looks like tagged pointer at first (but not). Anyway it introduced a level of complication that is not allowing debugger to read internal RoboVM structures like class info anymore.
LC_DYLD_CHAINED_FIXUPS and LC_DYLD_EXPORTS_TRIE
Apple: All programs and dylibs built with a deployment target of macOS 12 or iOS 15 or later now use the chained fixups format. This uses different load commands and LINKEDIT data, and won’t run or load on older OS versions.
As usual there is not much of technical details. Nice post by Noah Martinz
How iOS 15 makes your app launch faster light things a little but not enough to consider things clear or as technical spec.
Lucky for as Apple releases dyld sources as past of Open Source at Apple, and it is the only source of truth here.
Long story short – dyld
now organizes address bind/rebase as part of chained data and keep this chains inside pointers to be fixes.
That is a root of strange pointers like @10000101effaa0
.
RoboVM debugger operates with binary as it saved on file system we have to perform chained fixup to get proper pointer values to be able to read internal RoboVM structures required for debugger.
Approach
How to implement:
There is a lack of spec and doing same as in dyld is good option.
Mostly work to be done it is port of MachOLoaded::fixupAllChainedFixups
methods and its sequence calls.
In general things to be done are pretty simple – follow pointer chain, check bits, build new pointer value and put it back.
Fixing pointers
Another moment – is how to fix pointer. One approach would be is to create a remap
hashmap and look for values each time.
But amount of remap locations is huge enough even for simple project. Second approach is to use FileChannel.MapMode.PRIVATE
(copy-on-write) when mapping file channel to ByteBuffer and just fix pointer over file.
This will allow keeping debugger logic not affected but only image loading. And this approach to be taken.
Validation
There is dyldinfo
that shows useful information when called with -rebase -bind
.
But dyld
sources contains part for dyld_info
which has extra command line options to be useful (and missing in bundled with Xcode dyldinfo
): -fixup_chains
, -fixup_chain_details
.
dyld_info
to be build from sources that will allow to see extra debug output, compare results with own implementation. Also debug it to see how it works its a good option.
Check bonus part for compilation instructions.
Code
The fix was proposed as PR645
Bonus – building dyld_info
Its a bit tricky as depends on macosx.internal
and some headers that are missing.
- Download and unpack dyld-852.2.tar.gz
- Lucky for us, it contains
dyld.xcodeproj
. Open it; -
Select
dyld_info
scheme; - Build it, and it will fail with error:
unable to find sdk ‘macosx.internal’
-
Open project
dyld
settings ->Build Settings
and changeBase SDK
frommacosx.internal
tomacOS
: - Build it, and it will fail with complaint due missing headers like bellow:
fatal error: ‘corecrypto/ccdigest.h’ file not found
fatal error: ‘sandbox/private.h’ file not found - To fix this and other missing header issues, prepare location where these will be provided:
- Create
${dyld-852.2}/external/
folder where all missing headers will be located - Create a new
Configuration Setting File
with a namedyld_info
inconfigs
folder of Xcode project; - Put reference to
external/
todyld_info.xcconfig
:HEADER_SEARCH_PATHS=$(inherited) external/
- configure
dyld_info
to use this config:
- Create
- Missing libraries can be picked there:
corecrypto/
headers are present in xnu-7195.81.3. Copyxnu-7195.81.3.tar.gz/EXTERNAL_HEADERS/corecrypto/
to ‘${dyld-852.2}/external/corecrypto/`;_simple.h
is present in Libc-825.40.1. CopyLibc-825.40.1.tar.gz/gen/_simple.h
to${dyld-852.2}/external/_simple.h
libc_private.h
is present in Libc-1439.40.11. CopyLibc-1439.40.11.tar.gz/darwin/libc_private.h
to${dyld-852.2}/external/libc_private.h
sandbox/private.h
can be picked from github/OSXPrivateSDK and put it to${dyld-852.2}/external/include/sandbox/private.h
- Then it will be massively complains about missing
bridgeos
in differentAPI_AVAILABLE
macros and simples way is to redefinebridgeos
to something that is known to toolchain, likewatchos
(don’t care for it in this build).dyld_info.xcconfig
takes the final look:HEADER_SEARCH_PATHS = $(inherited) external/ GCC_PREPROCESSOR_DEFINITIONS = $(inherited) bridgeos=watchos
Compilation will success with some amount code level warnings. Binary can be run with required new params and debuggedm what was required.
Comments