mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 06:05:58 -06:00
505 lines
16 KiB
C++
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
|