c语言内嵌汇编代码之InputOperands使用时的注意事项

2019-10-14 16:29:35 浏览数 (1)

在阅读本文之前,请先阅读gcc的相关文档,确保对如何在c中使用汇编语言有个基本的认识。

文档地址为:

https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C


1. 编译器认为asm语句中的 input operands 只是用来读数据的,不会被修改,所以当该asm语句执行完毕之后,后面的代码如果还有地方使用 input operands,则不管在asm语句的汇编代码中有没有修改过 input operands,后面代码使用的 input operands 的值都是原来的值。

2. 如果想要告知编译器 input operands 在asm语句的汇编代码中有被修改过,只能通过将 input operands 绑定到 output operands 的形式。其实本质上编译器还是通过 output operands 得知数据被修改了的,只是此时由于 input operands 和 output operands 使用的是同一寄存器或内存地址,所以相当于编译器也间接得知了 input operands 的修改。

下面来看个具体例子:

代码语言:javascript复制
#include <stdio.h>

int inc1(int src) {
  asm("add $1, %0" : : "r" (src));
  return src;
}

int inc2(int src) {
  asm("add $1, %0" : "=r" (src) : "0" (src));
  return src;
}

int main(int argc, char *argv[]) {
  printf("inc1: %dn", inc1(1));
  printf("inc2: %dn", inc2(1));
}

该例子中的inc方法都是将src的值加1,然后再返回,所以理论上来说最终的输出都应该是2。

但我们执行看下结果:

代码语言:javascript复制
$ gcc main.c && ./a.out
inc1: 1
inc2: 2

由上可以看到,inc1的结果是1,这是因为编译器认为src只是asm语句的输入,所以它不会被修改,所以在return的时候直接返回了原值。

看下其对应的汇编代码:

代码语言:javascript复制
$ gcc -O3 main.c && objdump --disassemble=inc1 a.out
0000000000001180 <inc1>:
    1180:  89 f8                  mov    �i,�x
    1182:  83 c7 01               add    $0x1,�i
    1185:  c3                     retq

和我们想的一样,return时返回的就是原值。

inc2方法的返回结果是正确的,因为我们把输入绑定到了输出,编译器通过输出可以知道数据被修改过了。

同样看下其汇编代码:

代码语言:javascript复制
$ gcc -O3 main.c && objdump --disassemble=inc2 a.out
0000000000001190 <inc2>:
    1190:  89 f8                  mov    �i,�x
    1192:  83 c0 01               add    $0x1,�x
    1195:  c3                     retq

的确是正确的逻辑。

通过上面的例子我们可以看到,编译器认为asm语句的输入参数是只读的,所以我们在asm语句的汇编代码里也不要去修改输入参数的值,如果非要修改,一定要通过某种方式告知编译器,防止最终的逻辑错误。

0 人点赞