汇编程序学习
迪丽瓦拉
2024-03-01 23:42:29
0

在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,%eaxmovl $2,%ebxint $0x80

求最大值

movq64位操作数
movl32位操作数
movw16位操作数
movb8位操作数

其它指令也遵循这一规则
比较指令的使用方式

.section .data
data_items:
.long 3,67,234,98,1,3,234,93,234,93,0
.section .text
.globl _start
_start:movl $0,%edimovl data_items(,%edi,4),%eaxmovl %eax,%ebx
start_loop:cmpl $0,%eaxje loop_exitincl %edimovl data_items(,%edi,4), %eaxcmpl %ebx,%eaxjle start_loopmovl %eax,%ebxjmp start_loop
loop_exit:movl $1,%eaxint $0x80
  1. .byte 为1字节
  2. .int 为2字节
  3. .long 为4字节
  4. .ascii 后跟字符串,需手动添加\0,例 “hello world\0”

寻址模式

movl variable(base_offset,index,step_length)
variablestep_lenth必须为常数,缺省为0
base_offsetindex必须为寄存器,缺省为0
movl array(,%edi,4),addr=array+off+edi∗4addr=array+off+edi*4addr=array+off+edi∗4
缺省用法:

  1. movl 0x3ffff,%eax直接读内存地址不常用。
  2. movl (%eax),%ebx
  3. movl 2(%eax),%ebx

gcc嵌入式汇编

不使用c的变量直接用asm包起来,单行

#include
int func(){asm("movl $23,%eax");
}
int main(){printf("%d ",func());
}

volatile,防止编译器优化,重排指令

#include
int func(){asm volatile ("movl $23,%eax");
}
int main(){printf("%d ",func());
}

由于是嵌入式汇编,那么我们自然希望能在汇编中使用c语言的全局变量,静态变量和局部变量、参数等。

内嵌汇编

简单的内嵌汇编语法结构

asm asm-qualifiers (AssemblerInstructions)
  1. asm 可以直接写asm,但是但编译选项里有"-ansi"和“-std”选项时,这时的C方言无法识别asm,建议使用__asm__.即用__asm__适用范围更广
  2. asm-qualifiers,包括三个
    • volatile主要用于扩展内嵌汇编,简单的默认是volatile的了
    • inline 期望生成最短的代码
  3. 里面包含汇编代码的字符串,注意多行字符串写可用如下写法,并且注意换行时加“\”符号。多行写法技巧如下类比。
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

  1. volatile 禁掉编译器潜在的优化
    • 如果asm块里的代码赋值的变量没有被下文使用,代码可能丢弃
    • 如果代码在循环内每一轮运行都是一样的,可能被外提
    • 如果代码每次的返回值固定,则多次调用可能被合并。
    • 如果代码没有输出和goto声明,则其默认是volatile的
  2. inline,生成最小尺寸代码
  3. goto,包括跳转

AssemblerTemplate

代码模板,像汇编指令,但又有所扩展,就好比c++模板函数和普通函数的区别一样。

OutputOperands

输出操作数,即汇编代码写出的变量的值,这些变量即是输出操作数

InputOperands

输入操作数,即汇编代码读入变量的值,这些变量即是输入操作数

Clobbers

被汇编代码改变的寄存器和变量

GotoLabels

用到的C语言标签
实例 :输入操作数,即我们的指令用到的寄存器的值需要从变量里获得
反汇编发现,gcc喜欢把寄存器参数给压到栈上,暂时跳过。

#include
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
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
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;}
}
  1. 当我们把一个寄存器或内存声明为输入时,则在进入汇编前,gcc会生成代码把数据读入
  2. 当我们把一个寄存器或内存声明为输出时,则在离开汇编时,gcc会生成代码把数据读出
  3. 当我们把一个寄存器声明为输出且带&时,则是建议在最后一次操作该操作数后,就应把数据读出,否则在接下来的指令中可能会破坏这个值。

%0 和%1会使用同一个寄存器

#include
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
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);
}

相关内容