Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/64.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
从C程序读取标志寄存器_C_Assembly_X86_Flags - Fatal编程技术网

从C程序读取标志寄存器

从C程序读取标志寄存器,c,assembly,x86,flags,C,Assembly,X86,Flags,出于好奇,我试图阅读国旗登记册,并以一种好的方式打印出来 我试着用gcc的asm关键字来阅读它,但我无法让它工作。有什么提示吗?我正在运行Intel Core 2 Duo和Mac OS X。下面的代码就是我所拥有的。我希望它能告诉我是否发生溢流: #include <stdio.h> int main (void){ int a=10, b=0, bold=0; printf("%d\n",b); while(1){ a++; __asm__ ("pushf

出于好奇,我试图阅读国旗登记册,并以一种好的方式打印出来

我试着用gcc的asm关键字来阅读它,但我无法让它工作。有什么提示吗?我正在运行Intel Core 2 Duo和Mac OS X。下面的代码就是我所拥有的。我希望它能告诉我是否发生溢流:

#include <stdio.h>

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;
  }
}

可以使用PUSHF/PUSHFD/PUSHFQ指令(有关详细信息,请参阅)将标志寄存器推送到堆栈上。从那时起,您可以用C解释它。否则,您可以直接测试(针对无符号算术的进位标志或针对有符号算术的溢出标志)和分支

(具体来说,要测试溢出位,可以使用JO(设置时跳转)和JNO(未设置时跳转)进行分支——它是寄存器中的位11(基于0)

关于EFLAGS位布局:

一个非常粗糙的Visual C语法测试(只是砰的一声/一些跳转到调试流),因为我不知道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;
int test2=2147483647;//最大32位有符号整数(0x7fffffff)
无符号整数标志溢出,标志溢出;
__asm
{
mov ebx,test2//ebx=测试值
//无溢流测试
异或eax,eax//eax=0
添加eax,ebx//添加ebx
jno no_溢出//如果没有溢出则跳转
testoverflow:
//溢流测试
xor ecx,ecx//ecx=0
inc-ecx//ecx=1
添加ecx、ebx//溢出!
pushfd//存储标志(32位)
jo溢出//溢出时跳转
jmp done//未溢出时跳转:(
无溢出:
pushfd//存储标志(32位)
pop edx//edx=没有溢出的标志
jmp testoverflow//返回到下一个测试
溢出:
jmp结束//是的,我们结束了:)
完成:
pop eax//eax=带溢出标志
mov标志溢出,eax//存储
mov标志溢出,edx//存储
}

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

因此,要么编写增量+检入汇编程序,要么用C编写

另外,
lahf
仅从
eflags
加载
ah
(8位),溢出标志在该范围之外。最好使用
pushf;pop%eax

一些测试:

#include <stdio.h>

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
#包括
内部主(空){
INTA=2147483640,b=0,粗体=0;
printf(“%d\n”,b);
而(1){
a++;
__asm\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
“pop%%eax\n\t”
movl%%eax,%0\n\t
:“=r”(b)
:
:%eax
);
如果((b和0x800)!=(粗体和0x800)){
printf(“寄存器更改\n%x\t为\t%x\n”,粗体,b);
}
粗体=b;
}
}
$gcc-Wall-o ex2 ex2.c
$./ex2#完全靠运气工作
0
寄存器已更改
200206至200a96
寄存器已更改
200a96至200282
$gcc-Wall-O-O ex2 ex2.c
$./ex2#不起作用,编译器甚至还没有优化!
0

其他人提供了很好的替代代码以及您尝试执行的操作可能无法得到您想要的结果的原因,但您代码中的实际错误是,您在不弹出的情况下按下,破坏了堆栈状态。我将把asm重写为:

pushf
pop %0

或者,您可以在asm末尾添加$4、%%esp来修复堆栈指针,如果您更喜欢低效的方式。

这可能是的情况。要检查溢出,您不需要像您认为的那样获取硬件溢出标志,因为该标志可以很容易地从符号位计算出来

