#include "binobj.h" #include #include 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& 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 files; Vector 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 WhenConsole); void Run(String objectfile, CParser& binscript, String base_dir); public: Callback1 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 metadata; // int data_size; Vector relocations; Vector symbol_table; Vector 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 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& belem = blocks[i]; int flags = belem[0].flags; if(flags & (Block::FLG_ARRAY | Block::FLG_MASK)) { int count = Max(belem.GetKeys()) + 1; Vector 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 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& 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 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 []\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& 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