VEX不是一种新的语言,它是从机器码转化而来的一种中间表达式,那么为什么要用到这种中间表达式呢?从我理解的程度来说,不同的处理器有不同的架构,其机器码的表现形式也是不一样的,所以为了屏蔽这种差异性,产生了一种新的中间表达式。当然VEX的产生也是带有一定导向的,它可以表示出每一条机器指令对机器产生的影响,程序都走过了哪些路径等等,这样对于在测试中帮助程序改变所走路径,到达程序的高的覆盖率很有帮助。
学习VEX IR应该有一些学习汇编码的基础,下面讲几个VEX中会用到的指令概念:
1.CAS(compare-and-swap):CAS指令是并行程序设计最基础的基石,随着越来越多的本本都用上了双核,这个世界已经快速步入并行计算时代,CAS指令发挥的作用也就越来越大。CAS指令,在Intel CPU上称为CMPXCHG,作用是将指定内存地址的内容与所给的某个值相比,如果相等,则将其内容替换为所给的另一个值,这一系列操作是原子的,不可能被中断。基本上所有的同步机制,与信号量、Java中的synchronized等的实现最终都要用到CAS指令,即使锁无关的数据结构也离不开CAS指令。
2.load-link/store-conditional(LL/SC):它们是在多线程的环境下实现多线程同步的一对指令。Load-link返回一个存储器位置的当前值;跟在其后的store-conditional如果对同一存储器地址进行操作,那么将会做如下判定:如果从那条load-link指令开始起没有对该地址用store-conditional做过更新,那么一个新的值将会被写入该地址;否则:更新将会失败,使从load-link所读取的值被恢复。他们结合起来实现了一个lock-free 原子的read-modify-write操作。
一.VEX基本数据类型:
/* Always 8 bits. */
typedef unsigned char UChar;
typedef signed char Char;
typedef char HChar; /* signfulness depends on host */
/* Only to be used for printf etc
format strings */
/* Always 16 bits. */
typedef unsigned short UShort;
typedef signed short Short;
/* Always 32 bits. */
typedef unsigned int UInt;
typedef signed int Int;
/* Always 64 bits. */
typedef unsigned long long int ULong;
typedef signed long long int Long;
/* Always 128 bits. */
typedef UInt U128[4];
/* Always 256 bits. */
typedef UInt U256[8];
//集中所有128位的vector,记作v128
typedef
union {
UChar w8[16];
UShort w16[8];
UInt w32[4];
ULong w64[2];
}
V128;
static inline函数toBool,tochar,toHchar,toUchar,toUshort,toShort分别把Int型变量转换成to后面的类型,toUInt把long型变量转换成UInt。
不同的处理器的架构不同,host的字长(32位或64位)不一样,要先搞清楚字长,否则会导致编译错误。这里预编译了x86_64, i386,powerpc,powerpc_64,arm,AIX(64位和非64位),s390x,mips这9种不同的架构,分别定义了其VEX_HOST_WORDSIZE的大小(4或8)和VEX_REGPARM(_n)(??暂时不知到这是什么)。 Ptr_to_ULong 和ULong_to_Ptr函数的功能是 cast pointers to and from 64-bit integers(在不考虑host字长的情况下) ,知道host字长写这些函数会很方便。
二.VEX IR结构介绍:
VEX IR是一种隔离不同架构的中间表达式而不是一种语言,它更像是编译器运行的IR。它有一定的结构:
code block:
代码被分解成多个小的代码块(“superblock”,type:IRSB)。IRSB是单入口多出口的,IRSB里包含3个内容:1.a type environment,表明IRSB中每个临时变量的类型;2.a list of statement;3.a jump that exits from the end the IRSB。
statement and expression:
statement(type:IRStmt)表示有side-effects的操作,例如 guest register writes, stores, and assignments to temporaries.expression(type:IRExpr)表示没有side-effects的操作,这些操作可以包含子表达式和表达式树,例如 (3 + (4 * load(addr1))。
guest state 的存储:
guest state包括guest register和guest machine,VEX库将他们存储在一个默认的内存块。要对他们进行操作,必须用“Get”将guest state读到临时变量,用“Put”写回到guest state。
关于guest state和IR的例子可参考论文《Valgrind: A Framework for Heavyweight Dynamic Binary Instrumentation》3.6.
No need for deallocations:
当translation完成时,VEX的机制将自动回收allocated的memory。
1.statement种类定义:
/*标志META的tag不代表代码,而是关于代码的额外信息。删除这些表达式不影响代码的功能性行为,但是基于IR的instrument代码的工具需要这样的statement。*/
typedef
enum {
Ist_NoOp=0x19000,
Ist_IMark, /* META */
Ist_AbiHint, /* META */
Ist_Put,
Ist_PutI,
Ist_WrTmp,
Ist_Store,
Ist_CAS,
Ist_LLSC,
Ist_Dirty,
Ist_MBE, /* META (maybe) */
Ist_Exit
}
IRStmtTag;
/*下面的IRStat结构体里有一个数据成员IRStmtTag tag和一个共用体(共用提中罗列了总共的12种statement,每次只能用到一种staement)*/
typedef
struct _IRStmt {
IRStmtTag tag;
union {
struct {
} NoOp;//一般是IR优化的结果,可忽略。ppIRStmt output: IR-NoOp。
/*一条指令可转化为多条IR,要对每条指令的IR区分,IMark标志为每条机器指令的起始。
ppIRStmt output: ------ IMark(
eg. ------ IMark(0x4000792, 5, 0) ------,
addr和len分别代表被转化的机器指令的地址和长度,delta:For x86, amd64, ppc32,ppc64 and arm, the delta value is zero. For Thumb instructions, the delta value is one.
*/
struct {
Addr64 addr; /* instruction address */
Int len; /* instruction length */
UChar delta; /* addr = program counter as encoded in guest state
- delta */
} IMark;
/*ABI(应用二进制接口,机器码层的接口,是二进制代码之间的调用规则)。这里的AbiHint指示地址空间的一个给定chunk([base .. base+len-1])成为undefined。
ppIRStmt output: ====== AbiHint(
eg. ====== AbiHint(t1, 16, t2) ======
base是chunk基址,len是长度,nia是下一条指令的地址
*/
struct {
IRExpr* base; /* Start of undefined chunk */
Int len; /* Length of undefined chunk */
IRExpr* nia; /* Address of next (guest) insn */
} AbiHint;
//Put是寄存器的写操作,写的地址在寄存器中的偏移量固定。ppIRStmt output: PUT(
struct {
Int offset; /* Offset into the guest state */
IRExpr* data; /* The value to write */
} Put;
/*PutI也是寄存器的写操作,偏移量不固定 。详细描述见见GetI。ppIRStmt output: PUTI
eg. PUTI(64:8xF64)[t5,0] = t1
*/
struct {
IRPutI* details;
} PutI;
//临时变量赋值。ppIRStmt output: t
struct {
IRTemp tmp; /* Temporary (LHS of assignment) */
IRExpr* data; /* Expression (RHS of assignment) */
} WrTmp;
//写memory。 ppIRStmt output: ST
struct {
IREndness end; /* Endianness of the store */
IRExpr* addr; /* store address */
IRExpr* data; /* value to write */
} Store;
/*原子的比较和交换(compare-and-swap)操作,语义在IRCAs中定义。
ppIRStmt output:
t
eg
t1 = CASle(t2 :: t3->Add32(t3,1))
which denotes a 32-bit atomic increment
of a value at address t2
*/
struct {
IRCAS* details;
} CAS;
/*如果stroedata是NULL,那么这就是一个 Load-Linked操作:从memory加载数据。result = Load-Linked(addr, end),转换后的数据类型由result决定(I32,I64等)。
eg ppIRStmt output:
result = ( ST
eg t3 = ( STbe-Cond(t1, t2) )
ppIRStmt output:
result = LD
如果stroedata不是NULL,那么就是一个Store-Conditional。如果address之前loged reservation,那么操作就会fail,result为0,否则result为1。转化后的类型是storedata的类型,result是Ity_I1类型。
eg ppIRStmt output:
result = ( ST
eg t3 = ( STbe-Cond(t1, t2) )
*/
struct {
IREndness end;
IRTemp result;
IRExpr* addr;
IRExpr* storedata; /* NULL => LL, non-NULL => SC */
} LLSC;
/*调用一个具有side-efdfects的C函数(ie. is "dirty")
ppIRStmt output:
t
:::
eg.
t1 = DIRTY t27 RdFX-gst(16,4) RdFX-gst(60,4)
::: foo{0x380035f4}(t2)
*/
struct {
IRDirty* details;
} Dirty;
/*内存总线的事件:a fence, or acquisition/release of the hardware bus lock.
ppIRStmt output: MBusEvent-Fence,
MBusEvent-BusLock, MBusEvent-BusUnlock.
*/
struct {
IRMBusEvent event;
} MBE;
/*从IRSB的退出条件。
ppIRStmt output: if (
eg. if (t69) goto {Boring} 0x4000AAA:I32
*/
struct {
IRExpr* guard; /* Conditional expression */
IRConst* dst; /* Jump target (constant only) */
IRJumpKind jk; /* Jump kind */
Int offsIP; /* Guest state offset for IP */
} Exit;
} Ist;
}
IRStmt;
2.expression种类定义:
typedef struct _IRQop IRQop; /* forward declaration */
typedef struct _IRTriop IRTriop; /* forward declaration */
typedef
enum {
Iex_Binder=0x15000,
Iex_Get,
Iex_GetI,
Iex_RdTmp,
Iex_Qop,
Iex_Triop,
Iex_Binop,
Iex_Unop,
Iex_Load,
Iex_Const,
Iex_Mux0X,
Iex_CCall
}
IRExprTag;
/*expression stored as a tagged union.‘tag’标识了expression的种类。‘Iex’ is the union that holds the fields.如果有一个IRExpr e,e.tag=Iex_Load,则e是一个load expression,访问这块地址的方法是:e.Iex.Load.
未完待续~~~~