NASM源代码分析--预处理数据结构
来源:互联网 发布:淘宝天机平台论坛 编辑:程序博客网 时间:2024/06/01 09:13
NASM源代码分析--预处理数据结构
我是在读研究生,09年毕业,以前看过部分Linux源代码和BSD的TCP/IP协议,因为没有太全的资料,所以只看了比较重要的部分,没有看全。虽然看源代码是学编程最快捷的方法,但不得不承认看别人的代码有时候是一种煎药,特别是在没有详细的说明和注释的情况下。
由于之前用过一段时间的NASM用来编写一个小病毒,所以才有了看NASM源代码的想法。不过刚开始看NASM源代码的时候,确实有过一段低潮,因为除了源代码,就没有其他中文资料了,还好它的注释写得还是比较详细的。看完之后想留下点什么,所以就有了下面这些学习笔记,希望能给那些看NASM源代码的朋友一点帮助。
我是各学理科的,所以文笔不是很好,请大家谅解。当然这些记录肯定也有不对的地方,欢迎大家和我交流探讨,我的E-MAIL:huguozhu@gmail.com或huguozhu@sohu.com,谢谢大家。
本文是对前几篇文章的合计,没有新内容,预处理所有重要的数据结构都在这里了。
NASM源代码分析之预处理(1)
NASM源代码的整体框架,互联网上已经有资料介绍了,具体的大家可以参见《NASM源码阅读笔记》一文。
NASM编译源代码的时候,分为三种情况,分别是:
(1):只产生文件依赖关系(operating_mode = op_depend);
(2):只进行预处理(operating_mode = op_preprocess);
(3):预处理并生成目标代码(operating_mode = op_normal)。
第一种情况比较简单,代码量也比较少,很容易就看明白。第二种情况比第一种要难很多,而且涉及的数据结构也很多,刚开始看的时候很容易就看晕了。所以本人根据近段时间以来看代码的体会,总结了下面的内容,希望能给那些看NASM代码的朋友一些帮助。这也是我第一次用这种方式写下我的总结,肯定有不全,甚至时错误的地方,欢迎大家批评指正。我的QQ:79943558。
preprocess.c是处理预处理的最主要的文件,里面包括了几乎全部关于预处理的内容。其中最基本的结构是Token结构,代码如下:
struct Token
{
Token *next;
char *text; // 代表的Token内容,如"if","("
SMacro *mac; // 当指向的类型为TOK_SMAC_END有效
int type; // Token类型
};
Token在《编译原理》(龙书中文版P18-第8行)中的定义是:在一个产生式中,像if和括号这样的词法元素称为记号(token)。在NASM中,token指代的就是源程序中最小的元素。
其中,type的类型包括有:
enum
{
TOK_WHITESPACE = 1, // 空格
TOK_COMMENT, // 注释
TOK_ID, // 变量名,如 语句:count dd 0x13, count即为TOK_ID
TOK_PREPROC_ID, // 预处理名,以形如 %1或% 1或%{...},
TOK_STRING, // 以单引号('')或双引号("")包括的字符(串)
TOK_NUMBER, // 数字
TOK_SMAC_END, // 单行宏结束
TOK_OTHER, // >>, <<, >=, %%, &&, !=,<>或逗号,句号等
TOK_SMAC_PARAM, // 宏参数
TOK_INTERNAL_STRING // 于TOK_STRING类似,只是没有引号
};
当NASM对源文件进行预处理时,先将之Token化,Token化函数为preprocess.c/tokenise(char *line)。
例如语句:inc al;
经过tokenise处理之后分为四个token元素,分别为 : 1 inc, 2 空格,3 al, 4; 。如图:
nasm源代码分析之预处理(2)
在NASM预处理中,每次从源代码中读取一行,并保存于数据结构Line中。Line指代已经被Token化后的一行源代码。
struct Line // 指代一行代码(经过函数tokenise(char *line)处理后的,其中参数插入char *line为源程序中代码)。
{
Line *next; // Line链表
MMacro *finishes;
Token *first; // 指向代表"处理后的一行代码"的Token链的第一个元素
};
first指向代表该行代码的Token链表,结合《nasm源代码分析之预处理(1)》中的图,如果当前行代码是:“inc al;”,first就指向Token的链表头。
finishes项只有当Line结构指代多行宏的时候才有效,这个以后会有详述。
对于宏定义语句形如:
#define TRUE 1
#define f(x) (2*x+3)
在NASM中称为单行宏,以区别于用用 %macro...%endmarco 定义的多行宏。单行宏由数据结构struct SMacro表示:
struct SMacro
{
SMacro *next;
char *name; // 单行宏名
int casesense; // 大小写敏感?
int nparam; // 参数个数
int in_progress;
Token *expansion; // 对应的Token链表
};
name很容易理解,例如宏定义:#define foo(x) (2*x+3) ,则name=“foo”。
casesense代表该宏是否大小写敏感,如果casesense==0,则不敏感,即foo(x)、FOo(x)、FOO(x)和fOO(x)指的是同一个宏。casesense!=0,则大小写敏感,即代表的是不同的宏。
nparam指宏的参数个数,上例中参数个数,即nparam=1,如果宏定义为:
#define MYSTRING “Hello World”,则nparam = 0。可参见函数:preproc.c/mstrcmp()。
in_progress指当前是否有在读取该宏定义。
从本质上来说,struct SMacro代表的也是一行代码,和struct Line差不多,只是Line泛指一般的代码行。所以在SMacro中,Token *expansion的含义和Line中的Token *first项是一样的。
NASM源代码分析之预处理(3)
struct Include
{ Include *next;
FILE *fp;
Cond *conds;
Line *expansion;
char *fname;
int lineno, lineinc;
MMacro *mstk; /* stack of active macros/reps */
};
结构Include指代一个被包含的文件,比如在程序开头加入“ %include stdio.h”,则该Include结构即指向stdio.h这个文件,以下解释以这行代码为实例。
结构元素定义:
Include *next : Include 链表指针。
FILE *fp : 容易理解,指向该文件的指针。
Cond *conds: 见下面的Cond结构
Line *expansion: 在包含文件中添加额外行,例如在编译命令中通过-D预定义宏,则系统将该定义加入到包含文件中,具体代码大家可参见preproc.c中的Readline函数,开头的处理标准宏和预定义宏就用到了这个属性。
char *fname: 文件名,在此例中,*fname = “stdio.h”
int lineno: 在预定义输出文件中,该包含文件出现的开头行数。
int lineinc: 一般设为1,如果出现代码为:“%line nnn[+mmm] [filename]”时,则lineinc=mmm.
MMacro *mstk: 用于macros/reps,将到MMacro时再细谈。
struct Cond
{ Cond *next;
int state; // 条件状态,内容如下
};
Cond表示一个判断条件,可用于判断文件包含中的判断问题,例如:
%include “stdio.h”
包含在模块:
%ifdef ABC
%include “stdio.h”
.......
%endif
如果,ABC未被定义,则代表%include “stdio.h”的Include结构的cons项就等于
COND_IF_FALSE。
int state的值包括:
enum {
COND_IF_TRUE, COND_IF_FALSE,
COND_ELSE_TRUE, COND_ELSE_FALSE,
COND_NEVER
};
大家可能光看就可以明白它的意思了。
在代码中:
%if
... (1)
%else
... (2)
%endif
COND_IF_TRUE和COND_IF_FALSE用于在(1)中,
COND_ELSE_TRUE和COND_ELSE_FALSE用于(2)中。
NASM源代码分析之预处理(4)
上下文栈结构:
struct Context
{ Context *next;
SMacro *localmac;
char *name;
unsigned long number;
};
在NASM中用上下文栈(context-stack)实现在多个宏调用之间共享label(以下摘自《NASM中文手册》,抱歉不知道中文翻译是哪位,忠心感谢该作者),比如一个 ‘repeat' ... 'until'循环,'repeat'宏的展开可能需要能够去引用'until'中定义的宏。而且在 使用这样的宏时,你可能还会嵌套多层循环(具体参见NASM手册4.7 The Context Stack一节)。
如以下代码用到这个特性:
%macro repeat 0
%push repeat
%$begin:
%endmacro
%macro until 1
j%-1 %$begin
%pop
%endmacro
然后象下面这样使用它:
mov cx,string
repeat
add cx,3
scasb
until e
注:%$ :会定义一个对于当前栈顶的上下文来讲是本地的lable
%%:会定义一个对于它所在的那个宏来讲是本地的label一样
在这里使用context-stack是为了让宏repeat和until中的标号(label)%$begin指向一样,因为在两个宏中使用%$begin时,当前栈顶都是repeat。当然这只是上下文栈的其中一个用途。
结构中项的含义:
Context *next : 栈链表
SMacro *localmac: 在%push时,localmac=NULL;其余用于%$标号时有效。
char *name : 在代码 %push repeat ,name=“repeat”。
unsigned long number: 当前栈中context的个数。
- NASM源代码分析--预处理数据结构
- NASM源代码分析--预处理过程
- NASM源代码分析之预处理(1)
- NASM源代码分析之预处理(2)
- NASM源代码分析之预处理(3)
- NASM源代码分析—参数分析
- NASM源代码分析之NASM中的指令表示(1)
- nasm预处理器(1)
- nasm预处理器(2)
- nasm预处理器(3)
- nasm预处理器(4)
- NASM源代码分析—入口函数main()(1)
- libev源代码分析--数据结构
- Nginx源代码分析--基本数据结构--hash
- Nginx源代码分析--基本数据结构--hash
- Nginx源代码分析--基本数据结构--hash
- Nginx源代码分析--基本数据结构--hash
- Nginx源代码分析--基本数据结构--hash
- Hibernate使用自己设定的应用层缓存时,一些优化操作
- MySql Error 2003
- SQL取字段类型
- Solaris 的防火墙ipfilter配置
- (四)逃避的寓言:小猫逃开影子的招数
- NASM源代码分析--预处理数据结构
- NASM源代码分析—入口函数main()(1)
- linux下安装eclipse CDT
- 网页配色软件:Color Schemer Studio
- 透明和不规则 Swing 窗口
- 我开通这个blog的感想
- sN 重新签名API 实现
- location.href
- MySQL 备份和恢复