程序的机器级表示

AT&T与Intel格式的汇编代码

我们的表述是ATT(根据“AT&T”命名的, AT&T是运营贝尔实验室多年的公 司)格式的汇编代码,这是GCC、 OBJDUMP和其他一些我们使用的工具的默认格式。 其他一些编程工具,包括Microsoft的工具,以及来自Intel的文档,其汇编代码都是Intel格式的。这两种格式在许多方面有所不同。比如下面的几点:

  • intel代码省略了指示大小的后缀。比如使用push和pop,而不是pushl和popl。
  • intel代码省略了寄存器前面的“%”符号,用的是rbx而不是%rbx。
  • intel代码用不同的方式来描述内存中的位置,例如是“QWORD PTR[rbx]”,而不是“(%rbx)”。
  • 在带有多个操作数的指令情况下,列出操作数的顺序相反。当在两种格式之间进 行转换的时候,这一点非常令人困惑。

数据格式

由于是从16位体系结构扩展成32位的, Intel用术语“字(word)”表示16位数据类型。因此,称32位数为“双字(double words)”,称64位数为“四字(quad words)”。 在x86-64中。标准int值存储为双字(32位)。指针(在此用char *表示)存储为8字节的四字, 64位机器本来就预期如此。

信息访问

一个x86-64的中央处理单元(CPU)包含一组16个存储64位值的通用目的寄存器。 这些寄存器用来存储整数数据和指针。

寻址模式

有多种寻址模式,如下图所示:

数据传送 MOV类指令

这些指令把数据从源位置复制到目的位置,不做任何变化。MOV类由四条指令组成‥ movb、 movb、 movl和 movq。这些指令都执行同样的操作;主要区别在于它们操作的数据大小不同:分别是1、2、4和8字节。

算数逻辑操作

这种操作可以分为四种::加载有效地址、一元操作、二元操作和移位。

其中,leaq指令能执行加法和有限形式的乘法,在编译如上简单的算术表达式时,是很有用处的。

控制

机器代码提供两种基本的低级机制来实现有条件的 行为:测试数据值,然后根据测试的结果来改变控制流或者数据流。

条件码

除了整数寄存器, CPU还维护着一组单个位的条件码(condition code)寄存器,它们描述了最近的算术或逻辑操作的属性。可以检测这些寄存器来执行条件分支指令。最常用的条件码有:

  • CF:进位标志。最近的操作使最高位产生了进位。可用来检查无符号操作的溢出。
  • ZF:零标志。最近的操作得出的结果为0。
  • SF:符号标志。最近的操作得到的结果为负数。
  • OF:溢出标志。最近的操作导致一个补码溢出一正溢出或负溢出。

对于逻辑操作,例如xoR,进位标志和溢出标志会设置成0。对于移位操作,进位标志将设置为最后一个被移出的位,而溢出标志设置为0。INC和DEC指令会设置溢出和零标志,但是不会改变进位标志。

跳转指令

跳转(jump)指令会导致执行切换到程序中一个全新的位置。在汇编代码中,这些跳转的目的地通常用一个标号(lable)指明。常用的跳转指令如下:

循环

C语言提供了多种循环结构,即do-While、While和for。汇编中没有相应的指令存在,可以用条件测试和跳转组合起来实现循环的效果。

运行时栈

C语言过程调用机制的一个关键特性(大多数其他语言也是如此)在于使用了栈数据结构提供的后进先出的内存管理原则。在过程P调用过程Q的例子中,可以看到当Q在执行时, P以及所有在向上追溯到p的调用链中的过程,都是暂时被挂起的。当Q运行时,它只需要为局部变量分配新的存储空间,或者设置到另一个过程的调用。 另一方面,当Q返回时,任何它所分配的局部存储空间都可以被释放。因此,程序可以用栈来管理它的过程所需要的存储空间,栈和程序寄存器存放着传递控制和数据、分配内存所需要的信息。当P调用Q时,控制和数据信息添加到栈尾。当p返回时,这些信息会释放掉。通用的堆栈示意图如下:

栈上的局部存储

有些时候,局部数据必须存放在内存中,常见的情况包括:

  • 寄存器不足够存放所有的本地数据。
  • 对一个局部变量使用地址运算符“&”,因此能够为它产生一个地址。
  • 某些局部变量是数组或结构,因此必须能够通过数组或结构引用被访问到。在描述数组和结构分配时,我们会讨论这个间题。

一般来说,过程通过减小栈指针在栈上分配空间。分配的结果作为栈帧的一部分,标 号为“局部变量”。

