做影视网站对服务器要求,网站的运营,三门峡网站建设电话,网站是什么样子的更好的阅读体验#xff0c;请点击 YinKai s Blog 。 内存段
上面讨论的汇编程序的三个部分#xff0c;也代码各种内存段。
有趣的是#xff0c;如果将 section 关键字替换为 segment#xff0c;将会得到相同的结果#xff0c;这是因为对于汇编器而言#xff0c;这… 更好的阅读体验请点击 YinKai s Blog 。 内存段
上面讨论的汇编程序的三个部分也代码各种内存段。
有趣的是如果将 section 关键字替换为 segment将会得到相同的结果这是因为对于汇编器而言这两个关键字在某些上下文中是可以互相使用的这两个关键字都是为了告诉汇编器下面的代码是代码段。
内存段
在分段内存模型中系统内存被划分为不同的独立段组每个段组由位于段寄存器中的指针引用。
每个段用于包含特定类其型的数据。其中一个段用于包含指令代码另一个段用于存储数据元素第三个段用于保存程序堆栈。
这种划分使得程序可以更灵活地管理内存有选择地引用不同类型的数据和指令从而更有效地执行各种计算任务。
因此我们可以将各种内存段指定为
**数据段由 .data 部分和 .bss 部分表示。 .data 部分用于声明内存区域其中为程序存储数据元素该部分在数据元素声明后无法扩展并且在整个程序中保持静态。.bss 部分也是一个静态内存部分其中包含稍后在程序中声明的数据的缓冲区。该缓冲区被零填充。**代码段**它由 .text 部分表示。这定义了内存中存储指令代码的区域。这也是一个固定区域。**堆栈**该段包含传递给程序内的函数和过程的数据值。
寄存器
处理器操作主要涉及对数据的处理而数据通常存储在内存中。然而内存访问可能会降低处理器速度因为它需要通过控制总线发送请求并进行复杂的内存访问。
为了提高速度处理器包含一些内部存储位置称为寄存器。
处理器寄存器
IA-32架构中包含 10 个 32 位和 6 个 16 位的处理器寄存器主要分为三类
**通用寄存器**通用寄存器用于存储临时数据进行算术、逻辑运算等操作。**控制寄存器**控制寄存器用于控制和反映处理器的状态。**段寄存器**段寄存器用于存储各个段的起始地址实现内存访问和管理。
通用寄存器进一步可以分为
数据寄存器指针寄存器索引寄存器
数据寄存器
在IA-32架构中有四个32位的数据寄存器分别是EAX、EBX、ECX、EDX。这些寄存器可以按照不同的位数划分为更小的寄存器具体如下
作为完整的32位数据寄存器EAX、EBX、ECX、EDX。32 位寄存器的下半部分可用作四个 16 位数据寄存器AX、BX、CX 和 DX。上述4个16位寄存器的下半部分和上半部分可以用作8个8位数据寄存器AH、AL、BH、BL、CH、CL、DH和DL。
一些数据寄存器在算术运算中具有特定用途
AX 主累加器用于输入/输出和大多数算术指令。例如在乘法运算中根据操作数的大小将一个操作数存储在EAX或AX或AL寄存器中。BX 被称为基址寄存器用于索引寻址。CX 被称为计数寄存器与ECX一样存储迭代操作中的循环计数。DX 数据寄存器用于输入/输出操作与AX寄存器和DX一起使用用于涉及大值的乘法和除法运算。
指针寄存器
指针寄存器是指 32 位的 EIP、ESP 和 EBP 寄存器以及相应的 16 位 右部分 IP、SP 和 BP。
指针寄存器可以分为三类
**指令指针IP**16 位 IP 寄存器存储下一条要执行的指令的偏移地址。 IP 与 CS 寄存器代码段如CS : IP关联给出了代码段中当前指令的完整地址。堆栈指针SP 16 位 SP 寄存器提供程序堆栈内的偏移值。 SP 与 SS 寄存器堆栈段SS:SP相关指的是程序堆栈中数据或地址的当前位置。基址指针BP 16 位 BP 寄存器主要帮助引用传递给子程序的参数变量。 SS 寄存器中的地址与 BP 中的偏移量相结合得到参数的位置。 BP 还可以与 DI、SI索引寄存器 组合作为基址寄存器进行特殊寻址。
索引寄存器
索引寄存器包括32位的 ESI 和 EDI 以及它们的 16 位最右边的部分。SI 和 DI 通常用于索引寻址并有时用于执行加法和减法操作。这两个索引指针分别是
来源索引 (SI) 用作字符串操作的源索引。在字符串处理中SI通常用于指向源字符串的当前位置。目的地索引 (DI) 用作字符串操作的目标索引。DI通常用于指向目标字符串的当前位置特别是在字符串复制等操作中。
控制寄存器
控制寄存器包括 32 位指令指针寄存器和 32 位标志寄存器用于管理程序的执行流程和状态。其中的常见标志位有 溢出标志 (OF) 表示有符号算术运算后数据的高位最左位是否溢出。 方向标志 (DF) 确定移动或比较字符串数据的左或右方向。DF为0时字符串操作从左到右DF为1时字符串操作从右到左。 中断标志 (IF) 决定是否忽略或处理外部中断如键盘输入。IF为0时禁用外部中断为1时启用中断。 陷阱标志 (TF) 允许将处理器设置为单步模式以便一次执行一条指令常用于调试。 符号标志 (SF) 显示算术运算结果的符号由最左边位的高位表示。SF为0表示正结果为1表示负结果。 零标志 (ZF) 表示算术或比较运算的结果是否为零。ZF为1表示零结果为0表示非零结果。 辅助进位标志 (AF) 包含算术运算后从位 3 到位 4 的进位用于特殊的算术操作。 奇偶校验标志 (PF) 表示算术运算结果中1位的总数用于奇偶校验。PF为1表示奇数个1位为0表示偶数个1位。 进位标志 (CF) 包含算术运算后从高位最左边的进位也存储shift或rotate操作的最后一位内容。
段寄存器
段在计算机内存中是为了组织和管理存储空间而引入的概念。在汇编编程中处理器通过段寄存器来访问内存位置。以下是关于段的主要信息
代码段CS 包含要执行的指令的区域。由 16 位代码段寄存器CS 寄存器存储代码段的起始地址。 数据段DS 包含数据、常量和工作区的区域。由 16 位数据段寄存器DS 寄存器存储数据段的起始地址。 堆栈段SS 包含过程或子例程的数据和返回地址实现为堆栈数据结构。由16位堆栈段寄存器SS 寄存器存储堆栈的起始地址。 其他段寄存器 额外段ES 提供额外的段来存储数据。FS 和 GS 提供额外的段用于特定目的。
在汇编编程中程序需要访问内存位置。段内的所有内存位置都相对于段的起始地址。段从可被 16 整除的地址开始因此所有这类内存地址中最右边的十六进制数字通常是 0。为了引用段中的任何内存位置处理器将段寄存器中的段地址与该位置的偏移值组合起来。
示例
下面的程序会在代码中输出 9 个连续的星号。
section .textglobal _start ;必须为链接器声明gcc_start: ;告诉链接器入口点mov edx,len ;消息长度mov ecx,msg ;要写入的消息mov ebx,1 ;文件描述符stdoutmov eax,4 ;系统调用号sys_writeint 0x80 ;调用内核mov edx,9 ;消息长度mov ecx,s2 ;要写入的消息mov ebx,1 ;文件描述符stdoutmov eax,4 ;系统调用号sys_writeint 0x80 ;调用内核mov eax,1 ;系统调用号sys_exitint 0x80 ;调用内核section .data
msg db Displaying 9 stars,0xa ;一条消息
len equ $ - msg ;消息的长度
s2 times 9 db * ;9个星号 我们使用以下命令进行编译和执行
nasm -f elf nine_stars.asm
ld -m elf_i386 -s -o nine_stars nine_stars.o 输出结果如下
Displaying 9 stars
*********系统调用
系统调用是用户空间和内核空间之间接口的 API。我们之前已经使用过了 sys_write 和 sys_exit 这两个系统调用分别用于写入屏幕和退出程序。
Linux 系统调用
我们在汇编程序中使用系统调用需要按照如下步骤
将系统调用号放入 EAX 寄存器中将系统调用的参数存储在 EBX、ECX 等寄存器中调用相关中断0x80然后执行 EAX 中的系统调用号对应的程序结果通常返回 EAX 寄存器中
可以存储系统调用参数的存储器有 基址寄存器 EBX、计数寄存器 ECX、数据寄存器 EDX、源索引寄存器 ESI、目标索引寄存器 EDI、基址指针寄存器 EBP。
下面给大家演示一下几个示例
1使用 sys_exit
mov eax, 1 ; 系统调用号 sys_exit
int 0x80 ; 调用内核 2使用 sys_write
mov edx, 4 ; 消息长度
mov ecx, msg ; 要写入的消息
mov ebx, 1 ; 文件描述符
mov eax, 4 ; 系统调用号
int 0x80 ; 调用内核常见系统调用
%eaxName%ebx%ecx%edx%esx%edi1sys_exitint----2sys_forkstruct pt_regs----3sys_readunsigned intchar *size_t--4sys_writeunsigned intconst char *size_t--5sys_openconst char *intint--6sys_closeunsigned int----
示例
下面举一个复杂一点的例子包含了之前我们讲过的 data、bss、text 三个部分也希望通过这个例子加深一下大家对 data 部分和 bss 部分的区别
section .data ; 数据段userMsg db 请输入一个数字 ; 提示用户输入数字的消息lenUserMsg equ $-userMsg ; 消息的长度dispMsg db 您输入的是 lenDispMsg equ $-dispMsg section .bss ; 未初始化的数据num resb 5 ; 用于存储用户输入的变量5字节section .text ; 代码段global _start ; 声明程序入口点_start: ; 程序入口; 输出提示消息 请输入一个数字 mov eax, 4mov ebx, 1mov ecx, userMsgmov edx, lenUserMsgint 80h; 读取并存储用户输入mov eax, 3mov ebx, 2mov ecx, num mov edx, 5 ; 读取5字节的信息数字和符号1字节int 80h ; 输出消息 您输入的是 mov eax, 4mov ebx, 1mov ecx, dispMsgmov edx, lenDispMsgint 80h ; 输出用户输入的数字mov eax, 4mov ebx, 1mov ecx, nummov edx, 5int 80h ; 退出程序mov eax, 1mov ebx, 0int 80h 同样我们需要通过下述命令来编译运行
nasm -f elf get_num.asm # 将汇编程序编译成机器码
ld -m elf_i386 -s -o get_num get_num.o # 将目标文件和其他必要的文件组合成可执行文件
./get_num # 运行可执行文件 输出结果如下
请输入一个数字
123
您输入的是 123