• 汇编程序学习


    在64位下编译32位汇编

    as --32 exit.s -o exit.o
    ld exit.o -o exit -m elf_i386
    
    • 1
    • 2

    在64位下编译64位汇编

    as exit.s -o exit.o
    ld exit.o -o exit
    
    • 1
    • 2
    .section .data
    .section .text
    .globl _start
    _start:
        movl $1,%eax
        movl $2,%ebx
        int $0x80
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    求最大值

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    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
    int func(){
    	asm("movl $23,%eax");
    }
    int main(){
    	printf("%d ",func());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    #include
    int func(){
    	asm volatile ("movl $23,%eax");
    }
    int main(){
    	printf("%d ",func());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    内嵌汇编

    简单的内嵌汇编语法结构

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

    扩展型内嵌汇编

    优点:能够使用C语言的变量和跳转标签,更加智能化,且编译器能感知到内部的代码。
    缺点:放弃了完全控制生成的机器码的样式,由编译器按需增加一些汇编代码。

    不使用goto的语法结构

    asm asm-qualifiers ( AssemblerTemplate
    				: OutputOperands
    				[ : InputOperands
    				[ : Clobbers ] ])
    
    • 1
    • 2
    • 3
    • 4

    使用goto时的语法结构

    asm asm-qualifiers ( AssemblerTemplate
    				: OutputOperands
    				: InputOperands
    				: Clobbers
    				: GotoLabels)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    #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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    获取最低非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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    #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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    RabbitMQ之消息应答和持久化
    c++基础(六)——深拷贝与浅拷贝
    [前端]Preprocessor dependency "less" not found.
    【Linux】GDB调试
    家校协同小程序实战教程
    武汉大学:浅议重保期间的邮件防护
    (a /b)*c的值
    LLM_入门指南(零基础搭建大模型)
    CBitmap、CreateCompatibleBitmap、CreateBitmap
    NLP-Beginner:自然语言处理入门练习----task 2基于机器学习的文本分类
  • 原文地址:https://blog.csdn.net/weixin_39057744/article/details/128080696