xref: /xnu-10063.121.3/doc/debugging/debugging.md (revision 2c2f96dc2b9a4408a43d3150ae9c105355ca3daa)
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