【头歌】完整汇编语言程序设计

news2024/11/24 19:03:16

摘自头歌实训​​​​​​

目录

相关知识

1.1 RISC-V 汇编语言程序基本结构

1.2 RISC-V 汇编语言程序主要元素

1.2.1 汇编指令

1.2.2 标签

1.2.3 汇编指示语句

1.3 RISC-V 汇编语言程序示例


相关知识

RISC-V 操作数类型、基本调用约定等已在前序关卡中介绍,前序关卡主要完成 C 语言基本语句到 RISC-V 汇编指令的转换。本关卡将通过一个简单的 C 语言程序与其对应的 RISC-V 汇编语言程序向同学们介绍完整的汇编语言程序的基本结构。

1.1 RISC-V 汇编语言程序基本结构

RISC-V 汇编语言表示为文本文件,主要包含四种主要元素:

  • 汇编指令(Assembly instructions):包含助忆符和相关参数序列(即操作数)的字符序列,可被汇编器转换成机器指令。例如,字符串 addi a0, a1, 1 包含助忆符 addi 及其操作数 a0a1 和 1
  • 汇编指示语句(Assembly directives):用于辅助汇编的命令,通过汇编器转换成实际的若干条汇编指令,或通过汇编器进行解释。例如,指示语句 .word 10 告诉汇编器将一个 32 位 (.word)的值 10 汇编到程序中。汇编指示语句编码为字符串,包含一个以点(.)为前缀的指示语句名(directive name)及其对应的参数
  • 注释(Comments):用于提示代码相关信息,不影响目标代码生成,汇编器处理时丢弃注释语句
  • 标签(Labels):表示程序位置,通常用冒号(:)标记,可以用于标记程序位置,其他汇编指令或者汇编命令(如汇编指示语句)可通过标签查找程序位置

汇编器丢弃注释等操作通过一个汇编前处理过程实现,将所有注释丢弃,并删除多余的空格,处理后的汇编程序仅包含三种元素:标签、汇编指令和汇编指示语句。若分别用 <label><instruction><directive> 表示有效的标签、汇编指令和汇编指示语句,则完成的汇编语言程序可以用下图所示的正则表达式表示:

即:

  • 程序(PROGRAM) 由 若干行(LINES) 组成
  • 若干行(LINES) 包含至少一行(LINE),该行后可以通过换行符出现其他行
  • 一行(LINE) 有以下形式:
    • 空行([x] 代表 x 是可选内容,可以不出现)
    • 仅包含一个标签
    • 包含一个标签、后跟一条汇编指令
    • 仅包含一条汇编指令
    • 包含一个标签、后跟一条汇编指示语句
    • 仅包含一条汇编指示语句

1.2 RISC-V 汇编语言程序主要元素

1.2.1 汇编指令

汇编指令主要包含实际汇编指令和伪指令,其中的汇编指令可直接与 RISC-V 指令对应,例如 addi x0, x0, 0 可直接对应机器指令 00000013 (十六进制形式)。伪指令则没有直接对应的 RISC-V 指令,但是汇编器会将伪指令转换为若干条对应功能的机器指令,例如 nop 伪指令代表 空操作,该指令不存在实际的 RISC-V 对应指令,但是由于 addi x0, x0, 0 指令相当于不执行任何操作,因此汇编器将伪指令 nop 转换为 addi x0, x0, 0mv 伪指令代表将一个寄存器的值复制到另一个寄存器,因此伪指令 mv a5, a7 可以转换为实际指令 addi a5, a7, 0

汇编指令中的操作数主要包含下列类型:

  • 寄存器名:表示所有 ISA 寄存器中的某个寄存器,可以是通用的编号名称,即 x0 到 x31,也可以是按照调用约定规定的寄存器别名。例如,第 5 号寄存器的寄存器名可以是 x5,也可以是 t0 (临时寄存器)
  • 立即数值:立即数值是直接编码在机器指令中的常数值
  • 符号名:符号名可以在符号表中找到对应的符号,在汇编和链接的过程中会替换为实际符号表达的数值,可以是用户自定义的符号名,也可以是汇编器自动生成的符号名(例如标签对应的符号名)

