程序的机器级表示part1——程序编码与数据格式

news2024/11/24 7:50:05

目录

1. 汇编语言和机器级语言

1.1 不同的编程语言 

1.2 Linux下的汇编语言 

2. 程序编码 

1.1 机器级代码 

1.2 代码示例 

3. 数据格式 


本文基于CSAPP第三章撰写,主要介绍部分x86-64汇编的相关知识,后续会将该部分内容慢慢完善(PS:所有代码编译和汇编均在Linux环境下进行)

1. 汇编语言和机器级语言

1.1 不同的编程语言 

从计算机诞生至今,编程语言总数超过2500种编程语言,其发展大致经历了四个阶段

  • 机器语言:是一种二进制语言, 由二进制数0和1组成的指令代码的集合,机器能直接识别和 执行,各种机器的指令系统互不相同
  • 汇编语言:汇编指令是机器指令便于记忆和阅读的书写格式——助记符,与人类语言接近,add、mov、sub和call等
  • 面向过程的高级语言:如C语言
  • 面向对象的高级语言:如C++和Java

编程语言的发展简史——编年史

🔷 机器语言

在最早期采用穿孔纸带保存程序(1-打孔,0-不打孔)

优点:
1. 速度快    2. 占存储空间小    3. 翻译质量高

缺点:

1. 可移植性差    2. 编译难度大    3. 直观性差    4. 调试困难

🔷 汇编语言 

助记符代替机器指令的操作码,用地址符号或标号代替指令或操数的地址

  • 机器指令:1000100111011000
  • 操 作:寄存器bx的内容送到ax中
  • 汇编指令:mov %bx, %ax, 

汇编指令同机器指令是一一对应的关系 

优点:
1.执行速度快    2.占存储空间小    3.可读性有所提高 

缺点:

1.类似机器语言    2.可移植性差    3.与人类语言还相差很悬殊

🔷 高级语言 

C++和Java等高级语言与汇编语言及机器语言之间是一对多的关系

一条简单的C++语句会被扩展成多条汇编语言或者机器语言指令 

高级语言到机器语言的转换方法: 

  • 解释方式:通过解释程序,逐行转换成机器语言,转换一行运行一行
  • 编译方式(翻译方式):通过编译程序(编译、链接)将整个程序转换成机器语言

 汇编语言与高级语言的比较

应用程序类型高级语言汇编语言
用于单一平台的中到大型商业应用软件正式的结构化支持使组织和维护大量代码很方便最小的结构支持使程序员需要人工组织大量代码,使各种不同水平的程序员维护现存代码的难度极高
硬件驱动程序语言本身未必提供直接访问硬件的能力,即使提供了也因为要经常使
用大量的技巧而导致维护困难
硬件访问简单直接。当程序很短并且文档齐全时很容易维护
多种平台下的商业应用软件可移植性好,在不同平
台可以重新编译,需要
改动的源代码很少
必须为每种平台重新编写程序,通常要使用不同的汇编语言,难于维护
需要直接访问硬件的嵌入式系统计算机游戏由于生成的执行代码过
大,执行效率低
很理想,执行代码很小并且运行很快

1.2 Linux下的汇编语言 

x86汇编语言通常有两种风格——AT&T汇编、Intel 汇编,其中DOS操作系统采用Intel汇编风格,而Linux下采用AT&T汇编风格

AT&T汇编Intel 汇编说明
寄存器前缀%%eaxeaxIntel省略了寄存器前缀%
源/目的操作数顺序movl %eax,%ebxmov ebx,eaxAT&T源操作数在前,目的操作数在后,Intel反之
常数/立即数的格式$movl $0xd00d, %ebxmov ebx,0xd00dAT&T在立即数前加$
操作数长度标识

movw var_x, %bx

(b-1字节,w-2字节,l-4 字节,q-8 字节)

mov bx, word ptr var_x

Intel省略了指示大小的后缀

寻址方式

imm32(basepointer,

indexpointer,indexscale)

[basepointer+indexpointer

*indexscale + imm32]

Linux寻址将在后面介绍

2. 程序编码 

 对两个C语言文件p1.c和p2.c进行编译的命令行为

