汇编

汇编

1.编辑、汇编、链接和运行(或调试)

在我的汇编学习过程中,采用Netwide汇编器(NASM)编写代码。

使用的文本编辑器:vim

Linux默认编译器链接器:GCC

GCC代表GNU编译器集合,是Linux上的标准编译器和链接器工具。

安装GCC:sudo apt install gcc

安装NASM:sudo apt install build-essential nasm

在命令行中输入nasm -v如果安装成功则会返回版本号相应

  • 代码中,使用制表符,空格,换行符使代码更具有可读性
  • 每行一条指令
  • ; 后的文字使注释,计算机会忽略注释

(1)编辑代码

例:hello.asm的makefile

;hello.asm
section .data	;data段
	msg db 		"hello,world",10,0;data段声明变量msg
	msglen equ $-msg-1;msg长度,使用equ来计算,$表示当前地址,该表达式意思是当前地址减去msg的最后减一是减去结尾的0
section .bss	;bss段
section .text
	global main	;声明主函数
main:
	;push rbp
	;mov rbp,rsp
	;这段是函数序言,在这种简单的程序中不需要使用
	mov rax,1;1表示写入
	mov rdi,1;1表示标准输出
	mov rsi,msg;将变量msg加入rsi
	mov rdx,msglen;msg的长度
	syscall;执行系统调用,则在rsi中的msg将会标准输出到终端
	mov rax,60;60表示退出
	mov rdi,0;成功退出代码0放入rdi
	syscall;执行系统调用,终止进程

上述是一个简单的汇编程序,用于在终端上输出”hello,world“,接下来将结合这个程序了解汇编的基本结构。

  • .data段在section .data中,使用以下格式声明和定义初始化数据:

<变量名称> <类型> <值>

如果data段中包含变量,则源代码被汇编并链接到可执行文件是,将为该变量分配内存。变量名称是符号名称,对内存位置和变量的引用可以占用一个或多个内存位置。变量名称是指变量在内存中的起始地址。

  • .bss段(Block Start by Symbol),用来存放未初始化的变量。未初始化变量的空间在此段中声明,格式如下:

<变量名称> <类型> <数字>

.bss段的变量不包括任何值;这些值将在稍后的执行中分配。内存位置不是在编译时保留的,而是在执行中保留的。当程序开始执行时,程序会从操作系统中请求所需的内存,分配给.bss段的变量并初始化为0.如果执行时没有足够的内存可用于.bss的变量,程序将崩溃。

  • .text段是所有操作所在的位置;
    • 在上面的代码中,main:被称为标签

    • 系统调用代码1被放入寄存器rax中,表示”写入“。

    • 在Intel的汇编风格中,对于指令mov,mov destination,source表示将source的内容复制到destination中(注意,是复制

    • 在代码中,用于写入的输出目标存储在寄存器rdi中,1表示标准输出(在本例中,是输出在屏幕上)

    • 要显示的字符串的地址被放入寄存器rsi中

    • rdx中放置字符串的长度,(注意不能计算字符串结尾的0,它表示NULL,无意义)

    • 执行系统调用syscall,(个人理解)将执行前面处理好的寄存器状态和变量。syscall是对操作系统的函数调用

    • 为避免程序完成时出现错误消息,需要清除程序出口,首先要将60写入rax,表示退出,接下来吧表示成功的退出代码0放入rdi,然后执行系统调用。这样就能正常退出而不报错

      mov指令的一些用法:

      mov 寄存器 内存

      mov 寄存器 即时值

      mov 内存 寄存器

      不合法:mov 内存 内存

(2)编译

将前面提到的代码编辑入文件hello.asm中:

vim hello.asm

复制粘贴或者自己写都可以

然后创建makefile文件:

vim makefile

写入以下内容:

#hello.asm的makefile
hello:hello.o	;hello依赖于hello.o
	gcc -o hello hello.o -no-pie
hello.o:hello.asm	;hello.o依赖于hello.asm
	nasm -f elf64 -g -F dwarf hello.asm -l hello.lst

Makefile 是一种用于自动化编译和构建项目的工具,它会依据文件之间的依赖关系和规则来决定执行哪些命令。在这个 Makefile 中,存在两个规则,分别用于生成目标文件 hello.o 和可执行文件 hello

hello.o 是目标文件(Object File)

目标文件在整个软件开发和程序构建过程中扮演着重要角色,主要用途如下:

1. 模块化编译

在大型项目中,源代码通常会被分割成多个源文件,每个源文件可以独立进行编译。例如,一个项目可能包含多个 .c.asm 文件,通过分别编译这些文件,可以生成对应的目标文件。这样做的好处是,当某个源文件发生修改时,只需重新编译该文件对应的目标文件,而不需要重新编译整个项目,从而大大提高了编译效率。

2. 代码复用

目标文件可以被其他程序重复使用。例如,你编写了一个通用的数学库,将其编译成目标文件后,其他项目在需要使用该数学库的功能时,只需将这个目标文件与自己的目标文件链接在一起即可,无需重新编写数学库的代码。

3. 链接成可执行文件

目标文件本身不能直接运行,需要通过链接器(如 gccld 等)将一个或多个目标文件以及必要的库文件链接在一起,生成最终的可执行文件。在 Makefile 中,gcc -o hello hello.o -no-pie 这一命令就是将 hello.o 目标文件链接成可执行文件 hello

4. 调试信息存储

目标文件可以包含调试信息,如上述 Makefile 中使用的 -g -F dwarf 选项,会让 nasm 汇编器在生成目标文件时,将调试信息以 DWARF(Debugging With Attributed Record Formats)格式存储在目标文件中。这些调试信息可以帮助调试器(如 gdb)在调试程序时,将机器码映射回源代码,方便开发者定位和解决问题。

在命令行中输入:

makefile

这样系统会自动生成hello hello.o hello.lst

查看hello.lst:

     1                                  ;hello.asm
     2                                  section .data	;data段
     3 00000000 68656C6C6F2C776F72-     	msg db 		"hello,world",10,0;data段声明变量msg
     3 00000009 6C640A00           
     4                                  	msglen equ $-msg-1;msg长度,使用equ来计算,$表示当前地址,该表达式意思是当前地址减去msg的最后减一是减去结尾的0
     5                                  section .bss	;bss段
     6                                  section .text
     7                                  	global main	;声明主函数
     8                                  main:
     9                                  	;push rbp
    10                                  	;mov rbp,rsp
    11                                  	;这段是函数序言,在这种简单的程序中不需要使用
    12 00000000 B801000000              	mov rax,1;1表示写入
    13 00000005 BF01000000              	mov rdi,1;1表示标准输出
    14 0000000A 48BE-                   	mov rsi,msg;将变量msg加入rsi
    14 0000000C [0000000000000000] 
    15 00000014 BA0C000000              	mov rdx,msglen;msg的长度
    16 00000019 0F05                    	syscall;执行系统调用,则在rsi中的msg将会标准输出到终端
    17 0000001B B83C000000              	mov rax,60;60表示退出
    18 00000020 BF00000000              	mov rdi,0;成功退出代码0放入rdi
    19 00000025 0F05                    	syscall;执行系统调用,终止进程

第一列是行号,第二列是分配的内存地址(八位,并且从0开始),第三列是将汇编指令转换为机器码的十六进制表示在这其中的一些0是用于填充和内存对齐的,用于优化代码功能,以获得尽可能小的可执行文件和最快的代码。值得一提的是,机器码是第一代编程语言,而汇编则是第二代。

posted @ 2025-03-20 21:46  w0e6x  阅读(28)  评论(0)    收藏  举报