1.2.2 标签

标签可用于标记程序位置,可以标记指令和汇编指示语句的位置,在汇编和链接的过程中被翻译为实际的地址。GNU 汇编器通常接受两种类型的标签:符号标签和数字标签。

符号标签存储为符号表中的符号,用于标识全局变量和子程序,符号标签定义为标识符及冒号(例如:label:)。

数值标签则由十进制数字及冒号定义(例如:1:),用于局部参考,不出现在可执行文件的符号表中,在同一个汇编程序中可以反复重定义。对数值标签的引用需要包含后缀以表明数值标签的位置在引用之前(后缀 b) 或者引用之后(后缀 f)。

以上图所示的数值标签为例,图中包含数值标签 1,该标签定义了两次,其中,指令 beqz a1,1f 引用的标签位置为第二个标签(即该指令出现之后的标签 1),指令 j 1b 引用的标签位置则为第一个标签(即该指令出现之前的标签 1)。

1.2.3 汇编指示语句

汇编指示语句表示为前缀 . 加上一个标识符,用于控制汇编器行为,例如汇编指示语句 .section .data 表示指导汇编器将 .data 段转换为活跃段(active section),.data 10 汇编指示语句则表示指导汇编器汇编一个 32 位数值,并将其添加到当前的活跃段。

汇编指示语句的主要作用包括:

  • 向程序中添加数值,例如 .byte 汇编指示语句添加若干个 8 比特的数据、.string 汇编指示语句添加一个以空字符 NULL 结尾的字符串,用于添加数值的汇编指示语句如下图所示:

  • .section 汇编指示语句。RISC-V 汇编语言程序以“段”(section)的形式组织,每个段包含数据或指令,每个段映射到主存中的一段连续地址空间,对于 Linux 系统,RISC-V 编译器生成的可执行文件通常包含下列段: .text :存储程序指令的段,即 代码段.data :存储 初始化的全局变量 的段;.bss:存储 未初始化全局变量 的段;.rodata:存储常量的段,即 只读数据段。 .section secname 汇编指示语句汇编器处理的该语句之后的所有信息添加到 名为 secname 的段中

1.3 RISC-V 汇编语言程序示例

本节以一个简单的 C 语言程序及其对应的汇编语言程序介绍完整的 RISC-V 汇编语言程序结构。

以如下的 C 语言程序为例,该程序定义全局变量 n (整型) 及 fibonacci_array (长度为 100 的整型数组),在该程序的 main 函数中,将数组 fibonacci_array 的前两个元素初始化为 1,该数组存储一个斐波那契数列,本程序计算数组的前 n+1 个元素的数值:

#include <stdio.h>

int n = 10;
int fibonacci_array[100];

int main() {
fibonacci_array[0] = 1;
fibonacci_array[1] = 1;

int i;
for(i = 2; i <= n; ++ i) {
fibonacci_array[i] = fibonacci_array[i-2] + fibonacci_array[i-1];
}

printf("%d", fibonacci_array[n]);
return 0;
}

若只保留核心功能对应的汇编指令,上述的 C 语言程序对应的 RISC-V 汇编语言程序可以写为:

.data
    n: .word 10
    fibonacci_array: .zero 400
.text
    main:
        addi x5, x0, 1
        la x6, fibonacci_array
        sw x5, 0(x6)
        sw x5, 4(x6)
        la x7, n
        lw x7, 0(x7)
        addi x28, x0, 2
        for_loop:
            blt x7, x28, end_loop
            # load fibonacci_array[i-2] to x29
            addi x29, x28, -2
            slli x29, x29, 2
            add x29, x6, x29
            lw x29, 0(x29)
            # load fibonacci_array[i-1] to x30
            addi x30, x28, -1
            slli x30, x30, 2
            add x30, x6, x30
            lw x30, 0(x30)
            # add x29 (fibonacci_array[i-2]) and x30 (fibonacci_array[i-1]) to x31
            add x31, x29, x30
            # store x31 to fibonacci_array[i]
            addi x29, x28, 0
            slli x29, x29, 2
            add x29, x6, x29
            sw x31, 0(x29)
            # i = i + 1            
            addi x28, x28, 1
            j for_loop
        end_loop:
            addi x29, x7, 0
            slli x29, x29, 2
            add x29, x6, x29
            lw x29, 0(x29)
            addi a7, x0, 1
            addi a0, x29, 0
            ecall
        addi a7, x0, 10
        ecall

