194d3b452SApple OSS Distributions# XNU debugging 294d3b452SApple OSS Distributions 3*8d741a5dSApple OSS DistributionsDebugging XNU through kernel core files or with a live device. 494d3b452SApple OSS Distributions 594d3b452SApple OSS Distributions## Overview 694d3b452SApple OSS Distributions 7*8d741a5dSApple OSS DistributionsXNU’s debugging macros are compatible with Python 3.9+. Please be careful about pulling 894d3b452SApple OSS Distributionsin the latest language features. Some users are living on older Xcodes and may not have the newest 994d3b452SApple OSS DistributionsPython installed. 1094d3b452SApple OSS Distributions 1194d3b452SApple OSS Distributions## General coding tips 1294d3b452SApple OSS Distributions 1394d3b452SApple OSS Distributions### Imports 1494d3b452SApple OSS Distributions 1594d3b452SApple OSS DistributionsThe current implementation re-exports a lot of submodules through the XNU main module. This leads to some 1694d3b452SApple OSS Distributionssurprising behavior: 1794d3b452SApple OSS Distributions 1894d3b452SApple OSS Distributions* Name collisions at the top level may override methods with unexpected results. 1994d3b452SApple OSS Distributions* New imports may change the order of imports, leading to some surpising side effects. 2094d3b452SApple OSS Distributions 2194d3b452SApple OSS DistributionsPlease avoid `from xnu import *` where possible and always explicitly import only what is 2294d3b452SApple OSS Distributionsrequired from other modules. 2394d3b452SApple OSS Distributions 2494d3b452SApple OSS Distributions### Checking the type of an object 2594d3b452SApple OSS Distributions 2694d3b452SApple OSS DistributionsAvoid testing for a `type` explicitly like `type(obj) == type`. 2794d3b452SApple OSS DistributionsInstead, always use the inheritance-sensitive `isinstance(obj, type)`. 2894d3b452SApple OSS Distributions 2994d3b452SApple OSS Distributions### Dealing with binary data 3094d3b452SApple OSS Distributions 3194d3b452SApple OSS DistributionsIt’s recommended to use **bytearray**, **bytes**, and **memoryviews** instead of a string. 3294d3b452SApple OSS DistributionsSome LLDB APIs no longer accept a string in place of binary data in Python 3. 3394d3b452SApple OSS Distributions 3494d3b452SApple OSS Distributions### Accessing large amounts of binary data (or accessing small amounts frequently) 3594d3b452SApple OSS Distributions 3694d3b452SApple OSS DistributionsIn case you're planning on accessing large contiguous blocks of memory (e.g. reading a whole 10KB of memory), 3794d3b452SApple OSS Distributionsor you're accessing small semi-contiguous chunks (e.g. if you're parsing large structured data), then it might 3894d3b452SApple OSS Distributionsbe hugely beneficial performance-wise to make use of the `io.SBProcessRawIO` class. Furthermore, if you're in 3994d3b452SApple OSS Distributionsa hurry and just want to read one specific chunk once, then it might be easier to use `LazyTarget.GetProcess().ReadMemory()` 4094d3b452SApple OSS Distributionsdirectly. 4194d3b452SApple OSS Distributions 4294d3b452SApple OSS DistributionsIn other words, avoid the following: 4394d3b452SApple OSS Distributions 4494d3b452SApple OSS Distributions``` 4594d3b452SApple OSS Distributionsdata_ptr = kern.GetValueFromAddress(start_addr, 'uint8_t *') 4694d3b452SApple OSS Distributionswith open(filepath, 'wb') as f: 4794d3b452SApple OSS Distributions f.write(data_ptr[:4096]) 4894d3b452SApple OSS Distributions``` 4994d3b452SApple OSS Distributions 5094d3b452SApple OSS DistributionsAnd instead use: 5194d3b452SApple OSS Distributions 5294d3b452SApple OSS Distributions``` 5394d3b452SApple OSS Distributionsfrom core.io import SBProcessRawIO 5494d3b452SApple OSS Distributionsimport shutil 5594d3b452SApple OSS Distributions 5694d3b452SApple OSS Distributionsio_access = SBProcessRawIO(LazyTarget.GetProcess(), start_addr, 4096) 5794d3b452SApple OSS Distributionswith open(filepath, 'wb') as f: 5894d3b452SApple OSS Distributions shutil.copyfileobj(io_access, f) 5994d3b452SApple OSS Distributions``` 6094d3b452SApple OSS Distributions 6194d3b452SApple OSS DistributionsOr, if you're in a hurry: 6294d3b452SApple OSS Distributions 6394d3b452SApple OSS Distributions``` 6494d3b452SApple OSS Distributionserr = lldb.SBError() 6594d3b452SApple OSS Distributionsmy_data = LazyTarget.GetProcess().ReadMemory(start_addr, length, err) 6694d3b452SApple OSS Distributionsif err.Success(): 6794d3b452SApple OSS Distributions # Use my precious data 6894d3b452SApple OSS Distributions pass 6994d3b452SApple OSS Distributions``` 7094d3b452SApple OSS Distributions 7194d3b452SApple OSS DistributionsFor small semi-contiguous chunks, you can map the whole region and access random chunks from it like so: 7294d3b452SApple OSS Distributions 7394d3b452SApple OSS Distributions``` 7494d3b452SApple OSS Distributionsfrom core.io import SBProcessRawIO 7594d3b452SApple OSS Distributions 7694d3b452SApple OSS Distributionsio_access = SBProcessRawIO(LazyTarget.GetProcess(), start_addr, size) 7794d3b452SApple OSS Distributionsio_access.seek(my_struct_offset) 7894d3b452SApple OSS Distributionsmy_struct_contents = io_access.read(my_struct_size) 7994d3b452SApple OSS Distributions``` 8094d3b452SApple OSS Distributions 8194d3b452SApple OSS DistributionsNot only that, but you can also tack on a BufferedRandom class on top of the SBProcessRawIO instance, which 8294d3b452SApple OSS Distributionsprovides you with buffering (aka caching) in case your random small chunk accesses are repeated: 8394d3b452SApple OSS Distributions 8494d3b452SApple OSS Distributions``` 8594d3b452SApple OSS Distributionsfrom core.io import SBProcessRawIO 8694d3b452SApple OSS Distributionsfrom io import BufferedRandom 8794d3b452SApple OSS Distributions 8894d3b452SApple OSS Distributionsio_access = SBProcessRawIO(LazyTarget.GetProcess(), start_addr, size) 8994d3b452SApple OSS Distributionsbuffered_io = BufferedRandom(io_access) 9094d3b452SApple OSS Distributions# And then use buffered_io for your accesses 9194d3b452SApple OSS Distributions``` 9294d3b452SApple OSS Distributions 9394d3b452SApple OSS Distributions### Encoding data to strings and back 9494d3b452SApple OSS Distributions 9594d3b452SApple OSS DistributionsAll strings are now `unicode` and must be converted between binary data and strings explicitly. 9694d3b452SApple OSS DistributionsWhen no explicit encoding is selected then UTF-8 is the default. 9794d3b452SApple OSS Distributions 9894d3b452SApple OSS Distributions``` 9994d3b452SApple OSS Distributionsmystring = mybytes.decode() 10094d3b452SApple OSS Distributionsmybytes = mystring.encode() 10194d3b452SApple OSS Distributions``` 10294d3b452SApple OSS DistributionsIn most cases **utf-8** will work but be careful to be sure that the encoding matches your data. 10394d3b452SApple OSS Distributions 10494d3b452SApple OSS DistributionsThere are two options to consider when trying to get a string out of the raw data without knowing if 10594d3b452SApple OSS Distributionsthey are valid string or not: 10694d3b452SApple OSS Distributions 10794d3b452SApple OSS Distributions* **lossy conversion** - escapes all non-standard characters in form of ‘\xNNN’ 10894d3b452SApple OSS Distributions* **lossless conversion** - maps invalid characters to special unicode range so it can reconstruct 10994d3b452SApple OSS Distributionsthe string precisely 11094d3b452SApple OSS Distributions 11194d3b452SApple OSS DistributionsWhich to use depends on the transformation goals. The lossy conversion produces a printable string 11294d3b452SApple OSS Distributionswith strange characters in it. The lossless option is meant to be used when a string is only a transport 11394d3b452SApple OSS Distributionsmechanism and needs to be converted back to original values later. 11494d3b452SApple OSS Distributions 11594d3b452SApple OSS DistributionsSwitch the method by using `errors` handler during conversion: 11694d3b452SApple OSS Distributions 11794d3b452SApple OSS Distributions``` 11894d3b452SApple OSS Distributions# Lossy escapes invalid chars 11994d3b452SApple OSS Distributionsb.decode('utf-8', errors='`backslashreplace'`) 12094d3b452SApple OSS Distributions# Lossy removes invalid chars 12194d3b452SApple OSS Distributionsb.decode('utf-8', errors='ignore') 12294d3b452SApple OSS Distributions# Loss-less but may likely fail to print() 12394d3b452SApple OSS Distributionsb.decode('utf-8', errors='surrogateescape') 12494d3b452SApple OSS Distributions``` 12594d3b452SApple OSS Distributions 12694d3b452SApple OSS Distributions### Dealing with signed numbers 12794d3b452SApple OSS Distributions 12894d3b452SApple OSS DistributionsPython's int has unlimited precision. This may be surprising for kernel developers who expect 12994d3b452SApple OSS Distributionsthe behavior follows twos complement. 13094d3b452SApple OSS Distributions 13194d3b452SApple OSS DistributionsAlways use **unsigned()** or **signed()** regardless of what the actual underlying type is 13294d3b452SApple OSS Distributionsto ensure that macros use the correct semantics. 13394d3b452SApple OSS Distributions 13494d3b452SApple OSS Distributions## Testing changes 13594d3b452SApple OSS Distributions 136*8d741a5dSApple OSS DistributionsPlease check documentation here: <doc:macro_testing> 13794d3b452SApple OSS Distributions 13894d3b452SApple OSS Distributions### Coding style 13994d3b452SApple OSS Distributions 14094d3b452SApple OSS DistributionsUse a static analyzer like **pylint** or **flake8** to check the macro source code: 14194d3b452SApple OSS Distributions 14294d3b452SApple OSS Distributions``` 14394d3b452SApple OSS Distributions$ python3 -m pip install --user pylint flake8 14494d3b452SApple OSS Distributions 14594d3b452SApple OSS Distributions# Run the lint either by setting your path to point to one of the runtimes 14694d3b452SApple OSS Distributions# or through python 14794d3b452SApple OSS Distributions$ python3 -m pylint <src files/dirs> 14894d3b452SApple OSS Distributions$ python3 -m flake8 <src files/dirs> 14994d3b452SApple OSS Distributions``` 15094d3b452SApple OSS Distributions 15194d3b452SApple OSS Distributions### Correctness 15294d3b452SApple OSS Distributions 15394d3b452SApple OSS DistributionsEnsure the macro matches what LLDB returns from the REPL. For example, compare `showproc(xxx)` with `p/x *(proc_t)xxx`. 15494d3b452SApple OSS Distributions 15594d3b452SApple OSS Distributions``` 15694d3b452SApple OSS Distributions# 1. Run LLDB with debug options set 15794d3b452SApple OSS Distributions$ DEBUG_XNU_LLDBMACROS=1 xcrun -sdk <sdk> lldb -c core <dsympath>/mach_kernel 15894d3b452SApple OSS Distributions 15994d3b452SApple OSS Distributions# 2. Optionally load modified operating system plugin 16094d3b452SApple OSS Distributions(lldb) settings set target.process.python-os-plugin-path <srcpath>/tools/lldbmacros/core/operating_system.py 16194d3b452SApple OSS Distributions 16294d3b452SApple OSS Distributions# 3. Load modified scripts 16394d3b452SApple OSS Distributions(lldb) command script import <srcpath>/tools/lldbmacros/xnu.py 16494d3b452SApple OSS Distributions 16594d3b452SApple OSS Distributions# 4. Exercise macros 16694d3b452SApple OSS Distributions``` 16794d3b452SApple OSS Distributions 16894d3b452SApple OSS DistributionsDepending on the change, test other targets and architectures (for instance, both Astris and KDP). 16994d3b452SApple OSS Distributions 17094d3b452SApple OSS Distributions### Regression 17194d3b452SApple OSS Distributions 17294d3b452SApple OSS DistributionsThis is simpler than previous step because the goal is to ensure behavior has not changed. 17394d3b452SApple OSS DistributionsYou can speed up few things by using local symbols: 17494d3b452SApple OSS Distributions 17594d3b452SApple OSS Distributions``` 17694d3b452SApple OSS Distributions# 1. Get a coredump from a device and kernel UUID 17794d3b452SApple OSS Distributions# 2. Grab symbols with dsymForUUID 17894d3b452SApple OSS Distributions$ dsymForUUID --nocache --copyExecutable --copyDestination <dsym path> 17994d3b452SApple OSS Distributions 18094d3b452SApple OSS Distributions# 3. Run lldb with local symbols to avoid dsymForUUID NFS 18194d3b452SApple OSS Distributions 18294d3b452SApple OSS Distributions$ xcrun -sdk <sdk> lldb -c core <dsym_path>/<kernel image> 18394d3b452SApple OSS Distributions``` 18494d3b452SApple OSS Distributions 18594d3b452SApple OSS DistributionsThe actual steps are identical to previous testing. Run of a macro to different file with `-o <outfile>` 18694d3b452SApple OSS Distributionsoption. Then run `diff` on the outputs of the baseline and modified code: 18794d3b452SApple OSS Distributions 18894d3b452SApple OSS Distributions* No environment variables to get baseline 18994d3b452SApple OSS Distributions* Modified dSYM as described above 19094d3b452SApple OSS Distributions 19194d3b452SApple OSS DistributionsIt’s difficult to make this automated: 19294d3b452SApple OSS Distributions 19394d3b452SApple OSS Distributions* Some macros needs arguments which must be found in a core file. 19494d3b452SApple OSS Distributions* Some macros take a long time to run against a target (more than 30 minutes). Instead, a core dump 19594d3b452SApple OSS Distributions should be taken and then inspected afterwards, but this ties up a lab device for the duration of the 19694d3b452SApple OSS Distributions test. 19794d3b452SApple OSS Distributions* Even with coredumps, testing the macros takes too long in our automation system and triggers the 19894d3b452SApple OSS Distributions failsafe timeout. 19994d3b452SApple OSS Distributions 20094d3b452SApple OSS Distributions### Code coverage 20194d3b452SApple OSS Distributions 20294d3b452SApple OSS DistributionsUse code coverage to check which parts of macros have actually been tested. 20394d3b452SApple OSS DistributionsInstall **coverage** lib with: 20494d3b452SApple OSS Distributions 20594d3b452SApple OSS Distributions``` 20694d3b452SApple OSS Distributions$ python3 -m pip install --user coverage 20794d3b452SApple OSS Distributions``` 20894d3b452SApple OSS Distributions 20994d3b452SApple OSS DistributionsThen collect coverage:. 21094d3b452SApple OSS Distributions 21194d3b452SApple OSS Distributions``` 212*8d741a5dSApple OSS Distributions(lldb) xnudebug coverage /tmp/coverage.cov showallstacks 21394d3b452SApple OSS Distributions 214*8d741a5dSApple OSS Distributions... 21594d3b452SApple OSS Distributions 216*8d741a5dSApple OSS DistributionsCoverage info saved to: "/tmp/coverage.cov" 21794d3b452SApple OSS Distributions``` 21894d3b452SApple OSS Distributions 219*8d741a5dSApple OSS DistributionsYou can then run `coverage html --data-file=/tmp/coverage.cov` in your terminal 220*8d741a5dSApple OSS Distributionsto generate an HTML report. 22194d3b452SApple OSS Distributions 22294d3b452SApple OSS Distributions 22394d3b452SApple OSS DistributionsCombine coverage from multiple files: 22494d3b452SApple OSS Distributions 22594d3b452SApple OSS Distributions``` 22694d3b452SApple OSS Distributions# Point PATH to local python where coverage is installed. 22794d3b452SApple OSS Distributions$ export PATH="$HOME/Library/Python/3.8/bin:$PATH" 22894d3b452SApple OSS Distributions 22994d3b452SApple OSS Distributions# Use --keep to avoid deletion of input files after merge. 23094d3b452SApple OSS Distributions$ coverage combine --keep <list of .coverage files or dirs to scan> 23194d3b452SApple OSS Distributions 23294d3b452SApple OSS Distributions# Get HTML report or use other subcommands to inspect. 23394d3b452SApple OSS Distributions$ coverage html 23494d3b452SApple OSS Distributions``` 23594d3b452SApple OSS Distributions 23694d3b452SApple OSS DistributionsIt is possible to start coverage collection **before** importing the operating system library and 23794d3b452SApple OSS Distributionsloading macros to check code run during bootstrapping. 23894d3b452SApple OSS Distributions 239*8d741a5dSApple OSS DistributionsFor this, you'll need to run coverage manually: 240*8d741a5dSApple OSS Distributions# 1. Start LLDB 241*8d741a5dSApple OSS Distributions 242*8d741a5dSApple OSS Distributions# 2. Load and start code coverage recording. 243*8d741a5dSApple OSS Distributions(lldb) script import coverage 244*8d741a5dSApple OSS Distributions(lldb) script cov = coverage.Coverage(data_file=_filepath_) 245*8d741a5dSApple OSS Distributions(lldb) script cov.start() 246*8d741a5dSApple OSS Distributions 247*8d741a5dSApple OSS Distributions# 3. Load macros 248*8d741a5dSApple OSS Distributions 249*8d741a5dSApple OSS Distributions# 4. Collect the coverage. 250*8d741a5dSApple OSS Distributions(lldb) script cov.stop() 251*8d741a5dSApple OSS Distributions(lldb) script cov.save() 252*8d741a5dSApple OSS Distributions 25394d3b452SApple OSS Distributions### Performance testing 25494d3b452SApple OSS Distributions 25594d3b452SApple OSS DistributionsSome macros can run for a long time. Some code may be costly even if it looks simple because objects 25694d3b452SApple OSS Distributionsaren’t cached or too many temporary objects are created. Simple profiling is similar to collecting 25794d3b452SApple OSS Distributionscode coverage. 25894d3b452SApple OSS Distributions 25994d3b452SApple OSS DistributionsFirst setup your environment: 26094d3b452SApple OSS Distributions 26194d3b452SApple OSS Distributions``` 26294d3b452SApple OSS Distributions# Install gprof2dot 26394d3b452SApple OSS Distributions$ python3 -m pip install gprof2dot 26494d3b452SApple OSS Distributions# Install graphviz 26594d3b452SApple OSS Distributions$ brew install graphviz 26694d3b452SApple OSS Distributions``` 26794d3b452SApple OSS Distributions 26894d3b452SApple OSS DistributionsThen to profile commands, follow this sequence: 26994d3b452SApple OSS Distributions 27094d3b452SApple OSS Distributions``` 27194d3b452SApple OSS Distributions(lldb) xnudebug profile /tmp/macro.prof showcurrentstacks 27294d3b452SApple OSS Distributions[... command outputs ...] 27394d3b452SApple OSS Distributions 27494d3b452SApple OSS Distributions Ordered by: cumulative time 27594d3b452SApple OSS Distributions List reduced from 468 to 30 due to restriction <30> 27694d3b452SApple OSS Distributions 27794d3b452SApple OSS Distributions ncalls tottime percall cumtime percall filename:lineno(function) 27894d3b452SApple OSS Distributions [... profiling output ...] 27994d3b452SApple OSS Distributions 28094d3b452SApple OSS DistributionsProfile info saved to "/tmp/macro.prof" 28194d3b452SApple OSS Distributions``` 28294d3b452SApple OSS Distributions 28394d3b452SApple OSS DistributionsThen to visualize callgraphs in context, in a separate shell: 28494d3b452SApple OSS Distributions 28594d3b452SApple OSS Distributions``` 28694d3b452SApple OSS Distributions# Now convert the file to a colored SVG call graph 28794d3b452SApple OSS Distributions$ python3 -m gprof2dot -f pstats /tmp/macro.prof -o /tmp/call.dot 28894d3b452SApple OSS Distributions$ dot -O -T svg /tmp/call.dot 28994d3b452SApple OSS Distributions 29094d3b452SApple OSS Distributions# and view it in your favourite viewer 29194d3b452SApple OSS Distributions$ open /tmp/call.dot.svg 29294d3b452SApple OSS Distributions``` 29394d3b452SApple OSS Distributions 29494d3b452SApple OSS Distributions## Debugging your changes 29594d3b452SApple OSS Distributions 29694d3b452SApple OSS Distributions### Get detailed exception report 29794d3b452SApple OSS Distributions 29894d3b452SApple OSS DistributionsThe easiest way to debug an exception is to re-run your macro with the `--debug` option. 29994d3b452SApple OSS DistributionsThis turns on more detailed output for each stack frame that includes source lines 30094d3b452SApple OSS Distributionsand local variables. 30194d3b452SApple OSS Distributions 30294d3b452SApple OSS Distributions### File a radar 30394d3b452SApple OSS Distributions 30494d3b452SApple OSS DistributionsTo report an actionable radar, please use re-run your failing macro with `--radar`. 30594d3b452SApple OSS DistributionsThis will collect additional logs to an archive located in `/tmp`. 30694d3b452SApple OSS Distributions 30794d3b452SApple OSS DistributionsUse the link provided to create a new radar. 30894d3b452SApple OSS Distributions 30994d3b452SApple OSS Distributions### Debugging with pdb 31094d3b452SApple OSS Distributions 31194d3b452SApple OSS DistributionsYES, It is possible to use a debugger to debug your macro! 31294d3b452SApple OSS Distributions 31394d3b452SApple OSS DistributionsThe steps are similar to testing techniques described above (use scripting interactive mode). There is no point to 31494d3b452SApple OSS Distributionsdocument the debugger itself. Lets focus on how to use it on a real life example. The debugger used here is PDB which 31594d3b452SApple OSS Distributionsis part of Python installation so works out of the box. 31694d3b452SApple OSS Distributions 31794d3b452SApple OSS DistributionsProblem: Something wrong is going on with addkext macro. What now? 31894d3b452SApple OSS Distributions 31994d3b452SApple OSS Distributions (lldb) addkext -N com.apple.driver.AppleT8103PCIeC 32094d3b452SApple OSS Distributions Failed to read MachO for address 18446741875027613136 errormessage: seek to offset 2169512 is outside window [0, 1310] 32194d3b452SApple OSS Distributions Failed to read MachO for address 18446741875033537424 errormessage: seek to offset 8093880 is outside window [0, 1536] 32294d3b452SApple OSS Distributions Failed to read MachO for address 18446741875033568304 errormessage: seek to offset 8124208 is outside window [0, 1536] 32394d3b452SApple OSS Distributions ... 32494d3b452SApple OSS Distributions Fetching dSYM for 049b9a29-2efc-32c0-8a7f-5f29c12b870c 32594d3b452SApple 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 32694d3b452SApple OSS Distributions section '__TEXT' loaded at 0xfffffe001478c780 32794d3b452SApple OSS Distributions 32894d3b452SApple OSS DistributionsThere is no exception, lot of errors and no output. So what next? 32994d3b452SApple OSS DistributionsTry to narrow the problem down to an isolated piece of macro code: 33094d3b452SApple OSS Distributions 33194d3b452SApple OSS Distributions 1. Try to get values of globals through regular LLDB commands 33294d3b452SApple OSS Distributions 2. Use interactive mode and invoke functions with arguments directly. 33394d3b452SApple OSS Distributions 33494d3b452SApple OSS DistributionsAfter inspecting addkext macro code and calling few functions with arguments directly we can see that there is an 33594d3b452SApple OSS Distributionsexception in the end. It was just captured in try/catch block. So the simplified reproducer is: 33694d3b452SApple OSS Distributions 33794d3b452SApple OSS Distributions (lldb) script 33894d3b452SApple OSS Distributions >>> import lldb 33994d3b452SApple OSS Distributions >>> import xnu 34094d3b452SApple OSS Distributions >>> err = lldb.SBError() 34194d3b452SApple OSS Distributions >>> data = xnu.LazyTarget.GetProcess().ReadMemory(0xfffffe0014c0f3f0, 0x000000000001b5d0, err) 34294d3b452SApple OSS Distributions >>> m = macho.MemMacho(data, len(data)) 34394d3b452SApple OSS Distributions Traceback (most recent call last): 34494d3b452SApple OSS Distributions File "<console>", line 1, in <module> 34594d3b452SApple OSS Distributions File ".../lldbmacros/macho.py", line 91, in __init__ 34694d3b452SApple OSS Distributions self.load(fp) 34794d3b452SApple OSS Distributions File ".../site-packages/macholib/MachO.py", line 133, in load 34894d3b452SApple OSS Distributions self.load_header(fh, 0, size) 34994d3b452SApple OSS Distributions File ".../site-packages/macholib/MachO.py", line 168, in load_header 35094d3b452SApple OSS Distributions hdr = MachOHeader(self, fh, offset, size, magic, hdr, endian) 35194d3b452SApple OSS Distributions File ".../site-packages/macholib/MachO.py", line 209, in __init__ 35294d3b452SApple OSS Distributions self.load(fh) 35394d3b452SApple OSS Distributions File ".../lldbmacros/macho.py", line 23, in new_load 35494d3b452SApple OSS Distributions _old_MachOHeader_load(s, fh) 35594d3b452SApple OSS Distributions File ".../site-packages/macholib/MachO.py", line 287, in load 35694d3b452SApple OSS Distributions fh.seek(seg.offset) 35794d3b452SApple OSS Distributions File ".../site-packages/macholib/util.py", line 91, in seek 35894d3b452SApple OSS Distributions self._checkwindow(seekto, "seek") 35994d3b452SApple OSS Distributions File ".../site-packages/macholib/util.py", line 76, in _checkwindow 36094d3b452SApple OSS Distributions raise IOError( 36194d3b452SApple OSS Distributions OSError: seek to offset 9042440 is outside window [0, 112080] 36294d3b452SApple OSS Distributions 36394d3b452SApple OSS DistributionsClearly an external library is involved and execution flow jumps between dSYM and the library few times. 36494d3b452SApple OSS DistributionsLets try to look around with a debugger. 36594d3b452SApple OSS Distributions 36694d3b452SApple OSS Distributions (lldb) script 36794d3b452SApple OSS Distributions # Prepare data variable as described above. 36894d3b452SApple OSS Distributions 36994d3b452SApple OSS Distributions # Run last statement with debugger. 37094d3b452SApple OSS Distributions >>> import pdb 37194d3b452SApple OSS Distributions >>> pdb.run('m = macho.MemMacho(data, len(data))', globals(), locals()) 37294d3b452SApple OSS Distributions > <string>(1)<module>() 37394d3b452SApple OSS Distributions 37494d3b452SApple OSS Distributions # Show debugger's help 37594d3b452SApple OSS Distributions (Pdb) help 37694d3b452SApple OSS Distributions 37794d3b452SApple OSS DistributionsIt is not possible to break on exception. Python uses them a lot so it is better to put a breakpoint to source 37894d3b452SApple OSS Distributionscode. This puts breakpoint on the IOError exception mentioned above. 37994d3b452SApple OSS Distributions 38094d3b452SApple OSS Distributions (Pdb) break ~/Library/Python/3.8/lib/python/site-packages/macholib/util.py:76 38194d3b452SApple OSS Distributions Breakpoint 4 at ~/Library/Python/3.8/lib/python/site-packages/macholib/util.py:76 38294d3b452SApple OSS Distributions 38394d3b452SApple OSS DistributionsYou can now single step or continue the execution as usuall for a debugger. 38494d3b452SApple OSS Distributions 38594d3b452SApple OSS Distributions (Pdb) cont 38694d3b452SApple OSS Distributions > /Users/tjedlicka/Library/Python/3.8/lib/python/site-packages/macholib/util.py(76)_checkwindow() 38794d3b452SApple OSS Distributions -> raise IOError( 38894d3b452SApple OSS Distributions (Pdb) bt 38994d3b452SApple OSS Distributions /Volumes/.../Python3.framework/Versions/3.8/lib/python3.8/bdb.py(580)run() 39094d3b452SApple OSS Distributions -> exec(cmd, globals, locals) 39194d3b452SApple OSS Distributions <string>(1)<module>() 39294d3b452SApple OSS Distributions /Volumes/...dSYM/Contents/Resources/Python/lldbmacros/macho.py(91)__init__() 39394d3b452SApple OSS Distributions -> self.load(fp) 39494d3b452SApple OSS Distributions /Users/.../Library/Python/3.8/lib/python/site-packages/macholib/MachO.py(133)load() 39594d3b452SApple OSS Distributions -> self.load_header(fh, 0, size) 39694d3b452SApple OSS Distributions /Users/.../Library/Python/3.8/lib/python/site-packages/macholib/MachO.py(168)load_header() 39794d3b452SApple OSS Distributions -> hdr = MachOHeader(self, fh, offset, size, magic, hdr, endian) 39894d3b452SApple OSS Distributions /Users/.../Library/Python/3.8/lib/python/site-packages/macholib/MachO.py(209)__init__() 39994d3b452SApple OSS Distributions -> self.load(fh) 40094d3b452SApple OSS Distributions /Volumes/...dSYM/Contents/Resources/Python/lldbmacros/macho.py(23)new_load() 40194d3b452SApple OSS Distributions -> _old_MachOHeader_load(s, fh) 40294d3b452SApple OSS Distributions /Users/.../Library/Python/3.8/lib/python/site-packages/macholib/MachO.py(287)load() 40394d3b452SApple OSS Distributions -> fh.seek(seg.offset) 40494d3b452SApple OSS Distributions /Users/.../Library/Python/3.8/lib/python/site-packages/macholib/util.py(91)seek() 40594d3b452SApple OSS Distributions -> self._checkwindow(seekto, "seek") 40694d3b452SApple OSS Distributions > /Users/.../Library/Python/3.8/lib/python/site-packages/macholib/util.py(76)_checkwindow() 40794d3b452SApple OSS Distributions -> raise IOError( 40894d3b452SApple OSS Distributions 40994d3b452SApple OSS Distributions 41094d3b452SApple OSS DistributionsNow we can move a frame above and inspect stopped target: 41194d3b452SApple OSS Distributions 41294d3b452SApple OSS Distributions # Show current frame arguments 41394d3b452SApple OSS Distributions (Pdb) up 41494d3b452SApple OSS Distributions (Pdb) a 41594d3b452SApple OSS Distributions self = <fileview [0, 112080] <macho.MemFile object at 0x1075cafd0>> 41694d3b452SApple OSS Distributions offset = 9042440 41794d3b452SApple OSS Distributions whence = 0 41894d3b452SApple OSS Distributions 41994d3b452SApple OSS Distributions # globals, local or expressons 42094d3b452SApple OSS Distributions (Pdb) p type(seg.offset) 42194d3b452SApple OSS Distributions <class 'macholib.ptypes.p_uint32'> 42294d3b452SApple OSS Distributions (Pdb) p hex(seg.offset) 42394d3b452SApple OSS Distributions '0x89fa08' 42494d3b452SApple OSS Distributions 42594d3b452SApple OSS Distributions # Find attributes of a Python object. 42694d3b452SApple OSS Distributions (Pdb) p dir(section_cls) 42794d3b452SApple OSS Distributions ['__class__', '__cmp__', ... ,'reserved3', 'sectname', 'segname', 'size', 'to_fileobj', 'to_mmap', 'to_str'] 42894d3b452SApple OSS Distributions (Pdb) p section_cls.sectname 42994d3b452SApple OSS Distributions <property object at 0x1077bbef0> 43094d3b452SApple OSS Distributions 43194d3b452SApple OSS DistributionsUnfortunately everything looks correct but there is actually one ineteresting frame in the stack. The one which 43294d3b452SApple OSS Distributionsprovides the offset to the seek method. Lets see where we are in the source code. 43394d3b452SApple OSS Distributions 43494d3b452SApple OSS Distributions (Pdb) up 43594d3b452SApple OSS Distributions > /Users/tjedlicka/Library/Python/3.8/lib/python/site-packages/macholib/MachO.py(287)load() 43694d3b452SApple OSS Distributions -> fh.seek(seg.offset) 43794d3b452SApple OSS Distributions (Pdb) list 43894d3b452SApple OSS Distributions 282 not_zerofill = (seg.flags & S_ZEROFILL) != S_ZEROFILL 43994d3b452SApple OSS Distributions 283 if seg.offset > 0 and seg.size > 0 and not_zerofill: 44094d3b452SApple OSS Distributions 284 low_offset = min(low_offset, seg.offset) 44194d3b452SApple OSS Distributions 285 if not_zerofill: 44294d3b452SApple OSS Distributions 286 c = fh.tell() 44394d3b452SApple OSS Distributions 287 -> fh.seek(seg.offset) 44494d3b452SApple OSS Distributions 288 sd = fh.read(seg.size) 44594d3b452SApple OSS Distributions 289 seg.add_section_data(sd) 44694d3b452SApple OSS Distributions 290 fh.seek(c) 44794d3b452SApple OSS Distributions 291 segs.append(seg) 44894d3b452SApple OSS Distributions 292 # data is a list of segments 44994d3b452SApple OSS Distributions 45094d3b452SApple OSS DistributionsRunning debugger on working case and stepping through the load() method shows that this code is not present. 45194d3b452SApple OSS DistributionsThat means we are broken by a library update! Older versions of library do not load data for a section. 452