一个示例是,如果我们使用8位寄存器将127和127相加,会发生什么情况。127+127是254,但使用8位算术,结果将是1111110二进制,即2的补码中的-2,因此为负。正操作数的负结果(或反之亦然)是一个溢出。然后设置溢出标志,以便程序能够意识到问题并减轻此问题或发出错误信号。因此,当通过添加两个具有相同符号的数字(或减去两个具有相反符号的数字)更改最高有效位(此处视为符号位)时,将设置溢出标志.当两个加法操作数的符号不同(或两个减法操作数的符号相同)时,不会发生溢出

在内部,溢出标志通常由符号位的内部进位和进位的异或生成。由于符号位与被视为无符号的数字的最高有效位相同,溢出标志是“无意义”的,通常在无符号数字被加或减时被忽略

因此,C实现是

int add(int a, int b, int* overflowed)
{
    // do an unsigned addition since to prevent UB due to signed overflow
    unsigned int r = (unsigned int)a + (unsigned int)b;

    // if a and b have the same sign and the result's sign is different from a and b
    // then the addition was overflowed
    *overflowed = !!((~(a ^ b) & (a ^ r)) & 0x80000000);
    return (int)r;
}
通过这种方式,它可以在任何体系结构上可移植地工作,这与仅在x86上工作的解决方案不同。智能编译器可能会识别模式,并在可能的情况下更改为使用溢出标志。在大多数RISC体系结构(如MIPS或RISC-V)上,没有标志,必须通过分析像那样签名

有些编译器具有用于检查溢出的内部函数,如和中的
\uuu内置\uu add\uoverflow

add     w3, w0, w1  # r = a + b
eon     w0, w0, w1  # a = a ^ ~b
eor     w1, w3, w1  # b = b ^ r
str     w3, [x2]    # store sum ([x2] = r)
and     w0, w1, w0  # a = a & b = (a ^ ~b) & (b ^ r)
lsr     w0, w0, 31  # overflowed = a >> 31
ret
这只是我上面所写内容的一个变体

另见


对于unsigned int,这要容易得多

unsigned int a, b, result = a + b;
int overflowed = (result < a);
无符号整数a,b,结果=a+b;
int溢出=(结果
以下C程序在使用GCC和任何x86或x86_64机器按照调用约定编译时将读取标志寄存器,在调用约定中,整数返回到
%eax
。您可能需要将
-zexecstack
参数传递给编译器<
unsigned int a, b, result = a + b;
int overflowed = (result < a);
#include<stdio.h>
#include<stdlib.h>

int(*f)()=(void*)L"\xc3589c";

int main( int argc, char **argv ) {
  if( argc < 3 ) {
    printf( "Usage: %s <augend> <addend>\n", *argv );
    return 0;
  }
  int a=atoi(argv[1])+atoi(argv[2]);
  int b=f();
  printf("%d CF %d PF %d AF %d ZF %d SF %d TF %d IF %d DF %d OF %d IOPL %d NT %d RF %d VM %d AC %d VIF %d VIP %d ID %d\n", a, b&1, b/4&1, b>>4&1, b>>6&1, b>>7&1, b>>8&1, b>>9&1, b>>10&1, b>>11&1, b>>12&3, b>>14&1, b>>16&1, b>>17&1, b>>18&1, b>>19&1, b>>20&1, b>>21&1 );
}
0x0000000000000000:  9C    pushfq 
0x0000000000000001:  58    pop    rax
0x0000000000000002:  C3    ret    
int checked_add_int(int a, int b, bool *of) {
    int result;
    *of = __builtin_sadd_overflow(a, b, &result);
    return result;
}
checked_add_int:
        mov     eax, edi
        add     eax, esi             # can't use the normal lea eax, [rdi+rsi]
        seto    BYTE PTR [rdx]
        and     BYTE PTR [rdx], 1    # silly compiler, it's already 0/1
        ret
#include <stdbool.h>
int overflows(int a, int b) {
    bool of = __builtin_add_overflow_p(a, b, (int)0);
    return of;
}
overflows:
        xor     eax, eax
        add     edi, esi
        seto    al
        ret