xref: /xnu-11215/doc/debugging/debugging.md (revision 8d741a5d)
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