示例的 C 语言程序和 RISC-V 汇编语言程序对应关系如下图所示(单击图片可放大查看):

其中:

  • 全局变量的定义通过汇编指示语句实现,即在 .data 段定义初始化的全局变量,在该例子中,.data 段内定义了整型变量 n,整型类型为 4 字节,对应 RISC-V 中的一个“字”,因此其定义语句为 n: .word 10,该指示语句由三部分组成,即标签、数据类型、变量值,分别对应变量名称、变量大小、变量数值;.data 段还定义了整型数组 fibonacci_array,该数组类型为整型、长度为 100,故其大小为 400 字节,其定义语句为 fibonacci_array: .zero 400,该指示语句表示为 fibonacci_array 初始化 400 字节的内存空间,每个字节数值设置为 0,即定义长度为 100 的整型数组,并将数组元素值初始化为 0。
  • 变量的赋值通过组合一系列指令实现,该例子中,局部变量 i 直接存储在 x28 寄存器中,因此 i 的赋值可直接通过运算类指令 addi 实现;实际情况下的变量通常存储在内存区域中,则此时变量的运算和赋值需要通过访存指令和运算类指令实现,如例子中对 fibonacci_array[0]=1 的实现可分为如下步骤:
    1. 运算赋值语句右边表达式的值。本例中该值为常数 1,因而直接由指令 addi x5, x0, 1 得到赋值语句的右表达式值,并将其存放在 x5 寄存器中
    2. 获取变量内存地址。本例中通过伪指令 la x6,fibonacci_array 将数组的地址存放在 x6 寄存器中
    3. 存储变量值。本例中,fibonacci_array[0] 的地址可由 fibonacci_array 作为基地址、0 (即 0 * 4)作为偏移量计算得到,因此,可由指令 sw x5, 0(x6) 将赋值语句的右表达式值(当前存储在 x5 寄存器中)存储到变量对应的内存区域
  • 库函数的调用遵循子程序调用流程,但实际实现时候输入/输出功能由系统调用实现,在本例中,printf 函数仅输出一个整型数值,该功能可由系统调用直接实现,若编译器中规定该系统调用功能编号为 1、系统调用功能选择由 a7 寄存器指定,则本例中的 printf("%d", fibonacci_array[n]); 语句的实现可分为如下步骤:
    1. 获取输出整型的数值。由于变量 n 的地址已经通过伪指令 la x7, n 存放在 x7 寄存器中,故由 addi x29, x7, 0 和 slli x29, x29, 2 两条指令计算 fibonacci_array[n] 相对于 fibonacci_array 的偏移量(由于数组类型为整型,故偏移量为 n * 4,实际可通过逻辑左移两位实现等价功能);此例中数组 fibonacci_array 的地址已经通过伪指令 la x6, fibonacci_array 存放在 x6 寄存器中,因此 fibonacci_array[n] 的实际地址可以通过 x6 寄存器内容加上偏移量得到,即指令 addi x29, x6, x29 计算 fibonacci_array[n] 的实际地址并将其存放在 x29 寄存器中;最后,通过 lw x29, 0(x29) 指令将该数组元素的实际值从内存中加载到 x29 寄存器
    2. 设置系统调用编号及参数。本例使用的 RISC-V 汇编语言假设约定系统调用编号存放在 a7 寄存器中、参数存放在 a0 寄存器中,且打印整型数值的系统调用编号为 1,因此,可以通过指令 addi, a7, x0, 1 设置选择打印整型数值,通过指令 addi a0, x29, 0 将获取的整型数值存放在参数寄存器中,通过指令 ecall 完成系统调用
  • main 函数执行 return 0; 后实际程序还有其他处理流程,本例子中假设 main 函数执行 return 0; 表示程序完成功能、退出执行,因而可以通过 10 号系统调用退出程序执行。注意:实际的完整汇编程序中,return 0; 语句执行后会返回到调用 main 函数的地方继续执行