数组的分配和访问

C语言中的数组是一种将标量数据聚集成更大数据类型的方式。C语言实现数组的方式非常简单,因此很容易翻译成机器代码。C语言的一个不同寻常的特点是可以产生指向数组中元素的指针,并对这些指针进行运算。在机器代码中,这些指针会被翻译成地址计算。优化编译器非常善于简化数组索引所使用的地址计算。

指针运算

C语言允许对指针进行运算,而计算出来的值会根据该指针引用的数据类型的大小进行伸缩。单操作数操作符‘&’和‘*’可以产生指针和间接引用指针。对应的汇编代码如图所示。

变长数组

ISO C99引人了一种功能,允许数组的维度是表达式,在数组被分配的时候才计算出来。 在变长数组的C版本中,我们可以将一个数组声明如下:

int A[exp1][exp2];

它可以作为一个局部变量,也可以作为一个函数的参数,然后在遇到这个声明的时候,通过对表达式exp1和exp2求值来确定数组的维度。因此,例如要访问n×n的数组的元素i,j,我们可以写一个如下的函数:

对应的汇编代码如下:

异构的数据结构

结构

C语言的struct声明创建一个数据类型,将可能不同类型的对象聚合到一个对象中。用名字来引用结构的各个组成部分。类似于数组的实现,结构的所有组成部分都存放在内存中一段连续的区域内,而指向结构的指针就是结构第一个字节的地址。编译器维护关于每个结构类型的信息,指示每个字段(field)的字节偏移。它以这些偏移作为内存引用指令中的位移,从而产生对结构元素的引用。

联合

联合提供了一种方式,能够规避C语言的类型系统,允许以多种类型来引用一个对象。联合声明的语法与结构的语法一样,只不过语义相差比较大。它们是用不同的字段来引用相同的内存块。

数据对齐

许多计算机系统对基本的数据类型的合法地址做出了一些限制,要求某种类型对象的地址必须是某个值K(通常是2,4,8)的倍数。这种对其限制简化了形成处理器和内存系统之间接口的硬件设计。无论数据是否对齐,x86-64的硬件都能正确工作。不过,intel还是建议要对其数据以提高系统性能。

理解指针

  • 每个指针都对应一个类型。
  • 每个指针都有一个值
  • 指针用&运算符创建。
  • *操作符用于间接引用指针。
  • 数组与指针紧密联系。一个数组的名字可以像一个指针变量一样引用。
  • 将指针从一种类型转换成另一种类型不会改变它的值。
  • 指针也可以指向函数。

GDB调试器常用命令

内存越界引用和缓冲区溢出

c对数组不进行任何边界检查,因此当对越界的数组元素进行写操作会破坏存储在栈中的信息。一种特别常见的状态破坏称为缓冲区溢出。

缓冲区溢出一个很致命的使用就是让程序执行它本来不想执行函数,这是一种常见的通过计算机网络攻击系统安全的方法。输入给程序一个字符串,这个字符串包含一些可执行代码的字节编码,称为攻击代码,另外还有一些字节会用一个指向攻击代码的指针覆盖返回区域。那么执行ret指令的效果就是跳转到攻击代码。

对抗缓冲区溢出可以采取以下方法:

  • 栈随机化。使得栈的位置在程序每次运行时都有变化。
  • 栈破坏检测。能够检测到栈何时已被破坏。最新的GCC版本加入了一种栈保护者机制来检测缓冲区越界。其思想是在任何局部缓冲区与栈状态之间存储一个特殊的金丝雀值。
  • 限制可执行代码区域。消除攻击者向系统中插入可执行代码的能力。

