实施例一:
以C语言编写的example.c文件中包含以下代码:
static int number;
extern int GetParameter();
int GetSum(int a)
{
return GetParameter() + number + a;
}
软件运行环境为32位x86平台,操作系统为32位windows xp。
上述example.c文件编译为虚拟指令后其目标文件为ELF格式的example.o,虚拟机的入口函数为EnterVm,要求的参数为虚拟指令地址、重定位表。
本实施例的目标是构造一个COFF格式的目标文件——example.obj,其中包含GetSum函数及COFF格式要求的信息。
在软件开发工具(如Visual Studio 6.0)加入该目标文件(example.obj),用户在编程时就可以直接调用GetSum函数。
其中,ELF格式简介如下:
ELF = Executable and Linkable Format,可执行连接格式,是UNIX系统实验室(USL)作为应用程序二进制接口(Application Binary Interface,ABI)而开发和发布的。
ELF头位于文件的开始,描述了该文件的组织情况。sections保存着object文件的信息,较为常见的section包括指令,数据,符号表,字符串表,重定位信息等等。section头表(section header table)包含了描述文件sections的信息。每个section在这个表中有一个入口;每个入口给出了该section的名字,大小,等等信息。各部分分布如下表所示:
Elf header |
Program header table |
section1 |
…… |
section n |
section header table |
本例提供的方法包括如下步骤:
1. 利用现有的虚拟机编译器将源代码(example.c)编译为虚拟指令文件(example.o)。
2. 构造本地定义的函数、变量。根据本发明的一个实施例,构造函数具体包括:
1)添加虚拟指令符号(虚拟指令包含在虚拟指令文件example.o中);
2)添加虚拟机入口函数符号;
3)添加函数符号及函数体;
4)对虚拟机入口函数地址和虚拟指令地址进行重定位。
构造变量具体包括:
1)添加变量符号;
2)添加数据。
根据本发明的一个具体实施例,作为示例,添加过程示例代码如下所示,但是其不对本发明的内容作为限定,其它任何编程语言、代码均可实现类似功能的限定的内容都应视为本发明的范围之内:
#include <vector>
using namespace std;
//COFF文件中的符号
struct CoffSymbol
{
char Name[8];
int Value;
short SectionNumber;
short Type;
char StorageClass;
char NumberOfAuxSymbols;
};
//COFF文件中的重定位项
struct CoffRelocation {
int VirtualAddress;
int SymbolTableIndex;
short Type;
};
class COFF
{
public:
void createFunction(const char* name, const char* vmCode,int vmCodeSize)
{
//1. 添加虚拟指令符号
char vmCodeName[64]; //虚拟指令符号名
strcpy(vmCodeName, "vmCode_");
strcat(vmCodeName, name);
int vmCodeI = addSymbol(vmCodeName, 0, 0, 0); //添加虚拟指令符号
//2. 添加虚拟机入口函数符号
int enterVm = addSymbol("enterVm", 0, 0, 0x20);
//3. 添加函数符号及函数体
int func = addSymbol(name, 0, 0, 0x20); //添加函数符号
struct FunctionBody
{
char Jmp; //跳转指令xE9
unsigned enterVm; //虚拟机入口函数地址
void* vmCode; //虚拟指令地址
};
FunctionBody funcBody;
funcBody.Jmp = 0xE9;
funcBody.enterVm = 0;
funcBody.vmCode = 0;
memcpy(text, &funcBody, sizeof(funcBody)); //添加函数体
//4. 对虚拟机入口函数地址和虚拟指令地址进行重定位
addRelocation(enterVm, 0, textSize + 1); //重定位虚拟机入口函数地址
addRelocation(vmCodeI, 0, textSize + 5);//重定位虚拟指令地址
textSize += sizeof(funcBody);
}
void createVarible(const char* name, const char* var, intvarSize)
{
char varName[64]; //变量符号名
strcpy(varName, "var_");
strcat(varName, name);
int varI = addSymbol(varName, 0, textSize, 0); //添加变量符号
memcpy(data, var, varSize); //将变量添加到数据段
dataSize += varSize;
}
void createRelocations(
const char* relocation, int relSize,
const char* vmCode, int codeSize,
const char* elfSymbol, int symbolSize);
protected:
//添加符号
int addSymbol(const char* name, unsigned secIndex, unsignedoffset, int type)
{
CoffSymbol sym;
sym.NumberOfAuxSymbols = 0;
strcpy(sym.Name, name); //符号名长度大于8时不能直接拷贝,应按照COFF格式处理,此处简化处理。
sym.Type = type;
sym.Value = offset;
sym.StorageClass = 2;
sym.SectionNumber = secIndex + 1;//section numberone based,从开始
unsigned index = symbols.size();
symbols.push_back(sym);
return index;
}
//添加重定位项
void addRelocation(unsigned symIndex, unsigned secIndex,unsigned offset)
{
CoffRelocation rel;
rel.Type = 6;
rel.VirtualAddress = offset;
rel.SymbolTableIndex = symIndex;
relocations.push_back(rel);
}
//获取ELF符号的名字,按ELF文件格式根据sym->st_name查找即可,省略其实现
const char* GetELFSymbolName(ElfSymbol* sym);
char* text; //代码段数据
int textSize; //代码段大小
char* data; //数据段数据
int dataSize; //数据段大小
vector<CoffSymbol> symbols; //符号
vector<CoffRelocation> relocations; //重定位
};
以上代码中COFF::createFunction为构造函数, COFF::createVarible为构造变量。
3. 构造重定位信息,该信息包含虚拟指令文件中包含的所有符号和虚拟内存地址。包括:1)添加虚拟指令文件中的重定位表;2)添加虚拟指令;3)添加并定位虚拟指令符号;4)添加重定位项。根据本重定位表虚拟机可对虚拟指令中需要重定位的地方进行重定位。
根据本发明的一个具体实施例,作为示例,本步骤的示例代码如下所示,但是其不对本发明的内容作为限定,其它任何编程语言、代码均可实现类似功能的限定的内容都应视为本发明的范围之内:
void COFF::createRelocations(
const char* relocation, int relSize,
const char* vmCode, int codeSize,
const char* elfSymbol, int symbolSize)
{
addSymbol("_Relocation", 0, textSize, 0); //添加重定位表的变量符号
memcpy(text, relocation, relSize); //添加重定位表
textSize += relSize;
addSymbol("_VmCode", 0, textSize, 0); //添加变量符号vmCode
memcpy(text, vmCode, codeSize); //添加虚拟指令
textSize += codeSize;
addSymbol("_SymbolAddressTable", 0, textSize, 0); //添加符号地址表的变量符号
memcpy(text, elfSymbol, symbolSize); //添加符号表
int offset = textSize;
textSize += symbolSize;
ElfSymbol* psym = (ElfSymbol*)elfSymbol;
int symSize = symbolSize/sizeof(ElfSymbol);
for (int i=0; i<symSize; ++i)
{
psym[i].st_value = 0;
//按符号名查找其索引
int j=0;
for (j=0; j<symbols.size(); ++j)
{
if (strcmp(GetELFSymbolName(&psym[i]), symbols[j].Name) == 0)
{
break;
}
}
//对符号的st_value成员进行重定位,重定位后,其值为符号所代表的内容的虚拟内存地址
addRelocation(j, 0, offset + i*sizeof(ElfSymbol) + 4);
//设定虚拟指令符号的偏置值(offset, CoffSymbol.Value)
for (j=0; j<symbols.size(); ++j)
{
char vmCodeName[64]; //虚拟指令符号名
strcpy(vmCodeName, "vmCode_");
strcat(vmCodeName, GetELFSymbolName(&psym[i]));
if (strcmp(GetELFSymbolName(&psym[i]), symbols[j].Name) == 0)
{
symbols[j].Value = offset + psym[i].st_value;
break;
}
}
}
}
4. 设置COFF文件格式需要的其他信息,如文件头等。
5. 输出目标文件(example.obj)。
完成以上步骤后,将example.obj加入到Visual Studio 6.0的工程中,在该工程的任何源代码文件中只要声明“extern int GetSum(int a);”即可直接调用函数GetSum。