Title: Facilities for x86 debugging
1Facilities for x86 debugging
- Introduction to x86 CPU features that can assist
programmers in the debugging of their software
2Any project bugs?
- As you work on designing your solution for the
programming assignment in Project 2 it is
possible (likely?) that you may run into some
program failures - What can you do if your program doesnt behave as
you had expected it would? - How can you diagnose the causes?
- Where does your problem first appear?
3Single-stepping
- An ability to trace through your programs code,
one instruction at a time, often can be extremely
helpful in identifying where a program flaw is
occurring and also why - Intels x86 processor provides hardware
assistance in implementing a debugging
capability such as single-stepping.
4The EFLAGS register
8
16
R F
T F
TF TRAP flag (bit 8) By setting
this flag-bit in the EFLAGS register-image
that gets saved on the stack when a
pushfl is executed, and then executing
popfl, the CPU will begin triggering a
single-step exception after each
instruction-executes
RF RESUME flag (bit 16) By setting this
flag-bit in the EFLAGS register-image
that gets saved on the stack, the iret
instruction will be inhibited from generating
yet another CPU exception
5TF-bit in EFLAGS
- Our usedebug.s demo shows how to use the TF-bit
to perform single-stepping of a Linux
application (e.g., our linuxapp.o) - The popfw instruction is used to set TF
- The exception-handler for INT-1 displays
information about the state of the program - But single-stepping starts only AFTER the
immediately following instruction executes
6How to do it
- Heres a code-fragment that we could use to
initiate single-stepping from the start of our
ring3 application-progam
pushw userSS selector for ring3
stack-segment pushw userTOS offset for ring3
top-of-stack pushw userCS selector for
ring3 code-segment pushw 0 offset for the
ring3 entry-point pushfw push current
FLAGS btsw 8, (esp) set image of the
TF-bit popfw modify FLAGS to set
TF lret transfer to ring3 application
7Using assembler listings
- You can generate an assembler listing of the
instructions in our linuxapp.o file, then use
that listing to follow along while youre
single-stepping through that files code - Heres how to do it
- as al linuxapp.s gt linuxapp.lst
- (The -al option is for assembly listing)
8A slight flaw
- We cannot single-step the execution of an
int-0x80 instruction (Linuxs system-calls) - Our exception-handlers iret instruction will
restore the TF-bit to EFLAGS, but the single-step
trap doesnt take effect until after the
immediately following instruction - This means we skip seeing a display of the
registers immediately after int-0x80
9Fixing that flaw
- The x86 offers us a way to overcome the delayed
effect of TF when iret executes - We can use the Debug Registers to set an
instruction breakpoint which will interrupt the
CPU at a specific instruction-address - There are six Debug Registers
- DR0, DR1, DR2, DR3 (breakpoints)
- DR6 (the Debug Status register)
- DR7 (the Debug Control register)
10Breakpoint Address Registers
DR0
DR1
DR2
DR3
11Special MOV instructions
- Use mov reg, DRn to write into DRn
- Use mov DRn, reg to read from DRn
- Here reg stands for any one of the CPUs
general-purpose registers (e.g., EAX, etc.) - These special instructions are privileged
(i.e., they can only be executed by code that is
running in ring0)
12Debug Control Register (DR7)
15
0
0
0
G D
0
0
1
G E
L E
G 3
L 3
G 2
L 2
G 1
L 1
G 0
L 0
Least significant word
31
16
LEN 3
R/W 3
LEN 2
R/W 2
LEN 1
R/W 1
LEN 0
R/W 0
Most significant word
13What kinds of breakpoints?
LEN
R/W
LEN 00 one byte 01 two bytes 10
undefined 11 four bytes
R/W 00 break on instruction fetch only 01
break on data writes only 10 undefined
(unless DE set in CR4) 11 break on data reads
or writes (but not on instruction fetches)
14Control Register CR4
- The x86 CPU uses Control Register CR4 to activate
certain extended features of the processor, while
still allowing for backward compatibility of
software written for earlier Intel x86 processors - An example Debug Extensions (DE-bit)
31
3
0
other feature bits
D E
CR4
15Debug Status Register (DR6)
15
0
B D
0
1
1
1
1
1
1
1
B 3
B 2
B 1
B S
B T
1
B 0
Least significant word
31
16
unused ( all bits here are set to 1 )
Most significant word
LEGEND B0 (Breakpoint by DR0) BT (Break
on Task-switch trap) B1 (Breakpoint by DR1)
BS (Break on Single-step trap) B2 (Breakpoint
by DR2) BD (Break on Debug-register
access) B3 (Breakpoint by DR3)
16Where to set a breakpoint
- Suppose you want to trigger a debug trap at the
instruction immediately following the Linux
software int 0x80 system-call - Your debug exception-handler can use the saved
CSEIP values on its stack to check that int
0x80 has caused an exception - Machine-code is 0xCD, 0x80 (2 bytes)
- So set a breakpoint at address EIP2
17Computing a code-breakpoint
isrDBG pushal preserve general
registers pushl ds preserve DS
register pushl es preserve ES
register lds 40(esp), esi point DSESI to
faulting instruction cmpb 0xCD, (esi) a
software interrupt instruction? jne notINT if
not, dont set a breakpoint add 2, esi else
point past 2-byte instruction now we want to
compute the linear address represented by
the logical-address (i.e., segmentoffset values)
in DSESI NOTE Its easy for operating
systems like Linux, where segments for code
and data have a base-address thats equal to zero
but our current program-examples use
memory-segments that dont begin at address
0x00000000
18Segment-selector format
15
3 2 1 0
array-index for descriptor-table entry
R P L
T I
TI (Table Indicator) 0 GDT 1 LDT
19Segment-Descriptor Format
63
32
Base31..24
G
D
R S V
A V L
Limit 19..16
P
D P L
S
X
C / D
R / W
A
Base23..16
Base15..0
Limit15..0
0
31
Several instances of this basic
segment-descriptor data-structure will occur in
the Global Descriptor Table (and maybe also in
some Local Descriptor Tables)
20Getting the base-address
The base-address for the memory-segment whose
segment-selector is in register DS will need to
be extracted from its segment-descriptor mov ds
, ecx segment-selector to ECX lea theGDT,
ebx setup GDTs offset in EBX bt 2, ecx
is the selectors TI-bit set? jnc useEBX no,
do table-lookup in GDT lea theLDT, ebx else
do the lookup in LDT useEBX and 0xFFF8,
ecx isolate selectors index-field mov cs0(
ebx, ecx), eax descriptor
31..0 mov cs4(ebx, ecx), al descriptor
39..32 mov cs7(ebx, ecx), ah
descriptor 63..56 rol 16, eax rotate
these bits into position
21Enabling the breakpoint
instruction linear-address is base-address
plus segment-offset add eax, esi add
base-address to offset setup this
breakpoint-address in Debug Register
DR0 mov esi, dr0 breakpoint-address in
DR0 now activate a local code-breakpoint
for the address in DR0 mov dr7, eax bts
0, eax set LE0 (Local Enable 0) mov
eax, dr7 popl es popl ds popal iret
22Detecting a breakpoint
- Your debug exception-handler can read DR6 to
check for an occurrence of breakpoint0 - mov dr6, eax get debug status
- bt 0, eax breakpoint 0?
- jnc notBP0 no, another cause
- btsl 16, 12(ebp) set the RF-bit
- or disable breakpoint0 in register DR7
- notBP0
23Detecting a breakpoint
- Your debug exception-handler reads DR6 to learn
why a debug-exception occurred
EXAMPLE was this exception triggered by a
breakpoint defined in DR0DR3? mov dr6, eax
read debug status-register test 0xF, eax
any breakpoint matches? jz notBP no, leave
RF-bit unchanged OK, we need to set the
RF-bit (Resume Flag) before we execute iret
(so as not to immediately encounter the very same
breakpoint again) btsl 16, 48(esp) set
RF-bit in EFLAGS image notBP
24In-class exercise 1
- Our usedebug.s demo illustrates the idea of
single-stepping through a program, but after
several steps it encounter a General Protection
Exception (i.e., interrupt 0x0D) - You will recognize a display of information from
registers that gets saved on the stack - Can you determine why this fault occurs, and then
modify our code to eliminate it?
25The GP Faults stack-layout
EFLAGS
----- CS
EIP
error-code
EAX
ECX
EDX
EBX
ESP
EBP
ESI
EDI
----- DS
----- ES
----- FS
----- GS
26Intel x86 instruction-format
- Intels instructions vary in length from 1 to 15
bytes, and are comprised of five fields
instruction prefixes 0,1,2 or 3 bytes
opcode field 1 or 2 bytes
addressing mode field 0, 1 or 2 bytes
address displacement 0, 1, 2 or 4 bytes
immediate data 0, 1, 2 or 4 bytes
Maximum number of bytes 15
NOTE When the processors IA32e mode is
activated, some of these field-sizes may be
larger, to accommodate additional
addressing-modes and operand-sizes
27A few examples
- 1-byte instruction in dx, al
- 2-byte instruction int 0x16
- A prefixed instruction rep movsb
- And heres a 12-byte instruction cmpl 0,
fs0x400(ebx, edi, 2) - 1 prefix byte
- 1 opcode byte
- 2 address-mode bytes
- 4 address-displacement bytes
- 4 immediate-data bytes
28In-class exercise 2
- Modify the debug exception-handler in our
usedebug.s demo-program so it will use a
different Debug Register (i.e.,, DR1, DR2, or
DR3) to set an instruction-breakpoint at the
entry-point to your int 0x80 system-service
interrupt-routine (i.e., at isrDBG) - This can allow you to do single-stepping of your
system-call handlers (e.g., do_write)