《匯編語言:基于linux環境(第3版)》是風靡美國的經典匯編語言暢銷書籍的最新版,美國計算機領域著名作者jeffduntemann的力作。作者以其淵博的專業知識,豐富的實戰經驗,結合生動詳盡的實例,全面系統地介紹了linux環境下如何使用匯編語言進行程序設計以及與之有關的背景知識和相關工具的使用。本書寫作風格獨特,全書采用作者最有特色的對話式風格,結合大量源于生活的暗喻,將晦澀難懂的知識點條分縷析地呈現出來,以便讀者能以輕松愉快的心情學習。
《匯編語言:基于linux環境(第3版)》適合剛涉足linux環境下匯編語言的讀者,也可作為相關技術人員的參考書。
“你為什么要做那件事情?”
那是1985年,我和其他幾個焦躁不安的媒體大腕一起,乘坐一輛出租車去紐約參加記者招待會。那時我剛剛開始媒體生涯(作為PC Tech雜志的技術編輯),我的第一本書還有幾個月就要出版。我碰巧坐在一個已經成名的編程欄目的作者/大師旁邊。我們談論了很多這樣那樣的事情,他給我留下了深刻的印象。我不會說出他的名字,因為他已經在這個領域做了很多事情,如果他不把戒煙當成第一要務的話,肯定會作出更多的貢獻。
但是,碰巧我是一個Turbo Pascal的狂熱愛好者,我真正想做的事情是要學習如何編寫能夠使用全新的Microsoft Windows用戶界面的Turbo Pascal程序。在談到下面這個臭名昭著的問題之前,他皺了皺鼻子,做了個鬼臉苦笑道:
“你為什么想做那件事情呢?”
我以前從來沒有聽到過這個問題(盡管后來不止一次地聽到它),所以有點猝不及防。為什么?因為,嗯,因為……我想知道它的工作原理。
“嗨! 那是C語言要做的事情。”
更深一層的討論使我徹底對Pascal迷失了方向。但是通過一番尋根究底之后,我了解到,不能用Turbo Pascal編寫Windows應用程序。這樣做是不可能的。或者這個編程欄目作者/大師根本不知道怎么做,或者二者兼而有之。我不知道真相屬于哪一種,但是我的確知道了那個臭名昭著的問題的含義。
各位看官,注意:當某些人問你“為什么你想做那件事情?”,它的真實含義是:“你在問我如何做一件或者通過使用我喜歡的工具無法實現,或者徹底位于我的經驗之外的事情,但是我又不想承認這一點,以免丟臉。所以……如何和那些黑鷹隊較量?”
多年以來,我一遍又一遍地聽到這句話:
問:如何創建一個不用掃描就能讀出它的長度的C字符串?
答:你為什么想做那件事情?
問:如何用匯編語言編寫一個能夠在Turbo Pascal中調用的子程序?
答:你為什么想做那件事情?
問:如何用匯編語言編寫Windows應用程序?
答:你為什么想做那件事情?
你明白了吧。對于這個臭名昭著的問題的答案通常是一樣的,如果哪個狡猾的人這樣問你,你應該立即以最快的速度反駁道:因為我想知道它的工作原理。
這是一個很不充分的回答。我每次回答這個問題都用這個答案,但是有一次例外,那是很多年以前,我宣布要寫一本教那些初次接觸編程的人們如何用匯編語言來編寫程序的書。
問:天哪,你為什么想做那件事情?
答:因為要想具備理解所有其他編程體系工作原理的本事,這是現有的最佳方法。
對于程序員而言,有一件事情高于一切,那就是理解事情的工作原理。而且,學習如何成為一名程序員的過程幾乎就是一個掌握事情工作原理的過程。這可以在各個級別上完成,取決于你所使用的工具。如果你正在用Visual Basic編程,你必須理解某些事情的工作原理,但是這些事情總體來說局限于Visual Basic本身。Visual Basic在程序員和計算機之間放置了一個“層”,這個層隱藏了很多機制(同樣的情況適用于Delphi、Java、Python以及很多其他的高級語言編程環境)。如果你在使用C編譯器,那么你距離計算機更近了一些,你將看到更多的機制——而且必須看到,只有了解了這些機制的工作原理才能夠正確地使用它。然而,即便如此,還有一些內容是隱藏的,哪怕是對于那些老練的C程序員而言。
反過來,如果你在使用匯編語言,那么你與計算機的距離就最大限度地縮短了。匯編語言什么都沒有隱藏,而且也沒扣留任何權利。當然,從另一方面來講,由于在你和機器之間沒有任何神奇的“層”來替你“照顧”某些事情,所以再無法為自己的無知找到任何托詞。如果不知道事情的工作原理,那么你死定了——除非你擁有的知識夠多,能自己把它弄明白。
我創作本書的目的不完全是為了教你匯編語言本身,這是關鍵所在。如果說本書有一個根本的指導思想,那就是:引入一種受過訓練的對于機器的好奇心,以及一些基本的上下文,根據這些上下文,你能夠從最底層開始探索計算機,并具備竭盡所能掌握它的信心。這是一件困難的事情,但是只要專注些,耐心些,沒有什么你掌握不了的,而且只要有時間(我得提醒一下),可能相當長。
事實上,這里我真正想教給你的是如何學習。
你需要準備些什么
要想按照我教的方式編寫程序,你需要一臺運行Linux的基于Intel x86的計算機。本書的正文和示例都假設至少運行在386上,但是因為Linux本身至少需要386環境,所以你已經滿足硬件要求了。
你需要熟悉使用Linux。我不能在本書中教你如何安裝和運行Linux,不過在某些不太直觀易懂的情況下,我會提供一些暗示。如果你對Linux環境還沒有熟悉,我建議你找本教程,借助它來進行工作。這樣的書很多,但是我最喜歡的是William von Hagen撰寫的Ubuntu 8.10 Linux Bible(Linux for Dummies雖然不錯,但是內容不夠全面)。
到底使用哪個Linux版本并不是很重要,只要至少基于2.4版的內核即可,當然,2.6版更好一些。我用來編寫本書示例程序的版本是Ubuntu 8.10。哪一種圖形用戶界面并不重要,因為所有的程序都被編寫為運行在純文本Linux控制臺下。匯編程序本身,即NASM,也是純文本。
我在講解編程邏輯的時候以Kate編輯器為模型,它需要用到GUI。實際上你可以使用任何你想用的編輯器。并不是程序本身需要Kate編輯器,而是如果你剛剛開始接觸編程,或者經常使用高級語言的特定編輯環境的話,Kate編輯器是一個很好的選擇。
我在正文中引用的調試器是Gdb,但是大多數情況下是通過它的內置GUI前端——Insight來使用它。Insight需要一個有效的X Window子系統,但是并不綁定到某一特定的GUI系統上,如GNOME或KDE。
你不必事先知道如何安裝和配置這些工具,因為我會在合適的時候,在相應的章節里涵蓋所有必要工具的安裝及配置。
注意,對于其他不是基于Linux內核的Unix實現而言,其功能的執行方式可能與基于Linux內核的不完全相同。例如在調用內核時,BSD Unix就使用不同的規則。其他一些Unix版本(如Solaris)我并不擅長。
學習計劃
本書從基礎開始講起,我的意思是“零起點”。也許你已經入門,或者已略有心得也沒關系。我對待起點問題非常慎重。我仍然相信,依次閱讀本書各章并非浪費時間。復習很管用,嘿!你會發現,你了解的知識并不像想象中的那么多(我經常這樣)。
但是,如果你的時間很不充裕,下面這個學習進度安排表可以讓你偷點兒懶。
1.如果你已經理解了計算機編程的基本概念,可跳過第1章。
2.如果你已經理解了除了十進制之外的其他數制(尤其是十六進制和二進制)背后的思想,可跳過第2章。
3.如果你已經對計算機內部原理(內存、CPU結構等)有了一些了解,可跳過第3章。
4.如果你已經理解了x86內存尋址機制,可跳過第4章。
5.不。停!勾掉它(第4點)。即使你已經理解了x86內存尋址機制,也請閱讀第4章。
強調一下,這里的第5點是出于這樣的原因:匯編語言編程是關于內存尋址的編程。如果不理解內存尋址機制,學習所有其他關于匯編的知識將毫無用處。所以,不要跳過第4章,無論你了解了或者認為了解了其他知識與否。從這一章開始,一直通讀到本書的結尾。加載每一個示例程序,匯編每一個示例程序,運行每一個示例程序。力求理解每一個程序中的每一行代碼。對什么內容都不要無條件地相信。
而且,不僅限于此。當你理解了知識之后,修改那些示例程序。試著采用不同的方式完成它們。勇于嘗試我沒有提到的那些內容。大膽些。這樣還不夠,最好更瘋狂一些——這些二進制位是沒有感情的,可能發生的最糟糕的事情無非就是Linux拋出一個分段錯誤,它將會破壞你的程序(也許會傷害你的自尊),但是并不會破壞Linux(“保護模式”不是白叫的!)。唯一可能存在的困難是:當你在嘗試某些知識的時候,要了解程序不工作的原因,而且要像你了解所有它做的其他事情一樣的清晰。然后將其記錄下來。
最后是我追求的目標:教給你一種方法,通過這個方法,你可以理解計算機的每一個組成部分正在做的事情,以及它們是如何協同工作的。這并不意味著我本人會講解計算機的每一個組成部分——即使我活得再長,也做不到。計算過程已經不再像以前那樣簡單,但是如果具備了耐心研究和實驗的品質,你很有可能自己把它完成。最后,學習的唯一方法是:一切要靠自己。不能否認,你能夠找到一些指導性內容——來自朋友的,來自互聯網的,來自像本書一樣的書籍的,但那僅僅是指導,車軸上的潤滑劑而已。你必須搞清楚誰才是真正的主人,是你還是機器,然后使其成為主人。只有匯編語言程序員才真正有資格聲稱是主人,這是一個值得思考的事實。
留意大寫習慣
匯編語言是所有編程語言中最為獨特的一種,它對于大小寫的區分沒有統一的標準。在C語言中,所有的標識符都是區分大小寫的,但是,在匯編語言中,存在根本不區分大小寫的匯編編譯器。我在本書中用到的匯編編譯器NASM,只對程序員定義的標識符區分大小寫。但是,對于指令助記符以及寄存器名卻不區分大小寫。
在有關匯編語言的書籍中常有一些書寫習慣,其中之一就是將CPU指令助記符和寄存器名在正文中大寫,而在散布于正文之中的源代碼文件和代碼片段中小寫。這里,我也遵循這個習慣。在討論部分的正文里,我會說MOV、EAX寄存器、EFLAGS等。在示例代碼中,我會用mov、eax及eflags表示它們。
這樣做出于兩個原因。
* 在正文中,助記符和寄存器需要醒目。因為在眾多的一般文字之中,非常容易失去它們的線索。
* 為了閱讀和學習本書之外的其他已有文檔和源代碼,你需要能夠輕松地閱讀匯編語言,無論它是大寫的,小寫的,還是大小寫混合的。適應同一內容的不同表達方式,這一點非常重要。
這可能會激怒某些Unix界的人們,因為他們盲目崇拜小寫字符。我事先為激怒他們這件事情而道歉,但是我仍然堅定地認為,那樣做就是一種盲目崇拜,而且相當孩子氣。
為什么我又到這了
無論你選擇從什么地方開始學習本書,現在都是時間開始行動了。你只需記住,無論你面對的是什么,管它是什么黃鼠狼、機器,還是你自己的經驗不足,腦海中首先要記住一點:你學習的目的是為了搞清楚它的工作原理。
讓我們出發吧。
致謝
首先,我要感謝Wiley的Carol Long和Brain Herrmann,是他們允許這本書再版,并確保了再版的實現,而且比上次出版耗時更短。
對于所有三個版本,我都欠Michael Abrash 太多人情債,是他始終如一地在諸多方面為我提供非常明智的建議,尤其是那些Intel微體系結構之間的晦澀難解的區別。
盡管Randy Hyde、Frank Kotler、Beth和alt.lang.asm上的其余所有人沒有意識到,但是他們其實已經通過各種方式為我提供了幫助,正是由于他們聽取和回答來自匯編語言初學者的要求,才更有助于我決定哪些內容必須涵蓋在像本書一樣的書籍中,哪些不應該包含在里面。
最后,一如既往,向Carol致敬!是她40年如一日給予我絕對的支持和神圣的友誼,使我能夠從事諸如本書之類的項目,并且將其進行到底。
作者簡介
Jeff Duntemann,技術作家、編輯和講師,同時也是一個出版業分析師。在他涉足技術領域的30年中,他曾經擔任過施樂公司的程序員和系統分析員,Ziff-Davis出版公司的技術期刊編輯,Coriolis Group Books及后來的Paraglyph 雜志社的編輯部主任。他目前是一名技術出版顧問,同時擁有Copperwood出版社(lulu.com的按需印刷出版商)。Jeff與妻子Carol住在科羅拉多州斯普林斯市。
第1章 又一個令人愉快的星期六
1.1 一切盡在計劃之中
1.2 如果這是真實情況
1.3 此路不通,請繞行
1.4 像棋盤游戲一樣的匯編語言編程
第2章 外星基數
2.1 新數學怪物歸來
2.2 在火星上計數
2.3 八進制:綠色精怪怎樣偷走8和9的
2.4 十六進制:解決數字的短缺
2.5 從十六進制到十進制,從十進制到十六進制
2.6 十六進制運算
2.7 進制
2.8 進制簡寫方式:十六進制
第3章 摘下面具 第1章 又一個令人愉快的星期六
1.1 一切盡在計劃之中
1.2 如果這是真實情況
1.3 此路不通,請繞行
1.4 像棋盤游戲一樣的匯編語言編程
第2章 外星基數
2.1 新數學怪物歸來
2.2 在火星上計數
2.3 八進制:綠色精怪怎樣偷走8和9的
2.4 十六進制:解決數字的短缺
2.5 從十六進制到十進制,從十進制到十六進制
2.6 十六進制運算
2.7 進制
2.8 進制簡寫方式:十六進制
第3章 摘下面具
3.1 raxie,我們怎么不認識你
3.2 開關、晶體管和存儲器
3.3 遵循計劃行事的盒子
3.4 是什么vs.怎么做:體系結構和微體系結構
.3.5 工廠經理
第4章 位置,位置,位置
4.1 內存模式的樂趣
4.2 段的本質
4.3 16位和32位寄存器
4.4 三種主要的匯編編程模型
4.5 保護模式下不再允許我們做的事情
4.6 展望未來:64位“長模式”
第5章 匯編的權利
5.1 文件及其包含的內容
5.2 文本進去,代碼出來
5.3 匯編語言開發過程
5.4 沿著匯編小路旅行
第6章 有地兒,有工具
6.1 kate編輯器
6.2 linux和終端
6.3 使用linux make
6.4 insight調試器
第7章 跟蹤指令
7.1 為自己建立一個沙盒
7.2 cpu的標志位
7.3 有符號值和無符號值
7.4 隱式操作數和mul
7.5 閱讀和使用匯編語言參考資料
7.6 neg:求補(求補碼;即,與-1相乘)
第8章 我們的崇高目標
8.1 匯編語言程序的基本框架
8.2 通過堆棧實現后進先出
8.3 通過int80使用linux內核服務
8.4 設計一個有價值的程序
第9章 位、標志、分支和表
9.1 位就是二進制位(字節也是二進制位)
9.2 移位操作
9.3 位操作
9.4 標志、測試和分支
9.5 保護模式下內存尋址詳解
9.6 字符表轉換
9.7 用表來代替計算
第10章 分治
10.1 盒子里面的盒子
10.2 調用和返回
10.3 局部標號和跳轉的長度
10.4 生成外部過程庫
10.5 自定義過程的藝術
10.6 linux控制臺下的簡單光標控制
10.7 創建和使用宏
第11章 字符串奏鳴曲
11.1 匯編語言字符串的概念
11.2 rep stosb,軟件機槍
11.3 半自動武器:不帶rep的stosb
11.4 movsb:快速塊拷貝
11.5 將數據存儲到不連續的字符串中
11.6 命令行參數和堆棧檢查
11.7 使用scasb進行字符串搜索
第12章 c語言
12.1 什么是gnu
12.2 連接到標準的c函數庫
12.3 使用printf()格式化文本輸出
12.4 使用fgets()和scanf()進行數據輸入
12.5 駕馭時間
12.6 理解at&t指令助記符
12.7 產生隨機數
12.8 c如何看待命令行參數
12.9 簡單文件輸入/輸出
結論:不是結束,而是剛剛開始
附錄a 部分x86指令集
附錄b 字符集圖