如果用两个变量或一个变量和一个数字文字进行加法,为什么从 C++ 调用的汇编函数会在标志中给出不同的结果?
Why assembly function called from C++ give different result in flags if addition made with two variables or one variable and one numeric literal?
如果有人可以,请帮助我!
我写了一个 3 行汇编代码来取回 RFLAGS 的值:
PUBLIC x64rflags
.code
; Caller C++ function prototype: unsigned long long int x64rflags();
x64rflags PROC
pushfq ; RFLAGS into stack
pop rax ; RAX = RFLAGS
ret
x64rflags ENDP
End
我在 C++ 库中使用它来检查 C++ 程序的标志:
//The RFLAGS query function. Result in unsigned long long int value.
extern "C"
{
unsigned long long int x64rflags();
}
//The FLAGS bits decimal values
enum
{
FLAGS_CF = 1, //Carry
FLAGS_PF = 4, //Parity
FLAGS_AF = 16, //Adjust (Auxiliary carry)
FLAGS_ZF = 64, //Zero
FLAGS_SF = 128, //Sign
FLAGS_TF = 256, //Trap
FLAGS_IF = 512, //Interrupt enable
FLAGS_DF = 1024, //Direction
FLAGS_OF = 2048, //Overflow
FLAGS_IOPL_LOW = 4096, //I/O privilege level low bit
FLAGS_IOPL_HIGH = 8192, //I/O privilege level high bit
FLAGS_NT = 16384, //Nested task
FLAGS_RF = 65536, //Resume
FLAGS_VM = 131072, //Virtual 8086 mode
FLAGS_AC = 262144, //Alignment Check
FLAGS_VIF = 524288, //Virtual interrupt
FLAGS_VIP = 1048576, //Virtual interrupt Pending
FLAGS_ID = 2097152, //CPUID available
};
//The compare function of RFLAGS and inpected values
bool flagschk(unsigned long long int flags, unsigned long long int flagsToChk)
{
if (flagsToChk == (flagsToChk & flags))
{
return true;
}
else
{
return false;
}
}
这是我的 C++ 测试程序:
#define _UNICODE
#define UNICODE
#include <iostream>
#include <windows.h>
#include <string>
#include <x64rflags.h>
using namespace std;
typedef unsigned long long int ulli;
int main()
{
SetConsoleOutputCP(1250); //ANSI Central European; Central European (Windows)
ulli ullia, ullib, ullir, actrflags;
std::string stro1, stro2;
ullia = 0xffffffffffffffff;
ullib = 1;
ullir = ullia + ullib; //CF
actrflags = x64rflags(); //CALL THE RFLAGS RETURNER ASM FUNCTION
printf("1 Result: %llu\n", ullir);
stro1 = "1 The value of actrflags: ";
stro2 = std::to_string(unsigned long long int(actrflags));
stro1.append(stro2);
stro1.append("\n");
printf(stro1.c_str());
if(flagschk(actrflags, FLAGS_CF))
{
printf("1 condparam = TRUE\n\n");
}
else
{
printf("1 condparam = FALSE\n\n");
}
//*/
ullia = 0xffffffffffffffff;
ullir = ullia + 1; //CF
actrflags = x64rflags(); //CALL THE RFLAGS RETURNER ASM FUNCTION
printf("2 Result: %llu\n", ullir);
stro1 = "2 The value of actrflags: ";
stro2 = std::to_string(unsigned long long int(actrflags));
stro1.append(stro2);
stro1.append("\n");
printf(stro1.c_str());
if(flagschk(actrflags, FLAGS_CF))
{
printf("2 condparam = TRUE\n\n");
}
else
{
printf("2 condparam = FALSE\n\n");
}
return 0;
}
我的问题是它在两个探针上给我不同的结果。
第一个使用变量按预期工作:提高进位并回馈 "TRUE"
第二个使用一个变量和一个数字字面量不提高进位。
附加信息:
ml64 带选项 /c 编译的汇编代码
用 Code::Blocks 编写的 C++ 代码,使用 MSVC 2019 编译。
Intel x64Arch (core i7) 上的程序 运行。
这些程序(理论上)使用默认的 (x64 ABI) 调用约定。
如果我不在 rflags 查询之前的添加表达式中使用“=”,则在这两种情况下都不起作用。 (我想 MSVC 将加法处理为 "NOP" 并独立于加法返回最后一个进位...)
谁能解释一下这个异常的原因?
关于编译器如何根据 asm 选择实现 C +
运算符的任何期望都是没有根据的。一些调整选项可以使它更喜欢 lea
而不是不设置标志的 add
。 (对于 GCC,-mtune=atom
实际上就是这种情况)。
启用优化后,它可能会以不同的顺序执行,而不是在非内联函数调用之前。
或者对于 +1
具体来说,MSVC 可能选择了 inc
which doesn't modify CF. That looks like the case here, given that your FLAGS result differs only in the low bit. (CF is the lowest bit)。对于那一点,您正在读取 inc
之前恰好留在 CF 中的任何值。其他位由 inc
.
写入
您可以使用调试器单步检查编译器生成的 asm,或者将其放在 https://godbolt.org/
而且也没有理由假设在执行到达您的函数调用时 FLAGS 仍然从 +
的 asm 中设置。即使是内联 asm 也不能可靠地做到这一点。 如果您想从 add
读取 FLAGS,add
需要在您的 asm 中,而不是编译器生成的。
优化器不会将先前 C 语句的 FLAG 结果视为函数调用的输入,或者根本不是可观察到的副作用(编译器生成的代码除外)有意读取 FLAGS 结果,例如 jnz
在添加后实现 if(foo)
)
如果有人可以,请帮助我!
我写了一个 3 行汇编代码来取回 RFLAGS 的值:
PUBLIC x64rflags
.code
; Caller C++ function prototype: unsigned long long int x64rflags();
x64rflags PROC
pushfq ; RFLAGS into stack
pop rax ; RAX = RFLAGS
ret
x64rflags ENDP
End
我在 C++ 库中使用它来检查 C++ 程序的标志:
//The RFLAGS query function. Result in unsigned long long int value.
extern "C"
{
unsigned long long int x64rflags();
}
//The FLAGS bits decimal values
enum
{
FLAGS_CF = 1, //Carry
FLAGS_PF = 4, //Parity
FLAGS_AF = 16, //Adjust (Auxiliary carry)
FLAGS_ZF = 64, //Zero
FLAGS_SF = 128, //Sign
FLAGS_TF = 256, //Trap
FLAGS_IF = 512, //Interrupt enable
FLAGS_DF = 1024, //Direction
FLAGS_OF = 2048, //Overflow
FLAGS_IOPL_LOW = 4096, //I/O privilege level low bit
FLAGS_IOPL_HIGH = 8192, //I/O privilege level high bit
FLAGS_NT = 16384, //Nested task
FLAGS_RF = 65536, //Resume
FLAGS_VM = 131072, //Virtual 8086 mode
FLAGS_AC = 262144, //Alignment Check
FLAGS_VIF = 524288, //Virtual interrupt
FLAGS_VIP = 1048576, //Virtual interrupt Pending
FLAGS_ID = 2097152, //CPUID available
};
//The compare function of RFLAGS and inpected values
bool flagschk(unsigned long long int flags, unsigned long long int flagsToChk)
{
if (flagsToChk == (flagsToChk & flags))
{
return true;
}
else
{
return false;
}
}
这是我的 C++ 测试程序:
#define _UNICODE
#define UNICODE
#include <iostream>
#include <windows.h>
#include <string>
#include <x64rflags.h>
using namespace std;
typedef unsigned long long int ulli;
int main()
{
SetConsoleOutputCP(1250); //ANSI Central European; Central European (Windows)
ulli ullia, ullib, ullir, actrflags;
std::string stro1, stro2;
ullia = 0xffffffffffffffff;
ullib = 1;
ullir = ullia + ullib; //CF
actrflags = x64rflags(); //CALL THE RFLAGS RETURNER ASM FUNCTION
printf("1 Result: %llu\n", ullir);
stro1 = "1 The value of actrflags: ";
stro2 = std::to_string(unsigned long long int(actrflags));
stro1.append(stro2);
stro1.append("\n");
printf(stro1.c_str());
if(flagschk(actrflags, FLAGS_CF))
{
printf("1 condparam = TRUE\n\n");
}
else
{
printf("1 condparam = FALSE\n\n");
}
//*/
ullia = 0xffffffffffffffff;
ullir = ullia + 1; //CF
actrflags = x64rflags(); //CALL THE RFLAGS RETURNER ASM FUNCTION
printf("2 Result: %llu\n", ullir);
stro1 = "2 The value of actrflags: ";
stro2 = std::to_string(unsigned long long int(actrflags));
stro1.append(stro2);
stro1.append("\n");
printf(stro1.c_str());
if(flagschk(actrflags, FLAGS_CF))
{
printf("2 condparam = TRUE\n\n");
}
else
{
printf("2 condparam = FALSE\n\n");
}
return 0;
}
我的问题是它在两个探针上给我不同的结果。 第一个使用变量按预期工作:提高进位并回馈 "TRUE"
第二个使用一个变量和一个数字字面量不提高进位。
附加信息: ml64 带选项 /c 编译的汇编代码 用 Code::Blocks 编写的 C++ 代码,使用 MSVC 2019 编译。 Intel x64Arch (core i7) 上的程序 运行。 这些程序(理论上)使用默认的 (x64 ABI) 调用约定。 如果我不在 rflags 查询之前的添加表达式中使用“=”,则在这两种情况下都不起作用。 (我想 MSVC 将加法处理为 "NOP" 并独立于加法返回最后一个进位...)
谁能解释一下这个异常的原因?
关于编译器如何根据 asm 选择实现 C +
运算符的任何期望都是没有根据的。一些调整选项可以使它更喜欢 lea
而不是不设置标志的 add
。 (对于 GCC,-mtune=atom
实际上就是这种情况)。
启用优化后,它可能会以不同的顺序执行,而不是在非内联函数调用之前。
或者对于 +1
具体来说,MSVC 可能选择了 inc
which doesn't modify CF. That looks like the case here, given that your FLAGS result differs only in the low bit. (CF is the lowest bit)。对于那一点,您正在读取 inc
之前恰好留在 CF 中的任何值。其他位由 inc
.
您可以使用调试器单步检查编译器生成的 asm,或者将其放在 https://godbolt.org/
而且也没有理由假设在执行到达您的函数调用时 FLAGS 仍然从 +
的 asm 中设置。即使是内联 asm 也不能可靠地做到这一点。 如果您想从 add
读取 FLAGS,add
需要在您的 asm 中,而不是编译器生成的。
优化器不会将先前 C 语句的 FLAG 结果视为函数调用的输入,或者根本不是可观察到的副作用(编译器生成的代码除外)有意读取 FLAGS 结果,例如 jnz
在添加后实现 if(foo)
)