在64位下编译32位汇编
as --32 exit.s -o exit.o
ld exit.o -o exit -m elf_i386
在64位下编译64位汇编
as exit.s -o exit.o
ld exit.o -o exit
.section .data
.section .text
.globl _start
_start:
movl $1,%eax
movl $2,%ebx
int $0x80
求最大值
movq | 64位操作数 |
---|---|
movl | 32位操作数 |
movw | 16位操作数 |
movb | 8位操作数 |
其它指令也遵循这一规则
比较指令的使用方式
.section .data
data_items:
.long 3,67,234,98,1,3,234,93,234,93,0
.section .text
.globl _start
_start:
movl $0,%edi
movl data_items(,%edi,4),%eax
movl %eax,%ebx
start_loop:
cmpl $0,%eax
je loop_exit
incl %edi
movl data_items(,%edi,4), %eax
cmpl %ebx,%eax
jle start_loop
movl %eax,%ebx
jmp start_loop
loop_exit:
movl $1,%eax
int $0x80
- .byte 为1字节
- .int 为2字节
- .long 为4字节
- .ascii 后跟字符串,需手动添加\0,例
“hello world\0”
寻址模式
movl variable(base_offset,index,step_length)
variable
和step_lenth
必须为常数,缺省为0
base_offset
和index
必须为寄存器,缺省为0
例 movl array(,%edi,4)
,
a
d
d
r
=
a
r
r
a
y
+
o
f
f
+
e
d
i
∗
4
addr=array+off+edi*4
addr=array+off+edi∗4
缺省用法:
movl 0x3ffff,%eax
直接读内存地址不常用。movl (%eax),%ebx
movl 2(%eax),%ebx
gcc嵌入式汇编
不使用c的变量直接用asm包起来,单行
#include<stdio.h>
int func(){
asm("movl $23,%eax");
}
int main(){
printf("%d ",func());
}
volatile,防止编译器优化,重排指令
#include<stdio.h>
int func(){
asm volatile ("movl $23,%eax");
}
int main(){
printf("%d ",func());
}
由于是嵌入式汇编,那么我们自然希望能在汇编中使用c语言的全局变量,静态变量和局部变量、参数等。
内嵌汇编
简单的内嵌汇编语法结构
asm asm-qualifiers (AssemblerInstructions)
- asm 可以直接写asm,但是但编译选项里有"-ansi"和“-std”选项时,这时的C方言无法识别asm,建议使用__asm__.即用__asm__适用范围更广
- asm-qualifiers,包括三个
- volatile主要用于扩展内嵌汇编,简单的默认是volatile的了
- inline 期望生成最短的代码
- 里面包含汇编代码的字符串,注意多行字符串写可用如下写法,并且注意换行时加“\”符号。多行写法技巧如下类比。
int main(){
printf("a"\
"b");
printf("a\
b");
}
扩展型内嵌汇编
优点:能够使用C语言的变量和跳转标签,更加智能化,且编译器能感知到内部的代码。
缺点:放弃了完全控制生成的机器码的样式,由编译器按需增加一些汇编代码。
不使用goto的语法结构
asm asm-qualifiers ( AssemblerTemplate
: OutputOperands
[ : InputOperands
[ : Clobbers ] ])
使用goto时的语法结构
asm asm-qualifiers ( AssemblerTemplate
: OutputOperands
: InputOperands
: Clobbers
: GotoLabels)
asm-qualifiers
- volatile 禁掉编译器潜在的优化
- 如果asm块里的代码赋值的变量没有被下文使用,代码可能丢弃
- 如果代码在循环内每一轮运行都是一样的,可能被外提
- 如果代码每次的返回值固定,则多次调用可能被合并。
- 如果代码没有输出和goto声明,则其默认是volatile的
- inline,生成最小尺寸代码
- goto,包括跳转
AssemblerTemplate
代码模板,像汇编指令,但又有所扩展,就好比c++模板函数和普通函数的区别一样。
OutputOperands
输出操作数,即汇编代码写出的变量的值,这些变量即是输出操作数
InputOperands
输入操作数,即汇编代码读入变量的值,这些变量即是输入操作数
Clobbers
被汇编代码改变的寄存器和变量
GotoLabels
用到的C语言标签
实例 :输入操作数,即我们的指令用到的寄存器的值需要从变量里获得
反汇编发现,gcc喜欢把寄存器参数给压到栈上,暂时跳过。
#include<stdio.h>
int add(int a,int b){
asm ("addl %1,%0"
::"a"(b),"r"(a));
}
int add2(int a,int b){
return a+b;
}
int main(){
printf("%d ",add(3,44));
printf("%d ",add2(3,44));
}
#include<stdio.h>
int add(int a,int b){
asm ("addl %1,%0"
:"+a"(b),"+r"(a));
}
int add2(int a,int b){
return a+b;
}
int main(){
printf("%d ",add(3,44));
printf("%d ",add2(3,44));
}
获取最低非0位,利用bsfl
指令
#include<stdio.h>
int main(){
unsigned int num=1;
int width;
for(int i=0;i<32;i++){
__asm__("bsfl %1,%0":"=r"(width):"r"(num));
printf("%d ",width);
num<<=1;
}
}
- 当我们把一个寄存器或内存声明为输入时,则在进入汇编前,gcc会生成代码把数据读入
- 当我们把一个寄存器或内存声明为输出时,则在离开汇编时,gcc会生成代码把数据读出
- 当我们把一个寄存器声明为输出且带&时,则是建议在最后一次操作该操作数后,就应把数据读出,否则在接下来的指令中可能会破坏这个值。
%0 和%1会使用同一个寄存器
#include<stdio.h>
int main(){
int a=1,b=2;
asm volatile(
"movl $10,%0\n\t"
"movl $20,%1"
:"=r"(a)
:"r"(b)
);
printf("a=%d,b=%d",a,b);
}
#include<stdio.h>
int main(){
int a=1,b=2;
asm volatile(
"movl $10,%0\n\t"
"movl $20,%1"
:"=&r"(a)
:"r"(b)
);
printf("a=%d,b=%d",a,b);
}