gcc -Og -o p p1.c p2.c
  • gcc:指GCC编译器
  • Og:指明优化等级,-Og是基础优化项,告诉编译器使用生成符合原始C代码整体结构的机器代码的优化等级
  • -o:后面接生成的可执行程序的名称(若不使用,默认生成a.out)

再来回顾一下程序的编译链接过程

  1. 预处理阶段,C预处理器扩展源代码,插入所有用#include命令指定的文件,并展开所有的宏定义
  2. 编译阶段,编译器生成两个源文件的汇编代码,名字分别为p1.s 和 p2.s
  3. 汇编阶段,汇编器将汇编代码转化成二进制目标文件p1.o 和 p2.o(此时所有指令均为二进制,但还没有填入全局值的地址)
  4. 链接阶段,链接器将两个目标文件与库函数合并,生成可执行代码文件p

2.1 机器级代码 

对于机器级编程来说,有两种抽象尤为重要

  • 第一种是指令集体系结构或指令集架构(Instruction Set Architecture,ISA),它定义了处理器状态、指令的格式以及每条指令对状态的影响
  • 第二种是机器级程序使用的内存地址是虚拟地址,有关虚拟地址的内容以后将会介绍

在整个编译的过程中,编译器会完成大部分的工作,编译过后生成的汇编代码十分接近于机器代码,但与二进制的机器代码相比,汇编代码用文本格式表示,具有更好的可读性

x86-64的机器代码和原始的C代码差别很大,一些通常对C语言程序员隐藏的处理器状态都是可见的

  • 程序计数器(通常称为PC,在x86-64中用%rip表示):给出将要执行的下一条指令在内存中的地址
  • 整数寄存器文件:包含16个命名位置,分别储存64位的值。这些寄存器可以储存地址或整数数据,有的被用来记录程序状态、有的用来保存临时数据例如参数和局部变量,函数返回值等
  • 条件码寄存器:保存着最近执行的算术或逻辑指令的状态信息
  • 向量寄存器:存放一个或多个整数或浮点数值

机器级代码将内存简单地看成一个很大的、按字节寻址的数组(这意味着它并不像C语言一样会将内存分为多个部分,它只会按照地址来处理数据)。对于数据类型,汇编代码并不区分有符号或无符号整数、不区分各种指针,甚至不区分指针和整数 

一条机器指令只执行一个非常基本的操作。例如,将两个寄存器中的数值相加,或简单地在寄存器之间传送数据。然而,整个程序的构建正是由这一条条简单的指令所构成

2.2 代码示例 

 假设我们写了一个C语言程序mstore.c,包含如下函数定义

long mult2(long, long);

void multstore(long x, long y, long* dest)
{
    long t = mult2(x, y);
    *dest = t;
}

仅对其进行编译的命令行和生成的文件为

gcc -Og -S mstore.c

-rw-rw-r-- 1 hqs hqs 393 Jan 12 11:52 mstore.s

得到mstore.s文件,用cat将其打印到屏幕上可以看到原始C代码的汇编为

[hqs@VM-8-2-centos test]$ cat mstore.s
	.file	"mstore.c"
	.text
	.globl	multstore
	.type	multstore, @function
multstore:
.LFB0:
	.cfi_startproc
	pushq	%rbx
	.cfi_def_cfa_offset 16
	.cfi_offset 3, -16
	movq	%rdx, %rbx
	call	mult2
	movq	%rax, (%rbx)
	popq	%rbx
	.cfi_def_cfa_offset 8
	ret
	.cfi_endproc
.LFE0:
	.size	multstore, .-multstore
	.ident	"GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-44)"
	.section	.note.GNU-stack,"",@progbits

所有以‘.’开头的都是指导汇编器和链接器工作的伪指令,通常可以忽略。关于.cfi指令,有兴趣可以查看CFI directives (Using as) ,这里不对进行展开

上述汇编代码中最主要的几行如下,每条指令都对应一条机器级指令

multstore:
	pushq	%rbx
	movq	%rdx, %rbx
	call	mult2
	movq	%rax, (%rbx)
	popq	%rbx
	ret