系统调用的知识——是否联想到PWN中的ret2syscall了呢? 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/489535.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

云渲染是您3D项目的最佳选择吗?

市面上很多优秀的大型动画电影&#xff0c;一般都是由实力较大的视觉特效团队制作的&#xff0c;而且大部分使用的是基于云的渲染服务来进行最终渲染。像《哪吒之魔童降世》这样 IMAX 3D 首部国产动画电影、并且刷新了中国动画电影票房纪录的作品&#xff0c;就是现代3D动画提供…

ChatGPT做PPT方案,10组提示词方案!

今天我们要搞定的PPT内容是&#xff1a; 活动类型&#xff1a;节日活动、会员活动、新品活动分析类型&#xff1a;用户分析、新品立项、项目汇报内容类型&#xff1a;内容规划、品牌策划 用到的工具&#xff1a; mindshow 邀请码 6509097ChatGPT传送门&#xff08;免费使用…

《Linux0.11源码解读》理解(二) 加载setup、加载system

现在CPU开始执行bootsect&#xff0c;它的作用是把第二部分、第三部分程序陆续加载到内存中。把放到合理的内存位置需要先对内存进行规划。 根据上一节&#xff0c;boostsect当前所在内存位置是0x07c0&#xff0c;大小为512byte&#xff0c;现在要将其挪动到内存的0x9000&…

无代码时代来了,程序员会失业吗?不,程序员又不够用了!

有人问我无代码时代来了&#xff0c;程序员会失业吗&#xff1f;太难了&#xff0c;秃了头就算了&#xff0c;连工作也保不住了&#xff1f; 先说观点&#xff1a;并不会 因为&#xff0c;无代码不是真正意义上的无代码。 无代码开发的使用对象是编程小白&#xff08;我猿是…

Redis的命令以及数据类型

1.Redis的通用命令 KEYS&#xff1a;查看符合模板的所有key&#xff0c;不建议在生产环境设备上使用 DEL&#xff1a;删除一个指定的key EXISTS&#xff1a;判断key是否存在 TTL&#xff1a;查看一个KEY的剩余有效期 2.String类型 String类型&#xff0c;也就是字符串类型…

【Halcon】 Halcon 22.11 安装详细教程

文章目录 1安装2 获取许可证 license2.1 license下载2.2 激活 license放置在相应文件夹下 3 DLT 安装 1安装 1.解压安装包 2.打开运行 exe 程序 跳转至页面 点击“可获得的”&#xff0c;并安装 选择&#xff1a; AVAILABLE ->INSTALL 可获得的 ->安装 5. 等待安装 6…

jQuery -- 常用API(下)

4. jQuery属性操作 4.1 设置或获取元素固有属性值prop() 所谓元素固有属性就是元素本身自带的属性&#xff0c;比如 元素里面的 href &#xff0c;比如 元素里面的 type。 获取属性语法&#xff1a;prop(属性)设置属性语法&#xff1a;prop(属性, 属性值) 4.2 设置或获取元…

掌握黑客技术:从Kali渗透测试开始

前言 Kali Linux是一种流行的渗透测试和网络安全工具&#xff0c;广泛用于测试和评估网络系统和应用程序的安全性。下面是一个简单的Kali Linux使用教程&#xff08;Kali使用教程中需要学习的知识点&#xff09;&#xff0c;旨在帮助初学者了解如何使用Kali Linux进行渗透测试…

【计算机网络】网络分层结构

应用层 软件 传输层 TCP UDP协议 网络层 实现源主机到目标主机的传输 IP协议 可能会丢失&#xff0c;可能失序、可能错误 工作设备&#xff1a;路由器、防火墙 链路层 相邻两点的数据传输以帧为单位的数据。 工作设备&#xff1a;交换机 物理层 光信号 电信号 网络边…

15.JVM8为什么要增加元空间

