1*2c2f96dcSApple OSS Distributions# XNU debugging 2*2c2f96dcSApple OSS Distributions 3*2c2f96dcSApple OSS DistributionsDebugging xnu through kernel core files or with a live device. 4*2c2f96dcSApple OSS Distributions 5*2c2f96dcSApple OSS Distributions## Overview 6*2c2f96dcSApple OSS Distributions 7*2c2f96dcSApple OSS Distributionsxnu’s debugging macros are compatible with both Python 3.9+. Please be careful about pulling 8*2c2f96dcSApple OSS Distributionsin the latest language features. Some users are living on older Xcodes and may not have the newest 9*2c2f96dcSApple OSS DistributionsPython installed. 10*2c2f96dcSApple OSS Distributions 11*2c2f96dcSApple OSS Distributions## General coding tips 12*2c2f96dcSApple OSS Distributions 13*2c2f96dcSApple OSS Distributions### Imports 14*2c2f96dcSApple OSS Distributions 15*2c2f96dcSApple OSS DistributionsThe current implementation re-exports a lot of submodules through the XNU main module. This leads to some 16*2c2f96dcSApple OSS Distributionssurprising behavior: 17*2c2f96dcSApple OSS Distributions 18*2c2f96dcSApple OSS Distributions* Name collisions at the top level may override methods with unexpected results. 19*2c2f96dcSApple OSS Distributions* New imports may change the order of imports, leading to some surpising side effects. 20*2c2f96dcSApple OSS Distributions 21*2c2f96dcSApple OSS DistributionsPlease avoid `from xnu import *` where possible and always explicitly import only what is 22*2c2f96dcSApple OSS Distributionsrequired from other modules. 23*2c2f96dcSApple OSS Distributions 24*2c2f96dcSApple OSS Distributions### Checking the type of an object 25*2c2f96dcSApple OSS Distributions 26*2c2f96dcSApple OSS DistributionsAvoid testing for a `type` explicitly like `type(obj) == type`. 27*2c2f96dcSApple OSS DistributionsInstead, always use the inheritance-sensitive `isinstance(obj, type)`. 28*2c2f96dcSApple OSS Distributions 29*2c2f96dcSApple OSS Distributions### Dealing with binary data 30*2c2f96dcSApple OSS Distributions 31*2c2f96dcSApple OSS DistributionsIt’s recommended to use **bytearray**, **bytes**, and **memoryviews** instead of a string. 32*2c2f96dcSApple OSS DistributionsSome LLDB APIs no longer accept a string in place of binary data in Python 3. 33*2c2f96dcSApple OSS Distributions 34*2c2f96dcSApple OSS Distributions### Accessing large amounts of binary data (or accessing small amounts frequently) 35*2c2f96dcSApple OSS Distributions 36*2c2f96dcSApple OSS DistributionsIn case you're planning on accessing large contiguous blocks of memory (e.g. reading a whole 10KB of memory), 37*2c2f96dcSApple OSS Distributionsor you're accessing small semi-contiguous chunks (e.g. if you're parsing large structured data), then it might 38*2c2f96dcSApple OSS Distributionsbe hugely beneficial performance-wise to make use of the `io.SBProcessRawIO` class. Furthermore, if you're in 39*2c2f96dcSApple OSS Distributionsa hurry and just want to read one specific chunk once, then it might be easier to use `LazyTarget.GetProcess().ReadMemory()` 40*2c2f96dcSApple OSS Distributionsdirectly. 41*2c2f96dcSApple OSS Distributions 42*2c2f96dcSApple OSS DistributionsIn other words, avoid the following: 43*2c2f96dcSApple OSS Distributions 44*2c2f96dcSApple OSS Distributions``` 45*2c2f96dcSApple OSS Distributionsdata_ptr = kern.GetValueFromAddress(start_addr, 'uint8_t *') 46*2c2f96dcSApple OSS Distributionswith open(filepath, 'wb') as f: 47*2c2f96dcSApple OSS Distributions f.write(data_ptr[:4096]) 48*2c2f96dcSApple OSS Distributions``` 49*2c2f96dcSApple OSS Distributions 50*2c2f96dcSApple OSS DistributionsAnd instead use: 51*2c2f96dcSApple OSS Distributions 52*2c2f96dcSApple OSS Distributions``` 53*2c2f96dcSApple OSS Distributionsfrom core.io import SBProcessRawIO 54*2c2f96dcSApple OSS Distributionsimport shutil 55*2c2f96dcSApple OSS Distributions 56*2c2f96dcSApple OSS Distributionsio_access = SBProcessRawIO(LazyTarget.GetProcess(), start_addr, 4096) 57*2c2f96dcSApple OSS Distributionswith open(filepath, 'wb') as f: 58*2c2f96dcSApple OSS Distributions shutil.copyfileobj(io_access, f) 59*2c2f96dcSApple OSS Distributions``` 60*2c2f96dcSApple OSS Distributions 61*2c2f96dcSApple OSS DistributionsOr, if you're in a hurry: 62*2c2f96dcSApple OSS Distributions 63*2c2f96dcSApple OSS Distributions``` 64*2c2f96dcSApple OSS Distributionserr = lldb.SBError() 65*2c2f96dcSApple OSS Distributionsmy_data = LazyTarget.GetProcess().ReadMemory(start_addr, length, err) 66*2c2f96dcSApple OSS Distributionsif err.Success(): 67*2c2f96dcSApple OSS Distributions # Use my precious data 68*2c2f96dcSApple OSS Distributions pass 69*2c2f96dcSApple OSS Distributions``` 70*2c2f96dcSApple OSS Distributions 71*2c2f96dcSApple OSS DistributionsFor small semi-contiguous chunks, you can map the whole region and access random chunks from it like so: 72*2c2f96dcSApple OSS Distributions 73*2c2f96dcSApple OSS Distributions``` 74*2c2f96dcSApple OSS Distributionsfrom core.io import SBProcessRawIO 75*2c2f96dcSApple OSS Distributions 76*2c2f96dcSApple OSS Distributionsio_access = SBProcessRawIO(LazyTarget.GetProcess(), start_addr, size) 77*2c2f96dcSApple OSS Distributionsio_access.seek(my_struct_offset) 78*2c2f96dcSApple OSS Distributionsmy_struct_contents = io_access.read(my_struct_size) 79*2c2f96dcSApple OSS Distributions``` 80*2c2f96dcSApple OSS Distributions 81*2c2f96dcSApple OSS DistributionsNot only that, but you can also tack on a BufferedRandom class on top of the SBProcessRawIO instance, which 82*2c2f96dcSApple OSS Distributionsprovides you with buffering (aka caching) in case your random small chunk accesses are repeated: 83*2c2f96dcSApple OSS Distributions 84*2c2f96dcSApple OSS Distributions``` 85*2c2f96dcSApple OSS Distributionsfrom core.io import SBProcessRawIO 86*2c2f96dcSApple OSS Distributionsfrom io import BufferedRandom 87*2c2f96dcSApple OSS Distributions 88*2c2f96dcSApple OSS Distributionsio_access = SBProcessRawIO(LazyTarget.GetProcess(), start_addr, size) 89*2c2f96dcSApple OSS Distributionsbuffered_io = BufferedRandom(io_access) 90*2c2f96dcSApple OSS Distributions# And then use buffered_io for your accesses 91*2c2f96dcSApple OSS Distributions``` 92*2c2f96dcSApple OSS Distributions 93*2c2f96dcSApple OSS Distributions### Encoding data to strings and back 94*2c2f96dcSApple OSS Distributions 95*2c2f96dcSApple OSS DistributionsAll strings are now `unicode` and must be converted between binary data and strings explicitly. 96*2c2f96dcSApple OSS DistributionsWhen no explicit encoding is selected then UTF-8 is the default. 97*2c2f96dcSApple OSS Distributions 98*2c2f96dcSApple OSS Distributions``` 99*2c2f96dcSApple OSS Distributionsmystring = mybytes.decode() 100*2c2f96dcSApple OSS Distributionsmybytes = mystring.encode() 101*2c2f96dcSApple OSS Distributions``` 102*2c2f96dcSApple OSS DistributionsIn most cases **utf-8** will work but be careful to be sure that the encoding matches your data. 103*2c2f96dcSApple OSS Distributions 104*2c2f96dcSApple OSS DistributionsThere are two options to consider when trying to get a string out of the raw data without knowing if 105*2c2f96dcSApple OSS Distributionsthey are valid string or not: 106*2c2f96dcSApple OSS Distributions 107*2c2f96dcSApple OSS Distributions* **lossy conversion** - escapes all non-standard characters in form of ‘\xNNN’ 108*2c2f96dcSApple OSS Distributions* **lossless conversion** - maps invalid characters to special unicode range so it can reconstruct 109*2c2f96dcSApple OSS Distributionsthe string precisely 110*2c2f96dcSApple OSS Distributions 111*2c2f96dcSApple OSS DistributionsWhich to use depends on the transformation goals. The lossy conversion produces a printable string 112*2c2f96dcSApple OSS Distributionswith strange characters in it. The lossless option is meant to be used when a string is only a transport 113*2c2f96dcSApple OSS Distributionsmechanism and needs to be converted back to original values later. 114*2c2f96dcSApple OSS Distributions 115*2c2f96dcSApple OSS DistributionsSwitch the method by using `errors` handler during conversion: 116*2c2f96dcSApple OSS Distributions 117*2c2f96dcSApple OSS Distributions``` 118*2c2f96dcSApple OSS Distributions# Lossy escapes invalid chars 119*2c2f96dcSApple OSS Distributionsb.decode('utf-8', errors='`backslashreplace'`) 120*2c2f96dcSApple OSS Distributions# Lossy removes invalid chars 121*2c2f96dcSApple OSS Distributionsb.decode('utf-8', errors='ignore') 122*2c2f96dcSApple OSS Distributions# Loss-less but may likely fail to print() 123*2c2f96dcSApple OSS Distributionsb.decode('utf-8', errors='surrogateescape') 124*2c2f96dcSApple OSS Distributions``` 125*2c2f96dcSApple OSS Distributions 126*2c2f96dcSApple OSS Distributions### Dealing with signed numbers 127*2c2f96dcSApple OSS Distributions 128*2c2f96dcSApple OSS DistributionsPython's int has unlimited precision. This may be surprising for kernel developers who expect 129*2c2f96dcSApple OSS Distributionsthe behavior follows twos complement. 130*2c2f96dcSApple OSS Distributions 131*2c2f96dcSApple OSS DistributionsAlways use **unsigned()** or **signed()** regardless of what the actual underlying type is 132*2c2f96dcSApple OSS Distributionsto ensure that macros use the correct semantics. 133*2c2f96dcSApple OSS Distributions 134*2c2f96dcSApple OSS Distributions## Testing changes 135*2c2f96dcSApple OSS Distributions 136*2c2f96dcSApple OSS DistributionsThere is no perfect test suite to check that macros are producing a correct value compared to what 137*2c2f96dcSApple OSS Distributionsthe debugger sees in a target. 138*2c2f96dcSApple OSS Distributions 139*2c2f96dcSApple OSS DistributionsBe careful when touching common framework code. For larger changes, ask the Platform Triage team to 140*2c2f96dcSApple OSS Distributionsvalidate that the changes work in their environment before integration. 141*2c2f96dcSApple OSS Distributions 142*2c2f96dcSApple OSS Distributions### Coding style 143*2c2f96dcSApple OSS Distributions 144*2c2f96dcSApple OSS DistributionsUse a static analyzer like **pylint** or **flake8** to check the macro source code: 145*2c2f96dcSApple OSS Distributions 146*2c2f96dcSApple OSS Distributions``` 147*2c2f96dcSApple OSS Distributions$ python3 -m pip install --user pylint flake8 148*2c2f96dcSApple OSS Distributions 149*2c2f96dcSApple OSS Distributions# Run the lint either by setting your path to point to one of the runtimes 150*2c2f96dcSApple OSS Distributions# or through python 151*2c2f96dcSApple OSS Distributions$ python3 -m pylint <src files/dirs> 152*2c2f96dcSApple OSS Distributions$ python3 -m flake8 <src files/dirs> 153*2c2f96dcSApple OSS Distributions``` 154*2c2f96dcSApple OSS Distributions 155*2c2f96dcSApple OSS Distributions### Correctness 156*2c2f96dcSApple OSS Distributions 157*2c2f96dcSApple OSS DistributionsEnsure the macro matches what LLDB returns from the REPL. For example, compare `showproc(xxx)` with `p/x *(proc_t)xxx`. 158*2c2f96dcSApple OSS Distributions 159*2c2f96dcSApple OSS Distributions``` 160*2c2f96dcSApple OSS Distributions# 1. Run LLDB with debug options set 161*2c2f96dcSApple OSS Distributions$ DEBUG_XNU_LLDBMACROS=1 xcrun -sdk <sdk> lldb -c core <dsympath>/mach_kernel 162*2c2f96dcSApple OSS Distributions 163*2c2f96dcSApple OSS Distributions# 2. Optionally load modified operating system plugin 164*2c2f96dcSApple OSS Distributions(lldb) settings set target.process.python-os-plugin-path <srcpath>/tools/lldbmacros/core/operating_system.py 165*2c2f96dcSApple OSS Distributions 166*2c2f96dcSApple OSS Distributions# 3. Load modified scripts 167*2c2f96dcSApple OSS Distributions(lldb) command script import <srcpath>/tools/lldbmacros/xnu.py 168*2c2f96dcSApple OSS Distributions 169*2c2f96dcSApple OSS Distributions# 4. Exercise macros 170*2c2f96dcSApple OSS Distributions``` 171*2c2f96dcSApple OSS Distributions 172*2c2f96dcSApple OSS DistributionsDepending on the change, test other targets and architectures (for instance, both Astris and KDP). 173*2c2f96dcSApple OSS Distributions 174*2c2f96dcSApple OSS Distributions### Regression 175*2c2f96dcSApple OSS Distributions 176*2c2f96dcSApple OSS DistributionsThis is simpler than previous step because the goal is to ensure behavior has not changed. 177*2c2f96dcSApple OSS DistributionsYou can speed up few things by using local symbols: 178*2c2f96dcSApple OSS Distributions 179*2c2f96dcSApple OSS Distributions``` 180*2c2f96dcSApple OSS Distributions# 1. Get a coredump from a device and kernel UUID 181*2c2f96dcSApple OSS Distributions# 2. Grab symbols with dsymForUUID 182*2c2f96dcSApple OSS Distributions$ dsymForUUID --nocache --copyExecutable --copyDestination <dsym path> 183*2c2f96dcSApple OSS Distributions 184*2c2f96dcSApple OSS Distributions# 3. Run lldb with local symbols to avoid dsymForUUID NFS 185*2c2f96dcSApple OSS Distributions 186*2c2f96dcSApple OSS Distributions$ xcrun -sdk <sdk> lldb -c core <dsym_path>/<kernel image> 187*2c2f96dcSApple OSS Distributions``` 188*2c2f96dcSApple OSS Distributions 189*2c2f96dcSApple OSS DistributionsThe actual steps are identical to previous testing. Run of a macro to different file with `-o <outfile>` 190*2c2f96dcSApple OSS Distributionsoption. Then run `diff` on the outputs of the baseline and modified code: 191*2c2f96dcSApple OSS Distributions 192*2c2f96dcSApple OSS Distributions* No environment variables to get baseline 193*2c2f96dcSApple OSS Distributions* Modified dSYM as described above 194*2c2f96dcSApple OSS Distributions 195*2c2f96dcSApple OSS DistributionsIt’s difficult to make this automated: 196*2c2f96dcSApple OSS Distributions 197*2c2f96dcSApple OSS Distributions* Some macros needs arguments which must be found in a core file. 198*2c2f96dcSApple OSS Distributions* Some macros take a long time to run against a target (more than 30 minutes). Instead, a core dump 199*2c2f96dcSApple OSS Distributions should be taken and then inspected afterwards, but this ties up a lab device for the duration of the 200*2c2f96dcSApple OSS Distributions test. 201*2c2f96dcSApple OSS Distributions* Even with coredumps, testing the macros takes too long in our automation system and triggers the 202*2c2f96dcSApple OSS Distributions failsafe timeout. 203*2c2f96dcSApple OSS Distributions 204*2c2f96dcSApple OSS Distributions### Code coverage 205*2c2f96dcSApple OSS Distributions 206*2c2f96dcSApple OSS DistributionsUse code coverage to check which parts of macros have actually been tested. 207*2c2f96dcSApple OSS DistributionsInstall **coverage** lib with: 208*2c2f96dcSApple OSS Distributions 209*2c2f96dcSApple OSS Distributions``` 210*2c2f96dcSApple OSS Distributions$ python3 -m pip install --user coverage 211*2c2f96dcSApple OSS Distributions``` 212*2c2f96dcSApple OSS Distributions 213*2c2f96dcSApple OSS DistributionsThen collect coverage:. 214*2c2f96dcSApple OSS Distributions 215*2c2f96dcSApple OSS Distributions``` 216*2c2f96dcSApple OSS Distributions# 1. Start LLDB with your macros as described above. 217*2c2f96dcSApple OSS Distributions 218*2c2f96dcSApple OSS Distributions# 2. Load and start code coverage recording. 219*2c2f96dcSApple OSS Distributions(lldb) script import coverage 220*2c2f96dcSApple OSS Distributions(lldb) script cov = coverage.Coverage() 221*2c2f96dcSApple OSS Distributions(lldb) script cov.start() 222*2c2f96dcSApple OSS Distributions 223*2c2f96dcSApple OSS Distributions# 3. Do the testing. 224*2c2f96dcSApple OSS Distributions 225*2c2f96dcSApple OSS Distributions# 4. Collect the coverage. 226*2c2f96dcSApple OSS Distributions(lldb) script cov.stop() 227*2c2f96dcSApple OSS Distributions(lldb) script cov.save() 228*2c2f96dcSApple OSS Distributions``` 229*2c2f96dcSApple OSS Distributions 230*2c2f96dcSApple OSS DistributionsYou can override the default file (*.coverage*) by adding an additional environment variable to LLDB: 231*2c2f96dcSApple OSS Distributions 232*2c2f96dcSApple OSS Distributions``` 233*2c2f96dcSApple OSS Distributions$ env COVERAGE_FILE="${OUTDIR}/.coverage.mytest" # usual LLDB command line 234*2c2f96dcSApple OSS Distributions``` 235*2c2f96dcSApple OSS Distributions 236*2c2f96dcSApple OSS DistributionsCombine coverage from multiple files: 237*2c2f96dcSApple OSS Distributions 238*2c2f96dcSApple OSS Distributions``` 239*2c2f96dcSApple OSS Distributions# Point PATH to local python where coverage is installed. 240*2c2f96dcSApple OSS Distributions$ export PATH="$HOME/Library/Python/3.8/bin:$PATH" 241*2c2f96dcSApple OSS Distributions 242*2c2f96dcSApple OSS Distributions# Use --keep to avoid deletion of input files after merge. 243*2c2f96dcSApple OSS Distributions$ coverage combine --keep <list of .coverage files or dirs to scan> 244*2c2f96dcSApple OSS Distributions 245*2c2f96dcSApple OSS Distributions# Get HTML report or use other subcommands to inspect. 246*2c2f96dcSApple OSS Distributions$ coverage html 247*2c2f96dcSApple OSS Distributions``` 248*2c2f96dcSApple OSS Distributions 249*2c2f96dcSApple OSS DistributionsIt is possible to start coverage collection **before** importing the operating system library and 250*2c2f96dcSApple OSS Distributionsloading macros to check code run during bootstrapping. 251*2c2f96dcSApple OSS Distributions 252*2c2f96dcSApple OSS Distributions### Performance testing 253*2c2f96dcSApple OSS Distributions 254*2c2f96dcSApple OSS DistributionsSome macros can run for a long time. Some code may be costly even if it looks simple because objects 255*2c2f96dcSApple OSS Distributionsaren’t cached or too many temporary objects are created. Simple profiling is similar to collecting 256*2c2f96dcSApple OSS Distributionscode coverage. 257*2c2f96dcSApple OSS Distributions 258*2c2f96dcSApple OSS DistributionsFirst setup your environment: 259*2c2f96dcSApple OSS Distributions 260*2c2f96dcSApple OSS Distributions``` 261*2c2f96dcSApple OSS Distributions# Install gprof2dot 262*2c2f96dcSApple OSS Distributions$ python3 -m pip install gprof2dot 263*2c2f96dcSApple OSS Distributions# Install graphviz 264*2c2f96dcSApple OSS Distributions$ brew install graphviz 265*2c2f96dcSApple OSS Distributions``` 266*2c2f96dcSApple OSS Distributions 267*2c2f96dcSApple OSS DistributionsThen to profile commands, follow this sequence: 268*2c2f96dcSApple OSS Distributions 269*2c2f96dcSApple OSS Distributions``` 270*2c2f96dcSApple OSS Distributions(lldb) xnudebug profile /tmp/macro.prof showcurrentstacks 271*2c2f96dcSApple OSS Distributions[... command outputs ...] 272*2c2f96dcSApple OSS Distributions 273*2c2f96dcSApple OSS Distributions Ordered by: cumulative time 274*2c2f96dcSApple OSS Distributions List reduced from 468 to 30 due to restriction <30> 275*2c2f96dcSApple OSS Distributions 276*2c2f96dcSApple OSS Distributions ncalls tottime percall cumtime percall filename:lineno(function) 277*2c2f96dcSApple OSS Distributions [... profiling output ...] 278*2c2f96dcSApple OSS Distributions 279*2c2f96dcSApple OSS DistributionsProfile info saved to "/tmp/macro.prof" 280*2c2f96dcSApple OSS Distributions``` 281*2c2f96dcSApple OSS Distributions 282*2c2f96dcSApple OSS DistributionsThen to visualize callgraphs in context, in a separate shell: 283*2c2f96dcSApple OSS Distributions 284*2c2f96dcSApple OSS Distributions``` 285*2c2f96dcSApple OSS Distributions# Now convert the file to a colored SVG call graph 286*2c2f96dcSApple OSS Distributions$ python3 -m gprof2dot -f pstats /tmp/macro.prof -o /tmp/call.dot 287*2c2f96dcSApple OSS Distributions$ dot -O -T svg /tmp/call.dot 288*2c2f96dcSApple OSS Distributions 289*2c2f96dcSApple OSS Distributions# and view it in your favourite viewer 290*2c2f96dcSApple OSS Distributions$ open /tmp/call.dot.svg 291*2c2f96dcSApple OSS Distributions``` 292*2c2f96dcSApple OSS Distributions 293*2c2f96dcSApple OSS Distributions## Debugging your changes 294*2c2f96dcSApple OSS Distributions 295*2c2f96dcSApple OSS Distributions### Get detailed exception report 296*2c2f96dcSApple OSS Distributions 297*2c2f96dcSApple OSS DistributionsThe easiest way to debug an exception is to re-run your macro with the `--debug` option. 298*2c2f96dcSApple OSS DistributionsThis turns on more detailed output for each stack frame that includes source lines 299*2c2f96dcSApple OSS Distributionsand local variables. 300*2c2f96dcSApple OSS Distributions 301*2c2f96dcSApple OSS Distributions### File a radar 302*2c2f96dcSApple OSS Distributions 303*2c2f96dcSApple OSS DistributionsTo report an actionable radar, please use re-run your failing macro with `--radar`. 304*2c2f96dcSApple OSS DistributionsThis will collect additional logs to an archive located in `/tmp`. 305*2c2f96dcSApple OSS Distributions 306*2c2f96dcSApple OSS DistributionsUse the link provided to create a new radar. 307*2c2f96dcSApple OSS Distributions 308*2c2f96dcSApple OSS Distributions### Debugging with pdb 309*2c2f96dcSApple OSS Distributions 310*2c2f96dcSApple OSS DistributionsYES, It is possible to use a debugger to debug your macro! 311*2c2f96dcSApple OSS Distributions 312*2c2f96dcSApple OSS DistributionsThe steps are similar to testing techniques described above (use scripting interactive mode). There is no point to 313*2c2f96dcSApple OSS Distributionsdocument the debugger itself. Lets focus on how to use it on a real life example. The debugger used here is PDB which 314*2c2f96dcSApple OSS Distributionsis part of Python installation so works out of the box. 315*2c2f96dcSApple OSS Distributions 316*2c2f96dcSApple OSS DistributionsProblem: Something wrong is going on with addkext macro. What now? 317*2c2f96dcSApple OSS Distributions 318*2c2f96dcSApple OSS Distributions (lldb) addkext -N com.apple.driver.AppleT8103PCIeC 319*2c2f96dcSApple OSS Distributions Failed to read MachO for address 18446741875027613136 errormessage: seek to offset 2169512 is outside window [0, 1310] 320*2c2f96dcSApple OSS Distributions Failed to read MachO for address 18446741875033537424 errormessage: seek to offset 8093880 is outside window [0, 1536] 321*2c2f96dcSApple OSS Distributions Failed to read MachO for address 18446741875033568304 errormessage: seek to offset 8124208 is outside window [0, 1536] 322*2c2f96dcSApple OSS Distributions ... 323*2c2f96dcSApple OSS Distributions Fetching dSYM for 049b9a29-2efc-32c0-8a7f-5f29c12b870c 324*2c2f96dcSApple OSS Distributions Adding dSYM (049b9a29-2efc-32c0-8a7f-5f29c12b870c) for /Library/Caches/com.apple.bni.symbols/bursar.apple.com/dsyms/StarE/AppleEmbeddedPCIE/AppleEmbeddedPCIE-502.100.35~3/049B9A29-2EFC-32C0-8A7F-5F29C12B870C/AppleT8103PCIeC 325*2c2f96dcSApple OSS Distributions section '__TEXT' loaded at 0xfffffe001478c780 326*2c2f96dcSApple OSS Distributions 327*2c2f96dcSApple OSS DistributionsThere is no exception, lot of errors and no output. So what next? 328*2c2f96dcSApple OSS DistributionsTry to narrow the problem down to an isolated piece of macro code: 329*2c2f96dcSApple OSS Distributions 330*2c2f96dcSApple OSS Distributions 1. Try to get values of globals through regular LLDB commands 331*2c2f96dcSApple OSS Distributions 2. Use interactive mode and invoke functions with arguments directly. 332*2c2f96dcSApple OSS Distributions 333*2c2f96dcSApple OSS DistributionsAfter inspecting addkext macro code and calling few functions with arguments directly we can see that there is an 334*2c2f96dcSApple OSS Distributionsexception in the end. It was just captured in try/catch block. So the simplified reproducer is: 335*2c2f96dcSApple OSS Distributions 336*2c2f96dcSApple OSS Distributions (lldb) script 337*2c2f96dcSApple OSS Distributions >>> import lldb 338*2c2f96dcSApple OSS Distributions >>> import xnu 339*2c2f96dcSApple OSS Distributions >>> err = lldb.SBError() 340*2c2f96dcSApple OSS Distributions >>> data = xnu.LazyTarget.GetProcess().ReadMemory(0xfffffe0014c0f3f0, 0x000000000001b5d0, err) 341*2c2f96dcSApple OSS Distributions >>> m = macho.MemMacho(data, len(data)) 342*2c2f96dcSApple OSS Distributions Traceback (most recent call last): 343*2c2f96dcSApple OSS Distributions File "<console>", line 1, in <module> 344*2c2f96dcSApple OSS Distributions File ".../lldbmacros/macho.py", line 91, in __init__ 345*2c2f96dcSApple OSS Distributions self.load(fp) 346*2c2f96dcSApple OSS Distributions File ".../site-packages/macholib/MachO.py", line 133, in load 347*2c2f96dcSApple OSS Distributions self.load_header(fh, 0, size) 348*2c2f96dcSApple OSS Distributions File ".../site-packages/macholib/MachO.py", line 168, in load_header 349*2c2f96dcSApple OSS Distributions hdr = MachOHeader(self, fh, offset, size, magic, hdr, endian) 350*2c2f96dcSApple OSS Distributions File ".../site-packages/macholib/MachO.py", line 209, in __init__ 351*2c2f96dcSApple OSS Distributions self.load(fh) 352*2c2f96dcSApple OSS Distributions File ".../lldbmacros/macho.py", line 23, in new_load 353*2c2f96dcSApple OSS Distributions _old_MachOHeader_load(s, fh) 354*2c2f96dcSApple OSS Distributions File ".../site-packages/macholib/MachO.py", line 287, in load 355*2c2f96dcSApple OSS Distributions fh.seek(seg.offset) 356*2c2f96dcSApple OSS Distributions File ".../site-packages/macholib/util.py", line 91, in seek 357*2c2f96dcSApple OSS Distributions self._checkwindow(seekto, "seek") 358*2c2f96dcSApple OSS Distributions File ".../site-packages/macholib/util.py", line 76, in _checkwindow 359*2c2f96dcSApple OSS Distributions raise IOError( 360*2c2f96dcSApple OSS Distributions OSError: seek to offset 9042440 is outside window [0, 112080] 361*2c2f96dcSApple OSS Distributions 362*2c2f96dcSApple OSS DistributionsClearly an external library is involved and execution flow jumps between dSYM and the library few times. 363*2c2f96dcSApple OSS DistributionsLets try to look around with a debugger. 364*2c2f96dcSApple OSS Distributions 365*2c2f96dcSApple OSS Distributions (lldb) script 366*2c2f96dcSApple OSS Distributions # Prepare data variable as described above. 367*2c2f96dcSApple OSS Distributions 368*2c2f96dcSApple OSS Distributions # Run last statement with debugger. 369*2c2f96dcSApple OSS Distributions >>> import pdb 370*2c2f96dcSApple OSS Distributions >>> pdb.run('m = macho.MemMacho(data, len(data))', globals(), locals()) 371*2c2f96dcSApple OSS Distributions > <string>(1)<module>() 372*2c2f96dcSApple OSS Distributions 373*2c2f96dcSApple OSS Distributions # Show debugger's help 374*2c2f96dcSApple OSS Distributions (Pdb) help 375*2c2f96dcSApple OSS Distributions 376*2c2f96dcSApple OSS DistributionsIt is not possible to break on exception. Python uses them a lot so it is better to put a breakpoint to source 377*2c2f96dcSApple OSS Distributionscode. This puts breakpoint on the IOError exception mentioned above. 378*2c2f96dcSApple OSS Distributions 379*2c2f96dcSApple OSS Distributions (Pdb) break ~/Library/Python/3.8/lib/python/site-packages/macholib/util.py:76 380*2c2f96dcSApple OSS Distributions Breakpoint 4 at ~/Library/Python/3.8/lib/python/site-packages/macholib/util.py:76 381*2c2f96dcSApple OSS Distributions 382*2c2f96dcSApple OSS DistributionsYou can now single step or continue the execution as usuall for a debugger. 383*2c2f96dcSApple OSS Distributions 384*2c2f96dcSApple OSS Distributions (Pdb) cont 385*2c2f96dcSApple OSS Distributions > /Users/tjedlicka/Library/Python/3.8/lib/python/site-packages/macholib/util.py(76)_checkwindow() 386*2c2f96dcSApple OSS Distributions -> raise IOError( 387*2c2f96dcSApple OSS Distributions (Pdb) bt 388*2c2f96dcSApple OSS Distributions /Volumes/.../Python3.framework/Versions/3.8/lib/python3.8/bdb.py(580)run() 389*2c2f96dcSApple OSS Distributions -> exec(cmd, globals, locals) 390*2c2f96dcSApple OSS Distributions <string>(1)<module>() 391*2c2f96dcSApple OSS Distributions /Volumes/...dSYM/Contents/Resources/Python/lldbmacros/macho.py(91)__init__() 392*2c2f96dcSApple OSS Distributions -> self.load(fp) 393*2c2f96dcSApple OSS Distributions /Users/.../Library/Python/3.8/lib/python/site-packages/macholib/MachO.py(133)load() 394*2c2f96dcSApple OSS Distributions -> self.load_header(fh, 0, size) 395*2c2f96dcSApple OSS Distributions /Users/.../Library/Python/3.8/lib/python/site-packages/macholib/MachO.py(168)load_header() 396*2c2f96dcSApple OSS Distributions -> hdr = MachOHeader(self, fh, offset, size, magic, hdr, endian) 397*2c2f96dcSApple OSS Distributions /Users/.../Library/Python/3.8/lib/python/site-packages/macholib/MachO.py(209)__init__() 398*2c2f96dcSApple OSS Distributions -> self.load(fh) 399*2c2f96dcSApple OSS Distributions /Volumes/...dSYM/Contents/Resources/Python/lldbmacros/macho.py(23)new_load() 400*2c2f96dcSApple OSS Distributions -> _old_MachOHeader_load(s, fh) 401*2c2f96dcSApple OSS Distributions /Users/.../Library/Python/3.8/lib/python/site-packages/macholib/MachO.py(287)load() 402*2c2f96dcSApple OSS Distributions -> fh.seek(seg.offset) 403*2c2f96dcSApple OSS Distributions /Users/.../Library/Python/3.8/lib/python/site-packages/macholib/util.py(91)seek() 404*2c2f96dcSApple OSS Distributions -> self._checkwindow(seekto, "seek") 405*2c2f96dcSApple OSS Distributions > /Users/.../Library/Python/3.8/lib/python/site-packages/macholib/util.py(76)_checkwindow() 406*2c2f96dcSApple OSS Distributions -> raise IOError( 407*2c2f96dcSApple OSS Distributions 408*2c2f96dcSApple OSS Distributions 409*2c2f96dcSApple OSS DistributionsNow we can move a frame above and inspect stopped target: 410*2c2f96dcSApple OSS Distributions 411*2c2f96dcSApple OSS Distributions # Show current frame arguments 412*2c2f96dcSApple OSS Distributions (Pdb) up 413*2c2f96dcSApple OSS Distributions (Pdb) a 414*2c2f96dcSApple OSS Distributions self = <fileview [0, 112080] <macho.MemFile object at 0x1075cafd0>> 415*2c2f96dcSApple OSS Distributions offset = 9042440 416*2c2f96dcSApple OSS Distributions whence = 0 417*2c2f96dcSApple OSS Distributions 418*2c2f96dcSApple OSS Distributions # globals, local or expressons 419*2c2f96dcSApple OSS Distributions (Pdb) p type(seg.offset) 420*2c2f96dcSApple OSS Distributions <class 'macholib.ptypes.p_uint32'> 421*2c2f96dcSApple OSS Distributions (Pdb) p hex(seg.offset) 422*2c2f96dcSApple OSS Distributions '0x89fa08' 423*2c2f96dcSApple OSS Distributions 424*2c2f96dcSApple OSS Distributions # Find attributes of a Python object. 425*2c2f96dcSApple OSS Distributions (Pdb) p dir(section_cls) 426*2c2f96dcSApple OSS Distributions ['__class__', '__cmp__', ... ,'reserved3', 'sectname', 'segname', 'size', 'to_fileobj', 'to_mmap', 'to_str'] 427*2c2f96dcSApple OSS Distributions (Pdb) p section_cls.sectname 428*2c2f96dcSApple OSS Distributions <property object at 0x1077bbef0> 429*2c2f96dcSApple OSS Distributions 430*2c2f96dcSApple OSS DistributionsUnfortunately everything looks correct but there is actually one ineteresting frame in the stack. The one which 431*2c2f96dcSApple OSS Distributionsprovides the offset to the seek method. Lets see where we are in the source code. 432*2c2f96dcSApple OSS Distributions 433*2c2f96dcSApple OSS Distributions (Pdb) up 434*2c2f96dcSApple OSS Distributions > /Users/tjedlicka/Library/Python/3.8/lib/python/site-packages/macholib/MachO.py(287)load() 435*2c2f96dcSApple OSS Distributions -> fh.seek(seg.offset) 436*2c2f96dcSApple OSS Distributions (Pdb) list 437*2c2f96dcSApple OSS Distributions 282 not_zerofill = (seg.flags & S_ZEROFILL) != S_ZEROFILL 438*2c2f96dcSApple OSS Distributions 283 if seg.offset > 0 and seg.size > 0 and not_zerofill: 439*2c2f96dcSApple OSS Distributions 284 low_offset = min(low_offset, seg.offset) 440*2c2f96dcSApple OSS Distributions 285 if not_zerofill: 441*2c2f96dcSApple OSS Distributions 286 c = fh.tell() 442*2c2f96dcSApple OSS Distributions 287 -> fh.seek(seg.offset) 443*2c2f96dcSApple OSS Distributions 288 sd = fh.read(seg.size) 444*2c2f96dcSApple OSS Distributions 289 seg.add_section_data(sd) 445*2c2f96dcSApple OSS Distributions 290 fh.seek(c) 446*2c2f96dcSApple OSS Distributions 291 segs.append(seg) 447*2c2f96dcSApple OSS Distributions 292 # data is a list of segments 448*2c2f96dcSApple OSS Distributions 449*2c2f96dcSApple OSS DistributionsRunning debugger on working case and stepping through the load() method shows that this code is not present. 450*2c2f96dcSApple OSS DistributionsThat means we are broken by a library update! Older versions of library do not load data for a section. 451