将寄存器值读入C变量

将寄存器值读入C变量,c,gcc,assembly,inline-assembly,cpu-registers,C,Gcc,Assembly,Inline Assembly,Cpu Registers,我记得看到了一种使用扩展gcc内联汇编读取寄存器值并将其存储到C变量中的方法 intptr_t sp; asm ("movl %%esp, %0" : "=r" (sp) ); 我一辈子都记不起如何形成asm声明。这不是你想要的吗 语法: asm ("fsinx %1,%0" : "=f" (result) : "f" (angle)); 我不知道gcc,但在VS中,这是如何: int data = 0; __asm { mov ebx, 30 mov data, e

我记得看到了一种使用扩展gcc内联汇编读取寄存器值并将其存储到C变量中的方法

intptr_t sp;
asm ("movl %%esp, %0" : "=r" (sp) );
我一辈子都记不起如何形成asm声明。

这不是你想要的吗

语法:

 asm ("fsinx %1,%0" : "=f" (result) : "f" (angle));

我不知道gcc,但在VS中,这是如何:

int data = 0;   
__asm
{
    mov ebx, 30
    mov data, ebx
}
cout<<data;
int data=0;
__asm
{
mov ebx,30
mov数据
}

cout以下是获取ebx的方法:

int main()
{
    int i;
    asm("\t movl %%ebx,%0" : "=r"(i));
    return i + 1;
}
结果是:

main:
    subl    $4, %esp
    #APP
             movl %ebx,%eax
    #NO_APP
    incl    %eax
    addl    $4, %esp
    ret

编辑:


“=r”(i)是一个输出约束,告诉编译器第一个输出(%0)是一个寄存器,应该放在变量“i”中。在这个优化级别(-O5),变量i永远不会存储到内存中,而是保存在eax寄存器中,eax寄存器恰好也是返回值寄存器。

这将把堆栈指针寄存器移到sp变量中

intptr_t sp;
asm ("movl %%esp, %0" : "=r" (sp) );
#include <stdint.h>

int foo() {
    uint64_t rax_value;           // type width determines register size
    asm("" : "=a"(rax_value));  // =letter determines which register (or partial reg)

    uint32_t ebx_value;
    asm("" : "=b"(ebx_value));

    uint16_t si_value;
    asm("" : "=S"(si_value) );

    uint8_t sil_value;  // x86-64 required to use the low 8 of a reg other than a-d
       // With -m32:  error: unsupported size for integer register
    asm("# Hi mom, my output constraint picked %0" : "=S"(sil_value) );

    return sil_value + ebx_value;
}

只需将“esp”替换为您感兴趣的实际寄存器(但确保不要丢失%%),将“sp”替换为您的变量。

编者按:这种使用本地寄存器asm变量的方法是。它仍然经常在GCC上工作,但会随着叮当声而中断。(我认为,文件中的这一措辞是在该答案发布后添加的。)

intptr_t sp;
asm ("movl %%esp, %0" : "=r" (sp) );
对于32位x86,全局固定寄存器变量版本的性能代价很大,因为它只有7个GP整数寄存器(不包括堆栈指针)。这将减少到6。只有当你有一个全局变量,你的代码使用得很重时,才考虑这个问题。
到目前为止,我的回答与其他答案不同,因为我不确定你想要什么

此处
a5
是应使用的寄存器名称

寄存器名自然依赖于cpu,但这不是问题,因为特定寄存器通常对显式汇编指令有用(请参阅)。这两种情况通常都需要根据cpu类型对程序进行条件化

定义这样的寄存器变量不会保留寄存器;在流量控制确定变量的值不活动的地方,它仍可用于其他用途

-ffix-
reg

将名为reg的寄存器视为固定寄存器;生成的代码不应该引用它(除了作为堆栈指针、帧指针或其他固定角色)

这可以用更简单的方式复制Richard的答案

int main() {
    register int i asm("ebx");
    return i + 1;
}
虽然这是相当无意义的,因为您不知道
ebx
寄存器中有什么

#include <stdint.h>

int foo() {
    uint64_t rax_value;           // type width determines register size
    asm("" : "=a"(rax_value));  // =letter determines which register (or partial reg)

    uint32_t ebx_value;
    asm("" : "=b"(ebx_value));

    uint16_t si_value;
    asm("" : "=S"(si_value) );

    uint8_t sil_value;  // x86-64 required to use the low 8 of a reg other than a-d
       // With -m32:  error: unsupported size for integer register
    asm("# Hi mom, my output constraint picked %0" : "=S"(sil_value) );

    return sil_value + ebx_value;
}
如果将这两者结合起来,则使用
gcc-ffix ebx
编译此文件

#include <stdio.h>
register int counter asm("ebx");
void check(int n) {
    if (!(n % 2 && n % 3 && n % 5)) counter++;
}
int main() {
    int i;
    counter = 0;
    for (i = 1; i <= 100; i++) check(i);
    printf("%d Hamming numbers between 1 and 100\n", counter);
    return 0;
}
#包括
寄存器int计数器asm(“ebx”);
无效检查(int n){
如果(!(n%2&&n%3&&n%5))计数器++;
}
int main(){
int i;
计数器=0;

对于(i=1;i来自GCC文件本身:

#包括
无效gav(){
//rgv_t argv=get();
注册无符号长i asm(“rax”);
注册无符号长ii asm(“rbx”);
printf(“我是gav-第一个arguman是:%s-第二个arguman是:%s\n”,(char*)I,(char*)ii);
}
内部主(空)
{
char*test=“I`m main”;
char*test1=“我是main2”;
printf(“0x%llx\n”,(无符号长)和gav);
asm(“调用%P0”):“i”((无符号长时间)和gav),“a”(测试),“b”(测试1));
返回0;
}

当内联
asm
语句运行时,您无法知道编译器生成的代码将在任何寄存器中存储什么值,因此该值通常没有意义,最好使用调试器在断点处停止时查看寄存器值

这就是说,如果你要做这个奇怪的任务,你还不如高效地完成它

在某些目标(如x86)上,您可以使用特定的寄存器输出约束告诉编译器输出将在哪个寄存器中。使用带有空asm模板的特定寄存器输出约束(零指令)告诉编译器asm语句不关心输入上的寄存器值,但之后给定的C变量将在该寄存器中

#include <stdint.h>

int foo() {
    uint64_t rax_value;           // type width determines register size
    asm("" : "=a"(rax_value));  // =letter determines which register (or partial reg)

    uint32_t ebx_value;
    asm("" : "=b"(ebx_value));

    uint16_t si_value;
    asm("" : "=S"(si_value) );

    uint8_t sil_value;  // x86-64 required to use the low 8 of a reg other than a-d
       // With -m32:  error: unsupported size for integer register
    asm("# Hi mom, my output constraint picked %0" : "=S"(sil_value) );

    return sil_value + ebx_value;
}
请注意编译器生成的直接从指定寄存器将两个输出相加的代码。还请注意RBX的push/pop,因为RBX在x86-64 System V调用约定中是一个保留调用的寄存器。(以及基本上所有32位和64位x86调用约定).但是我们已经告诉编译器我们的asm语句在那里写了一个值。(使用空的asm语句是一种攻击;没有语法可以直接告诉编译器我们只想读取一个寄存器,因为就像我说的,当插入asm语句时,你不知道编译器对寄存器做了什么。)

编译器会将您的asm语句视为它实际写入了该寄存器,因此,如果以后需要该值,它会在asm语句“运行”时将其复制到另一个寄存器(或溢出到内存)


另一种是
b
(bl/bx/ebx/rbx)、
c
(…/rcx)、
d
(…/rdx)、
S
(sil/si/esi/rsi)、
d
(…/rdi)
。bpl/bp/ebp/rbp没有特定的约束,即使它在没有帧指针的函数中并不特别。(可能是因为使用它会使您的代码不是带有
-fno省略帧指针的编译器

您可以使用
寄存器uint64\u t rbp\u var asm(“rbp”)
,在这种情况下
asm(“:”=r“(rbp\u var));
保证
“=r”
constraint将选择
rbp
。类似地,r8-r15也没有任何明确的约束。在某些架构(如ARM)上,asm寄存器变量是指定asm输入/输出约束所需寄存器的唯一方法。(请注意,;不能保证变量的值在任何其他时间都会在该寄存器中


没有什么可以阻止编译器放置这些asm