汇编语言之基础(三 嵌入汇编)
嵌入汇编
-
说明:
用来在c语言中写汇编程序
汇编语言无法实现内存到内存操作‘
但是可以实现寄存器到内存的操作
-
通用写法:
__asm__ volatile("汇编指令" 必须存在字段
: =限制符(输出参数) 这是可选的可以不需要写
: 限制符(输入参数) 这是可选的可以不需要写
: 保留列表
);
其中volatile代表是否优化,存在代表不允许编译器优化
-
汇编搭配宏
使用汇编语言最常用的方式就是把汇编语言写在宏里,加上({})这种GNU独有的语法,在{}像函数一样进行定义,把最后一个语句作为返回值返回
#define get_value(input, output) ({ \
__asm__ volatile("mov %0, %1" \
: "=a"(output) \
: "a"(input) \
); \
output; \
})
a是限制符,是对参数的说明,对于输入参数来说就是把输入参数放到eax寄存器中
输出参数代表汇编结束把该寄存器内容给到输出参数
通过通用寄存器间接操作变量,汇编语言是操作 寄存器的,不支持直接操作内存
=就是用来区分输入参数
- 汇编多指令两种写法:
__asm__ volatile("mov %0, %1\n" \
"mov %0, %1\n" \
: "=a"(output) \
: "a"(input) \
);
每行用换行符分割,作为一行的结束
__asm__ volatile("mov %0, %1;" \
"mov %0, %1;" \
: "=a"(output) \
: "a"(input) \
); \
每行用;分割,作为一行的结束
__asm__ volatile("mov %0, %1; \
mov %0, %1;" \
: "=a"(output) \
: "a"(input) \
); \
相当于直接使用一条指令,用\进行连接
值得注意的是,在gcc编译嵌入汇编,汇编指令中寄存器访问需要加两个%%
-
定义寄存器变量:
register char ___res; 编译器会为我们分配寄存器
如果想指定寄存器,那么register char ___res asm(“‘eax’”);
-
当使用标号“0”代表使用同上个位置相同的寄存器
-
编译器规定把输出输入寄存器统一编号,顺序从输出寄存器序列从左到右从上到下以%0开始,分别记为 %0 %1
-
看一下常用的限定符
-
关于volatile也可以放在没有返回值的函数前面,volatile void print();这样的好处是明确告诉编译器该函数不会返回,这样就可以让gcc产生更好的代码
asm volatile ( "movl %%eax, %%ecx\n"
"movl %%ebx, %%eax\n"
"mov %%ecx, %%ebx\n"
: "=a"(output), "=b"(input)
: "a"(output), "b"(input)
);
printf("input=%d, output = %d\n", input, output);
汇编交换两个值,注意的是,input output 既可以在输入的地方,也可以在输出的地方
-
使用汇编可以做什么?
在Linux中,应用层想要调用内核层的函数,不能直接调用,需要使用系统调用函数,而调用系统调用的方式就是通过汇编,使用软中断实现,INT 80H , INT是使用Linux内核指令,80H是中断向量号,用于执行系统调用
fd文件描述符,0代表标准输入,1代表标准输出,2代表异常输出
代码:
asm volatile (
"movl $4, %%eax\n"
"movl $1, %%ebx\n"
"movl %0, %%ecx\n"
"movl %1, %%edx\n"
"int $0x80 \n"
:
: "r"(s), "r"(len)
: "eax", "ebx", "ecx", "edx"
);
对于 sys_write这个系统调用,需要%eax寄存器写入4,代表选择sys_write, ebx寄存器代表fd的值,ecx代表字符串指针,edx代表写的个数
注意的是,嵌入式汇编除了汇编模板外,其他参数都可以省略
当省略的参数在中间时,对应分隔符 “:” 不可省略
当省略保留列表时,对应分隔符可以省略
当可选参数被省略,寄存器前使用单个%作为参数
当可选参数不被省略,寄存器前使用两个%%作为参数