11# Reproducing Runs
22
33It is unlikely you'll find any bugs with this harness (if you do, report them to edk2!),
4- but we can still test the "repro" functionality which allows you to replay an execution
5- of a testcase from an input file. After pressing Ctrl+C during execution, list the
6- corpus files (tip: ` ! ` in front of a line in the SIMICS console lets you run shell
7- commands):
4+ but we can still test the
5+ [ "repro" functionality] ( ../../fuzzing/analyzing-results.md )
6+ which allows you to replay an execution of a testcase from an input file.
7+
8+ ## Listing and Examining Testcases
9+
10+ After pressing Ctrl+C during execution, list the corpus files (tip: ` ! ` in front of a
11+ line in the SIMICS console lets you run shell commands):
812
913``` txt
1014simics> !ls corpus
@@ -30,8 +34,14 @@ We can tell the fuzzer that we want to run with this specific input by using:
3034simics> @tsffs.iface.fuzz.repro("%simics%/corpus/4385dc33f608888d")
3135```
3236
33- The simulation will run once with this input, then output a message that you can replay
34- the simulation by running:
37+ > ** Note:** You can change the testcase you are examining by choosing a different one
38+ > with ` tsffs.iface.fuzz.repro ` , but you cannot resume fuzzing after entering repro
39+ > mode due to inconsistencies with the simulated system clock.
40+
41+ ## Inspecting State with SIMICS Reverse Debugging
42+
43+ By default, the simulation runs the testcase through to completion and saves a bookmark
44+ at the point the harness was triggered. You can then replay the execution by running:
3545
3646``` txt
3747simics> reverse-to start
@@ -41,12 +51,197 @@ From here, you can examine memory and registers (with `x`), single step executio
4151and more! Check out the SIMICS documentation and explore all the deep debugging
4252capabilities that SIMICS offers. When you're done exploring, run ` c ` to continue.
4353
44- You can change the testcase you are examining by choosing a different one with
45- ` tsffs.iface.fuzz.repro ` , but you cannot resume fuzzing after entering repro mode due
46- to inconsistencies with the simulated system clock.
54+ ## Debugging Live with a GDB Stub
55+
56+ Reverse debugging is great for inspecting state after a testcase has run. If you instead
57+ want to step through execution as it happens (setting breakpoints, watching registers
58+ change, using a familiar GDB workflow), you can attach a GDB stub before the testcase
59+ executes.
60+
61+ ** Step 1: Enable repro mode without auto continue.**
62+
63+ Add the following to your SIMICS script:
64+
65+ ``` python
66+ @tsffs.iface.fuzz.repro (" %s imics%/corpus/4385dc33f608888d" )
67+ @tsffs.repro_auto_continue = False
68+ ```
69+
70+ With this set, when the harness start (` HARNESS_START ` ) is hit, TSFFS will write the
71+ testcase into the target's buffer and pause the simulation there, waiting for you to
72+ resume manually.
73+
74+ ``` c
75+ if (!Input) {
76+ return EFI_OUT_OF_RESOURCES;
77+ }
78+
79+ HARNESS_START (Input, &InputSize); // <-- Simulation is paused here
80+
81+ Print (L"Input: %p Size: %d\n", Input, InputSize);
82+ ```
83+
84+ **Step 2: Start the GDB stub.**
85+
86+ SIMICS exposes a GDB remote stub via the `new-gdb-remote` command. Start it by
87+ appending it to your invocation on the command line so it runs after your script
88+ finishes loading:
89+
90+ ```sh
91+ ./simics run.simics -e new-gdb-remote
92+ ```
93+
94+ Or add it at the end of your script directly, after the ` run ` line:
95+
96+ ``` txt
97+ new-gdb-remote
98+ ```
99+
100+ SIMICS output:
101+
102+ ``` txt
103+ [tsffs info] Stopped for repro. Restore to start bookmark with 'reverse-to start'
104+ No CPU is specified; using current processor.
105+ [gdb0 info] Attached to CPU: qsp.mb.cpu0.core[0][0]
106+ Warning: This can expose the target system on the host local network.
107+ [gdb0 info] Awaiting GDB connections on port 9123.
108+ [gdb0 info] Connect from GDB using: "target remote localhost:9123"
109+ ```
110+
111+ ** Step 3: Connect from your GDB client.**
112+
113+ From a separate terminal, connect to the stub. You now have full GDB control over the
114+ simulated target. Set breakpoints, step through instructions, inspect memory and
115+ registers. The simulation will only advance when you tell it to.
116+
117+ ``` sh
118+ (gdb) target remote :9123
119+ Remote debugging using :9123
120+ warning: No executable has been specified and target does not support
121+ determining executable automatically. Try using the " file" command.
122+ 0x00000000dd5b7c8c in ?? ()
123+ (gdb) bt
124+ # 0 0x00000000dd5b7c8c in ?? ()
125+ # 1 0x0000000000000000 in ?? ()
126+ (gdb) x/10i $rip
127+ => 0xdd5b7c8c: cpuid
128+ 0xdd5b7c8e: incq -0x128(%rbp)
129+ 0xdd5b7c95: jne 0xdd5b7743
130+ 0xdd5b7c9b: lea 0x2a6a4(%rip),%rcx # 0xdd5e2346
131+ 0xdd5b7ca2: call 0xdd5b5af9
132+ 0xdd5b7ca7: mov 0x4f6a2(%rip),%rax # 0xdd607350
133+ 0xdd5b7cae: mov $0 x1,%edx
134+ 0xdd5b7cb3: mov -0x118(%rbp),%rcx
135+ 0xdd5b7cba: call * 0x30(%rax)
136+ 0xdd5b7cbd: test %rax,%rax
137+ (gdb)
138+ ```
139+
140+ ## Source-Level Debugging
141+
142+ Once connected with a GDB stub, you can go further and load debug symbols so GDB shows
143+ C source lines, function names, and local variables instead of raw addresses. This
144+ requires two additional pieces of setup: OS awareness in the SIMICS script, and loading
145+ the symbols at the correct address in GDB.
146+
147+ ** Step 1: Add OS awareness to the script.**
148+
149+ Add the following block to your ` run.simics ` right after the ` load-target ` line:
47150
48- > ** Tip:** If you want to attach a debugger (such as a GDB stub) before the
49- > testcase executes, set ` @tsffs.repro_auto_continue = False ` . When the harness
50- > is triggered, TSFFS will prepare the testcase but wait for you to resume
51- > simulation manually. See [ Disable Auto-Continue in Repro Mode] ( ../../config/common-options.md#disable-auto-continue-in-repro-mode )
52- > for details.
151+ ``` txt
152+ new-os-awareness name = qsp.software
153+ qsp.software.insert-tracker tracker = uefi_fw_tracker_comp
154+ qsp.software.tracker.detect-parameters -load
155+ qsp.software.enable-tracker
156+ ```
157+
158+ This instructs SIMICS to track which EFI modules are loaded and at what addresses.
159+
160+ ** Step 2: Find the load address of ` Tutorial.efi ` .**
161+
162+ After the simulation has started and the target has booted, run in the SIMICS console:
163+
164+ ``` txt
165+ simics> qsp.software.tracker.list-modules max = 100
166+ ```
167+
168+ Look for ` Tutorial.efi ` in the output:
169+
170+ ``` txt
171+ │ 85│Tutorial.efi │ 0xdd548000│ 0xc3b80│
172+ ```
173+
174+ The ` Loaded Address ` column gives the base address of the module (` 0xdd548000 ` in this
175+ example).
176+
177+ ** Step 3: Find the ` .text ` section RVA.**
178+
179+ On your host, inspect the debug file produced by the build:
180+
181+ ``` sh
182+ $ objdump -h project/Tutorial.debug
183+ project/Tutorial.debug: file format elf64-x86-64
184+
185+ Sections:
186+ Idx Name Size VMA LMA File off Algn
187+ 0 .text 0009a9d4 0000000000000240 0000000000000240 00000100 2** 6
188+ 1 .data ...
189+ ```
190+
191+ The VMA of the ` .text ` section is ` 0x240 ` . Since EFI binaries are position-independent,
192+ this equals the RVA to add to the load address.
193+
194+ ** Step 4: Load symbols in GDB.**
195+
196+ In your GDB session, load the debug file using the load address from step 2 and the
197+ ` .text ` VMA from step 3:
198+
199+ ``` sh
200+ (gdb) add-symbol-file project/Tutorial.debug 0xdd548000+0x240
201+ ```
202+
203+ GDB will now resolve addresses to source lines and function names. You can set
204+ breakpoints by function or file location, step through C source, and inspect named
205+ variables.
206+
207+ ** Step 5: Map source paths.**
208+
209+ The debug symbols reference the paths as they existed inside the Docker build container
210+ (rooted at ` /edk2 ` ). The build script can copy those sources to your host under
211+ ` edk2-uefi/edk2/ ` when run with ` COPY_SOURCES=1 ` :
212+
213+ ``` sh
214+ COPY_SOURCES=1 ./build.sh
215+ ```
216+
217+ Then tell GDB how to map the container paths to your local copy:
218+
219+ ``` sh
220+ (gdb) set substitute-path /edk2 /absolute/path/to/examples/tutorials/edk2-uefi/edk2
221+ ```
222+
223+ GDB will now find source files automatically when stepping through code.
224+
225+ ``` sh
226+ (gdb) bt
227+ # 0 0x00000000dd5b89c6 in UefiMain (SystemTable=<optimized out>, ImageHandle=<optimized out>) at /edk2/Tutorial/Tutorial.c:58
228+ # 1 ProcessModuleEntryPointList (SystemTable=<optimized out>, ImageHandle=<optimized out>) at /edk2/Tutorial/Build/CryptoPkg/All/DEBUG_GCC/X64/Tutorial/Tutorial/DEBUG/AutoGen.c:319
229+ # 2 _ModuleEntryPoint (ImageHandle=<optimized out>, SystemTable=<optimized out>) at /edk2/MdePkg/Library/UefiApplicationEntryPoint/ApplicationEntryPoint.c:58
230+ # 3 0x00000000df32e14c in ?? ()
231+ # 4 0x00000001df322a88 in ?? ()
232+ # 5 0x00000000df322a98 in ?? ()
233+ # 6 0x00000000df322a20 in ?? ()
234+ # 7 0x0000000000000000 in ?? ()
235+ (gdb) list .
236+ 53 BOOLEAN Status = X509VerifyCert(Cert, CertSize, CACert, CACertSize);
237+ 54
238+ 55 if (Status) {
239+ 56 HARNESS_ASSERT ();
240+ 57 } else {
241+ 58 HARNESS_STOP ();
242+ 59 }
243+ 60
244+ 61 if (Input) {
245+ 62 FreePages(Input, EFI_SIZE_TO_PAGES(MaxInputSize));
246+ (gdb)
247+ ` ` `
0 commit comments