汇编程序学习

在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

求最大值

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,%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
  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), a d d r = a r r a y + o f f + e d i ∗ 4 addr=array+off+edi*4 addr=array+off+edi4
缺省用法:

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

%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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值