接下来我们使用mstore.c文件汇编后生成的mstore.o文件,所使用的命令行和生成的文件为

gcc -Og -c mstore.c

-rw-rw-r-- 1 hqs hqs 1368 Jan 12 12:06 mstore.o

使用gdb对这段代码进行调试,使用如下的命令行可以展示函数multstore的二进制代码 

[hqs@VM-8-2-centos test]$ gdb mstore.o
(gdb) x/14xb multstore

这条命令告诉GDB显示(简写为‘x’)从函数multstore开始位置往后的14字节(简写为'b')内容的十六进制表示(简写为'x')

得到的结果为如下 

(gdb) x/14xb multstore 
0x0 <multstore>:	0x53	0x48	0x89	0xd3	0xe8	0x00	0x00	0x00
0x8 <multstore+8>:	0x00	0x48	0x89	0x03	0x5b	0xc3

但是仅仅这样并没有很好地观察到汇编代码和二进制机器代码的对应关系,这时使用objdump -d命令完成对于.o文件的反汇编

objdump -d:

  • 检查目标代码的有用工具
  • 分析指令的位模式
  • 生成近似的汇编代码表述/译文
  • 可处理a.out (完整可执行文件)或 .o文件 

可以看到下面的每一条汇编指令都存在一条相应的二进制指令,比如 push %rbx对应的二进制指令为53,mov %rdx %rbx对应的二进制指令为 48 89 d3

[hqs@VM-8-2-centos test]$ objdump -d mstore.o

mstore.o:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <multstore>:
   0:	53                   	push   %rbx
   1:	48 89 d3             	mov    %rdx,%rbx
   4:	e8 00 00 00 00       	callq  9 <multstore+0x9>
   9:	48 89 03             	mov    %rax,(%rbx)
   c:	5b                   	pop    %rbx
   d:	c3                   	retq    

Disassembly of section .text 表示这是.text节的反汇编,也就是程序的代码段的产生的汇编

目标文件的分段格式

最左边的一行表示这条指令的相对于.text段起始地址的偏移量,也就是相对于程序第一条代码的偏移量,可以理解为相对位置,因此最后一条指令的偏移量为十六进制数d,也就是13,而指令长度为1个字节,因此这段程序的机器级代码总长度为14字节

机器级代码和它的反汇编表示有一些值得注意的地方

  • x86-64的指令长度为1-15字节不等。常用指令字节数少,不常用指令字节数多
  • 设计指令格式的方式是,从某个给定位置开始,可以将字节唯一的解码成机器指令。比如说遇到53,便知道是push指令的开头;遇到48,知道是mov指令的开头
  • 反汇编器只是基于机器代码文件中的字节序来确定汇编代码,不访问程序的源代码或汇编代码
  • 反汇编器的命名规则和GCC生成的汇编有一些差异,比如说省略操作数大小指示符

接下来我们创建一个完整的工程,添加一个main函数,其中包括mult2函数的定义

#include <stdio.h>

void multstore(long, long, long*);

int main() 
{
	long d;
	multstore(2, 3, &d);
	printf("2 * 3 --> %ld\n", d);
	return 0;
}

long mult2(long a, long b) 
{
	long s = a * b;
	return s;
}

用下面的命令生成可执行程序prog

gcc -Og -o prog main.c mstore.c

-rwxrwxr-x 1 hqs hqs 8456 Jan 12 13:49 prog

同样用objdump -d查看prog文件的反汇编

000000000040056b <multstore>:
  40056b:	53                   	push   %rbx
  40056c:	48 89 d3             	mov    %rdx,%rbx
  40056f:	e8 ef ff ff ff       	callq  400563 <mult2>
  400574:	48 89 03             	mov    %rax,(%rbx)
  400577:	5b                   	pop    %rbx
  400578:	c3                   	retq

这时prog文件相比于之前的mstore.o文件,经过了链接的过程,所有的指令都带上了地址,而不再是相对于起始位置的偏移了,之后在链接部分会介绍可重定位目标文件和可执行目标文件文件的差别

3. 数据格式 

