程序的机器级表示

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. Oracle Function:COUNT

    Description The Oracle/PLSQL COUNT function returns the count of an expression. The COUNT(*) functio ...

  2. C++创建窗口程序初步

    写在前面:本博客为本人原创,严禁任何形式的转载!本博客只允许放在博客园(.cnblogs.com),如果您在其他网站看到这篇博文,请通过下面这个唯一的合法链接转到原文! 本博客全网唯一合法URL:ht ...

  3. IO流(10)复制多级文件夹

    import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.File;import ja ...

  4. JS DOM节点

    html代码: <body onload ="loaded12()"> <form name="form1" action="htt ...

  5. 实习培训——Java基础(3)

    实习培训——Java基础(3) 1 Java 继承 1.1  super和this关键字 super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类. this关键字 ...

  6. KMP(http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2772)

    #include <stdio.h>#include <string.h>#include <stdlib.h>char a[1000001],b[1000001] ...

  7. 蒙特卡罗(Monte Carlo)方法简介

    蒙特卡罗(Monte Carlo)方法,也称为计算机随机模拟方法,是一种基于"随机数"的计算方法. 二 解决问题的基本思路 Monte Carlo方法的基本思想很早以前就被人们所发 ...

  8. [LeetCode] 437. Path Sum III_ Easy tag: DFS

    You are given a binary tree in which each node contains an integer value. Find the number of paths t ...

  9. docker搭建本地仓库并制作自己的镜像

    原文地址https://blog.csdn.net/junmoxi/article/details/80004796 1. 搭建本地仓库1.1 下载仓库镜像1.2 启动仓库容器2. 在CentOS容器 ...

  10. CRM项目总结-封装PortletURLUtil

    package com.ebizwindow.crm.utils; import java.security.Key; import java.util.List; import javax.port ...