图书前言

关于技术编辑

David Stafford是汇编语言低级编程的爱好者,从8位处理器到现代64位多核架构,他都游刃有余。他住在西雅图地区,从事机器人人工智能领域的工作。

致    谢

感谢在本书撰写和出版过程中以各种方式帮助我的所有人。首先感谢Wiley的Jim Minatel和Pete Gaughan,他们启动了这个项目并确保了项目的完成。还要感谢David Stafford,他担任技术编辑并不断提出宝贵的建议。

GitHub的Antony成功创建了一个适用于Linux的AppImage版本,用于古怪、过时但非常易用的Insight调试器;该调试器在本书第3版于2009年出版后不久就从Linux存储库中移除了。非常感谢Antony完成了这个非常特别的项目。你可以在此找到他的Insight AppImage:https://appimage.github.io/Insight。

还要特别感谢Dmitriy Manushin,他创建了SASM,这是一个面向初学者的免费汇编语言集成开发环境(IDE):https://dman95.github.io/SASM/english.html。

当我在glibc中遇到奇怪的问题时,Contrapositive Diary上的专家团队帮我解决了问题:

Jim Strickland

Bill Buhler

Jason Bucata

Jonathan O’Neal

Bruce和Keith(没有留下姓氏,没关系——建议很宝贵)

最后,和往常一样,我要向Carol致敬。感谢她54年来的支持和珍贵友谊,这不仅让我充满活力,还让我有能力承担这样充满挑战的项目,并坚持到底,无论这些项目一路上让我多么抓狂!

前    言

“你为什么要那样做?”

那是1985年,我在纽约市乘坐包车去参加一个新闻发布会,同行的还有一群焦躁不安的媒体狂人。我当时刚刚开始我的科技记者生涯(担任PC Tech Journal的技术编辑),我的第一本书还要几个月后才会问世。碰巧,我坐在一位著名的编程作家/专家旁边,我对他印象深刻,因为他一直在对我唠叨一些事情。他确实很烦人,但我现在才明白为什么他那么令人讨厌:他的生活和工作与我完全没有交集。

在我们的聊天中,我无意中透露了我是一个Turbo Pascal狂热爱好者,而我真正想做的是学习如何编写利用全新Microsoft Windows用户界面的Turbo Pascal程序。他皱了皱鼻子,苦笑了一下,然后问出了那个著名的问题:

“你为什么要那样做?”

那时我还从未听过这个问题(尽管后来我听到了很多次),这让我很惊讶。“为什么?因为,嗯,因为……我想知道它是如何工作的。”我这样想着,于是答道,

“呵呵,那就是C语言的用途。”

进一步的讨论并没有让我在Pascal的方向上有所进展。但经过一番探索,我明白了不能用Turbo Pascal编写Windows应用程序。这是不可能的。或者……那位编程作家/专家不知道怎么做。也许两者皆有。我从未了解过1985年的真实情况。Delphi在1995年彻底解答了这个问题。但我确实了解了那个著名问题的含义。

请注意,如果有人问你:“你为什么要那样做?”它的真正意思是:你问我如何做一件事情,这件事要么不可能用我偏爱的工具实现,要么完全超出了我的经验范围,但我不想通过承认这一点而失去面子。

这些年来我一次又一次听到这样的话:

问:如何设置C字符串,以便不必扫描即可读取其长度?

答:你为什么要那样做?

问:如何编写可从Turbo Pascal调用的汇编语言子程序?

答:你为什么要那样做?

问:如何用汇编语言编写Windows应用程序?

答:你为什么要那样做?

……

你明白这个意思了。对于这个著名的问题,答案永远都是一样的。如果那些狡猾的人问你这个问题,你就尽快反击:“因为我想知道它是如何运作的。”

这是一个完全足够的回答。每次我都用这个答案,除了很多年前的一个例外,那时我提出我想写一本书,教人们如何将汇编语言编程作为他们的第一次编程体验。

问:天哪,你为什么要那样做?

答:因为这是培养理解编程世界其他部分如何运作所需技能的最佳方式。

成为一名程序员,首先要做到的一点是理解事物的工作原理。此外,学习成为一名程序员几乎完全是一个了解事物如何运作的过程。这可以在不同层次上完成,具体取决于你使用的工具。

如果你使用的是Visual Basic编程,你需要理解某些事物的工作原理,但这些事物基本上都局限于Visual Basic本身。Visual Basic在程序员与计算机之间设置了一道屏障,隐藏了大量的底层机制。Delphi、Lazarus、Java、Python及许多其他高级编程环境也是如此。如果你使用的是C编译器,你会离机器更近,你会看到更多的底层机制——因此,你必须理解这些机制的工作原理才能使用它们。然而,即使是资深的C程序员,也有许多东西仍然隐藏在他们的视野之外。

一方面,如果你在使用汇编语言编程,你会尽可能地接近机器。汇编语言不隐藏任何东西,也不会保留任何能力。当然,另一方面,你和机器之间没有任何神奇的层可以消除任何无知并且可以“处理”一切。如果你不理解某些东西的工作原理,你就会陷入困境——除非你足够了解,能够自己弄明白。

这是一个关键点:我编写本书的目标并非仅是讲授汇编语言本身。如果说本书有一个首要目标的话,那就是让你对机器底层产生一定的好奇心,同时提供一些基本的背景知识,帮助你开始探索机器的最底层,以及给予你信心去尽力尝试。这是困难的地方,但只要你集中注意力、耐心等待并投入必要的时间(可能会相当长),你完全可以掌握它。

实际上,我真正教给你的,是如何去学习。

你需要什么