由于是从16位体系结构扩展为32位的,Intel用术语“字(word)”来表示16位数据类型

在x86-64指令集中,各种数据类型之间的关系为 

C声明Intel数据类型汇编代码后缀大小(字节)
char字节b1
shortw2
int双字l4
long四字q8
char*四字q8
float单精度s4
double双精度l8

大多数GCC生成的汇编代码指令都有一个字符的后缀,表示操作数的大小,如

movq	%rdx, %rbx  

mov是原始指令,后面跟了一个q,说明在两个寄存器之间操作的数是四字类型的,在C语言中可表示long或者指针。因此,一条指令可能有多个变种,mov指令可能有movb、movw、movl和movq四种类型

值得注意的是,汇编代码也使用后缀 'l' 来表示四字节整数和双精度浮点数,由于前者使用整数寄存器,后者使用浮点寄存器,所有并不会产生歧义

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

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

相关文章

Web Spider XHR断点 千千XX 歌曲下载(三)

Web Spider XHR断点 千千XX 歌曲下载 首先声明: 此次案例只为学习交流使用&#xff0c;切勿用于其他非法用途 注&#xff1a;网站url、接口url请使用base64.b64decode自行解码 文章目录Web Spider XHR断点 千千XX 歌曲下载前言一、资源推荐二、任务说明三、网站分析四、XHR断点…

knife4j使用与步骤

1、导入依赖<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.3</version> </dependency>2、knife4j的配置类&#xff08;可有可无&#xff09;package…

为什么程序员的工资比其他行业高这么多?

不止一次听到有人说程序员工资高&#xff0c;更有甚者喊着“把IT工资打下来”。 拜托大哥大姐们&#xff01;看事情要客观好吧&#xff01;&#xff01; 虽然看起来程序员工资是不少&#xff0c;对比其他行业确实会高一些&#xff0c;但并不代表程序员这个岗位工资就要压到三千…

Elasticsearch:如何在 Elasticsearch 中正确使用同义词功能

同义词用于提高搜索质量并扩大匹配范围。 例如&#xff0c;搜索 England 的用户可能希望找到包含 British 或 UK 的文档&#xff0c;尽管这三个词完全不同。 Elasticsearch 中的同义词功能非常强大&#xff0c;如果实施得当&#xff0c;可以使你的搜索引擎更加健壮和强大。 在…

详解结构体内存对齐

目录 前言 一、结构体内存对齐规则 二、 offsetof 宏 三、结构体内存对齐的原因 四、 修改默认对齐数 前言 引入问题&#xff1a; #include <stdio.h>struct S {char c1;int i;char c2; };int main() {printf("%zd\n", sizeof(struct S));return 0; } 程…

干货 | 人脸识别技术的风险及应对方案

以下内容整理自清华大学《数智安全与标准化》课程大作业期末报告同学的汇报内容。第一部分&#xff1a;人脸识别技术概述人脸识别的发展阶段&#xff0c;主要分为三个阶段&#xff1a;起步阶段&#xff08;1950s-1980s&#xff09;&#xff0c;这一阶段的人脸识别只是作为一般性…

房产管理系统---系统安全性需求分析

数图互通高校房产管理系统是基于公司自主研发的FMCenterV5.0平台&#xff0c;是针对中国高校房产的管理特点和管理要求&#xff0c;研发的一套标准产品&#xff1b;通过在中国100多所高校的成功实施和迭代&#xff0c;形成了一套成熟、完善、全生命周期的房屋资源管理解决方案。…

Linux学习笔记——HBase集群安装部署

5.11、大数据NoSQL数据库HBase集群部署 5.11.1、简介 HBase是一种分布式、可扩展、支持海量数据存储的NoSQL数据库。 和Redis一样&#xff0c;HBase是一款KeyValue型存储的数据库。 不过和Redis设计方向不同&#xff1a; Redis设计为少量数据&#xff0c;超快检索HBase设计…

【部署】Docker容器

Docker 使用 Google 公司推出的 Go 语言进行开发实现&#xff0c;基于 Linux 内核的 cgroup、namespace 以及 OverlayFS 类的 Union FS 等技术&#xff0c;对进程进行封装隔离&#xff0c;属于操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程&#xff0…

