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