精华内容
下载资源
问答
  • 为什么linux没有汇编语言
    千次阅读
    2021-05-15 07:49:40

    linux下的汇编教程

    第一部分 Linux下ARM汇编语法尽管在Linux下使用C或C++编写程序很方便,但汇编源程序用于系统最基本的初始化,如初始化堆栈指针、设置页表、操作 ARM的协处理器等。初始化完成后就可以跳转到C代码执行。需要注意的是,GNU的汇编器遵循AT&T的汇编语法,可以从GNU的站点(www.gnu.org)上下载有关规范。

    一. Linux汇编行结构

    任何汇编行都是如下结构:

    [:] [} @ comment

    [:] [} @ 注释

    Linux ARM 汇编中,任何以冒号结尾的标识符都被认为是一个标号,而不一定非要在一行的开始。

    【例1】定义一个"add"的函数,返回两个参数的和。

    .section .text, “x”

    .global add @ give the symbol add external linkage

    add:

    ADD r0, r0, r1  @ add input arguments

    MOV pc, lr @ return from subroutine

    @ end of program

    二. Linux 汇编程序中的标号

    标号只能由a~z,A~Z,0~9,“.”,_等字符组成。当标号为0~9的数字时为局部标号,局部标号可以重复出现,使用方法如下:

     标号f: 在引用的地方向前的标号

     标号b: 在引用的地方向后的标号

    【例2】使用局部符号的例子,一段循环程序

    1:

    subs r0,r0,#1        @每次循环使r0=r0-1

    bne 1f         @跳转到1标号去执行

    局部标号代表它所在的地址,因此也可以当作变量或者函数来使用。

    三. Linux汇编程序中的分段

    (1).section伪操作

    用户可以通过.section伪操作来自定义一个段,格式如下:

    .section section_name [, "flags"[, %type[,flag_specific_arguments]]]

    每一个段以段名为开始, 以下一个段名或者文件结尾为结束。这些段都有缺省的标志(flags),连接器可以识别这些标志。(与armasm中的AREA相同)。

    下面是ELF格式允许的段标志

    含义

    a 允许段

    w 可写段

    x 执行段

    【例3】定义段

    .section .mysection @自定义数据段,段名为 “.mysection”

    .align  2

    strtemp:

    .ascii  "Temp string \n\0"

    (2)汇编系统预定义的段名

    .text  @代码段

    .data  @初始化数据段

    .bss  @未初始化数据段

    .sdata @

    .sbss  @

    需要注意的是,源程序中.bss段应该在.text之前。

    四. 定义入口点

    汇编程序的缺省入口是 start标号,用户也可以在连接脚本文件中用ENTRY标志指明其它入口点。

    【例4】定义入口点

    .section.data

    .section .bss

    .section .text

    .globl _start

    _start:

    五. Linux汇编程序中的宏定义

    格式如下:

    .macro 宏名 参数名列表   @伪指令.macro定义一个宏

    宏体

    .endm  @.endm表示宏结束

    如果宏使用参数,那么在宏体中使用该参数时添加前缀“\”。宏定义时的参数还可以使用默认值。

    可以使用.exitm伪指令来退出宏。

    【例5】宏定义

    .macro SHIFTLEFT a, b

    .if \b ”表示不相等,其他的符号如:+、-、*、/、%、、>>、|、&、^、!、==、>=、 {,}

    分配number_of_bytes字节的数据空间,并填充其值为fill_byte,若未指定该值,缺省填充0。(与armasm中的SPACE功能相同)

    (10).word  {,} …

    插入一个32-bit的数据队列。(与armasm中的DCD功能相同)

    可以使用.word把标识符作为常量使用

    例如:

    Start:

    valueOfStart:

    .word Start

    这样程序的开头Start便被存入了内存变量valueOfStart中。

    (11).hword  {,} …

    插入一个16-bit的数据队列。(与armasm中的DCW相同)

    八. GNU ARM汇编特殊字符和语法

    代码行中的注释符号: ‘@’

    整行注释符号: ‘#’

    语句分离符号: ‘;’

    直接操作数前缀: ‘#’ 或 ‘$’

    第二部分 GNU的编译器和调试工具

    一. 编译工具

    1.编辑工具介绍

    GNU提供的编译工具包括汇编器as、C编译器gcc、C++编译器g++、连接器ld和二进制转换工具objcopy。基于ARM平台的工具分别为 arm-linux-as、arm-linux-gcc、arm-linux-g++、arm-linux-ld和arm-linux- objcopy。GNU的编译器功能非常强大,共有上百个操作选项,这也是这类工具让初学者头痛的原因。不过,实际开发中只需要用到有限的几个,大部分可以采用缺省选项。GNU工具的开发流程如下:编写C、C++语言或汇编源程序,用gcc或g++生成目标文件,编写连接脚本文件,用连接器生成最终目标文件(elf格式),用二进制转换工具生成可下载的二进制代码。

    (1)编写C、C++语言或汇编源程序

    通常汇编源程序用于系统最基本的初始化,如初始化堆栈指针、设置页表、操作ARM的协处理器等。初始化完成后就可以跳转到C代码执行。需要注意的是,GNU的汇编器遵循AT&T的汇编语法,读者可以从GNU的站点(www.gnu.org)上下载有关规范。汇编程序的缺省入口是 start标号,用户也可以在连接脚本文件中用ENTRY标志指明其它入口点(见下文关于连接脚本的说明)。

    (2)用gcc或g++生成目标文件

    如果应用程序包括多个文件,就需要进行分别编译,最后用连接器连接起来。如笔者的引导程序包括3个文件:init.s(汇编代码、初始化硬件)xmrecever.c(通信模块,采用Xmode协议)和flash.c(Flash擦写模块)。

    分别用如下命令生成目标文件: arm-linux-gcc-c-O2-oinit.oinit.s arm-linux-gcc-c-O2-oxmrecever.oxmrecever.c arm-linux-gcc-c-O2-oflash.oflash.c 其中-c命令表示只生成目标代码,不进行连接;-o命令指明目标文件的名称;-O2表示采用二级优化,采用优化后可使生成的代码更短,运行速度更快。如果项目包含很多文件,则需要编写makefile文件。关于makefile的内容,请感兴趣的读者参考相关资料。

    (3)编写连接脚本文件

    gcc等编译器内置有缺省的连接脚本。如果采用缺省脚本,则生成的目标代码需要操作系统才能加载运行。为了能在嵌入式系统上直接运行,需要编写自己的连接脚本文件。编写连接脚本,首先要对目标文件的格式有一定了解。GNU编译器生成的目标文件缺省为elf格式。elf文件由若干段(section)组成,如不特殊指明,由C源程序生成的目标代码中包含如下段:.text(正文段)包含程序的指令代码;.data(数据段)包含固定的数据,如常量、字符串;.bss(未初始化数据段)包含未初始化的变量、数组等。C++源程序生成的目标代码中还包括.fini(析构函数代码)和. init(构造函数代码)等。连接器的任务就是将多个目标文件的.text、.data和.bss等段连接在一起,而连接脚本文件是告诉连接器从什么地址开始放置这些段。例如连接文件link.lds为:

    ENTRY(begin)

    SECTION

    {

    .=0x30000000;

    .text:{*(.text)}

    .data:{*(.data)}

    .bss:{*(.bss)}

    }

    其中,ENTRY(begin)指明程序的入口点为begin标号;.=0x00300000指明目标代码的起始地址为0x30000000,这一段地址为MX1的片内RAM;.text:{*(.text)}表示从0x30000000开始放置所有目标文件的代码段,随后的.data:{* (.data)}表示数据段从代码段的末尾开始,再后是.bss段。

    (4)用连接器生成最终目标文件

    有了连接脚本文件,如下命令可生成最终的目标文件:

    arm-linux-ld –no stadlib –o bootstrap.elf -Tlink.lds init.o xmrecever.o flash.o

    其中,ostadlib表示不连接系统的运行库,而是直接从begin入口;-o指明目标文件的名称;-T指明采用的连接脚本文件(也可以使用-Ttext address,address表示执行区地址);最后是需要连接的目标文件列表。

    (5)生成二进制代码

    连接生成的elf文件还不能直接下载执行,通过objcopy工具可生成最终的二进制文件:

    arm-linux-objcopy –O binary bootstrap.elf bootstrap.bin

    其中-O binary指定生成为二进制格式文件。Objcopy还可以生成S格式的文件,只需将参数换成-O srec。还可以使用-S选项,移除所有的符号信息及重定位信息。如果想将生成的目标代码反汇编,还可以用objdump工具:

    arm-linux-objdump -D bootstrap.elf

    至此,所生成的目标文件就可以直接写入Flash中运行了。

    2.Makefile实例

    example: head.s  main.c

    arm-linux-gcc -c -o head.o head.s

    arm-linux-gcc -c -o main.o main.c

    arm-linux-ld -Tlink.lds head.o ain.o -o example.elf

    arm-linux-objcopy -O binary -S example_tmp.o example

    arm-linux-objdump -D -b binary -m arm  example >ttt.s

    二. 调试工具

    Linux下的GNU调试工具主要是gdb、gdbserver和kgdb。其中gdb和gdbserver可完成对目标板上Linux下应用程序的远程调试。gdbserver是一个很小的应用程序,运行于目标板上,可监控被调试进程的运行,并通过串口与上位机上的gdb通信。开发者可以通过上位机的 gdb输入命令,控制目标板上进程的运行,查看内存和寄存器的内容。gdb5.1.1以后的版本加入了对ARM处理器的支持,在初始化时加入- target==arm参数可直接生成基于ARM平台的gdbserver。gdb工具可以从ftp: //ftp.gnu.org/pub/gnu/gdb/上下载。

    对于Linux内核的调试,可以采用kgdb工具,同样需要通过串口与上位机上的gdb通信,对目标板的Linux内核进行调试。可以从http://oss.sgi.com/projects/kgdb/上了解具体的使用方法。

    参考资料:

    1. Richard Blum,Professional Assembly Language

    2. GNU ARM 汇编快速入门,http://blog.chinaunix.net/u/31996/showart.php?id=326146

    3. ARM GNU 汇编伪指令简介,http://www.cppblog.com/jb8164/archive/2008/01/22/41661.aspx

    4. GNU汇编使用经验,http://blog.chinaunix.net/u1/37614/showart_390095.html

    5. GNU的编译器和开发工具,http://blog.ccidnet.com/blog-htm-do-showone-uid-34335-itemid-81387-type-blog.html

    6. 用GNU工具开发基于ARM的嵌入式系统,http://blog.163.com/liren0@126/blog/static/32897598200821211144696/

    7. objcopy命令介绍,http://blog.csdn.net/junhua198310/archive/2007/06/27/1669545.aspx

    bss = "Block Started by Symbol" (由符号启始的区块)

    这个缩写也许有其它说法,但事实上我们采用这个缩写的本意是

    "Block Started by Symbol"。它是 FAP 上的伪指令,FAP

    (Fortran Assembly [-er?] Program) 是指 IBM 704-709-7090-7094

    这种机型的组译器。这个指令可定义自己的标号,并且预留一定数目

    的字组空间。

    更多相关内容
  • Linux下的汇编语言.pdf

    2022-07-04 11:25:36
    Linux下的汇编语言.pdf
  • linux内嵌汇编语言

    2018-09-03 10:14:52
    虽然Linux的核心代码大部分是用C语言编写的,但是不可避免的其中还是有一部分是用汇编语言写成的。有些汇编语言代码是直接写在汇编源程序中的,特别是Linux的启动代码部分;还有一些则是利用gcc的内嵌汇编语言嵌在...
  • 《深入理解程序设计:使用Linux汇编语言》介绍了Linux平台下的汇编语言编程,教你从计算机的角度看问题,从而了解汇编语言及计算机的工作方式,成就自己的优秀程序员之梦夯实基础。 很多人都认为汇编语言晦涩难懂...
  • as linux 入门as linux 入门as linux 入门as linux 入门as linux 入门as linux 入门
  • Linux 汇编语言开发指南Linux 汇编语言开发指南Linux 汇编语言开发指南
  • 汇编语言基于Linux

    2018-11-29 09:28:09
    学习linux内核,一定要学习汇编语言,这是一本完全基于linux讲解汇编的书
  • 汇编语言讲解 主要是linux源码中用到的 简单明了 很快熟悉汇编语言
  • 深入理解程序设计使用Linux汇编语言_(美)JONATHAN+BRATLETT著;郭晴霞译 .pdf
  • Linux汇编语言书两本

    2018-07-02 15:12:18
    涉及到Linux的嵌入式开发的两本汇编语言。都是非常经典的外文翻译过来的,对Linux汇编语言编写嵌入式程序有很大帮助。
  • 汇编语言编程的好教材,希望对大家有所帮助
  • Linux汇编语言编译

    2020-10-16 21:34:58
    Linux汇编的使用一、安装nasm编译器二、编译过程简介三、编译实例 一、安装nasm编译器 输入以下命令安装nasm编译器 sudo apt install nasm 二、编译过程简介 1、预处理 预处理的过程主要包括以下过程: (1) 将...

    一、安装nasm编译器

    输入以下命令安装nasm编译器

    sudo apt install nasm
    

    在这里插入图片描述

    二、编译过程简介

    1、预处理
    预处理的过程主要包括以下过程:
    (1) 将所有的#define 删除,并且展开所有的宏定义,并且处理所有的条件预编 译指令,比如#if #ifdef #elif #else #endif 等。
    (2) 处理#include 预编译指令,将被包含的文件插入到该预编译指令的位置。
    (3) 删除所有注释“//”和“/* */”。
    (4) 添加行号和文件标识,以便编译时产生调试用的行号及编译错误警告行号。
    (5) 保留所有的#pragma 编译器指令,后续编译过程需要使用它们。

    2、编译
    就是对预处理完的文件进行一系列的词法分析,语法分析,语义分析及 优化后生成相应的汇编代码。

    3、汇编
    汇编过程调用对汇编代码进行处理,生成处理器能识别的指令,保存在后缀为.o
    的目标文件中。由于每一个汇编语句几乎都对应一条处理器指令,因此,汇编相
    对于编译过程比较简单,通过调用 Binutils 中的汇编器 as 根据汇编指令和处理
    器指令的对照表一一翻译即可。
    当程序由多个源代码文件构成时,每个文件都要先完成汇编工作,生成.o 目标 文件后,才能进入下一步的链接工作。注意:目标文件已经是最终程序的某一部 分了,但是在链接之前还不能执行。

    4、链接
    链接到静态库或动态库,可参考另一篇文档
    https://blog.csdn.net/zmhDD/article/details/109100294

    三、编译实例

    1、编辑文档 hello.asm

    ; hello.asm 
    section .data            ; 数据段声明
            msg db "Hello, world!", 0xA     ; 要输出的字符串
            len equ $ - msg                 ; 字串长度
    section .text            ; 代码段声明
    global _start            ; 指定入口函数
    _start:                  ; 在屏幕上显示一个字符串
            mov edx, len     ; 参数三:字符串长度
            mov ecx, msg     ; 参数二:要显示的字符串
            mov ebx, 1       ; 参数一:文件描述符(stdout) 
            mov eax, 4       ; 系统调用号(sys_write) 
            int 0x80         ; 调用内核功能
                             ; 退出程序
            mov ebx, 0       ; 参数一:退出代码
            mov eax, 1       ; 系统调用号(sys_exit) 
            int 0x80         ; 调用内核功能
    
    

    2、先生成中间 .o 文件

    nasm -f elf64 hello.asm
    

    在这里插入图片描述
    3、生成可执行文件

    ld -s -o hello hello.o
    

    在这里插入图片描述
    4、执行结果
    在这里插入图片描述

    展开全文
  • linux下用汇编语言编写的代码具有两种不同的形式,第一种是完全汇编代码,第二种是内嵌汇编代码,各种编译器都做了扩充,linux平台下的GCC就是
  • Linux下的汇编语言程序开发.pdf
  • 一、简介作为最基本的编程语言之一,汇编语言虽然应用的范围不算很广,但重要性却勿庸置疑,因为它能够完成许多其它语言所无法完成的功能。就拿 Linux 内核来讲,虽
  • 《深入理解程序设计:使用Linux汇编语言》介绍了Linux平台下的汇编语言编程,教你从计算机的角度看问题,从而了解汇编语言及计算机的工作方式,成就自己的优秀程序员之梦夯实基础。很多人都认为汇编语言晦涩难懂,...
  • Linux汇编语言AT&T开发指南,这是经过剪裁后不包含广告链接和文字的版本。
  • 汇编语言基于linux环境,第三版!对于掌握linux环境下的汇编很有帮助!
  • 开源汇编器,支持x86机器汇编语言,功能比较强大,类似于NASM及GAS等工具
  • Linux 汇编语言开发指南 汇编语言的优点是速度快 可以直接对硬件进行操作 这对诸如图形处理等关键应用是非常重要的Linux 是一个用 C 语言开发的操作系统 这使得很多程序员开始忘记在 Linux 中还可以直接使用汇编这一...
  • 汇编语言基于Linux环境(第3版)Assembly Language Step-by-Step - Programming with Linux, 3rd Edition
  • ARM Linux 启动代码汇编语言部分分析(noname LINUX , 分析
  • Linux汇编语言

    千次阅读 2018-07-18 08:13:07
    .word相当于int类型,是GNU汇编中的元素,word=32bit=4字节。 2.关键字.global .global就是相当于C语言中的Extern,声明此变量,并且告诉链接器此变量是全局的,外部可以访问 。 3.标号 _start后面加上一个...

    1.类型.word

    .word相当于int类型,是GNU汇编中的元素,word=32bit=4字节。

    2.关键字.global

    .global就是相当于C语言中的Extern,声明此变量,并且告诉链接器此变量是全局的,外部可以访问

    3.标号

    _start后面加上一个冒号’ : ,表示其是一个标号Label,类似于C语言goto后面的标号。而_start标号后面的:

    b    reset

    就是跳转到对应的标号为reset的位置。

    4.如何查看C或者汇编的源代码对应的真正的汇编代码

    首先解释一下,由于汇编代码中会存在一些伪指令等内容,所以,写出来的汇编代码,并不一定是真正可以执行的代码,这些 类似于伪指令的汇编代码,经过汇编器,转换或翻译成真正的可以执行的汇编指令。所以,上面才会有将“汇编源代码”转换为“真正的汇编代码”这说。然后,此处对于有些人不是很熟悉的,如何查看源代码真正对应的汇编代码呢?此处,对于汇编代码,有两种:

    一种是只是经过编译阶段,生成了对应的汇编代码。

    另外一种是,编译后的汇编代码,经过链接器链接后,对应的汇编代码。

    总的来说,两者区别很小,后者主要是更新了外部函数的地址等等,对于汇编代码本身,至少对于我们一般所去查看源代码所对应的汇编来说,两者可以视为没区别。

    【查看源码所对应的汇编代码】

    1)对于编译所生成的汇编的查看方式是:

    用交叉编译器的 dump 工具去将汇编代码都导出来:

    arm-linux-objdump –d cpu/s5pc11x/start.o > uboot_start.o_dump_result.txt

    返样就把 start.o 中的汇编代码导出到 uboot_start.o_dump_result.txt 中了。

    然后查看 uboot_start.o_dump_result.txt,即可找到对应的汇编代码。

    2)对于链接所生成的汇编的查看方式是:

    和上面方法一样,即:

    arm-linux-objdump –d u-boot > whole_uboot_dump_result.txt

    然后打开该 txt,找到 stack_setup 部分的代码:

    两者不一样地方在于,我们 uboot 设置了 text_base,即代码段的基地址,上面编译后的汇编代码,经过链接后,更新了对应的基地址,所以看起来,所以代码对应的地址,都变了,但是具体地址中的汇编代码,除了个别调用凼数的地址和跳转到某个标号的地址外,其他都还是一样的。

    5.LDR指令

    首先我们要看一下ARM架构:LDR/STR架构:

    LDR指令的格式为:

    LDR{条件} 目的寄存器,<存储器地址>

    LDR指令用于从存储器中将一个32位的字数据传送到目的寄存器中。该指令通常用于从存储器中读取32位的字数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。该指令在程序设计中比较常用,取寻址方式灵活多样,请读者认真掌握。

    指令示例:

    LDR R0,[R1] ;                     将存储器地址为R1的字数据读入寄存器R0

    LDR R0,[R1,R2] ;                    将存储器地址为R1+R2的字数据读入寄存器R0

    LDR R0,[R1,#8] ;                          将存储器地址为R1+8的字数据读入寄存器R0

    LDR R0,[R1,R2]!;                 将存储器地址为R1+R2的字数据读入寄存器R0,幵将新地址R1+R2

    入R1

    LDR R0,[R1,#8];            将存储器地址为R1+8的字数据读入寄存器R0,幵将新地址R1+8写入R1

    LDR R0,[R1],R2 ;          将存储器地址为R1的字数据读入寄存器R0,幵将新地址R1+R2写入R1

    LDR R0,[R1,R2,LSL#2]!;       将存储器地址为R1+R2×4的字数据读入寄存器R0,幵将新地址R1+R2×4写入R1

    LDRR0,[R1],R2,LSL#2 ;           将存储器地址为R1的字数据读入寄存器R0,幵将新地址

    R1+R2×4写入R1

     “ARM是RISC结构,数据从内存到CPU之间的移动只能通过L/S指令来完成,也就是ldr/str指令。比如想把数据从内存中某处读取到寄存器中,只能使用ldr

    比如:

    ldr r0, 0x12345678

    就是把0x12345678这个地址中的值存放到r0中。

    6.MRS指令

    MRS指令的格式为:

    MRS{条件} 通用寄存器,程序状态寄存器(CPSR戒SPSR

    MRS指令用于将程序状态寄存器的内容传送到通用寄存器中。该指令一般用在以下两种情冴:

    Ⅰ.当需要改变程序状态寄存器的内容时,可用MRS将程序状态寄存器的内容读入通用寄存器,修改后再写回程序状态寄存器。

    Ⅱ.当在异常处理或进程切换时,需要保存程序状态寄存器的值,可先用该指令读出程序状态寄存器的值,然后保存。

    指令示例:

    MRS R0,CPSR ;传送CPSR的内容到R0

    MRS R0,SPSR ;传送SPSR的内容到R0

    所以,上述汇编代码含义为,将CPSR的值赋给R0寄存器

    7.bic指令

    BIC指令的格式为:

    BIC{条件}{S} 目的寄存器,操作数1,操作数2

    BIC指令用于清除操作数1的某些位,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。操作数2为32位的掩码,如果在掩码中设置(置1)了某一位,则清除返一位。未设置的掩码位保持不变。

    而0x1f=11111b

    所以,此行代码的含义就是,清除r0的bit[4:0]

    8.orr指令

    ORR指令的格式为:

    ORR{条件}{S} 目的寄存器,操作数1,操作数2

    ORR指令用于在两个操作数上迕行逻辑戒运算,幵把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于设置操作数1的某些位。

    指令示例:

    ORR R0,R0,#3 该指令设置R0的0、 1位,其余位保持不变。

    所以此行汇编代码的含义为:

    而0xd3=1101 0111

    将r0与0xd3算数或运算,然后将结果给r0,即把r0的bit[7:6]和bit[4]和bit[2:0]置为1

    9.msr指令

    MSR指令的格式为:

    MSR{条件} 程序状态寄存器(CPSR或SPSR)_<域>,操作数

    MSR指令用于将操作数的内容传送到程序状态寄存器的特定域中。其中,操作数可以为通用寄存器戒立即数。 <域>用于设置程序状态寄存器中需要操作的位,32位的程序状态寄存器可分为

    4个域:

    位[31:24]为条件标志位域,用f表示;

    位[23:16]为状态位域,用s表示;

    位[15:8]为扩展位域,用x表示;

    位[7:0]为控制位域,用c表示;

    该指令通常用于恢复戒改变程序状态寄存器的内容,在使用时,一般要在MSR指令中指明将要操作的域。

    指令示例:

    MSR CPSR,R0 ;传送R0的内容到CPSR

    MSR SPSR,R0 ;传送R0的内容到SPSR

    MSR CPSR_c,R0 ;传送R0的内容到SPSR,但仅仅修改CPSR中的控制位域”

    此行汇编代码含义为,将r0的值赋给CPSR

    10.align

    .balignl是.balign的变体

     .align伪操作用于表示对齐方式:通过添加填充字节使当前位置

    满足一定的对齐方式。.balign的作用同.align

    .align {alignment} {,fill} {,max} 其中:alignment用于指定对齐方式,可能的取值为2的次幂,缺省为4。fill是填充内容,缺省用0填充。max是填充字节@数最大值,如果填充字节数超过max,  就不进行对齐,例如:

    .align 4  /* 指定对齐方式为字对齐 */

    11.macro----.endm

    https://www.crifan.com/files/doc/docbook/uboot_starts_analysis/release/htmls/images/macro_syntax.png

    所以,此处就相当于一个无参数的宏bad_save_user_regs,也就相当于一个函数了。

    12.ldmstm指令

    https://www.crifan.com/files/doc/docbook/uboot_starts_analysis/release/htmls/images/ldm_stm_syntax.png

    其中条件码的含义:

    https://www.crifan.com/files/doc/docbook/uboot_starts_analysis/release/htmls/images/cond_format.png

    批量数据加载/存储指令ARM微处理器所支持批量数据加载/存储指令可以一次在一片连续的存储器单元和多个寄存器之间传送数据,批量加载指令用于将一片连续的存储器中的数据传送到多个寄存器,批量数据存储指令则完成相反的操作。常用的加载存储指令如下:

    LDM(或STM)指令的格式为:

    LDM(或STM){条件}{类型} 基址寄存器{!},寄存器列表{∧}

    LDM(或STM)指令用于从由基址寄存器所指示的一片连续存储器到寄存器列表所指示的多个寄存器之间传送数据,该指令的常见用途是将多个寄存器的内容入栈或出栈。

    其中,{类型}为以下几种情况:IA 每次传送后地址加1;IB 每次传送前地址加1;DA 每次传送后地址减1;DB 每次传送前地址减1;FD 满递减堆栈;ED 空递减堆栈;FA 满递增堆栈;EA 空递增堆栈;{!}为可选后缀,若选用该后缀,则当数据传送完毕之后,将最后的地址写入基址寄存器,否则基址寄存器的内容不改变。基址寄存器不允许为R15,寄存器列表可以为R0~R15的任意组合。{∧}为可选后缀,当指令为LDM且寄存器列表中包含R15,选用该后缀时表示:除了正常的数据传送之外,还将SPSR复制到CPSR。同时,该后缀还表示传入或传出的是用户模式下的寄存器,而不是当前模式下的寄存器。

    指令示例:

    STMFD R13!,{R0,R4-R12,LR} ;将寄存器列表中的寄存器(R0,R4到R12,LR)存入堆栈。LDMFD R13!,{R0,R4-R12,PC} ;将堆栈内容恢复到寄存器(R0,R4到R12,LR)。所以,此行的含义是,将r0到r12的值,一个个地传送到对应的地址上,基地址是sp的值,传完一个,sp的值加4,一直到传送完为止。此处,可见,前面那行代码:sp = sp - 72就是为此处传送r0到r12,共13个寄存器,地址空间需要13*4=72个字节,

    即前面sp减去72,就是为了腾出空间,留此处将r0到r12的值,放到对应的位置的。

    此处的含义就是,将_armboot_start中的值,参考前面内容,即为_start,而_start的值:从 Nor Flash启动时:_stat=0relocate代码之后为:_start=TEXT_BASE=0x33D00000此处是已经relocate代码了,所以应该理解为后者,即_start=0x33D00000所以:r2=0x33D00000

    13.Ldr伪指令

    伪指令,就是“伪”的指令,是针对“真”的指令而言的。真的指令就是那些常见的指令,比如上面说的arm的ldr,bic,msr等等指令,是arm体系架构中真正存在的指令,你在arm汇编指令集中找得到对应的含义。而伪指令是写出来给汇编程序看的,汇编程序能看的伪指令具体表示的是啥意思,然后将其翻译成真正的指令戒者迕行相应的处理。

    伪指令ldr诧法和含义:

    http://blog.csdn.net/lihaoweiV/archive/2010/11/24/6033003.aspx

    “另外迓还有一个就是ldr伪指令,虽然ldr伪指令和ARM的ldr指令很像,但是作用不太一样。 ldr伪指令可以在立即数前加上=,以表示把一个地址写到某寄存器中,比如:ldr r0, =0x12345678这样样,就把 0x12345678 这个地址写到 r0 中了。所以,ldr 伪指令和 mov 是比较相似的。”

    叧丌过 mov 指令后面的立即数是有限制的,返个立即数,能够必须由一个 8 位的二迚制数,即属亍 0x00-0xFF 内的某个值,经过偶数次右移后得到,这样才是合法数据,而 ldr 伪指令没有返个限制。

    那为何 ldr 伪指令的操作数没有限制呢,那是因为其是伪指令,写出来的伪指令,最终会被编译器解释成为真正的,合法的指令的,一般都是对应的 mov 指令。返样的话,写汇编程序的时候,使用 MOV 指令是比较麻烦的,因为有些简单的数据比较容易看出来,有些数据即丌容易看出来是否是合法数据。所以,对此,ldr 伪指令的出现,就是为了解决返个问题的,你叧管放心用 ldr 伪指令,丌用关心操作数,而写出的 ldr 伪指令,编译器会帮你翻译成对应的真正的汇编指令的。而关亍编译器是如何将返些ldr伪指令翻译成为真正的汇编指令的,我的理解是,其自劢会去算出来对应的操作数,是否是合法的mov 的操作数,如果是,就将该ldr伪指令翻译成mov指令,否则就用别的方式处理,我所观察到的,其中一种方式就是,单独申请一个4字节的空间用于存放操作数,然后用ldr指令实现。

    14.Mov指令

    mov指令语法:

    “1、 MOV指令

    MOV指令的格式为:

    MOV{条件}{S} 目的寄存器,源操作数

    MOV指令可完成从另一个寄存器、被移位的寄存器或将一个立即数加载到目的寄存器。其中S选项决定指令的操作是否影响CPSR中条件标志位的值,当没有S时指令丌更新CPSR中条件标志位的值。

    指令示例:

    MOV R1,R0 ;将寄存器R0的值传送到寄存器R1

    MOV PC,R14 ;将寄存器R14的值传送到PC,常用亍子程序返回

    MOV R1,R0,LSL#3 ;将寄存器R0的值左移3位后传送到R1

    不过对于MOV指令多说一句,那就是,一般可以用类似亍:

    MOV R0,R0

    的指令来实现NOP操作。

    15.cmp指令

    cmp是比较指令,cmp的功能是相当于减法指令,只是不保存结果.cmp指令执行后,将对标志寄存器产生影响.其他相关指令通过识别这些被影响的标志寄存器来得知比较结果.

    功能: 计算操作对象1 - 操作对象2 但不保存结果,仅仅根据计算结果对标志寄存器进行设置.比如cmp ax,ax  是做ax - ax 的运算,结果为0,但并不在ax中保存,仅影响flag的相关各位.

    指令执行后: zf = 1,pf = 1,sf = 0,cf = 0,of = 0;

      假设现在AX寄存器中的数是0002H,BX寄存器中的数是0003H。
    执行的指令是:CMP  AX,  BX
       执行这条指令时,先做用AX中的数减去BX中的数的减法运算。

    列出二进制运算式子:

               0000 0000 0000 0010
          -  0000 0000 0000 0011
    _________________________________
    (借位1) 1111 1111 1111 1111
    所以,运算结果是 0FFFFH
    根据这个结果,各标志位将会被分别设置成以下值:
             CF=1,因为有借位
             OF=0,未溢出
             SF=1,结果是负数
             ZF=0,结果不全是零
          还有AF, PF等也会相应地被设置。
        CMP 比较指令做了减法运算以后,根据运算结果设置了各个标志位。标志位设置过以后,0FFFFH这个减法运算的结果就没用了,它被丢弃,不保存。执行过了CMP指令以后,除了CF,ZF,OF, SF,等各个标志位变化外,其它的数据不变。

    对照普通的减法指令 SUB   AX,  BX,它们的区别就在于:

         SUB指令执行过以后,原来AX中的被减数丢了,被换成了减法的结果。
         CMP指令执行过以后,被减数、减数都保持原样不变。

    16.Sub指令

    SUB指令介绍:

    SUB:不带借位的减法指令。

    指令格式:SUB OP1,OP2
     

    指令功能:(OP1)←(OP1)-(OP2),将OP1-OP2的值,保存在OP1中,如:SUB [EAX],1 以EAX寄存器为内存地址,将该地址的值减1,

    指令介绍

    目的操作数减去源操作数,结果放在目的操作数中。源操作数原有内容不变,并根据运算结果置标志位SF,ZF,AF,PF,CF,OF

    SUB指令可以进行字节或字的减法运算,源操作数和目的操作数的约定与ADD指令相同。

    操作数的类型可以根据程序员的要求约定为带符号数或者无符号数。当无符号数的较小数减去较大数时,因不够减而产生借位,此时进位标志CF置1.当带符号数的较小数减去较大数时,将会得到负的结果,则符号位SF置1.带符号数相减,如果溢出,则OF置1.
    【例】
    1. SUB BL,AL
    设(BL)=23H,(AL)=78H,(BL)=23H-78H=ABH(1010101)
    根据运算结果,各标志位为:CF=1,ZF=0,SF=1,OF=0,PF=0,AF=1
    2. SUB SI,SI
    寄存器自身相减,则结果为零,此时:
    OF=0,SF=0,ZF=1,PF=1,CF=0

    展开全文
  • as86 - as86-8086..80386处理器的汇编程序
  • 很简单,使用gcc即可,我们写如下程序:pop eax复制代码保存为文本文件,命名为test.s然后使用gcc在32位的环境下编译gcc test.s复制代码结果会报错,为什么呢?/usr/bin/ld: /tmp/ccj87S8M.o: relocation R_X86_64_...

    很简单,使用gcc即可,我们写如下程序:

    pop eax

    复制代码

    保存为文本文件,命名为test.s

    然后使用gcc在32位的环境下编译

    gcc test.s

    复制代码

    结果会报错,为什么呢?

    /usr/bin/ld: /tmp/ccj87S8M.o: relocation R_X86_64_32S against undefined >symbol eax' can not be used when making a PIE object; recompile with -fPIC /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o: In function_start':

    (.text+0x20): undefined reference to `main'

    /usr/bin/ld: final link failed: Invalid operation

    collect2: error: ld returned 1 exit status

    因为gcc默认汇编是要编译链接一气呵成的。这里我们使用参数-c,即可只编译不链接。

    gcc -c test.s

    复制代码

    这样就出来了一个test.o文件,里面只有pop eax的机器码,我们可以使用objdump -d查看。

    objdump -d test.o

    复制代码

    可以看到pop eax被转化为了popq,为什么呢?因为gcc采用的是AT&T 语法pop eax要写成pop %eax再编译才行5df9b5593c23b70fef92a7b417957ff5.png

    既然gcc编译这个并不准确,那么有什么是准确的呢?我们可以采用nasm,nasm使用intel语法,兼容这个格式。

    没安装的可以安装下,很快

    apt install nasm

    复制代码

    之后使用nasm编译这个文件

    nasm -f elf32 test.s

    复制代码

    之后生成的test.o文件就可以用objdump正常分析。08b88346178010b286bc2289d819630f.png

    我们也可以在objdump后面加-M intel来转化为intel语法,这种语法没有百分号。

    401cfcb031631c7585c38f7c5ee8957b.png

    除此之外,直接使用pwntools里面的函数似乎更加方便,比如

    from pwn import *

    context.arch='i386' #指定架构

    print(asm('mov eax, 0'))

    复制代码

    结果

    b'\xb8\x00\x00\x00\x00'

    复制代码

    如果有报错没装binutil,请参考这个页面安装对应的,比如安装在Mac OS:

    brew install https://raw.githubusercontent.com/Gallopsled/pwntools-binutils/master/osx/binutils-$ARCH.rb

    复制代码

    这里的$ARCH一般只要填i386或者amd64就行了。

    展开全文
  •  自己一直用Windows操作系统,但是我们的项目是在Linux系统上,这两者的汇编语言系统是不同的,windows一直是Intel汇编,而Linux是At&T汇编。为了调试的方便,我决定现在windows中尝试着写点嵌入式汇编,然后再在...
  • Linux GNU汇编语言

    2011-11-14 14:02:20
    不错的介绍GNU汇编的文档,通过这个文档可以了解GNU汇编的基本知识。
  • Linux中的汇编语言.pdf

    2021-09-27 13:02:17
    Linux中的汇编语言.pdf
  • 汇编,要能吃苦,有耐心,持之以恒,才能成功。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 81,542
精华内容 32,616
热门标签
关键字:

为什么linux没有汇编语言