AS汇编器源码剖析-第3章-指令字符的识别
来源:互联网 发布:mysql大数据统计报表 编辑:程序博客网 时间:2024/06/08 00:43
AS汇编器源码剖析-指令字符的识别
汇编器AS读取一个汇编文件后,对文本内容处理后,会逐行进行解析。最基础的部分归结于对一行汇编的编码。assemble_one()是入口函数,实际上调用的是各处理器文件定义的md_assemble()函数。
创建hash表
和通常设想的不一样,md_assemble()并不是通过正则表达式或者精巧的字符分析来讲字符串转化为汇编词法语法要素,而是通过hash表按照字符串找到对应的操作码、操作数、条件码,所以先从hash表讲起。
创建opcode hash表
各种hansh表的创建函数为各处理器定义的md_begin()函数,调用链为As.c的main -> perform_an_assembly_pass -> md_begin()。
Opcode也就是指令,这个是最重要的hash表。opcode 的hash表为aarch64_ops_hsh,opcode比较特殊,是独立的调用函数 fill_instruction_hash_table ();其他hash表基本是在md_begin()里处理完的。
hash表的节点结构体定义:
struct templates
{
aarch64_opcode *opcode;
struct templates *next;
};
以指令的name,也就是字符串如"adc","adcs"之类作为key,调用如下函数将全部的opcode全部插入hash表。
checked_hash_insert(aarch64_ops_hsh, opcode->name, (void *) new_templ);
创建寄存器 hash表
由于oprand的变化比较复杂,所以并不是通过一个hash表对oprand进行分析,而是进行了分类,每个类有单独的hash进行识别处理。
armV8寄存器定义在reg_names。
static const reg_entry reg_names[] = {
/*Integer registers. */
REGSET31 (x, R_64), REGSET31 (X, R_64),
REGSET31 (w, R_32), REGSET31 (W, R_32),
REGDEF(wsp, 31, SP_32), REGDEF (WSP, 31, SP_32),
REGDEF (sp, 31, SP_64), REGDEF (SP, 31, SP_64),
REGDEF (wzr, 31, Z_32), REGDEF (WZR, 31, Z_32),
REGDEF (xzr, 31, Z_64), REGDEF (XZR, 31, Z_64),
/*Coprocessor register numbers. */
REGSET (c, CN), REGSET (C, CN),
/*Floating-point single precision registers. */
REGSET (s, FP_S), REGSET (S, FP_S),
/*Floating-point double precision registers. */
REGSET (d, FP_D), REGSET (D, FP_D),
/*Floating-point half precision registers. */
REGSET (h, FP_H), REGSET (H, FP_H),
/*Floating-point byte precision registers. */
REGSET (b, FP_B), REGSET (B, FP_B),
/*Floating-point quad precision registers. */
REGSET (q, FP_Q), REGSET (Q, FP_Q),
/*FP/SIMD registers. */
REGSET (v, VN), REGSET (V, VN),
};
寄存器的定义结构为reg_entry
/* Structure for a hash table entry for aregister. */
typedef struct
{
const char *name;
unsigned char number;
unsigned char type;
unsigned char builtin;
} reg_entry;
注意寄存器里分了大小写,如x寄存器定义了两套,分别是REGSET31(x, R_64), REGSET31 (X, R_64)。
REGSET31 (x, R_64)实际上定义了31个寄存器,定义如下:”
#define REGSET31(p,t) \
REGNUM(p, 0,t), REGNUM(p, 1,t), REGNUM(p, 2,t), REGNUM(p, 3,t), \
……
REGNUM(p,24,t), REGNUM(p,25,t), REGNUM(p,26,t), REGNUM(p,27,t), \
REGNUM(p,28,t), REGNUM(p,29,t), REGNUM(p,30,t)
以REGNUM(x, 1, R_64)为例,经过展开几层宏,最终如下:
{ #x1, 1, REG_TYPE_ R_64, TRUE }
“#x1”的含义是,把x1变成字符串。在#define中,标准只定义了#和##两种操作。#用来把参数转换成字符串,##则用来连接两个前后两个参数,把它们变成一个字符串。
对比寄存器的定义结构reg_entry,解析如下:
#x1
name
1
number
REG_TYPE_ R_64
type
TRUE
builtin
寄存器的类型定义如下
#define AARCH64_REG_TYPES \
BASIC_REG_TYPE(R_32) /* w[0-30]*/ \
BASIC_REG_TYPE(R_64) /* x[0-30]*/ \
BASIC_REG_TYPE(SP_32) /* wsp */ \
BASIC_REG_TYPE(SP_64) /* sp */ \
BASIC_REG_TYPE(Z_32) /*wzr */ \
BASIC_REG_TYPE(Z_64) /*xzr */ \
BASIC_REG_TYPE(FP_B) /* b[0-31]*//* NOTE: keep FP_[BHSDQ] consecutive! */\
BASIC_REG_TYPE(FP_H) /* h[0-31]*/ \
BASIC_REG_TYPE(FP_S) /* s[0-31]*/ \
BASIC_REG_TYPE(FP_D) /* d[0-31]*/ \
BASIC_REG_TYPE(FP_Q) /* q[0-31]*/ \
BASIC_REG_TYPE(CN) /*c[0-7] */ \
BASIC_REG_TYPE(VN) /* v[0-31] */ \
/*Typecheck: any 64-bit int reg (inc SP exc XZR) */ \
MULTI_REG_TYPE(R64_SP, REG_TYPE(R_64) | REG_TYPE(SP_64)) \
/*Typecheck: any int (inc {W}SP inc [WX]ZR) */ \
MULTI_REG_TYPE(R_Z_SP, REG_TYPE(R_32) | REG_TYPE(R_64) \
| REG_TYPE(SP_32) | REG_TYPE(SP_64) \
| REG_TYPE(Z_32) | REG_TYPE(Z_64)) \
/*Typecheck: any [BHSDQ]P FP. */ \
MULTI_REG_TYPE(BHSDQ, REG_TYPE(FP_B) | REG_TYPE(FP_H) \
| REG_TYPE(FP_S) | REG_TYPE(FP_D) |REG_TYPE(FP_Q)) \
/*Typecheck: any int or [BHSDQ]P FP or V reg (exc SP inc [WX]ZR) */ \
MULTI_REG_TYPE(R_Z_BHSDQ_V, REG_TYPE(R_32) | REG_TYPE(R_64) \
| REG_TYPE(Z_32) | REG_TYPE(Z_64) |REG_TYPE(VN) \
| REG_TYPE(FP_B) | REG_TYPE(FP_H) \
| REG_TYPE(FP_S) | REG_TYPE(FP_D) |REG_TYPE(FP_Q)) \
/*Any integer register; used for error messages only. */ \
MULTI_REG_TYPE(R_N, REG_TYPE(R_32) | REG_TYPE(R_64) \
| REG_TYPE(SP_32) | REG_TYPE(SP_64) \
| REG_TYPE(Z_32) | REG_TYPE(Z_64)) \
/*Pseudo type to mark the end of the enumerator sequence. */ \
BASIC_REG_TYPE(MAX)
最终以寄存器名为key,把reg_names所有的寄存器放入了aarch64_reg_hsh表。每个项的value是该寄存器定义相对于reg_names首地址的偏移,而不是寄存器的编号之类的。
for (i = 0; i < ARRAY_SIZE (reg_names);i++)
{checked_hash_insert (aarch64_reg_hsh,reg_names[i].name,(void *) (reg_names + i));}
创建condition hash表
所有的condition定义在aarch64_conds。
/* Table of all conditional affixes. */
const aarch64_cond aarch64_conds[16] =
{
{{"eq"}, 0x0},
{{"ne"}, 0x1},
{{"cs", "hs"}, 0x2},
{{"cc", "lo", "ul"}, 0x3},
{{"mi"}, 0x4},
{{"pl"}, 0x5},
{{"vs"}, 0x6},
{{"vc"}, 0x7},
{{"hi"}, 0x8},
{{"ls"}, 0x9},
{{"ge"}, 0xa},
{{"lt"}, 0xb},
{{"gt"}, 0xc},
{{"le"}, 0xd},
{{"al"}, 0xe},
{{"nv"}, 0xf},
};
Cond的结构体定义如下:
typedef uint32_t aarch64_insn;
typedef struct
{
/*A list of names with the first one as the disassembly preference;
terminated by NULL if fewer than 3. */
const char *names[3];
aarch64_insn value;
} aarch64_cond;
建立hash的代码代码如下:
for (i = 0; i < ARRAY_SIZE(aarch64_conds); i++)
{
unsigned int j;
/* A condition code may have alias(es), e.g."cc", "lo" and "ul" are
the same condition code. */
for (j = 0; j < ARRAY_SIZE(aarch64_conds[i].names); ++j)
{
const char *name = aarch64_conds[i].names[j];
if (name == NULL)
break;
checked_hash_insert (aarch64_cond_hsh, name,
(void *) (aarch64_conds + i));
/* Also hash the name in the upper case. */
checked_hash_insert (aarch64_cond_hsh,get_upper_str (name),
(void *) (aarch64_conds + i));
}
}
代码需要注意的地方有有如下几点:
1. aarch64_conds里,不同的cond可能对应相同的value,如 {{"cc", "lo","ul"}, 0x3}。说明{"cc", "lo", "ul"}这3个cond对应的encoding是相同的。所以这里有第二个for循环和ARRAY_SIZE(aarch64_conds[i].names)的处理。
我觉得还不如把{"cc", "lo", "ul"}做成3个item,只需要把value写成一样就好了,不要把复杂度耦合到hash代码里。
比如把
{{"cc", "lo", "ul"}, 0x3},
改为
{"cc", 0x3},
{"lo",0x3},
{"ul",0x3},
2. 和寄存器直接定义了大小写不同,cond的大小写处理是靠把每个key变成大写之后再插入一次。
3. 和寄存器hash一样,key对应的value并不是和key的含义直接相关的,而是偏移。这个也暗示了,真正有价值的,能影响encoding的不是这个value,而是根据这个value,找到aarch64_conds里的值,应该就是struct aarch64_cond-> aarch64_insn value。
创建寄存器nzcv表
Nzcv全表如下:一共有2^4个项。
static const asm_nzcv nzcv_names[] = {
{"nzcv", B (n, z, c, v)},
{"nzcV", B (n, z, c, V)},
{"nzCv", B (n, z, C, v)},
{"nzCV", B (n, z, C, V)},
{"nZcv", B (n, Z, c, v)},
{"nZcV", B (n, Z, c, V)},
{"nZCv", B (n, Z, C, v)},
{"nZCV", B (n, Z, C, V)},
{"Nzcv", B (N, z, c, v)},
{"NzcV", B (N, z, c, V)},
{"NzCv", B (N, z, C, v)},
{"NzCV", B (N, z, C, V)},
{"NZcv", B (N, Z, c, v)},
{"NZcV", B (N, Z, c, V)},
{"NZCv", B (N, Z, C, v)},
{"NZCV", B (N, Z, C, V)}
};
#define N 1
#define n 0
#define Z 1
#define z 0
#define C 1
#define c 0
#define V 1
#define v 0
#define B(a,b,c,d) (((a) << 3) | ((b)<< 2) | ((c) << 1) | (d))
typedef struct
{
const char *template;
uint32_t value;
} asm_nzcv;
既然原始表已经提供了所有的信息,不存在大小写,也不存在多个key对应一个value。创建hash表也就比condition hash简单的多。
for(i = 0; i < ARRAY_SIZE (nzcv_names); i++)
checked_hash_insert(aarch64_nzcv_hsh, nzcv_names[i].template, (void *) (nzcv_names + i));
注意这里的hash value还是偏移,说明能影响encoding的不是这个value,而是B (n, z,c, v)之类的值。
其他的hash表
最复杂的hash表创建过程也不过是创建conditionhash表。其他hash表的建立过程最多是跳过空或者无效定义的表项,把key转成大写再插入一次而已。
其他的hash还有如下:
aarch64_shift_hsh
aarch64_sys_regs_hsh
aarch64_pstatefield_hsh
aarch64_sys_regs_ic_hsh
aarch64_sys_regs_dc_hsh
aarch64_sys_regs_at_hsh
aarch64_sys_regs_tlbi_hsh
aarch64_barrier_opt_hsh
aarch64_pldop_hsh
aarch64_hint_opt_hsh
利用hash查找代码字符串含义
查找opcode hash进行encoding
hash_find_n的参数char *start,int len分别表示字符串的其实地址和长度,实际上就是key。返回的是hash节点
lookup_mnemonic (const char *start, intlen)
{
templates *templ = NULL;
templ = hash_find_n (aarch64_ops_hsh, start, len);
return templ;
}
查找cond hash进行encoding
可以看出aarch64_cond_hsh的长度是固定的。由于创建hash表时value存放的是偏移,所以代码里将查找到的value赋值给aarch64_cond *cond,也就是直接取得了在aarch64_conds里对应的元素的位置,可以利用cond的结构体成员直接进行逻辑操作。
除了opcode比较特殊外,其他所有的hash表查找都是类似cond hash的操作方式,所以就不重复分析了。
cond = hash_find_n (aarch64_cond_hsh, end + 1,2);
if(cond)
{
inst.cond = cond->value;
*str = end + 3;
}
- AS汇编器源码剖析-第3章-指令字符的识别
- AS汇编器源码剖析-第5章-用eclipse编译调试AS
- AS汇编器源码剖析-第4章-编译一行汇编
- AS汇编器源码剖析-第1章-Arm操作码
- AS汇编器源码剖析-第2章-Armoprand操作数
- 汇编器源码剖析
- 汇编器源码剖析
- 反汇编器源码剖析
- 汇编字符串串操作指令
- 第五章: 汇编器指令
- STL源码剖析 - 第2章 空间配置器
- 【STL源码剖析读书笔记】【第2章】空间配置器
- STL源码剖析 - 第3章 迭代器的概念与traits编程技法
- 《STL源码剖析》学习笔记-第3章 迭代器
- as汇编器命令 中文翻译 第七章
- 汇编器as
- 一个汇编指令查询程序的Visual Basic源码
- 汇编学习笔记第4~10章:汇编原理-汇编指令实战
- 4.搜索之路——solr部署到Tomcat
- DataReader只读
- CentOS 7.x安装配置
- 5.搜索之路——solr与lucene和nutch关系
- 设计原则——开放封闭原则(Open Close Principle)
- AS汇编器源码剖析-第3章-指令字符的识别
- 6.搜索之路——solr使用
- ListView添加HeadView后布局紊乱的问题
- Python3.5+PyQt5.6环境搭建
- Android View坐标系
- 系统不支持:mysql
- info.plist
- WinRAR 5.31 x64去广告破解
- 实习总结之如何做好产品经理?