代码分析:NASM源码阅读笔记
生活随笔
收集整理的這篇文章主要介紹了
代码分析:NASM源码阅读笔记
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
NASM源碼閱讀筆記
NASM(Netwide Assembler)的使用文檔和代碼間的注釋相當(dāng)齊全,這給閱讀源碼
提供了很大的方便。按作者的說法,這是一個(gè)模塊化的,可重用的x86匯編器,
而且能夠被嵌入進(jìn)其它的程序中,比如做為一個(gè)高級(jí)語言編譯器的后端程序。下面
的文字希望能對(duì)大家有所幫助。錯(cuò)誤之處,多多指正。:>
一、各模塊簡(jiǎn)介:
? ?NASM按功能將匯編器的各個(gè)部分獨(dú)立為模塊,各個(gè)模塊之間并不進(jìn)行直接的聯(lián)系。
? ?這種編程結(jié)構(gòu)使得閱讀源代碼變得輕松起來,可以將各個(gè)模塊獨(dú)立出來分別閱讀。
? ?在源碼中的doc/internal.doc中有一個(gè)模塊間的結(jié)構(gòu)圖:
? ? ? ? ? ? ? ? ?+--- preproc.c ----+
? ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ?|
? ? ? ? ? ? ? ? ?+---- parser.c ----+
? ? ? ? ? ? ? ? ?| ? ? ? ?| ? ? ? ? |
? ? ? ? ? ? ? ? ?| ? ?? float .c ? ? ?|
? ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ?|
? ? ? ? ? ? ? ? ?+--- assemble.c ---+
? ? ? ? ? ? ? ? ?| ? ? ? ?| ? ? ? ? |
? ? ? ?nasm.c ---+ ? ? insnsa.c ? ? +--- nasmlib.c
? ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ?|
? ? ? ? ? ? ? ? ?+--- listing.c ----+
? ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ?|
? ? ? ? ? ? ? ? ?+---- labels.c ----+
? ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ?|
? ? ? ? ? ? ? ? ?+--- outform.c ----+
? ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ?|
? ? ? ? ? ? ? ? ?+----- *out.c -----+
?圖中沒有出現(xiàn)的其它模塊有eval.c和float.c。eval.c是表達(dá)式求值模塊,用于
?preproc.c,parser.c和*out.c;float.c是浮點(diǎn)數(shù)常量存儲(chǔ)轉(zhuǎn)換模塊,用于parser.c。
?各模塊將需要導(dǎo)出的函數(shù)和數(shù)據(jù)結(jié)構(gòu)的定義放入對(duì)應(yīng)的頭文件中,以便被其它模塊調(diào)用。
? /* 需要注意的是許多函數(shù)指針或包含函數(shù)指針的結(jié)構(gòu)以參數(shù)的形式在各模塊間傳遞,這種
? *間接性使得模塊之間的結(jié)構(gòu)更加清晰。
?? */
1 、強(qiáng)大的預(yù)處理器preproc.c:
預(yù)處理部分是NASM中最復(fù)雜,也是代碼最多的模塊,有四千多行代碼。
它使用自己的源碼掃描程序ppscan,支持名字相同但參數(shù)不同的宏,支持
各個(gè)宏通過上下文相關(guān)堆棧來交換信息,支持宏內(nèi)進(jìn)行預(yù)處理循環(huán)等。
? (可參閱NASM使用文檔來了解預(yù)處理的功能和使用方法)
? ? ? ?preproc.c對(duì)外僅導(dǎo)出一個(gè)結(jié)構(gòu):
? ? ? ? ?? typedef ? struct ?{?
? ? ? ? ? ? ?? /*
? ? ? ? ? ? ? ?* Called at the start of a pass; given a file name, the number
? ? ? ? ? ? ? ?* of the pass, an error reporting function, an evaluator
? ? ? ? ? ? ? ?* function, and a listing generator to talk to.
? ? ? ? ? ? ? ? */
? ? ? ? ? ? ?? void ?(*reset) ( char ?*,? int , efunc, evalfunc, ListGen *);
? ? ? ? ? ? ?? /*
? ? ? ? ? ? ? ?* Called to fetch a line of preprocessed source. The line
? ? ? ? ? ? ? ?* returned has been malloc'ed, and so should be freed after
? ? ? ? ? ? ? ?* use.
? ? ? ? ? ? ? ? */
? ? ? ? ? ? ?? char ?*(*getline) ( void );
? ? ? ? ? ? ?? /*
? ? ? ? ? ? ? ?* Called at the end of a pass.
? ? ? ? ? ? ? ? */
? ? ? ? ? ? ?? void ?(*cleanup) ( int );
? ? ? ? ?? } Preproc;
? ? ? ? ? 其中reset用于初始化,cleanup用于預(yù)處理后的清理工作。
? ? ? ? ? 而函數(shù)名getline則是相對(duì)于主程序來說的。它對(duì)編譯器內(nèi)嵌的宏和匯編源程序
? ? ? ? ? 從上至下進(jìn)行處理,記錄遇到的宏信息,用實(shí)際的代碼和數(shù)據(jù)替換調(diào)用的宏,并
? ? ? ? ? 返回一行可以被下一個(gè)模塊(這里是parser.c)使用的“規(guī)范的”匯編代碼。
? ? ? ? ?? /* 預(yù)處理部分相對(duì)于主程序后面調(diào)用的模塊來說是透明的。 */
?? 2 、功能單一的解析模塊parser.c:
? ? ? ? ? ?按作者的說法,解析模塊的作用就是解析由`標(biāo)號(hào)',`指令',`操作數(shù)'和`注釋'組成的
? ? ? ?“規(guī)范的”匯編代碼行,填充并返回這行代碼的insn結(jié)構(gòu):
? ? ? ? ?? typedef ? struct ?{? ? ? ? ? ? ? ? ? ? ? /* ?an instruction itself? */
? ? ? ? ? ? ?? char ?*label; ? ? ? ? ? ? ? ? ? ? /* ?the label defined, or NULL? */
? ? ? ? ? ? ?? int ?prefixes[MAXPREFIX]; ? ? ?? /* ?instruction prefixes, if any? */
? ? ? ? ? ? ?? int ?nprefix; ? ? ? ? ? ? ? ? ? ? /* ?number of entries in above? */
? ? ? ? ? ? ?? int ?opcode; ? ? ? ? ? ? ? ? ? ?? /* ?the opcode - not just the string? */
? ? ? ? ? ? ?? int ?condition; ? ? ? ? ? ? ? ? ? /* ?the condition code, if Jcc/SETcc? */
? ? ? ? ? ? ?? int ?operands; ? ? ? ? ? ? ? ? ?? /* ?how many operands? 0-3?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? * (more if db et al)? */
? ? ? ? ? ? ? operand oprs[ 3 ]; ? ? ? ? /* ?the operands, defined as above? */
? ? ? ? ? ? ? extop *eops; ? ? ? ? ? ? ? ? ? ? /* ?extended operands? */
? ? ? ? ? ? ?? int ?eops_float; ? ? ? ? ? ? ? ? /* ?true if DD and floating? */
? ? ? ? ? ? ?? long ?times; ? ? ? ? ? ? ? ? ? ?? /* ?repeat count (TIMES prefix)? */
? ? ? ? ? ? ?? int ?forw_ref; ? ? ? ? ? ? ? ? ?? /* ?is there a forward reference?? */
? ? ? ? ?? } insn;
? ? ? ? ? 該模塊導(dǎo)出三個(gè)函數(shù):
? ? ? ? ?? void ?parser_global_info ( struct ?ofmt *output, loc_t *locp);
? ? ? ? ? insn *parse_line ( int ?pass,? char ?*buffer, insn *result,
? ? ? ? ? ? ? ? ? ? ? ? ?efunc error, evalfunc evaluate, ldfunc ldef);
? ? ? ? ?? void ?cleanup_insn (insn *instruction);
? ? ? ? ? 其中parser_global_info用于解析前的初始化工作(這里是初始化輸出的文件格式和
? ? ? ? ? 當(dāng)前行所在的位置,在定義標(biāo)號(hào)時(shí)需要用到)。cleanup_insn用于解析后的清理工作。
? ? ? ? ? 而parse_line的功能就是按照上面提到的`標(biāo)號(hào)',`指令',`操作數(shù)'和`注釋'的
? ? ? 順序?qū)uffer里的代碼進(jìn)行解析,并對(duì)相應(yīng)部分的合法性做一些檢查,比如檢查指令的前綴是
? ? ? 不是超過了所能允許的最大值,操作數(shù)是否為內(nèi)存地址,代碼中是否含有一個(gè)向前的引用等。
? ? ? 它還提供了對(duì)偽指令DB,DD,RESB,RESD,INCBIN等的支持。
?? 3 、代碼生成器assemble.c:
? ? ? ? 當(dāng)parser.c填充好insn結(jié)構(gòu)后,assemble.c就按照Intel機(jī)器碼規(guī)則生成實(shí)際的
? ? ?機(jī)器碼,并傳給out*.c來生成具體格式的目標(biāo)文件。
? ? ? ? ? 它導(dǎo)出兩個(gè)函數(shù):
? ? ? ? ?? long ?insn_size ( long ?segment,? long ?offset,? int ?bits,? unsigned ? long ?cpu,
? ? ? ? ? ? ? ? ? ? ? ?insn *instruction, efunc error);
? ? ? ? ?? long ?assemble ( long ?segment,? long ?offset,? int ?bits,? unsigned ? long ?cpu,
? ? ? ? ? ? ? ? ? ? ? insn *instruction,? struct ?ofmt *output, efunc error,
? ? ? ? ? ? ? ? ? ? ? ListGen *listgen);
? ? ? ? ? 其中insn_size一般用在第一遍分析源碼時(shí),用于確定某一行代碼在實(shí)際目標(biāo)文件中所需的
? ? ? 空間,而assemble則一般用在第二遍分析源碼時(shí),它轉(zhuǎn)化insn結(jié)構(gòu)中的指令為實(shí)際的機(jī)器碼,
? ? ? 并輸出到out*.c中以生成具體格式的目標(biāo)文件。
?? 4 、簡(jiǎn)單的表達(dá)式求值模塊eval.c:
? eval.c用于計(jì)算并返回代碼中表達(dá)式的值,其中運(yùn)算符||,^^,&&等用于條件匯編%if類指令
? 的表達(dá)式中,返回真或假,|,^,&,<<,+,*,/等用于對(duì)常量值進(jìn)行運(yùn)算,SEG,WRT等則用于得到
? ? ? 程序中段(或節(jié))實(shí)際的基址和偏移。
? ? ? ? ? eval.c的功能比較簡(jiǎn)單,不能像MASM那樣處理類似(eax !=? 0 )這樣的表達(dá)式。 (后記1) 。
? eval.c利用標(biāo)準(zhǔn)的scan程序掃描表達(dá)式緩沖區(qū),找出存在的運(yùn)算符進(jìn)行運(yùn)算,并將
? 處理后的值存入expr結(jié)構(gòu)中:
? ? ? ? ? ? /*
? ? ? ? ? ? * Expression-evaluator datatype. Expressions, within the
? ? ? ? ? ? * evaluator, are stored as an array of these beasts, terminated by
? ? ? ? ? ? * a record with type== 0. ?Mostly, it's a vector type: each type
? ? ? ? ? ? * denotes some kind of a component, and the value denotes the
? ? ? ? ? ? * multiple of that component present in the expression. The
? ? ? ? ? ? * exception is the WRT type, whose `value' field denotes the
? ? ? ? ? ? * segment to which the expression is relative. These segments will
? ? ? ? ? ? * be segment-base types, i.e. either odd segment values or SEG_ABS
? ? ? ? ? ? * types. So it is still valid to assume that anything with a
? ? ? ? ? ? * `value' field of zero is insignificant.
? ? ? ? ? ?? */
? ? ? ? ? ? typedef ? struct ?{?
? ? ? ? ? ? ? ? long ?type; ? ? ? ? ? ? ? ? ? ? ? ? ? ?? /* ?a register, or EXPR_xxx? */
? ? ? ? ? ? ? ? long ?value; ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* ?must be >=? 32 ?bits? */
? ? ? ? ? ? } expr;
? ? ? ? ? ?值得注意的是eval.c只在第一遍分析源碼時(shí)處理運(yùn)算符||,^^,&&等,因?yàn)樗鼈冎辉?/span>
? ? ? ?預(yù)處理表達(dá)式中才可能出現(xiàn)。
? ? 5. ?標(biāo)號(hào)處理器label.c:
? ? ? ? ? ?NASM的標(biāo)號(hào)分局部標(biāo)號(hào)和全局標(biāo)號(hào)兩種(外部標(biāo)號(hào)可看作是在另一個(gè)源程序中的全局標(biāo)號(hào))。
? ? ? ? ? ?局部標(biāo)號(hào)名最終將由兩部分組成: "上一個(gè)全局標(biāo)號(hào)名+局部標(biāo)號(hào)名" 。
? ? ? ? ? ?全局標(biāo)號(hào)名最終將由三部分組成: "lprefix+全局標(biāo)號(hào)名+lpostfix" 。其中l(wèi)prefix和lpostfix
? ? ? ?都是可選的,它們分別用于在所有的全局標(biāo)號(hào)名的前面和后面添加字符串。比如在編譯時(shí)指定選項(xiàng)
? ? ? ?--prefix_ 將會(huì)在所在的全局標(biāo)號(hào)名前加下劃線,這就會(huì)和在C中生成標(biāo)號(hào)的情況差不多。
? ? ? ? ? ?最終lable.c會(huì)將標(biāo)號(hào)的相關(guān)信息傳給對(duì)應(yīng)的out*.c來生成目標(biāo)文件中的符號(hào)。
? ? ? ? ? ?這個(gè)模塊導(dǎo)出以下數(shù)據(jù)和函數(shù):
? ? ? ? ? ? ? ? extern ? char ?lprefix[PREFIX_MAX];
? ? ? ? ? ? ? ? extern ? char ?lpostfix[PREFIX_MAX];
? ? ? ? ? ? ? ? int ?lookup_label ( char ?*label,? long ?*segment,? long ?*offset);
? ? ? ? ? ? ? ? int ?is_extern ( char ?*label);
? ? ? ? ? ? ? ? void ?define_label ( char ?*label,? long ?segment,? long ?offset,? char ?*special,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? int ?is_norm,? int ?isextrn,? struct ?ofmt *ofmt, efunc error);
? ? ? ? ? ? ? ? void ?redefine_label ( char ?*label,? long ?segment,? long ?offset,? char ?*special,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? int ?is_norm,? int ?isextrn,? struct ?ofmt *ofmt, efunc error);
? ? ? ? ? ? ? ? void ?define_common ( char ?*label,? long ?segment,? long ?size,? char ?*special,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct ?ofmt *ofmt, efunc error);
? ? ? ? ? ? ? ? void ?declare_as_global ( char ?*label,? char ?*special, efunc error);
? ? ? ? ? ? ? ? int ?init_labels ( void );
? ? ? ? ? ? ? ? void ?cleanup_labels ( void );
? ? ? ? 函數(shù)init_labels和cleanup_labels分別用于內(nèi)部數(shù)據(jù)的初始化和清理工作。
? ? ? ? 函數(shù)lookup_label用于查找存在的標(biāo)號(hào)并返回對(duì)應(yīng)的段和偏移的值。
? ? ? ? 函數(shù)define_label用于定義標(biāo)號(hào)并將信息傳遞給相應(yīng)的out*.c來生成目標(biāo)文件中的符號(hào)。
? ? ? ? ? ?函數(shù)redefine_label用于檢查標(biāo)號(hào)是否存在定位錯(cuò)誤并對(duì)其進(jìn)行修正(按作者的說法,雖然大多
? ? ? ? ? ?數(shù)的匯編器都存在這個(gè)功能,但在這里好像并不能起到什么作用)。
? ? 6. ?列表文件生成模塊listing.c:
? ? ? ? ? ?list.c用于生成列表文件。列表文件的格式如下:
? ? ? ? 列表文件的行號(hào) 代碼在目標(biāo)文件中的偏移 <列表嵌套層數(shù)> 源代碼行或宏展開后的代碼
? ? ? ? ? ?其中的列表嵌套層數(shù)用于INCBIN,TIMES,INCLUDE和MACRO等偽指令,用于表示這些指令展開后在
? ? ? ?代碼中嵌套的層數(shù)。在實(shí)際生成的列表文件中不對(duì)TIMES指令和指定了.nolist的宏進(jìn)行展開操作。
? ? ? ? list.c對(duì)外導(dǎo)出如下結(jié)構(gòu):
? ? ? ? ? ? ? ?ListGen nasmlist = {?
? ? ? ? ? ? ? ? ? ?list_init,
? ? ? ? ? ? ? ? ? ?list_cleanup,
? ? ? ? ? ? ? ? ? ?list_output,
? ? ? ? ? ? ? ? ? ?list_line,
? ? ? ? ? ? ? ? ? ?list_uplevel,
? ? ? ? ? ? ? ? ? ?list_downlevel
? ? ? ? ? ? ? ? };
? ? ? ? 其中l(wèi)ist_init和list_cleanup分別用于模塊內(nèi)部數(shù)據(jù)的初始化和清理操作。
? ? ? ? list_output用于生成列表文件格式中的的前兩部分,list_line用于生成列表文件格式中的
? ? ? ?后兩部分。list_upleve和list_downlevel被其它模塊調(diào)用,以更新list.c中的列表嵌套層數(shù)。
? ? ? ?值得注意的是源碼中的數(shù)據(jù)結(jié)構(gòu)MacroInhibit目前在模塊中并沒有什么實(shí)際的作用,而且list.c
? ? ? ?也沒有對(duì)傳入的參數(shù)進(jìn)行充分的處理。按作者的說法,這些函數(shù)實(shí)現(xiàn)并沒有經(jīng)過很好的考慮,
? ? ? ?它們只是因?yàn)?/span> "差不多能夠工作" ,所以才被保留至今。
? ? 7. ?浮點(diǎn)數(shù)轉(zhuǎn)換模塊float.c:
? ? ? ? ? ? float .c模塊導(dǎo)出一個(gè)函數(shù)float_const:
? ? ? ? ? ? ? ? int ?float_const ( char ?*number,? long ?sign,? unsigned ? char ?*result,? int ?bytes,
? ? ? ? ? ? ? ? efunc error);
? ? ? ? ? ?這個(gè)函數(shù)負(fù)責(zé)將指令DD, DQ和DT后的浮點(diǎn)數(shù)常量轉(zhuǎn)換為Intel機(jī)器內(nèi)部的數(shù)據(jù)表達(dá)形式。
? ? ? ? ? ? float .c能識(shí)別的浮點(diǎn)數(shù)格式如下:
? ? ? ? ? ? ? ?dd ? ? 1.2 ?? ? ? ? ? ? ? ? ? ? ; an easy one
? ? ? ? ? ? ? ?dq ? ? 1.e10 ?? ? ? ? ? ? ? ? ? ;? 10 , 0 00 , 0 00 , 0 00
? ? ? ? ? ? ? ?dq ? ? 1.e+10 ?? ? ? ? ? ? ? ? ?; synonymous with? 1.e10
? ? ? ? ? ? ? ?dq ? ? 1.e-10 ?? ? ? ? ? ? ? ? ?;? 0.000 ? 0 00 ? 0 00 ? 1
? ? ? ? ? ? ? ?dt ? ? 3.141592653589793238462 ?; pi
? ? ? ? ? ?需要注意的是NASM不提供浮點(diǎn)數(shù)的表達(dá)式求值運(yùn)算,這是因?yàn)镹ASM認(rèn)為不能保證所有
? ? ? ?存在ANSI C編譯器的的系統(tǒng)都提供浮點(diǎn)數(shù)運(yùn)算的庫(kù)函數(shù),而自己實(shí)現(xiàn)又有點(diǎn)得不償失。
? ? 8. 指令模板集模塊insnsa.c:
? ? ? ? ? ?NASM的CVS庫(kù)中有三個(gè)與該模塊有關(guān)的文件:insns.h(指令模板結(jié)構(gòu)定義和其它常量定義),
? ? ? ?insns.dat(NASM能識(shí)別的Intel指令集信息表),insns.pl(用于從insns.dat中生成insnsa.c,
? ? ? ?insnsd.c, insnsi.h, insnsn.c等文件的Perl腳本)。
? ? ? ? ? 在insns.pl生成的四個(gè)文件中,insnsa.c和insnd.c是C格式的指令集信息表,分別用于
? ? ? ?assemble.c和ndisasm.c。insnsi.h是指令集名字的枚舉表,insnsn.c是指令集名字的字符串表,
? ? ? ?這兩個(gè)文件用在掃描(nasmlib.c)和反匯編(ndisasm.c)中。
? ? 9. ?為其它模塊提供支持的nasmlib.c:
? ? ? ? ? ?nasmlib.c提供如下函數(shù):
? ? ? ? ? ? ?·具有報(bào)錯(cuò)和記錄功能的內(nèi)存分配和釋放函數(shù);
? ? ? ? ? ? ?·字符串和數(shù)字間的轉(zhuǎn)換和賦值函數(shù);
? ? ? ? ? ? ?·對(duì)動(dòng)態(tài)分配的隨機(jī)存儲(chǔ)數(shù)組(RAA)和順序存儲(chǔ)數(shù)組(SAA)進(jìn)行管理的函數(shù);
? ? ? ? ? ? ?·一個(gè)標(biāo)準(zhǔn)的源碼掃描函數(shù);
? ? ? ? ? ? ?·表達(dá)式處理的庫(kù)函數(shù);
? ? ? ? ? ? ?·為目標(biāo)文件自動(dòng)添加擴(kuò)展名的函數(shù);
? ? ? ? ? ? ?·對(duì)源碼和目標(biāo)文件提供支持的其它函數(shù)。
? ? 10. ?目標(biāo)格式文件輸出模塊output\*out.c和對(duì)其進(jìn)行管理的模塊outform.c:
? ? ? ? ? ? ? ?output目錄下的*out.c是NASM所能輸出的目標(biāo)格式文件的源代碼實(shí)現(xiàn)。每一個(gè)輸出模塊
? ? ? ? ? ?一般只導(dǎo)出一個(gè)ofmt形式的數(shù)據(jù)結(jié)構(gòu)(ofmt數(shù)據(jù)結(jié)構(gòu)的定義可參見nasm.h文件)。
? ? ? ? ? ? ? ?out*.c還提供了對(duì)特定格式的附加指令支持。比如如果在生成OBJ格式時(shí)想導(dǎo)入其它DLL的
? ? ? ? ? ?函數(shù),可以使用import指令,如下將導(dǎo)入wsock32.dll中的函數(shù)WSAStartup:
? ? ? ? ? ? ? ?import WSAStartup wsock32.dll
? ? ? ? ? ? ? ?outform.c提供了對(duì)上述模塊進(jìn)行查找,列舉和注冊(cè)的功能。
二、流程簡(jiǎn)介:
? ?主程序nasm.c的流程如下:
? ? ? ?( 1 )初始化需要的數(shù)據(jù)結(jié)構(gòu),注冊(cè)編譯器支持的目標(biāo)文件格式。
? ? ? ?( 2 )使用內(nèi)部函數(shù)parse_cmdline解析命令行參數(shù),設(shè)置對(duì)應(yīng)的參數(shù)值或輸出相應(yīng)的幫助信息后退出。
? ? ? ?( 3 )如果生成的目標(biāo)格式中含有附加的標(biāo)準(zhǔn)宏,調(diào)用預(yù)處理中的函數(shù)對(duì)其進(jìn)行注冊(cè)。
? ? ? ?( 4 )調(diào)用函數(shù)parser_global_info和eval_global_info分別初始化parser.c和eval.c。
? ? ? ?( 5 )根據(jù)變量operating_mode的值判斷操作模式,默認(rèn)為op_normal。
? ? ? ? ? ? 1. 如果operating_mode為op_depend(編譯時(shí)指定了參數(shù)-M),則對(duì)外輸出makefile格式的文件依賴關(guān)系;
? ? ? ? ? ? 2. 如果operating_mode為op_preprocess(編譯時(shí)指定了參數(shù)-e),則只對(duì)源代碼進(jìn)行預(yù)處理操作,并添加相應(yīng)的
? ? ? ? ? ? ?行號(hào)信息,然后輸出到目標(biāo)文件或標(biāo)準(zhǔn)輸出( stdout );
? ? ? ? ? ? 3. 如果operating_mode為op_normal,則先調(diào)用函數(shù)init_labels和ofmt->init分別初始化label.c和out*.c,
? ? ? ? ? ? ?然后調(diào)用函數(shù)assemble_file對(duì)源文件進(jìn)行編譯處理。
? ? ? ?( 6 )釋放掉程序動(dòng)態(tài)分配的內(nèi)存(RAA和SAA),調(diào)用函數(shù)eval_cleanup和nasmlib_cleanup進(jìn)行相應(yīng)模塊的清理工作。
? ? ? ? ? 然后根據(jù)變量terminate_after_phase的值設(shè)置程序返回值并結(jié)束運(yùn)行。
? ?其中的函數(shù)assemble_file負(fù)責(zé)了最主要的工作,它接收源程序文件名,并調(diào)用各個(gè)模塊對(duì)源程序進(jìn)行預(yù)處理,
? ?解析,編譯,生成指定目標(biāo)格式文件等操作。
? ? ? ?assemble_file函數(shù)流程如下:
? ? ? ?( 1 )初始化目標(biāo)文件格式中的段(節(jié))和偏移值,初始化預(yù)處理模塊,設(shè)置當(dāng)前掃描次數(shù)。
? ? ? ?( 2 )調(diào)用預(yù)處理模塊中的函數(shù)preproc->getline()返回一行可以被解析使用的“標(biāo)準(zhǔn)”匯編代碼,并增加當(dāng)前行數(shù)。
? ? ? ?( 3 )調(diào)用函數(shù)getkw(line,&value)來判斷當(dāng)前行格式是否為[directive value]并返回directive和value相應(yīng)的值。
? ? ? ? ? 這里的directive是指NASM自己的一些偽指令,例如SECTION,EXTERN,GLOBAL等。若上述格式getkw無法識(shí)別,
? ? ? ? ? 則調(diào)用ofmt->directive (line+ 1 , value,? 1 )來判斷是否為目標(biāo)文件格式的附加指令。
? ? ? ?( 4 )若當(dāng)前行不是上述格式,則調(diào)用標(biāo)準(zhǔn)的解析函數(shù)parse_line解析當(dāng)前行。
? ? ? ?( 5 )記錄或處理當(dāng)前行指令中的向前引用。
? ? ? ?( 6 )處理EQU指令,并只在第二次解析時(shí)才處理標(biāo)號(hào)前綴為 ".." 的特殊EQU指令。
? ? ? ?( 7 )調(diào)用編譯模塊進(jìn)行處理。第一次掃描源碼時(shí)調(diào)用insn_size,并對(duì)偽指令RESB,DB等調(diào)用
? ? ? ? ? 函數(shù)ofmt->current_dfmt->debug_typevalue(typeinfo)來設(shè)置調(diào)試信息;第二次掃描時(shí)則直接調(diào)用assemble來
? ? ? ? ? 生成目標(biāo)代碼。
? ? ? ?( 8 )調(diào)用函數(shù)preproc->cleanup()和nasmlist.cleanup()進(jìn)行掃描后的清理工作。
? ? ? ?( 9 )若掃描次數(shù)小于 2 ,則跳轉(zhuǎn)到( 1 )進(jìn)行下一遍掃描。
三、閱讀小結(jié)
? ? ? ?通過對(duì)NASM源碼的閱讀,我認(rèn)為它有以下優(yōu)點(diǎn):
? ? ? ?( 1 )采用了模塊化的開發(fā)模式,各個(gè)模塊間相對(duì)獨(dú)立,便于擴(kuò)展功能和修改,也利于被其它程序使用。
? ? ? ?( 2 )預(yù)處理功能比較強(qiáng)大,對(duì)宏的命名和參數(shù)量要求比較靈活。
? ? ? ?( 3 )使用了通過HASH運(yùn)算將數(shù)組和鏈表結(jié)合在一起的數(shù)據(jù)結(jié)構(gòu),提高了查找效率。
? ? ? ?( 4 )使用文檔和代碼間的注釋相當(dāng)齊全,便于他人閱讀。
? ? ? ?同時(shí),NASM也存在以下問題:
? ? ? ?( 1 )各模塊間無法進(jìn)行相互間的通信。這就導(dǎo)致模塊間存在一些重復(fù)性代碼。另外在模塊內(nèi)部也有少許重復(fù)代碼。
? ? ? ?( 2 )listing.c模塊和eval.c模塊功能簡(jiǎn)單,特別是listing.c目前尚不完善。
? ? ? ?( 3 )實(shí)質(zhì)上的架構(gòu)只提供了兩遍的源碼掃描,這就導(dǎo)致它對(duì)表達(dá)式的要求比較嚴(yán)格,不能處理需要多遍掃描才能
? ? ? ? 完成的表達(dá)式,這方面的內(nèi)容可參閱NASM的幫助文檔中的Critical Expressions一節(jié)。
? ? ? ?( 4 )在可輸出的目標(biāo)格式文件中,只有少數(shù)幾個(gè)支持生成含有調(diào)試信息的目標(biāo)文件。
NASM(Netwide Assembler)的使用文檔和代碼間的注釋相當(dāng)齊全,這給閱讀源碼
提供了很大的方便。按作者的說法,這是一個(gè)模塊化的,可重用的x86匯編器,
而且能夠被嵌入進(jìn)其它的程序中,比如做為一個(gè)高級(jí)語言編譯器的后端程序。下面
的文字希望能對(duì)大家有所幫助。錯(cuò)誤之處,多多指正。:>
一、各模塊簡(jiǎn)介:
? ?NASM按功能將匯編器的各個(gè)部分獨(dú)立為模塊,各個(gè)模塊之間并不進(jìn)行直接的聯(lián)系。
? ?這種編程結(jié)構(gòu)使得閱讀源代碼變得輕松起來,可以將各個(gè)模塊獨(dú)立出來分別閱讀。
? ?在源碼中的doc/internal.doc中有一個(gè)模塊間的結(jié)構(gòu)圖:
? ? ? ? ? ? ? ? ?+--- preproc.c ----+
? ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ?|
? ? ? ? ? ? ? ? ?+---- parser.c ----+
? ? ? ? ? ? ? ? ?| ? ? ? ?| ? ? ? ? |
? ? ? ? ? ? ? ? ?| ? ?? float .c ? ? ?|
? ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ?|
? ? ? ? ? ? ? ? ?+--- assemble.c ---+
? ? ? ? ? ? ? ? ?| ? ? ? ?| ? ? ? ? |
? ? ? ?nasm.c ---+ ? ? insnsa.c ? ? +--- nasmlib.c
? ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ?|
? ? ? ? ? ? ? ? ?+--- listing.c ----+
? ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ?|
? ? ? ? ? ? ? ? ?+---- labels.c ----+
? ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ?|
? ? ? ? ? ? ? ? ?+--- outform.c ----+
? ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ?|
? ? ? ? ? ? ? ? ?+----- *out.c -----+
?圖中沒有出現(xiàn)的其它模塊有eval.c和float.c。eval.c是表達(dá)式求值模塊,用于
?preproc.c,parser.c和*out.c;float.c是浮點(diǎn)數(shù)常量存儲(chǔ)轉(zhuǎn)換模塊,用于parser.c。
?各模塊將需要導(dǎo)出的函數(shù)和數(shù)據(jù)結(jié)構(gòu)的定義放入對(duì)應(yīng)的頭文件中,以便被其它模塊調(diào)用。
? /* 需要注意的是許多函數(shù)指針或包含函數(shù)指針的結(jié)構(gòu)以參數(shù)的形式在各模塊間傳遞,這種
? *間接性使得模塊之間的結(jié)構(gòu)更加清晰。
?? */
1 、強(qiáng)大的預(yù)處理器preproc.c:
預(yù)處理部分是NASM中最復(fù)雜,也是代碼最多的模塊,有四千多行代碼。
它使用自己的源碼掃描程序ppscan,支持名字相同但參數(shù)不同的宏,支持
各個(gè)宏通過上下文相關(guān)堆棧來交換信息,支持宏內(nèi)進(jìn)行預(yù)處理循環(huán)等。
? (可參閱NASM使用文檔來了解預(yù)處理的功能和使用方法)
? ? ? ?preproc.c對(duì)外僅導(dǎo)出一個(gè)結(jié)構(gòu):
? ? ? ? ?? typedef ? struct ?{?
? ? ? ? ? ? ?? /*
? ? ? ? ? ? ? ?* Called at the start of a pass; given a file name, the number
? ? ? ? ? ? ? ?* of the pass, an error reporting function, an evaluator
? ? ? ? ? ? ? ?* function, and a listing generator to talk to.
? ? ? ? ? ? ? ? */
? ? ? ? ? ? ?? void ?(*reset) ( char ?*,? int , efunc, evalfunc, ListGen *);
? ? ? ? ? ? ?? /*
? ? ? ? ? ? ? ?* Called to fetch a line of preprocessed source. The line
? ? ? ? ? ? ? ?* returned has been malloc'ed, and so should be freed after
? ? ? ? ? ? ? ?* use.
? ? ? ? ? ? ? ? */
? ? ? ? ? ? ?? char ?*(*getline) ( void );
? ? ? ? ? ? ?? /*
? ? ? ? ? ? ? ?* Called at the end of a pass.
? ? ? ? ? ? ? ? */
? ? ? ? ? ? ?? void ?(*cleanup) ( int );
? ? ? ? ?? } Preproc;
? ? ? ? ? 其中reset用于初始化,cleanup用于預(yù)處理后的清理工作。
? ? ? ? ? 而函數(shù)名getline則是相對(duì)于主程序來說的。它對(duì)編譯器內(nèi)嵌的宏和匯編源程序
? ? ? ? ? 從上至下進(jìn)行處理,記錄遇到的宏信息,用實(shí)際的代碼和數(shù)據(jù)替換調(diào)用的宏,并
? ? ? ? ? 返回一行可以被下一個(gè)模塊(這里是parser.c)使用的“規(guī)范的”匯編代碼。
? ? ? ? ?? /* 預(yù)處理部分相對(duì)于主程序后面調(diào)用的模塊來說是透明的。 */
?? 2 、功能單一的解析模塊parser.c:
? ? ? ? ? ?按作者的說法,解析模塊的作用就是解析由`標(biāo)號(hào)',`指令',`操作數(shù)'和`注釋'組成的
? ? ? ?“規(guī)范的”匯編代碼行,填充并返回這行代碼的insn結(jié)構(gòu):
? ? ? ? ?? typedef ? struct ?{? ? ? ? ? ? ? ? ? ? ? /* ?an instruction itself? */
? ? ? ? ? ? ?? char ?*label; ? ? ? ? ? ? ? ? ? ? /* ?the label defined, or NULL? */
? ? ? ? ? ? ?? int ?prefixes[MAXPREFIX]; ? ? ?? /* ?instruction prefixes, if any? */
? ? ? ? ? ? ?? int ?nprefix; ? ? ? ? ? ? ? ? ? ? /* ?number of entries in above? */
? ? ? ? ? ? ?? int ?opcode; ? ? ? ? ? ? ? ? ? ?? /* ?the opcode - not just the string? */
? ? ? ? ? ? ?? int ?condition; ? ? ? ? ? ? ? ? ? /* ?the condition code, if Jcc/SETcc? */
? ? ? ? ? ? ?? int ?operands; ? ? ? ? ? ? ? ? ?? /* ?how many operands? 0-3?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? * (more if db et al)? */
? ? ? ? ? ? ? operand oprs[ 3 ]; ? ? ? ? /* ?the operands, defined as above? */
? ? ? ? ? ? ? extop *eops; ? ? ? ? ? ? ? ? ? ? /* ?extended operands? */
? ? ? ? ? ? ?? int ?eops_float; ? ? ? ? ? ? ? ? /* ?true if DD and floating? */
? ? ? ? ? ? ?? long ?times; ? ? ? ? ? ? ? ? ? ?? /* ?repeat count (TIMES prefix)? */
? ? ? ? ? ? ?? int ?forw_ref; ? ? ? ? ? ? ? ? ?? /* ?is there a forward reference?? */
? ? ? ? ?? } insn;
? ? ? ? ? 該模塊導(dǎo)出三個(gè)函數(shù):
? ? ? ? ?? void ?parser_global_info ( struct ?ofmt *output, loc_t *locp);
? ? ? ? ? insn *parse_line ( int ?pass,? char ?*buffer, insn *result,
? ? ? ? ? ? ? ? ? ? ? ? ?efunc error, evalfunc evaluate, ldfunc ldef);
? ? ? ? ?? void ?cleanup_insn (insn *instruction);
? ? ? ? ? 其中parser_global_info用于解析前的初始化工作(這里是初始化輸出的文件格式和
? ? ? ? ? 當(dāng)前行所在的位置,在定義標(biāo)號(hào)時(shí)需要用到)。cleanup_insn用于解析后的清理工作。
? ? ? ? ? 而parse_line的功能就是按照上面提到的`標(biāo)號(hào)',`指令',`操作數(shù)'和`注釋'的
? ? ? 順序?qū)uffer里的代碼進(jìn)行解析,并對(duì)相應(yīng)部分的合法性做一些檢查,比如檢查指令的前綴是
? ? ? 不是超過了所能允許的最大值,操作數(shù)是否為內(nèi)存地址,代碼中是否含有一個(gè)向前的引用等。
? ? ? 它還提供了對(duì)偽指令DB,DD,RESB,RESD,INCBIN等的支持。
?? 3 、代碼生成器assemble.c:
? ? ? ? 當(dāng)parser.c填充好insn結(jié)構(gòu)后,assemble.c就按照Intel機(jī)器碼規(guī)則生成實(shí)際的
? ? ?機(jī)器碼,并傳給out*.c來生成具體格式的目標(biāo)文件。
? ? ? ? ? 它導(dǎo)出兩個(gè)函數(shù):
? ? ? ? ?? long ?insn_size ( long ?segment,? long ?offset,? int ?bits,? unsigned ? long ?cpu,
? ? ? ? ? ? ? ? ? ? ? ?insn *instruction, efunc error);
? ? ? ? ?? long ?assemble ( long ?segment,? long ?offset,? int ?bits,? unsigned ? long ?cpu,
? ? ? ? ? ? ? ? ? ? ? insn *instruction,? struct ?ofmt *output, efunc error,
? ? ? ? ? ? ? ? ? ? ? ListGen *listgen);
? ? ? ? ? 其中insn_size一般用在第一遍分析源碼時(shí),用于確定某一行代碼在實(shí)際目標(biāo)文件中所需的
? ? ? 空間,而assemble則一般用在第二遍分析源碼時(shí),它轉(zhuǎn)化insn結(jié)構(gòu)中的指令為實(shí)際的機(jī)器碼,
? ? ? 并輸出到out*.c中以生成具體格式的目標(biāo)文件。
?? 4 、簡(jiǎn)單的表達(dá)式求值模塊eval.c:
? eval.c用于計(jì)算并返回代碼中表達(dá)式的值,其中運(yùn)算符||,^^,&&等用于條件匯編%if類指令
? 的表達(dá)式中,返回真或假,|,^,&,<<,+,*,/等用于對(duì)常量值進(jìn)行運(yùn)算,SEG,WRT等則用于得到
? ? ? 程序中段(或節(jié))實(shí)際的基址和偏移。
? ? ? ? ? eval.c的功能比較簡(jiǎn)單,不能像MASM那樣處理類似(eax !=? 0 )這樣的表達(dá)式。 (后記1) 。
? eval.c利用標(biāo)準(zhǔn)的scan程序掃描表達(dá)式緩沖區(qū),找出存在的運(yùn)算符進(jìn)行運(yùn)算,并將
? 處理后的值存入expr結(jié)構(gòu)中:
? ? ? ? ? ? /*
? ? ? ? ? ? * Expression-evaluator datatype. Expressions, within the
? ? ? ? ? ? * evaluator, are stored as an array of these beasts, terminated by
? ? ? ? ? ? * a record with type== 0. ?Mostly, it's a vector type: each type
? ? ? ? ? ? * denotes some kind of a component, and the value denotes the
? ? ? ? ? ? * multiple of that component present in the expression. The
? ? ? ? ? ? * exception is the WRT type, whose `value' field denotes the
? ? ? ? ? ? * segment to which the expression is relative. These segments will
? ? ? ? ? ? * be segment-base types, i.e. either odd segment values or SEG_ABS
? ? ? ? ? ? * types. So it is still valid to assume that anything with a
? ? ? ? ? ? * `value' field of zero is insignificant.
? ? ? ? ? ?? */
? ? ? ? ? ? typedef ? struct ?{?
? ? ? ? ? ? ? ? long ?type; ? ? ? ? ? ? ? ? ? ? ? ? ? ?? /* ?a register, or EXPR_xxx? */
? ? ? ? ? ? ? ? long ?value; ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* ?must be >=? 32 ?bits? */
? ? ? ? ? ? } expr;
? ? ? ? ? ?值得注意的是eval.c只在第一遍分析源碼時(shí)處理運(yùn)算符||,^^,&&等,因?yàn)樗鼈冎辉?/span>
? ? ? ?預(yù)處理表達(dá)式中才可能出現(xiàn)。
? ? 5. ?標(biāo)號(hào)處理器label.c:
? ? ? ? ? ?NASM的標(biāo)號(hào)分局部標(biāo)號(hào)和全局標(biāo)號(hào)兩種(外部標(biāo)號(hào)可看作是在另一個(gè)源程序中的全局標(biāo)號(hào))。
? ? ? ? ? ?局部標(biāo)號(hào)名最終將由兩部分組成: "上一個(gè)全局標(biāo)號(hào)名+局部標(biāo)號(hào)名" 。
? ? ? ? ? ?全局標(biāo)號(hào)名最終將由三部分組成: "lprefix+全局標(biāo)號(hào)名+lpostfix" 。其中l(wèi)prefix和lpostfix
? ? ? ?都是可選的,它們分別用于在所有的全局標(biāo)號(hào)名的前面和后面添加字符串。比如在編譯時(shí)指定選項(xiàng)
? ? ? ?--prefix_ 將會(huì)在所在的全局標(biāo)號(hào)名前加下劃線,這就會(huì)和在C中生成標(biāo)號(hào)的情況差不多。
? ? ? ? ? ?最終lable.c會(huì)將標(biāo)號(hào)的相關(guān)信息傳給對(duì)應(yīng)的out*.c來生成目標(biāo)文件中的符號(hào)。
? ? ? ? ? ?這個(gè)模塊導(dǎo)出以下數(shù)據(jù)和函數(shù):
? ? ? ? ? ? ? ? extern ? char ?lprefix[PREFIX_MAX];
? ? ? ? ? ? ? ? extern ? char ?lpostfix[PREFIX_MAX];
? ? ? ? ? ? ? ? int ?lookup_label ( char ?*label,? long ?*segment,? long ?*offset);
? ? ? ? ? ? ? ? int ?is_extern ( char ?*label);
? ? ? ? ? ? ? ? void ?define_label ( char ?*label,? long ?segment,? long ?offset,? char ?*special,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? int ?is_norm,? int ?isextrn,? struct ?ofmt *ofmt, efunc error);
? ? ? ? ? ? ? ? void ?redefine_label ( char ?*label,? long ?segment,? long ?offset,? char ?*special,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? int ?is_norm,? int ?isextrn,? struct ?ofmt *ofmt, efunc error);
? ? ? ? ? ? ? ? void ?define_common ( char ?*label,? long ?segment,? long ?size,? char ?*special,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct ?ofmt *ofmt, efunc error);
? ? ? ? ? ? ? ? void ?declare_as_global ( char ?*label,? char ?*special, efunc error);
? ? ? ? ? ? ? ? int ?init_labels ( void );
? ? ? ? ? ? ? ? void ?cleanup_labels ( void );
? ? ? ? 函數(shù)init_labels和cleanup_labels分別用于內(nèi)部數(shù)據(jù)的初始化和清理工作。
? ? ? ? 函數(shù)lookup_label用于查找存在的標(biāo)號(hào)并返回對(duì)應(yīng)的段和偏移的值。
? ? ? ? 函數(shù)define_label用于定義標(biāo)號(hào)并將信息傳遞給相應(yīng)的out*.c來生成目標(biāo)文件中的符號(hào)。
? ? ? ? ? ?函數(shù)redefine_label用于檢查標(biāo)號(hào)是否存在定位錯(cuò)誤并對(duì)其進(jìn)行修正(按作者的說法,雖然大多
? ? ? ? ? ?數(shù)的匯編器都存在這個(gè)功能,但在這里好像并不能起到什么作用)。
? ? 6. ?列表文件生成模塊listing.c:
? ? ? ? ? ?list.c用于生成列表文件。列表文件的格式如下:
? ? ? ? 列表文件的行號(hào) 代碼在目標(biāo)文件中的偏移 <列表嵌套層數(shù)> 源代碼行或宏展開后的代碼
? ? ? ? ? ?其中的列表嵌套層數(shù)用于INCBIN,TIMES,INCLUDE和MACRO等偽指令,用于表示這些指令展開后在
? ? ? ?代碼中嵌套的層數(shù)。在實(shí)際生成的列表文件中不對(duì)TIMES指令和指定了.nolist的宏進(jìn)行展開操作。
? ? ? ? list.c對(duì)外導(dǎo)出如下結(jié)構(gòu):
? ? ? ? ? ? ? ?ListGen nasmlist = {?
? ? ? ? ? ? ? ? ? ?list_init,
? ? ? ? ? ? ? ? ? ?list_cleanup,
? ? ? ? ? ? ? ? ? ?list_output,
? ? ? ? ? ? ? ? ? ?list_line,
? ? ? ? ? ? ? ? ? ?list_uplevel,
? ? ? ? ? ? ? ? ? ?list_downlevel
? ? ? ? ? ? ? ? };
? ? ? ? 其中l(wèi)ist_init和list_cleanup分別用于模塊內(nèi)部數(shù)據(jù)的初始化和清理操作。
? ? ? ? list_output用于生成列表文件格式中的的前兩部分,list_line用于生成列表文件格式中的
? ? ? ?后兩部分。list_upleve和list_downlevel被其它模塊調(diào)用,以更新list.c中的列表嵌套層數(shù)。
? ? ? ?值得注意的是源碼中的數(shù)據(jù)結(jié)構(gòu)MacroInhibit目前在模塊中并沒有什么實(shí)際的作用,而且list.c
? ? ? ?也沒有對(duì)傳入的參數(shù)進(jìn)行充分的處理。按作者的說法,這些函數(shù)實(shí)現(xiàn)并沒有經(jīng)過很好的考慮,
? ? ? ?它們只是因?yàn)?/span> "差不多能夠工作" ,所以才被保留至今。
? ? 7. ?浮點(diǎn)數(shù)轉(zhuǎn)換模塊float.c:
? ? ? ? ? ? float .c模塊導(dǎo)出一個(gè)函數(shù)float_const:
? ? ? ? ? ? ? ? int ?float_const ( char ?*number,? long ?sign,? unsigned ? char ?*result,? int ?bytes,
? ? ? ? ? ? ? ? efunc error);
? ? ? ? ? ?這個(gè)函數(shù)負(fù)責(zé)將指令DD, DQ和DT后的浮點(diǎn)數(shù)常量轉(zhuǎn)換為Intel機(jī)器內(nèi)部的數(shù)據(jù)表達(dá)形式。
? ? ? ? ? ? float .c能識(shí)別的浮點(diǎn)數(shù)格式如下:
? ? ? ? ? ? ? ?dd ? ? 1.2 ?? ? ? ? ? ? ? ? ? ? ; an easy one
? ? ? ? ? ? ? ?dq ? ? 1.e10 ?? ? ? ? ? ? ? ? ? ;? 10 , 0 00 , 0 00 , 0 00
? ? ? ? ? ? ? ?dq ? ? 1.e+10 ?? ? ? ? ? ? ? ? ?; synonymous with? 1.e10
? ? ? ? ? ? ? ?dq ? ? 1.e-10 ?? ? ? ? ? ? ? ? ?;? 0.000 ? 0 00 ? 0 00 ? 1
? ? ? ? ? ? ? ?dt ? ? 3.141592653589793238462 ?; pi
? ? ? ? ? ?需要注意的是NASM不提供浮點(diǎn)數(shù)的表達(dá)式求值運(yùn)算,這是因?yàn)镹ASM認(rèn)為不能保證所有
? ? ? ?存在ANSI C編譯器的的系統(tǒng)都提供浮點(diǎn)數(shù)運(yùn)算的庫(kù)函數(shù),而自己實(shí)現(xiàn)又有點(diǎn)得不償失。
? ? 8. 指令模板集模塊insnsa.c:
? ? ? ? ? ?NASM的CVS庫(kù)中有三個(gè)與該模塊有關(guān)的文件:insns.h(指令模板結(jié)構(gòu)定義和其它常量定義),
? ? ? ?insns.dat(NASM能識(shí)別的Intel指令集信息表),insns.pl(用于從insns.dat中生成insnsa.c,
? ? ? ?insnsd.c, insnsi.h, insnsn.c等文件的Perl腳本)。
? ? ? ? ? 在insns.pl生成的四個(gè)文件中,insnsa.c和insnd.c是C格式的指令集信息表,分別用于
? ? ? ?assemble.c和ndisasm.c。insnsi.h是指令集名字的枚舉表,insnsn.c是指令集名字的字符串表,
? ? ? ?這兩個(gè)文件用在掃描(nasmlib.c)和反匯編(ndisasm.c)中。
? ? 9. ?為其它模塊提供支持的nasmlib.c:
? ? ? ? ? ?nasmlib.c提供如下函數(shù):
? ? ? ? ? ? ?·具有報(bào)錯(cuò)和記錄功能的內(nèi)存分配和釋放函數(shù);
? ? ? ? ? ? ?·字符串和數(shù)字間的轉(zhuǎn)換和賦值函數(shù);
? ? ? ? ? ? ?·對(duì)動(dòng)態(tài)分配的隨機(jī)存儲(chǔ)數(shù)組(RAA)和順序存儲(chǔ)數(shù)組(SAA)進(jìn)行管理的函數(shù);
? ? ? ? ? ? ?·一個(gè)標(biāo)準(zhǔn)的源碼掃描函數(shù);
? ? ? ? ? ? ?·表達(dá)式處理的庫(kù)函數(shù);
? ? ? ? ? ? ?·為目標(biāo)文件自動(dòng)添加擴(kuò)展名的函數(shù);
? ? ? ? ? ? ?·對(duì)源碼和目標(biāo)文件提供支持的其它函數(shù)。
? ? 10. ?目標(biāo)格式文件輸出模塊output\*out.c和對(duì)其進(jìn)行管理的模塊outform.c:
? ? ? ? ? ? ? ?output目錄下的*out.c是NASM所能輸出的目標(biāo)格式文件的源代碼實(shí)現(xiàn)。每一個(gè)輸出模塊
? ? ? ? ? ?一般只導(dǎo)出一個(gè)ofmt形式的數(shù)據(jù)結(jié)構(gòu)(ofmt數(shù)據(jù)結(jié)構(gòu)的定義可參見nasm.h文件)。
? ? ? ? ? ? ? ?out*.c還提供了對(duì)特定格式的附加指令支持。比如如果在生成OBJ格式時(shí)想導(dǎo)入其它DLL的
? ? ? ? ? ?函數(shù),可以使用import指令,如下將導(dǎo)入wsock32.dll中的函數(shù)WSAStartup:
? ? ? ? ? ? ? ?import WSAStartup wsock32.dll
? ? ? ? ? ? ? ?outform.c提供了對(duì)上述模塊進(jìn)行查找,列舉和注冊(cè)的功能。
二、流程簡(jiǎn)介:
? ?主程序nasm.c的流程如下:
? ? ? ?( 1 )初始化需要的數(shù)據(jù)結(jié)構(gòu),注冊(cè)編譯器支持的目標(biāo)文件格式。
? ? ? ?( 2 )使用內(nèi)部函數(shù)parse_cmdline解析命令行參數(shù),設(shè)置對(duì)應(yīng)的參數(shù)值或輸出相應(yīng)的幫助信息后退出。
? ? ? ?( 3 )如果生成的目標(biāo)格式中含有附加的標(biāo)準(zhǔn)宏,調(diào)用預(yù)處理中的函數(shù)對(duì)其進(jìn)行注冊(cè)。
? ? ? ?( 4 )調(diào)用函數(shù)parser_global_info和eval_global_info分別初始化parser.c和eval.c。
? ? ? ?( 5 )根據(jù)變量operating_mode的值判斷操作模式,默認(rèn)為op_normal。
? ? ? ? ? ? 1. 如果operating_mode為op_depend(編譯時(shí)指定了參數(shù)-M),則對(duì)外輸出makefile格式的文件依賴關(guān)系;
? ? ? ? ? ? 2. 如果operating_mode為op_preprocess(編譯時(shí)指定了參數(shù)-e),則只對(duì)源代碼進(jìn)行預(yù)處理操作,并添加相應(yīng)的
? ? ? ? ? ? ?行號(hào)信息,然后輸出到目標(biāo)文件或標(biāo)準(zhǔn)輸出( stdout );
? ? ? ? ? ? 3. 如果operating_mode為op_normal,則先調(diào)用函數(shù)init_labels和ofmt->init分別初始化label.c和out*.c,
? ? ? ? ? ? ?然后調(diào)用函數(shù)assemble_file對(duì)源文件進(jìn)行編譯處理。
? ? ? ?( 6 )釋放掉程序動(dòng)態(tài)分配的內(nèi)存(RAA和SAA),調(diào)用函數(shù)eval_cleanup和nasmlib_cleanup進(jìn)行相應(yīng)模塊的清理工作。
? ? ? ? ? 然后根據(jù)變量terminate_after_phase的值設(shè)置程序返回值并結(jié)束運(yùn)行。
? ?其中的函數(shù)assemble_file負(fù)責(zé)了最主要的工作,它接收源程序文件名,并調(diào)用各個(gè)模塊對(duì)源程序進(jìn)行預(yù)處理,
? ?解析,編譯,生成指定目標(biāo)格式文件等操作。
? ? ? ?assemble_file函數(shù)流程如下:
? ? ? ?( 1 )初始化目標(biāo)文件格式中的段(節(jié))和偏移值,初始化預(yù)處理模塊,設(shè)置當(dāng)前掃描次數(shù)。
? ? ? ?( 2 )調(diào)用預(yù)處理模塊中的函數(shù)preproc->getline()返回一行可以被解析使用的“標(biāo)準(zhǔn)”匯編代碼,并增加當(dāng)前行數(shù)。
? ? ? ?( 3 )調(diào)用函數(shù)getkw(line,&value)來判斷當(dāng)前行格式是否為[directive value]并返回directive和value相應(yīng)的值。
? ? ? ? ? 這里的directive是指NASM自己的一些偽指令,例如SECTION,EXTERN,GLOBAL等。若上述格式getkw無法識(shí)別,
? ? ? ? ? 則調(diào)用ofmt->directive (line+ 1 , value,? 1 )來判斷是否為目標(biāo)文件格式的附加指令。
? ? ? ?( 4 )若當(dāng)前行不是上述格式,則調(diào)用標(biāo)準(zhǔn)的解析函數(shù)parse_line解析當(dāng)前行。
? ? ? ?( 5 )記錄或處理當(dāng)前行指令中的向前引用。
? ? ? ?( 6 )處理EQU指令,并只在第二次解析時(shí)才處理標(biāo)號(hào)前綴為 ".." 的特殊EQU指令。
? ? ? ?( 7 )調(diào)用編譯模塊進(jìn)行處理。第一次掃描源碼時(shí)調(diào)用insn_size,并對(duì)偽指令RESB,DB等調(diào)用
? ? ? ? ? 函數(shù)ofmt->current_dfmt->debug_typevalue(typeinfo)來設(shè)置調(diào)試信息;第二次掃描時(shí)則直接調(diào)用assemble來
? ? ? ? ? 生成目標(biāo)代碼。
? ? ? ?( 8 )調(diào)用函數(shù)preproc->cleanup()和nasmlist.cleanup()進(jìn)行掃描后的清理工作。
? ? ? ?( 9 )若掃描次數(shù)小于 2 ,則跳轉(zhuǎn)到( 1 )進(jìn)行下一遍掃描。
三、閱讀小結(jié)
? ? ? ?通過對(duì)NASM源碼的閱讀,我認(rèn)為它有以下優(yōu)點(diǎn):
? ? ? ?( 1 )采用了模塊化的開發(fā)模式,各個(gè)模塊間相對(duì)獨(dú)立,便于擴(kuò)展功能和修改,也利于被其它程序使用。
? ? ? ?( 2 )預(yù)處理功能比較強(qiáng)大,對(duì)宏的命名和參數(shù)量要求比較靈活。
? ? ? ?( 3 )使用了通過HASH運(yùn)算將數(shù)組和鏈表結(jié)合在一起的數(shù)據(jù)結(jié)構(gòu),提高了查找效率。
? ? ? ?( 4 )使用文檔和代碼間的注釋相當(dāng)齊全,便于他人閱讀。
? ? ? ?同時(shí),NASM也存在以下問題:
? ? ? ?( 1 )各模塊間無法進(jìn)行相互間的通信。這就導(dǎo)致模塊間存在一些重復(fù)性代碼。另外在模塊內(nèi)部也有少許重復(fù)代碼。
? ? ? ?( 2 )listing.c模塊和eval.c模塊功能簡(jiǎn)單,特別是listing.c目前尚不完善。
? ? ? ?( 3 )實(shí)質(zhì)上的架構(gòu)只提供了兩遍的源碼掃描,這就導(dǎo)致它對(duì)表達(dá)式的要求比較嚴(yán)格,不能處理需要多遍掃描才能
? ? ? ? 完成的表達(dá)式,這方面的內(nèi)容可參閱NASM的幫助文檔中的Critical Expressions一節(jié)。
? ? ? ?( 4 )在可輸出的目標(biāo)格式文件中,只有少數(shù)幾個(gè)支持生成含有調(diào)試信息的目標(biāo)文件。
總結(jié)
以上是生活随笔為你收集整理的代码分析:NASM源码阅读笔记的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ruby源码分析
- 下一篇: 内核代号101 — 动手写自己的内核