汇编:Linux汇编基本框架与系统调用

在Linux操作系统下进行汇编编程时,基本的汇编程序框架通常包括以下几个部分:

全局段声明(section declarations):定义数据段、代码段等。

入口点(entry point) :程序的执行起点,通常为_startmain

系统调用(system calls):通过操作系统提供的接口完成基本功能,如输入输出。

以下是一个简单的32位Linux汇编程序的框架示例,使用了NASM汇编器:

1.创建.asm汇编文件
sudo vim Hello.asm
2.编写汇编代码

这段汇编代码是一个在Linux系统下编写的简单程序,使用系统调用输出"Hello World!"并换行,然后退出程序。

section .data
    msg db "Hello World!",0xa
    len equ $ - msg
​
section .text
    global _start
​
_start:
    mov edx,len
    mov ecx,msg
    mov ebx,1
    mov eax,4
    int 0x80
​
    mov ebx,0
    mov eax,1
    int 0x80
数据段(section .data):

section .data:定义数据段,包含已初始化的数据。

msg db "Hello World!",0xa:定义一个字符串"Hello World!",并在末尾添加换行符(ASCII码为0xa)。db指令用于定义字节序列(data byte)。

len equ $ - msg:定义一个常量len,其值是当前地址($)减去字符串msg的起始地址,即字符串的长度。这里,$表示当前汇编位置的地址,因此$ - msg计算了字符串msg的长度。

代码段(section .text):

section .text:定义代码段,包含程序的指令。

global _start:定义入口点_start,告诉链接器程序的起始位置。

程序入口(_start):

_start::程序的入口点。

1)打印内容

mov edx, len:将字符串长度len(即12,包括换行符)加载到寄存器edx中。这是系统调用write所需的第三个参数,表示要写入的字节数。

mov ecx, msg:将字符串地址msg加载到寄存器ecx中。这是系统调用write所需的第二个参数,表示要写入的缓冲区地址。

mov ebx, 1:将文件描述符1(标准输出stdout)加载到寄存器ebx中。这是系统调用write所需的第一个参数,表示目标文件描述符。

mov eax, 4:将系统调用号4sys_write)加载到寄存器eax中。这告诉内核要执行write系统调用。

int 0x80:触发中断0x80,向内核发出系统调用请求。

这段代码执行的是write系统调用,具体调用格式如下:

复制代码
ssize_t write(int fd, const void *buf, size_t count);

在汇编中,参数按照以下顺序放入寄存器:

  1. fd(文件描述符):ebx

  2. buf(缓冲区地址):ecx

  3. count(字节数):edx

2)退出程序

mov ebx, 0:将退出状态码0(表示正常退出)加载到寄存器ebx中。

mov eax, 1:将系统调用号1sys_exit)加载到寄存器eax中。这告诉内核要执行exit系统调用。

int 0x80:再次触发中断0x80,向内核发出系统调用请求。

这段代码执行的是exit系统调用,具体调用格式如下:

复制代码
void _exit(int status);

在汇编中,参数按照以下顺序放入寄存器:

  1. status(退出状态码):ebx

通过这段代码,程序将输出"Hello World!"并换行,然后正常退出

更多的系统调用可以根据程序架构查看文件:
复制代码
/usr/include/asm/unistd_32.h    #x86架构程序的系统调用头文件
​
/usr/include/asm/unistd_64.h    #x64架构程序的系统调用头文件

这两个文件分别定义了32位架构和64位架构汇编的系统调用的编号;系统调用号在汇编程序中非常重要,因为它们用于指示内核执行特定的操作。例如,sys_write系统调用的编号在unistd_64.h上是1;在unistd_32.h上是4。

3.生成目标文件(汇编)
nasm -f elf32 Hello.asm 

使用 nasm 汇编器将 Hello.asm 汇编成目标文件 Hello.o;这条命令会生成一个名为 Hello.o 的目标文件。

-f指定格式位32位的elf格式(因为上述代码中使用的系统调用是32位的系统调用)所以此时在汇编的使用要指定32位架构格式的目标文件。

4.链接

使用 ld 链接器将目标文件 Hello.o 链接成可执行文件 Hello32;将32位目标文件链接成可执行文件。

bash 复制代码
ld -m elf_i386 -s -o Hello32 Hello.o
5.执行程序
bash 复制代码
./Hello32

尽管大多数情况下x64系统可以运行x86程序,但有些情况下可能会出现兼容性问题:

复制代码
①缺少32位库:如果系统中没有安装需要的32位库或特定的依赖项,32位程序将无法运行。
②系统调用和内核模块:某些32位程序可能直接使用特定的系统调用或内核模块,这些调用或模块在64位系统中不可用或行为不同。
③依赖特定硬件特性:某些32位程序可能依赖于特定的硬件特性或设备驱动,这些特性或驱动在64位系统中不可用或需要特定的32位支持。

这里顺便记录一下简答的x64汇编架构;

以下是一个简单的32位Linux汇编程序的框架示例,同样使用NASM汇编器:

这段汇编代码是一个Linux 64平台下的程序,实现了输出"Hello World"并换行,并正常退出的功能。

bash 复制代码
section .data
    msg db "Hello World",0xa
    len equ $ - msg
​
section .text
    global _start
​
_start:
    mov edx,len
    mov esi,msg
    mov edi,1
    mov eax,1  ;调用号为1因为是64位程序所以对应的调用号可以在/usr/include/asm/unistd_64.h文件中进行查阅
    syscall
​
    mov edi,0   ;调用号为0因为是64位程序所以对应的调用号可以在/usr/include/asm/unistd_64.h文件中查阅
    mov eax,60
    syscall

这段代码实现了以下功能:

  1. 输出字符串

    • 使用sys_write系统调用(系统调用号为1),它的参数分别是:edx寄存器存放字符串长度,esi寄存器存放字符串的地址,edi寄存器存放文件描述符(1表示标准输出)。

    • 执行syscall指令来触发系统调用。

  2. 退出程序

    • 使用sys_exit系统调用(系统调用号为60),它的参数是退出状态码,这里设置为0表示正常退出。

    • 再次执行syscall指令来触发系统调用。

相关推荐
并不会28 分钟前
常见 CSS 选择器用法
前端·css·学习·html·前端开发·css选择器
衣乌安、31 分钟前
【CSS】居中样式
前端·css·css3
兔老大的胡萝卜32 分钟前
ppk谈JavaScript,悟透JavaScript,精通CSS高级Web,JavaScript DOM编程艺术,高性能JavaScript pdf
前端·javascript
低代码布道师34 分钟前
CSS的三个重点
前端·css
耶啵奶膘2 小时前
uniapp-是否删除
linux·前端·uni-app
王哈哈^_^4 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie4 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic5 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿5 小时前
webWorker基本用法
前端·javascript·vue.js
Ai 编码助手5 小时前
MySQL中distinct与group by之间的性能进行比较
数据库·mysql