Interpreting eFlags in DDD
-
01-07-2021 - |
Вопрос
I am attempting to learn how to write and understand x86 Assembly
as well as how to use GDB
and related tools effectively. To do this, I am using DDD
as a front-end for GDB
.
I am having trouble understanding what the condition flags (eflags?) are given that they appear to all be stored in the same register. I will post the register, the assembly code, and the related C code. Thank you for any assistance.
The register is displayed as follows at the given breakpoint: 0x293 [CF AF SF IF]
The following is the C code being run. (This is not an example of my coding style. I am trying to force GCC to use the compl
operation.)
int main( int argc, char* argv[] )
{
int a = 0;
int b = 2;
if( a == b ) // There is a breakpoint here!
goto EQUAL;
else
goto NEQUAL;
EQUAL:
return 3;
NEQUAL:
return 1;
}
The following is the assembly my machine broke it down into:
Dump of assembler code for function main:
0x0000000000400474 <+0>: push %rbp
0x0000000000400475 <+1>: mov %rsp,%rbp
0x0000000000400478 <+4>: mov %edi,-0x14(%rbp)
0x000000000040047b <+7>: mov %rsi,-0x20(%rbp)
0x000000000040047f <+11>: movl $0x0,-0x8(%rbp)
0x0000000000400486 <+18>: movl $0x2,-0x4(%rbp)
0x000000000040048d <+25>: mov -0x8(%rbp),%eax
0x0000000000400490 <+28>: cmp -0x4(%rbp),%eax
0x0000000000400493 <+31>: jne 0x40049d <main+41> # Break point here
0x0000000000400495 <+33>: nop
0x0000000000400496 <+34>: mov $0x3,%eax
0x000000000040049b <+39>: jmp 0x4004a3 <main+47>
0x000000000040049d <+41>: nop
0x000000000040049e <+42>: mov $0x1,%eax
0x00000000004004a3 <+47>: leaveq
0x00000000004004a4 <+48>: retq
End of assembler dump.
Решение
The eflags register is made up of single bits, each being a flag.
When displaying the flags, they can be combined in a larger numeric entity (like 0x293 in your example), or each can have a symbol on its own (like in "[CF AF SF IF]" with the carry flag CF, adjust flag AF, sign flag SF and interrupt flag IF.
The Intel 64 and IA 32 Architecture Software Developer's Manual Vol. 1 describes the flags in detail in chapter 3.4.3.
The most important (for application developers) are:
bit | sym | name ------------------ 0 | CF | carry 1 | -- | (always 1) 2 | PF | parity 3 | -- | (always 0) 4 | AF | adjust 5 | -- | (always 0) 6 | ZF | zero 7 | SF | sign 8 | TF | trap 9 | IF | interrupt 10 | DF | direction 11 | OF | overflow
Combining those in your example (CF AF SF IF) gives the binary value 1010010011, where the rightmost digit is the carry flag, and the leftmost the interrupt flag. Converted to hexadecimal it gives exactly 0x293.
Другие советы
The (R/E)FLAGS
register contains the following condition flags:
OF (overflow flag) - it's used to determine overflow of integer arithmetic operations (e.g. ADD
, ADC
, SUB
, SBB
instructions) on signed operands. Conditional branches/jumps (the Jcc
instruction) can directly examine this flag.
SF (sign flag) - it's used to determine whether the result of the last integer arithmetic operation is a negative value or not, SF is basically a copy of the sign bit (=most significant bit) of the result. Again, conditional branches/jumps can examine it directly.
ZF (zero flag) - it's used to determine whether the result of the last integer arithmetic or logical operation is 0 or not. Conditional branches/jumps can examine it directly as well.
CF (carry flag) - similarly to the overflow flag (OF) it can be used to determine overflow of integer arithmetic operations performed on unsigned operands. Note the difference: CF for unsigned, OF for signed. Also note that the CPU often calculates both CF and OF and it's the programmer's responsibility to examine the correct flag. In most cases the CPU cannot and does not distinguish between signed and unsigned operands because the result is calculated in the same way, and it's all data to the CPU. It's the programmer's responsibility to interpret values as signed or unsigned, not the CPU's. Conditional branches/jumps can examine CF directly as well.
Edit: there are also rotate through carry
instructions (RCL
and RCR
), which use CF.
Integer comparison (with the CMP
instruction) results in the four above flags set. If you intend to compare unsigned integers, you then examine ZF and/or CF. For signed integer comparison you need to examine ZF and/or the SF + OF pair. See the conditional branch/jump instructions (Jcc
: JB
, JNC
, JE
, JNGE
, etc etc).
The parity flag, PF, and the auxiliary carry flag, AF, are the other condition flags, but they are used rarely.
The rest of the bits in (R/E)FLAGS
are either system or control flags. Of them you probably want to know just two:
DF (direction flag) - it's used to set the direction of string instructions. String instructions are used for sequential accessing of memory for things like block memory copy, block memory comparison, block memory search, block memory fill (see MOVSB/W/D/Q
, CMPSB/W/D/Q
, SCASB/W/D/Q
, STOSB/W/D/Q
, LODSB/W/D/Q
). The value of this flag tells whether the memory operand address(es) should be incremented or decremented after each iteration.
IF (interrupt flag) - it's used to enable or disable handling of hardware interrupts. If you get into very low-level programing (drivers, OS kernel and the like), you'll have to work with this flag.
That's about it. All the details are available in the official CPU documentation from Intel and AMD. Go, download the CPU manuals.
Edit: You may find these questions/answers useful: 1, 2.
Also useful to know is that:
- not all instructions modify condition flags
- some modify them always and set to 0 or 1
- some set them to unspecified values and you can't rely on or expect a 0 or 1