JVM从永久代至元空间内存结构变化图 变化后的java8图 变化之前java7以及之前各代的图 看出变化了吗&#xff1f;堆和方法区连在了一起&#xff0c;但这并不能说堆和方法区是一起的&#xff0c;它们在逻辑上依旧是分开的。但在物理上来说&#xff0c;它们又是连续的一块内存。…

面了一个4年经验的测试工程师,自动化都不会也要15k,我也是醉了····

在深圳这家金融公司也待了几年&#xff0c;被别人面试过也面试过别人&#xff0c;大大小小的事情也见识不少&#xff0c;今天又是团面的一天&#xff0c; 一百多个人都聚集在一起&#xff0c;因为公司最近在谈项目出来面试就2个人&#xff0c;无奈又被叫到面试房间。 整个过程…

mxf文件格式详解

MXF是英文Material eXchange Format&#xff08;素材交换格式&#xff09;的缩语。MXF是SMPTE&#xff08;美国电影与电视工程师学会&#xff09;组织定义的一种专业音视频媒体文件格式。MXF主要应用于影视行业媒体制作、编辑、发行和存储等环节。SMPTE381M&#xff08;把MPEG格…

如何在 Linux 中使用 GPG 加密和解密文件?

什么是 GPG&#xff1f; GPG&#xff08;GNU Privacy Guard&#xff09;是一种免费的开源加密软件&#xff0c;用于保护计算机数据的机密性和完整性。 它使用非对称加密算法&#xff0c;也称为公钥加密算法&#xff0c;其中数据被加密和解密时使用不同的密钥。每个用户都有一个…

【C++】类与对象(2)

【C】类与对象&#xff08;2&#xff09; 作者&#xff1a;爱写代码的刚子 时间&#xff1a;2023.5.4 本篇博客有关构造函数、析构函数、拷贝构造的知识&#xff0c;由于本篇博客可能比较详细&#xff0c;还剩一些内容没介绍&#xff0c;所以我将剩余的知识放在下一篇博客。 目…

探究Spring中Bean的线程安全性问题

前言 今天同事笑嘻嘻的凑过来&#xff0c;问了我一个问题&#xff1a;spring中的bean是线程安全的吗&#xff1f;。我内心一想肯定是安全的&#xff0c;毕竟这样多项目在用。但是转念一想&#xff0c;他那贱兮兮的表情&#xff0c;多半是在给我挖坑。于是我自信的回答他&#x…

如何在 DigitalOcean 中部署 ONLYOFFICE 文档

现在您可使用通过 DigitalOcean 市场提供的一键式应用在 DigitalOcean 云架构中轻松部署 Docker 版本的 ONLYOFFICE 文档。 一键式应用是一个包含所有必要预配置组件的镜像&#xff0c;可用于便捷地在运行有 Ubuntu OS 的 DigitalOcean 服务器上部署 ONLYOFFICE&#xff1a; D…

单元测试~

文章目录 单元测试Junit单元测试框架 单元测试 Junit单元测试框架

客户端无法连接docker启动的nacos-config服务

nacos踩坑记录: 基于镜像仓库教程 : https://hub.docker.com/r/nacos/nacos-server 在虚拟机启动了nacos服务,控制台可以正常打开.但是客户端无法正确连接.但是又没有明确的异常信息,只有一行warn日志:There is no content for NacosPropertySource from dataId[application.ym…

面试必问的【网络io】,1.5W字超全面总结

今天分享的基本上一面试就会被问的网络IO。文中涉及的代码部分不太重要&#xff0c;重要的是对这概念的理解。在看文章之前大家也可通过下面的思维导图看看自己是否能回答出来。 ​大纲 1 阻塞与非阻塞--开胃菜 阻塞 我们知道在调用某个函数的时候无非就是两种情况&#xff0…

1553B总线与FlexRay总线的协议转换

1553B总线与FlexRay总线的协议转换 背景技术&#xff1a; 某一1553B数字时分系统&#xff0c;存在响应型和周期型两种类型的指令传输。如果在1553B总线上传输周期型指令&#xff0c;不仅会造成BC->RT调度指令的大量重复&#xff0c;还存在BC设备和RT设备时间不同步的风险&a…