从C程序读取标志寄存器

为了好奇,我试图读取标志寄存器并以一种很好的方式将其打印出来。

我已经尝试使用gcc的asm关键字阅读它,但我无法让它工作。 任何提示怎么做? 我正在运行Intel Core 2 Duo和Mac OS X.以下代码就是我所拥有的。 我希望能告诉我是否发生溢出:

#include  int main (void){ int a=10, b=0, bold=0; printf("%d\n",b); while(1){ a++; __asm__ ("pushf\n\t" "movl 4(%%esp), %%eax\n\t" "movl %%eax , %0\n\t" :"=r"(b) : :"%eax" ); if(b!=bold){ printf("register changed \n %d\t to\t %d",bold , b); } bold = b; } } 

这给出了分段错误。 当我运行gdb时,我得到了这个:

 Program received signal EXC_BAD_ACCESS, Could not access memory. Reason: KERN_INVALID_ADDRESS at address: 0x000000005fbfee5c 0x0000000100000eaf in main () at asm.c:9 9 asm ("pushf \n\t" 

您可以使用PUSHF / PUSHFD / PUSHFQ指令(有关详细信息,请参阅http://siyobik.info/main/reference/instruction/PUSHF%2FPUSHFD )将标志寄存器推入堆栈。 从那里你可以用C解释它。否则你可以直接测试(对于无符号算术的进位标志或带符号算术的溢出标志)和分支。

(具体来说,要测试溢出位,可以使用JO(如果设置则跳转)和JNO(如果未设置则跳转)进行分支 – 它在寄存器中的位#11(从0开始))

关于EFLAGS位布局: http : //en.wikibooks.org/wiki/X86_Assembly/X86_Architecture#EFLAGS_Register

一个非常粗略的Visual C语法测试(只是wham-bam /一些跳转到调试流程),因为我不知道GCC语法:

 int test2 = 2147483647; // max 32-bit signed int (0x7fffffff) unsigned int flags_w_overflow, flags_wo_overflow; __asm { mov ebx, test2 // ebx = test value // test for no overflow xor eax, eax // eax = 0 add eax, ebx // add ebx jno no_overflow // jump if no overflow testoverflow: // test for overflow xor ecx, ecx // ecx = 0 inc ecx // ecx = 1 add ecx, ebx // overflow! pushfd // store flags (32 bits) jo overflow // jump if overflow jmp done // jump if not overflown :( no_overflow: pushfd // store flags (32 bits) pop edx // edx = flags w/o overflow jmp testoverflow // back to next test overflow: jmp done // yeah we're done here :) done: pop eax // eax = flags w/overflow mov flags_w_overflow, eax // store mov flags_wo_overflow, edx // store } if (flags_w_overflow & (1 << 11)) __asm int 0x3 // overflow bit set correctly if (flags_wo_overflow & (1 << 11)) __asm int 0x3 // overflow bit set incorrectly return 0; 

编译器可以重新排序指令,因此您不能依赖增量旁边的lahf 。 事实上,可能根本没有增量。 在您的代码中,您不使用a的值,因此编译器可以完全优化它。

因此,要么在汇编程序中写入increment + check,要么在C中写入。

此外, lahf加载ah (8位),而溢出标志位于其之外。 更好地使用pushf; pop %eax pushf; pop %eax

一些测试:

 #include  int main (void){ int a=2147483640, b=0, bold=0; printf("%d\n",b); while(1){ a++; __asm__ __volatile__ ("pushf \n\t" "pop %%eax\n\t" "movl %%eax, %0\n\t" :"=r"(b) : :"%eax" ); if((b & 0x800) != (bold & 0x800)){ printf("register changed \n %x\t to\t %x\n",bold , b); } bold = b; } } $ gcc -Wall -o ex2 ex2.c $ ./ex2 # Works by sheer luck 0 register changed 200206 to 200a96 register changed 200a96 to 200282 $ gcc -Wall -O -o ex2 ex2.c $ ./ex2 # Doesn't work, the compiler hasn't even optimized yet! 0 

其他人提供了很好的替代代码,并说明为什么你要做的事情可能没有给出你想要的结果,但你的代码中的实际错误是你通过推送而没有弹出来破坏堆栈状态。 我会把asm重写为:

 pushf pop %%0 

但是如果你愿意的话,你可以在asm的末尾add $4,%%esp来修复堆栈指针。

这可能是XY问题的情况

要检查溢出,您不需要使用溢出标志,只需检查符号位就足够了

 int a, b; unsigned int r = (unsigned int)a + (unsigned int)b; // do unsigned addition since signed addition do not overflow in C int overflowed = ((~(a ^ b)) & (a ^ r)) & 0x80000000; // if a and b have same sign and the result's sign is different from a and b then the addition was overflowed int result = (int)r; 

这种方式可以移植,不需要在x86上。 在MIPS上没有标志,所有签名/无符号溢出必须由软件通过分析这样的符号位来检查

对于unsigned int,它更容易

 unsigned int a, b, result = a + b; int overflowed = (result < a);