ultimatepp/uppsrc2/coff/binobj/binobj.cpp
cxl 8173788599 coff moved to uppsrc2
git-svn-id: svn://ultimatepp.org/upp/trunk@7711 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2014-09-21 15:46:17 +00:00

505 lines
16 KiB
C++

#include "binobj.h"
#include <plugin/z/z.h>
#include <plugin/bz2/bz2.h>
NAMESPACE_UPP
BinObjInfo::BinObjInfo()
{
}
void BinObjInfo::Parse(CParser& binscript, String base_dir)
{
while(!binscript.IsEof()) {
String binid = binscript.ReadId();
bool ba = (binid == "BINARY_ARRAY");
bool bm = (binid == "BINARY_MASK");
if(binid == "BINARY" || ba || bm) {
binscript.PassChar('(');
Block blk;
blk.scriptline = binscript.GetLine();
blk.ident = binscript.ReadId();
ArrayMap<int, Block>& brow = blocks.GetAdd(blk.ident);
binscript.PassChar(',');
blk.index = -1;
if(ba) {
blk.flags |= Block::FLG_ARRAY;
blk.index = binscript.ReadInt();
if(blk.index < 0 || blk.index > 1000000)
binscript.ThrowError(NFormat("invalid array index: %d", blk.index));
binscript.PassChar(',');
}
else if(bm) {
blk.flags |= Block::FLG_MASK;
blk.index = brow.GetCount();
}
String file = binscript.ReadString();
if(binscript.Id("ZIP"))
blk.encoding = Block::ENC_ZIP;
else if(binscript.Id("BZ2"))
blk.encoding = Block::ENC_BZ2;
binscript.PassChar(')');
FindFile ff;
String searchpath = NormalizePath(file, base_dir);
String searchdir = GetFileDirectory(searchpath);
Vector<String> files;
Vector<int64> lengths;
if(ff.Search(searchpath))
do
if(ff.IsFile()) {
files.Add(ff.GetName());
lengths.Add(ff.GetLength());
}
while(ff.Next());
if(files.IsEmpty())
binscript.ThrowError(NFormat("'%s' not found or not a file", file));
if(!(blk.flags & Block::FLG_MASK) && files.GetCount() > 1)
binscript.ThrowError(NFormat("Multiple files found (e.g. %s, %s) in single file mode", files[0], files[1]));
IndexSort(files, lengths);
for(int i = 0; i < files.GetCount(); i++) {
blk.file = AppendFileName(searchdir, files[i]);
blk.length = (int)lengths[i];
int f = brow.Find(blk.index);
if(f >= 0)
binscript.ThrowError(NFormat("%s[%d] already seen at line %d", blk.ident, blk.index, brow[f].scriptline));
if(blk.index < 0 && !brow.IsEmpty() || blk.index >= 0 && !brow.IsEmpty() && brow[0].index < 0)
binscript.ThrowError(NFormat("%s: mixing non-array and array elements", blk.ident));
brow.Add(blk.index, blk);
if(!(blk.flags & Block::FLG_MASK))
break;
blk.index++;
}
}
else
binscript.ThrowError("binary script item identifier expected");
}
}
class BinObj : public BinObjInfo {
public:
BinObj(Callback1<String> WhenConsole);
void Run(String objectfile, CParser& binscript, String base_dir);
public:
Callback1<String> WhenConsole;
private:
void PrepareSymbolTable(String binfile);
void PrepareMetaData();
void FixAuxSymbols(int data_size);
void WriteFile(String objectfile);
int AddSymbol(COFF_IMAGE_SYMBOL& sym, const char *name);
int AddMetaData(int value, int reloc_section = -1);
static int Align(int len) { return (len + 3) & -4; }
private:
Vector<byte> metadata;
// int data_size;
Vector<byte> relocations;
Vector<byte> symbol_table;
Vector<byte> symbol_table_strtbl;
enum {
SCN_COMDAT,
SCN_METADATA,
SCN_DATA,
};
int aux_metadata_scn;
int aux_data_scn;
int symtbl_metasec;
int symtbl_datasec;
};
static const char metadata_secn[] = ".data$BM";
static const char data_secn[] = ".data$BD";
BinObj::BinObj(Callback1<String> WhenConsole_)
: WhenConsole(WhenConsole_)
{
symbol_table_strtbl.SetCountR(4);
}
void BinObj::Run(String objectfile, CParser& binscript, String base_dir)
{
Parse(binscript, base_dir);
PrepareSymbolTable(binscript.GetFileName());
PrepareMetaData();
WriteFile(objectfile);
}
void BinObj::PrepareMetaData()
{
// data_size = 0;
for(int i = 0; i < blocks.GetCount(); i++) {
String ident;
ident << '_' << blocks.GetKey(i);
ArrayMap<int, Block>& belem = blocks[i];
int flags = belem[0].flags;
if(flags & (Block::FLG_ARRAY | Block::FLG_MASK)) {
int count = Max(belem.GetKeys()) + 1;
Vector<Block *> blockref;
blockref.SetCount(count, 0);
for(int a = 0; a < belem.GetCount(); a++) {
Block& b = belem[a];
// b.offset = data_size;
blockref[b.index] = &b;
// data_size += Align(b.length + 1);
}
COFF_IMAGE_SYMBOL countsym;
Zero(countsym);
countsym.Value = AddMetaData(count);
countsym.SectionNumber = SCN_METADATA;
countsym.StorageClass = COFF_IMAGE_SYM_CLASS_EXTERNAL;
countsym.Type = 4;
AddSymbol(countsym, ident + "_count");
COFF_IMAGE_SYMBOL lensym;
Zero(lensym);
lensym.Value = metadata.GetCount();
lensym.SectionNumber = SCN_METADATA;
lensym.StorageClass = COFF_IMAGE_SYM_CLASS_EXTERNAL;
lensym.Type = COFF_IMAGE_SYM_TYPE_INT + 256 * COFF_IMAGE_SYM_DTYPE_ARRAY;
AddSymbol(lensym, ident + "_length");
for(int a = 0; a < count; a++) {
if(blockref[a])
blockref[a]->len_meta_offset = metadata.GetCount();
AddMetaData(-1);
}
COFF_IMAGE_SYMBOL ptrsym;
Zero(ptrsym);
ptrsym.Value = metadata.GetCount();
ptrsym.SectionNumber = SCN_METADATA;
ptrsym.StorageClass = COFF_IMAGE_SYM_CLASS_EXTERNAL;
ptrsym.Type = COFF_IMAGE_SYM_TYPE_INT + 256 * COFF_IMAGE_SYM_DTYPE_ARRAY;
AddSymbol(ptrsym, ident);
for(int a = 0; a < count; a++) {
if(blockref[a])
blockref[a]->off_meta_offset = metadata.GetCount();
AddMetaData(0, blockref[a] ? symtbl_datasec : 0);
}
if(flags & Block::FLG_MASK) {
Vector<int> fn_offsets;
fn_offsets.SetCount(count);
for(int a = 0; a < count; a++) {
String fn;
if(blockref[a])
fn = GetFileName(blockref[a]->file);
int pos = metadata.GetCount();
fn_offsets[a] = pos;
metadata.SetCountR(pos + fn.GetLength() + 1);
memcpy(&metadata[pos], fn, fn.GetLength() + 1);
}
metadata.SetCountR(Align(metadata.GetCount()), 0);
COFF_IMAGE_SYMBOL filesym;
Zero(filesym);
filesym.Value = metadata.GetCount();
filesym.SectionNumber = SCN_METADATA;
filesym.StorageClass = COFF_IMAGE_SYM_CLASS_EXTERNAL;
filesym.Type = COFF_IMAGE_SYM_TYPE_INT + 256 * COFF_IMAGE_SYM_DTYPE_ARRAY;
AddSymbol(filesym, ident + "_files");
for(int a = 0; a < count; a++)
AddMetaData(fn_offsets[a], symtbl_metasec);
}
}
else {
Block& b = belem[0];
COFF_IMAGE_SYMBOL lensym;
Zero(lensym);
b.len_meta_offset = metadata.GetCount();
lensym.Value = AddMetaData(b.length);
lensym.SectionNumber = SCN_METADATA;
lensym.StorageClass = COFF_IMAGE_SYM_CLASS_EXTERNAL;
lensym.Type = COFF_IMAGE_SYM_TYPE_INT;
AddSymbol(lensym, ident + "_length");
COFF_IMAGE_SYMBOL datasym;
Zero(datasym);
b.off_meta_offset = metadata.GetCount();
datasym.Value = AddMetaData(0, symtbl_datasec);
datasym.SectionNumber = SCN_METADATA;
datasym.StorageClass = COFF_IMAGE_SYM_CLASS_EXTERNAL;
datasym.Type = COFF_IMAGE_SYM_TYPE_INT;
AddSymbol(datasym, ident);
}
}
}
void BinObj::PrepareSymbolTable(String binfile)
{
{ // FILE
COFF_IMAGE_SYMBOL filesym;
Zero(filesym);
filesym.SectionNumber = -2;
filesym.StorageClass = COFF_IMAGE_SYM_CLASS_FILE;
filesym.NumberOfAuxSymbols = int(binfile.GetLength() / sizeof(COFF_IMAGE_SYMBOL) + 1);
memcpy(symbol_table.GetIter(AddSymbol(filesym, ".file")), binfile, binfile.GetLength() + 1);
}
{ // metadata section header
symtbl_metasec = symbol_table.GetCount() / COFF_IMAGE_SIZEOF_SYMBOL;
COFF_IMAGE_SYMBOL msecsym;
Zero(msecsym);
msecsym.SectionNumber = 1;
msecsym.StorageClass = COFF_IMAGE_SYM_CLASS_STATIC;
msecsym.NumberOfAuxSymbols = 1;
aux_metadata_scn = AddSymbol(msecsym, metadata_secn);
}
{ // data section header
symtbl_datasec = symbol_table.GetCount() / COFF_IMAGE_SIZEOF_SYMBOL;
COFF_IMAGE_SYMBOL dsecsym;
Zero(dsecsym);
dsecsym.SectionNumber = 2;
dsecsym.StorageClass = COFF_IMAGE_SYM_CLASS_STATIC;
dsecsym.NumberOfAuxSymbols = 1;
aux_data_scn = AddSymbol(dsecsym, data_secn);
}
}
void BinObj::FixAuxSymbols(int data_size)
{
{ // metadata section info symbol
COFF_IMAGE_AUX_SYMBOL msecaux;
Zero(msecaux);
msecaux.Section.Length = metadata.GetCount();
msecaux.Section.NumberOfRelocations = int(relocations.GetCount() / sizeof(COFF_IMAGE_RELOCATION));
msecaux.Section.Number = SCN_METADATA;
memcpy(symbol_table.GetIter(aux_metadata_scn), &msecaux, COFF_IMAGE_SIZEOF_SYMBOL);
}
{ // data section info symbol
COFF_IMAGE_AUX_SYMBOL dsecaux;
Zero(dsecaux);
dsecaux.Section.Length = data_size;
dsecaux.Section.Number = SCN_DATA;
memcpy(symbol_table.GetIter(aux_data_scn), &dsecaux, COFF_IMAGE_SIZEOF_SYMBOL);
}
Poke32le(symbol_table_strtbl.Begin(), symbol_table_strtbl.GetCount());
}
void BinObj::WriteFile(String objectfile)
{
FileOut fo;
if(!fo.Open(objectfile))
throw Exc(NFormat("error creating file '%s'", objectfile));
int section_map_offset = sizeof(COFF_IMAGE_FILE_HEADER);
int symtbl_offset = section_map_offset + 2 * sizeof(COFF_IMAGE_SECTION_HEADER);
int strtbl_offset = symtbl_offset + symbol_table.GetCount();
int reloc_offset = strtbl_offset + symbol_table_strtbl.GetCount();
int sec1_offset = reloc_offset + relocations.GetCount();
int sec2_offset = sec1_offset + metadata.GetCount();
fo.Put('\0', sec2_offset);
int data_size = 0;
{ // data section
ASSERT(fo.GetPos() == sec2_offset);
for(int i = 0; i < blocks.GetCount(); i++) {
const ArrayMap<int, Block>& belem = blocks[i];
for(int a = 0; a < belem.GetCount(); a++) {
const Block& b = belem[a];
String data = LoadFile(b.file);
if(data.IsVoid())
throw Exc(NFormat("error reading file '%s'", b.file));
if(data.GetLength() != b.length)
throw Exc(NFormat("length of file '%s' changed (%d -> %d) during object creation",
b.file, b.length, data.GetLength()));
switch(b.encoding) {
case Block::ENC_ZIP: data = ZCompress(data); break;
case Block::ENC_BZ2: data = BZ2Compress(data); break;
}
int offset = (int)fo.GetPos() - sec2_offset;
fo.Put(data, data.GetLength());
int align = Align(data.GetLength() + 1);
fo.Put('\0', align - data.GetLength());
ASSERT(b.len_meta_offset >= 0);
Poke32le(&metadata[b.len_meta_offset], data.GetLength());
Poke32le(&metadata[b.off_meta_offset], offset);
data_size += align;
}
}
}
fo.Seek(0);
FixAuxSymbols(data_size);
{ // file header
ASSERT(fo.GetPos() == 0);
COFF_IMAGE_FILE_HEADER hdr;
Zero(hdr);
hdr.Machine = COFF_IMAGE_FILE_MACHINE_I386;
hdr.NumberOfSections = 2;
hdr.TimeDateStamp = (dword)time(NULL);
hdr.PointerToSymbolTable = symtbl_offset;
hdr.NumberOfSymbols = symbol_table.GetCount() / COFF_IMAGE_SIZEOF_SYMBOL;
hdr.Characteristics = COFF_IMAGE_FILE_LINE_NUMS_STRIPPED
| COFF_IMAGE_FILE_LOCAL_SYMS_STRIPPED
| COFF_IMAGE_FILE_BYTES_REVERSED_LO
| COFF_IMAGE_FILE_32BIT_MACHINE;
fo.Put(&hdr, sizeof(COFF_IMAGE_FILE_HEADER));
}
{ // section map
ASSERT(fo.GetPos() == section_map_offset);
COFF_IMAGE_SECTION_HEADER meta;
Zero(meta);
strncpy((char *)meta.Name, metadata_secn, 8);
meta.PointerToRawData = sec1_offset;
meta.SizeOfRawData = metadata.GetCount();
meta.PointerToRelocations = (relocations.IsEmpty() ? 0 : reloc_offset);
meta.NumberOfRelocations = int(relocations.GetCount() / sizeof(COFF_IMAGE_RELOCATION));
meta.Characteristics = COFF_IMAGE_SCN_CNT_INITIALIZED_DATA
| COFF_IMAGE_SCN_ALIGN_4BYTES
| COFF_IMAGE_SCN_MEM_READ | COFF_IMAGE_SCN_MEM_WRITE;
fo.Put(&meta, sizeof(COFF_IMAGE_SECTION_HEADER));
COFF_IMAGE_SECTION_HEADER data;
Zero(data);
strncpy((char *)data.Name, data_secn, 8);
data.PointerToRawData = sec2_offset;
data.SizeOfRawData = data_size;
data.Characteristics = COFF_IMAGE_SCN_CNT_INITIALIZED_DATA
| COFF_IMAGE_SCN_ALIGN_4BYTES
| COFF_IMAGE_SCN_MEM_READ | COFF_IMAGE_SCN_MEM_WRITE;
fo.Put(&data, sizeof(COFF_IMAGE_SECTION_HEADER));
}
{ // symbol table
ASSERT(fo.GetPos() == symtbl_offset);
fo.Put(symbol_table.Begin(), symbol_table.GetCount());
ASSERT(fo.GetPos() == strtbl_offset);
fo.Put(symbol_table_strtbl.Begin(), symbol_table_strtbl.GetCount());
}
{ // relocations
ASSERT(fo.GetPos() == reloc_offset);
fo.Put(relocations.Begin(), relocations.GetCount());
}
{ // metadata section
ASSERT(fo.GetPos() == sec1_offset);
fo.Put(metadata.Begin(), metadata.GetCount());
}
fo.Close();
if(fo.IsError())
throw Exc(NFormat("error writing file '%s'", objectfile));
}
int BinObj::AddMetaData(int value, int reloc_symbol)
{
int len = metadata.GetCount();
metadata.SetCountR(len + 4);
Poke32le(metadata.GetIter(len), value);
if(reloc_symbol >= 0) {
COFF_IMAGE_RELOCATION reloc;
reloc.VirtualAddress = len;
reloc.SymbolTableIndex = reloc_symbol;
reloc.Type = COFF_IMAGE_REL_I386_DIR32;
int off = relocations.GetCount();
relocations.InsertN(off, sizeof(COFF_IMAGE_RELOCATION));
memcpy(relocations.GetIter(off), &reloc, sizeof(COFF_IMAGE_RELOCATION));
}
return len;
}
int BinObj::AddSymbol(COFF_IMAGE_SYMBOL& sym, const char *name)
{
sym.N.Name.Short = sym.N.Name.Long = 0;
int nl = (int)strlen(name);
if(nl <= 8)
memcpy(sym.N.ShortName, name, nl);
else {
int symlen = symbol_table_strtbl.GetCount();
sym.N.Name.Long = symlen;
symbol_table_strtbl.InsertN(symlen, nl + 1);
memcpy(symbol_table_strtbl.GetIter(symlen), name, nl + 1);
}
int len = symbol_table.GetCount();
symbol_table.InsertN(len, COFF_IMAGE_SIZEOF_SYMBOL * (1 + sym.NumberOfAuxSymbols));
memcpy(symbol_table.GetIter(len), &sym, COFF_IMAGE_SIZEOF_SYMBOL);
return (sym.NumberOfAuxSymbols ? len + COFF_IMAGE_SIZEOF_SYMBOL : -1);
}
void BinaryToObject(String objectfile, CParser& binscript, String base_dir, Callback1<String> WhenConsole)
{
BinObj(WhenConsole).Run(objectfile, binscript, base_dir);
}
#ifdef flagMAIN
#define BINOBJ_VERSION "1.0.r1"
#define BINOBJ_DATE Date(2004, 10, 2)
#define BINOBJ_COPYRIGHT "Copyright (c) 2004 Tomas Rylek"
static String Usage()
{
String out;
out <<
"BINOBJ: binary to object converter\n"
"Version: " BINOBJ_VERSION "\n"
"Release date: " << BINOBJ_DATE << "\n"
BINOBJ_COPYRIGHT "\n"
"\n"
"Usage: binobj <definition file> [<output object file>]\n"
"\n"
"Binary definition file syntax:\n"
"BINARY(ident, \"abc.def\") // expose file abc.def as identifier ident\n"
"BINARY_ARRAY(array, 0, \"abc.def\") // expose file abc.def as array[0]\n"
"\n"
"Interfacing declarations:\n"
"\n"
"extern const unsigned char ident[];\n"
"extern const int ident_length;\n"
"extern const unsigned char *array[];\n"
"extern const int array_length[];\n"
"extern const int array_count;\n"
;
return out;
}
static void TryMain()
{
const Vector<String>& cmdline = CommandLine();
String binfile, outfile;
for(int i = 0; i < cmdline.GetCount(); i++) {
String arg = cmdline[i];
if(IsNull(binfile))
binfile = AppendExt(arg, ".bin");
else if(IsNull(outfile))
outfile = AppendExt(arg, ".obj");
else
throw Exc(NFormat("no use for argument '%s'", arg));
}
if(IsNull(binfile)) {
puts(Usage());
SetExitCode(-1);
return;
}
if(IsNull(outfile))
outfile = ForceExt(binfile, ".obj");
String bindata = LoadFile(binfile);
if(bindata.IsVoid())
throw Exc(NFormat("error reading file '%s'", binfile));
CParser parser(bindata, binfile);
BinaryToObject(outfile, parser, GetFileDirectory(binfile));
}
CONSOLE_APP_MAIN
{
try {
TryMain();
}
catch(Exc e) {
puts("BINOBJ: " + e);
}
}
#endif
END_UPP_NAMESPACE