算法刷题打卡第63天:对称二叉树

对称二叉树 难度&#xff1a;简单 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;false…

BOM浏览器对象模型

文章目录一、BOM概述1、什么是BOM2、BOM的构成二、window 对象的常见事件1、窗口加载事件&#xff08;1&#xff09;window.onload&#xff08;3&#xff09;DOMContentLoaded2、调整窗口大小事件三、定时器1、两种定时器2、setTimeout()定时器3、停止 setTimeout() 定时器4、s…

如何使用CMD修复硬盘命令来解决硬盘问题?

随着计算机的越来越普及&#xff0c;现在在我们的日常生活中都会使用到计算机电脑。硬盘作为计算机电脑的主要存储设备&#xff0c;里面存储着我们平时使用的软件文件、文档资料、照片等重要的数据文件。一旦硬盘损坏会给我们带来许多不必要的麻烦&#xff0c;那硬盘损坏有哪些…

图解卡尔曼滤波(Kalman Filter)

背景关于滤波首先援引来自知乎大神的解释。“一位专业课的教授给我们上课的时候&#xff0c;曾谈到&#xff1a;filtering is weighting&#xff08;滤波即加权&#xff09;。滤波的作用就是给不同的信号分量不同的权重。最简单的loss pass filter&#xff0c; 就是直接把低频的…

【Linux操作系统】1. Linux操作系统简介、安装

前言 本系列是Linux操作系统的一些知识以及实践内容&#xff0c;Linux操作系统作为开发最常使用的操作系统&#xff0c;是必备的一门求职、提升技术。本文先介绍Linux操作系统&#xff0c;并安装一个Linux操作系统。 Linux操作系统简介 Linux&#xff0c;全称GNU/Linux&#…

Javadoc

Javadoc 在学习JavaSE时&#xff0c;我们知道Java支持三种注释方式&#xff1a; 单行注释多行注释文档注释 Javadoc是文档注释&#xff0c;用来对类或方法进行标准的注释&#xff0c;在开发中写好JavaDoc非常重要。 在调用方法时&#xff0c;你可能会看到这样的情景 这种注…

Unity - 搬砖日志 - 如何设置AssetDatabase.Create(“xxx.asset“, mesh) 的Read/Write=false

最近很忙&#xff0c;想写的 BLOG 都遗漏编写了 踩坑的时间比较多&#xff0c;充电的时间少了很多 为了减少以后自己填坑时间&#xff0c;随便简单的记录一下 搬砖日志 环境 unity : 2020.3.37f1 pipeline : brp 问题 因为之前搜索、购买、使用了各式各样的 LOD 插件、工具…

机器学习100天(三十一):031 K近邻回归算法

机器学习100天,今天讲的是:K 近邻回归算法! 《机器学习100天》完整目录:目录 一、理论介绍 我们之前讲了 K 近邻分类算法,用来处理分类问题。其实 K 近邻也可以用来处理回归问题。 如左图所示,K 近邻分类算法的思路是选取与测试样本距离最近的前 k 个训练样本。然后对…

回收租赁商城系统功能拆解07讲-订单列表

回收租赁系统适用于物品回收、物品租赁、二手买卖交易等三大场景。 可以快速帮助企业搭建类似闲鱼回收/爱回收/爱租机/人人租等回收租赁商城。 回收租赁系统支持智能评估回收价格&#xff0c;后台调整最终回收价&#xff0c;用户同意回收后系统即刻放款&#xff0c;用户微信零…

寒假题练——day(3)

题目1&#xff1a; 给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 进阶&#xff1a;尝试设计时间复杂度为 O(n)、空间复杂度为…

58、正则表达式

目录 一、快速入门 二、正则表达式基本语法 1、基本介绍&#xff1a; 2、正则表达式底层实现 3、元字符&#xff08;Metacharacter&#xff09;- 转义号\\ &#xff08;1&#xff09;限定符 &#xff08;2&#xff09;选择匹配符 &#xff08;5&#xff09;字符匹配符…