要按照我所讲授的方式进行编程,你需要一台运行64位Linux发行版的英特尔计算机。我在编写这本书时使用的是Linux Mint Cinnamon V20.3 Una。Una是这个Linux Mint版本的代码名称,是“Linux Mint 20.3”的简写。我推荐使用Mint;它给我带来的麻烦比我用过的任何其他发行版都少,而且自从Linux首次出现以来,我就断断续续地使用它。我认为你选择哪个图形化shell并不重要。我喜欢Cinnamon,你可以使用自己喜欢或熟悉的任何界面。

你需要在用户级别上对Linux有一定的熟练掌握能力。我无法在本书中教你如何安装、配置和运行Linux。如果你还不熟悉Linux,请找一本教程,学习并掌握它。网上有很多这样的教程。

你需要一款名为SASM的免费软件,这是一种简单的汇编编程交互式开发环境。它基本上包括一个编辑器、一个构建系统,以及一个标准Linux调试器gdb的前端。你还需要一个免费的汇编器,名为NASM。

你不需要提前知道如何下载、安装和配置这些工具,因为在适当的时机,我会详细介绍所有必要的工具安装和配置方式。

请注意,其他不基于Linux内核的UNIX实现可能在底层的工作方式上有所不同。例如,BSD UNIX在进行系统调用时使用了不同的约定,而其他UNIX版本(如Solaris)则超出了我的经验范围。

请记住,本书是关于x64架构的。在x64的范围内,我也会讲解x86架构的元素。32位x86和64位x64之间的差距比16位x86和32位x86之间的差距要小得多。如果你已经扎实掌握了32位x86的基础知识,你将很快掌握本书的大部分内容。如果你能做到这一点,那很好——只是请记住,本书是为了那些刚刚开始在英特尔CPU上编程的人准备的。

还要记住,这本书的版面受出版商的限制:纸张、油墨和装订都不免费。这意味着我必须在这些限制内缩小我讲授和解释的内容范围。我希望有足够的篇幅来介绍AVX数学子系统,而实际上没有。但是,我相信,一旦你读完这本书,就能搞清楚其中的大部分内容。

总体规划

本书从最基础的知识开始讲解。也许你在这个阶段,已经完全掌握了这些。我尊重这一点。我仍然认为,从第1章开始,一直到最后一章按顺序阅读不会有什么坏处。复习是有用的,你可能会意识到你对某些内容的了解没有你想象的那么深刻(这种情况经常发生在我身上)。

如果时间紧迫,可以按照下面的建议进行学习:

●如果你已经了解计算机编程的基本思想,请跳过第1章。

●如果你已经了解十进制以外的其他数字进制(尤其是十六进制和二进制)背后的思想,请跳过第2章。

●如果你已经掌握了计算机的内部结构(内存、CPU架构等),请跳过第3章。

●如果你已经了解x64内存寻址,请跳过第4章。

●不。停下来。即使你已经了解x64内存寻址,也请阅读第4章。

最后一项需要特别强调一下,原因是:汇编语言编程涉及内存寻址。如果你不理解内存寻址,那么你在汇编语言中学到的其他任何知识都不会对你有帮助。所以,无论你已经知道或认为自己知道什么,都不要跳过第4章。从那里开始,一直到最后。内存寻址在书的其余部分会经常出现,它实际上是汇编语言编程的核心。

加载每个示例程序,汇编每个程序,并运行它们。努力理解每个程序中的每一行。不要盲目相信任何东西。此外,别仅仅停留于此。随着你对事物的理解加深,可修改示例程序。尝试不同的方法,尝试一些我没有提到的做法,要大胆,可以尽情尝试。最糟糕的情况只是Linux抛出一个分段错误,这可能损坏你的程序,但不会损害Linux。唯一的注意事项是,当你尝试某些做法时,尽量理解为什么它没有像你理解的其他有效事物那样井井有条地工作。即使程序运行正常,也要在SASM调试器中逐步调试。

这就是我最终追求的目标:展示如何理解机器的每一个细节是如何运作的,以及它的所有组件是如何协同工作的。这并不意味着我会亲自解释它的每一个细节——因为没有人能活得足够久去做到这一点,而且计算机技术已经不再简单了。如果你已经养成耐心研究和实验的习惯,你可以自己弄明白。归根结底,这就是学习的唯一途径:靠自己。从朋友、网络或像这样的书籍中得到的指导只是引导和润滑剂。你必须决定谁才是主宰,是你还是机器,并使之成为现实。

关于大写惯例的说明

汇编语言在编程语言中有一个独特之处,即没有统一的大小写区分标准。在C语言中,所有标识符都区分大小写,而我见过一些汇编器完全不区分大小写。本书介绍的汇编器NASM,仅对程序员定义的标识符区分大小写,指令助记符和寄存器名称是不区分大小写的。

汇编语言文献中有一个习惯,那就是在章节描述中将CPU指令助记符用大写字母表示,而源代码文件和文本中的代码段用小写字母表示。本书也会遵循这一习惯。在正文中,会使用MOV、CALL和CMP;而在示例代码中,则使用mov、call和cmp。代码片段和代码清单将以等宽的Courier风格字体呈现。当提到寄存器时,会使用大写字母(但不使用Courier字体),而在代码片段和清单中则使用小写字母。

在正文中,助记符需要突出显示(用大写字母)。在一大堆普通的大小写字母混合的单词中,很容易忘记它们。

要阅读和学习本书以外的现有文档和源代码,你需要能够轻松地阅读大写、小写以及混合大小写的汇编语言。熟悉不同的表达方式非常重要。

记住你为什么在这里

不管你选择从书的哪一部分开始,现在是时候开始了。只要记住,假如遇到困难,无论是面对微妙的问题还是面对机器故障,你要始终把这个目标放在心中:要努力搞清楚它是如何运作的。

让我们一起出发吧。

源代码下载

可扫描封底二维码,下载源代码。