CSAPP 第三章 读书笔记的更多相关文章

  1. Linux内核分析第三章读书笔记

    第三章 进程管理 3.1 进程 进程就是处于执行期的程序 进程就是正在执行的程序代码的实时结果 线程:在进程中活动的对象.每个线程都拥有一个独立的程序计数器.进程栈和一组进程寄存器. 内核调度的对象是 ...

  2. 2013337朱荟潼 Linux第三章读书笔记——进程管理

    第三章 进程管理 总结 fork创造的子进程复制了父进程资源,包括内存及进程描述符的内容,资源的复制而不是指针的复制. vfork的行为更像一个线程(指没有自已独立的内存空间),更明显的是vfork的 ...

  3. 20135320赵瀚青LINUX第三章读书笔记

    第三章 进程管理 3.1 进程 进程的定义: 是处于执行期的程序以及它所包含的资源的总称. 线程的定义: 是在进程中活动的对象. 每个线程都拥有一个独立的程序计数器.进程栈和一组进程寄存器. 内核调度 ...

  4. 链接器(linker)的作用——CSAPP第7章读书笔记

    首先说说我为什么要去读这一章.这个学期开OS的课,在Morden Operating System上读到和Process有关的内容时看到这样一句话:“Process is fundamentally ...

  5. 《Linux内核设计与实现》第三章读书笔记

    一.进程(任务)描述 1.进程是处于执行期的程序:除了可执行程序代码,还包括打开的文件.挂起的信号.内核内部数据.一个或者多个执行线程等多种资源 线程是在进程活动中的对象:内核调度的对象是线程而不是进 ...

  6. 《Ansible自动化运维:技术与最佳实践》第三章读书笔记

    Ansible 组件介绍 本章主要通过对 Ansible 经常使用的组件进行讲解,使对 Ansible 有一个更全面的了解,主要包含以下内容: Ansible Inventory Ansible Ad ...

  7. Android深度探索--HAL与驱动开发----第三章读书笔记

    1. 什么是Git? Git是一个开源的分布式版本控制系统,用以有效.高速的处理从很小到非常大的项目版本管理.Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开 ...

  8. 《Linux内核分析》之第三章读书笔记

    进程管理 进程是处于执行期的程序以及相关的资源的总称,也称作任务.执行线程,简称线程,是在进程中活动的对象. 可以两个或两个以上的进程执行同一个程序 也可以两个或两个以上并存的进程共享许多资源 内核调 ...

  9. Linux内核分析第四章 读书笔记

    Linux内核分析第四章 读书笔记 第一部分--进程调度 进程调度:操作系统规定下的进程选取模式 面临问题:多任务选择问题 多任务操作系统就是能同时并发地交互执行多个进程的操作系统,在单处理器机器上这 ...

随机推荐

  1. 【紫书】Play on Words UVA - 10129 欧拉回路

    题意:给你1e5个字符串,若前一个的末尾字母等于当前的首字母,则可以连在一起(成语接龙一个意思)判断是否可以将他们连在一起 题解:将首位看作点,单词看作边.变成欧拉回路问题. 判断出入度是否相等,再用 ...

  2. iOS-数据缓存(转载)

    一.关于同一个URL的多次请求 有时候,对同一个URL请求多次,返回的数据可能都是一样的,比如服务器上的某张图片,无论下载多少次,返回的数据都是一样的. 上面的情况会造成以下问题 (1)用户流量的浪费 ...

  3. I Hate It---hdu1754线段树

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=1754 和上一题一样是模板题,就是那道题求得是和,这道求得是最大值: #include<iostrea ...

  4. flask-WTForms组件

    WTForms是一个支持多个web框架的form组件 主要能够帮助我们生成html标签 对数据进行验证 安装 pip install wtforms Wtforms的使用 这里借助一个用户登录注册的示 ...

  5. grunt学习一

    grunt是前端自动化工具之一.下面是是grunt的简单小示例: 在使用grunt,确保安装nodejs,如果不清楚,可以百度找相关教程,这个教程已经烂大街了. 1.打开cmd,以管理员的身份.(或者 ...

  6. Django +uwsgi+python3+nginx + mysql 部署

    环境: 服务器ip:192.168.0.110 centos服务器 6.4 + mysql 5.6 + django1.11 +nginx 1.13.7 + uwsgi 2.0.18 uwsgi介绍 ...

  7. Python 使用ctypes调用 C 函数

    在python中通过ctypes可以直接调用c的函数,非常简单易用 下面就一步一步解释用法吧,以Linux为例讲解. 1, 首先确定你的python支持不支持ctypes python2.7以后cty ...

  8. Centos安装ELK5.3.2

    一.注意情况 1.elk的版本要一致. 2.ElasticSearch是基于lucence开发的,也就是运行需要java支持.所以要先安装JAVA环境.由于es5.x依赖于JDK1.8,所以需要安装J ...

  9. POJ1062:昂贵的聘礼(枚举+迪杰斯特拉)

    http://poj.org/problem?id=1062 Description 年轻的探险家来到了一个印第安部落里.在那里他和酋长的女儿相爱了,于是便向酋长去求亲.酋长要他用10000个金币作为 ...

  10. 支持向量机(SVM)、支持向量回归(SVR)

    1.支持向量机( SVM )是一种比较好的实现了结构风险最小化思想的方法.它的机器学习策略是结构风险最小化原则 为了最小化期望风险,应同时最小化经验风险和置信范围) 支持向量机方法的基本思想: ( 1 ...