第三章:程序的机器级表示(下)

(上)文中我们讨论了如何实现程序的控制,本文我们会看看如何实现不同的数据结构,比如数组、结构体等。由于C语言缺乏边界检查,因此会使程序出现缓冲区溢出的问题,我们也会介绍系统为了应对该问题而提供的一些安全保护机制。

数组

C语言中的数组是一种聚集标量数据的方式。
T A[N] 声明了数据类型为T,长度为N的数组A。假设数组 A 的起始地址为x_A,则数组元素 A[i] 的地址为x_A + i。也可以用A作为指向数组开头的指针,这个指针的值就是x_A

C语言允许对指针进行运算。比如E是一个指向类型为 T 的数据的指针,E的值为x_E,那么表达式E+i的值就是x_E + L * iL是数据类型 T 以字节为单位的大小。

数组中的每个元素其实都有2个属性,一个是它的存储地址,另一个是它存储的内容。对于元素的存储地址,可以通过取地址运算符&来获得,得到一个指向该位置的指针(指针其实就是地址的抽象表述);对于元素存储的内容,可以通过指针运算符*从该地址处取数据。

数组 int E[N]

多维数组(嵌套数组)

二维数组int A[5][3]在内存中的存储方式如下:

二维数组的存储

在计算机系统中,我们通常把内存抽象为一个巨大的数组。对于二维数组A, 可以看出,数组元素在内存中按照行优先的顺序存储。

变长数组

在C89标准中,C语言只支持大小在编译时就能确定的多维数组,因此在需要变长数组时不得不使用malloc这样的函数为数组分配空间。而ISO C99标准引入了变长数组的功能,允许数组的维度是表达式

变长数组

变长数组可以作为一个局部变量;也可以作为函数参数,这里参数n 必须在参数A[n][n]之前,这样函数就可以在遇到这个数组时计算出数组的维度。

异质的数据结构

C语言提供了将不同类型的对象组合到一起的数据类型——结构体。类似数组,结构体的所有组成部分都存放在内存中一段连续的区域内,而指向结构体的指针就是结构体第一个字节的地址。
我们通过起始地址加偏移量的方式来访问结构体中的字段。

访问结构体

假设struct rec* 类型的变量r 存在寄存器%rdi中,上图两条汇编指令则将元素r->i 复制到r->j

  • 由于字段i相对于结构体起始地址的偏移量为0,因此字段i的地址就是r的值,而字段j的偏移量为4,因此需要将r加上偏移量4。

数据对齐

此外,为了提供内存系统的性能,许多计算机系统对于数据存储的合法地址做了一些对齐限制,即要求任何K字节的基本对象的地址必须是K的倍数

对齐限制

在上图结构体中,字段j是int类型,占4个字节,它的起始地址必须为4的倍数。因此编译器会在字段cj之间插入一个3字节的间隙,使得字段j的偏移量为8,满足4字节对齐限制。从而使得整个结构体的大小变成12字节。

联合体union

与结构体不同,联合体中的所有字段共享同一存储区域,因此联合体的大小取决于它的最大字段的大小。

struct vs. union

联合体一般应用在当我们知道两个不同字段的使用是互斥的,那么将这两个字段声明为联合体,可以减小占用的内存空间。

机器级程序中数据与控制的交互

理解指针

指针是C语言的一个核心特色,以一种统一方式,对不同数据结构中的元素产生引用。指针有以下特点:

  • 每个指针都对应一个类型。这个类型表明该指针指向的是哪一类对象。
  • 每个指针都有一个值。这个值是某个指定类型对象的地址。特殊的空指针没有指向任何地方。
  • 指针用&运算符创建。
  • *操作符用于间接引用指针,其结果是一个 ,该值的类型与指针的类型一致。间接引用是用内存引用来实现的,要么是存储到一个指定的地址,要么是从指定的地址读取。
  • 数组与指针与紧密相连。一个数组的名字可以像一个指针变量一样引用(但是不能修改)。

内存越界引用(缓冲区溢出)

C语言对于数组引用不进行任何边界检查,并且局部变量和状态信息都存放在栈中。因此,对越界的数组元素的写操作会破坏存储在栈中的状态信息。比如当存储的返回地址的值被破坏了,那么ret指令会导致程序跳到一个完全意向不到的地方。

对抗缓冲区溢出攻击

许多计算机病毒利用缓冲区溢出的方式对计算机系统进行攻击,因此现代的编译器和操作系统实现了很多机制,来避免入侵者通过这种攻击方式获得系统控制权:

  • 栈随机化。栈的位置在程序每次运行时都有变化。因此,即使许多机器都运行同样的代码,它们的栈地址也是不同的。
  • 栈破坏检测。虽然C中没有可靠的方法阻止对数组的越界写,但是我们可以在发生了越界写的时候,尝试及时检测到它。GCC编译器在产生的汇编代码中加入了栈保护者机制来检测缓冲区越界。就是在缓冲区与栈保存的状态值之间存一个金丝雀值,该值是随机产生的,因此攻击者没有简单方式知道它是什么。在函数返回之前,程序检测金丝雀值是否改变来判断是否被攻击。
栈保护者机制
  • 限制可执行代码区域。消除攻击者向系统中插入可执行代码的能力,一种方法是限制哪些内存区域能够存放可执行代码。
    许多系统允许三种访问形式:(从内存读数据)、(存储数据到内存)、执行(将内存的内容看作机器代码)。现在处理器的内存保护引入了不可执行位,将读和执行访问模式分开。有了这个特性,栈可以被标记为可读可写,但是不可执行,而检查页是否可执行由硬件来完成,效率上没有损失。

浮点代码

处理器的浮点体系结构影响对浮点数据操作的程序如何被映射到机器上。其中AVX浮点体系结构允许数据存储在16个YMM寄存器中,每个寄存器都是256位(32字节)。当对标量数据操作时,这些寄存器只保存浮点数,而且只使用低32位(对于float)或64位(对于double)。

当函数包含指针、整数和浮点数混合的参数时,指针和整数通过通用寄存器传递,而浮点值通过YMM寄存器传递。

用AVX为浮点数上的操作产生的机器代码风格类似于为整数上的操作产生的代码风格。它们都使用一组寄存器来保存和操作数据值,以及传递函数参数。

总结

本章我们只分析了C语言到x86-64处理器的代码映射,C++的编译和C非常相似,但是Java的实现方式却完全不同。Java的目标代码是一种特殊的二进制表示,称为Jave字节代码,该代码可以看成是虚拟机的机器级程序。还有一种称为及时编译的方法,动态地将字节代码序列翻译成机器指令,当代码要执行多次时(比如在循环中),该方法执行起来更快。
字节代码作为程序的低级表示,优点是相同的代码可以在许多不同的机器上执行,而本章讨论的机器代码只能在x86-64机器上运行。

参考:
Datawhale 开源 408 计划——《深入理解计算机系统》

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容