mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 22:02:49 -06:00
341 lines
10 KiB
C++
341 lines
10 KiB
C++
#include "coff.h"
|
|
|
|
NAMESPACE_UPP
|
|
|
|
#ifdef PLATFORM_WIN32
|
|
|
|
enum { HEADER_SIZE = sizeof(COFF_IMAGE_ARCHIVE_MEMBER_HEADER) };
|
|
|
|
static double ToLibraryTime(Time time)
|
|
{
|
|
if(IsNull(time))
|
|
return Null;
|
|
SYSTEMTIME tm, tb;
|
|
Zero(tm); Zero(tb);
|
|
tm.wYear = time.year; tb.wYear = 1970;
|
|
tm.wMonth = time.month; tb.wMonth = 1;
|
|
tm.wDay = time.day; tb.wDay = 1;
|
|
tm.wHour = time.hour; tb.wHour = 2;
|
|
tm.wMinute = time.minute;
|
|
tm.wSecond = time.second;
|
|
FileTime /*ftl,*/ ftg;
|
|
SystemTimeToFileTime(&tm, &ftg);
|
|
// LocalFileTimeToFileTime(&ftl, &ftg);
|
|
FileTime fbl, fbg;
|
|
SystemTimeToFileTime(&tb, &fbl);
|
|
LocalFileTimeToFileTime(&fbl, &fbg);
|
|
#ifdef PLATFORM_WIN32
|
|
int64 tg = ftg.dwLowDateTime + (int64(1) << 32) * ftg.dwHighDateTime;
|
|
int64 bg = fbg.dwLowDateTime + (int64(1) << 32) * fbg.dwHighDateTime;
|
|
return (double)((tg - bg) / 10000000);
|
|
#else
|
|
return tg - bg;
|
|
#endif
|
|
}
|
|
|
|
ArchiveJob::Object::Object(ArchiveJob& archive, int index, String fn, String od, double ft, int ho, bool nf)
|
|
: archive(archive), index(index), filename(fn), object_data(od), filetime(ft), header_offset(ho), newfile(nf)
|
|
{
|
|
trimmed_name = archive.TrimObjectName(filename);
|
|
longname_offset = -1;
|
|
header_offset = -1;
|
|
}
|
|
|
|
void ArchiveJob::Object::ReadObject()
|
|
{
|
|
if(archive.verbose)
|
|
PutStdOut(NFormat("%s: reading object file (%d B)", filename, object_data.GetLength()));
|
|
const byte *begin = object_data;
|
|
|
|
const COFF_IMAGE_FILE_HEADER *header = (const COFF_IMAGE_FILE_HEADER *)begin;
|
|
const COFF_IMAGE_SYMBOL *symtbl = (const COFF_IMAGE_SYMBOL *)(begin + header->PointerToSymbolTable), *symptr = symtbl;
|
|
const char *strtbl = (const char *)(symtbl + header->NumberOfSymbols);
|
|
for(int i = 0; i < (int)header->NumberOfSymbols; i++, symptr++)
|
|
{
|
|
if(symptr->StorageClass == COFF_IMAGE_SYM_CLASS_STATIC && symptr->SectionNumber != 0
|
|
&& symptr->Value == 0 && symptr->NumberOfAuxSymbols >= 1)
|
|
; // ignore section symbols
|
|
else if((symptr->StorageClass == COFF_IMAGE_SYM_CLASS_EXTERNAL || symptr->StorageClass == COFF_IMAGE_SYM_CLASS_STATIC)
|
|
&& (symptr->SectionNumber != 0 || symptr->Value != 0))
|
|
archive.AddGlobal(index, COFFSymbolName(*symptr, strtbl));
|
|
i += symptr->NumberOfAuxSymbols;
|
|
symptr += symptr->NumberOfAuxSymbols;
|
|
}
|
|
}
|
|
|
|
ArchiveJob::ArchiveJob()
|
|
{
|
|
keep_object_paths = false;
|
|
use_existing_archive = true;
|
|
trim_longname_objects = false;
|
|
skip_duplicates = true;
|
|
verbose = false;
|
|
#if defined(CPU_IA32)
|
|
machine = COFF_IMAGE_FILE_MACHINE_I386;
|
|
#else
|
|
machine = COFF_IMAGE_FILE_MACHINE_UNKNOWN;
|
|
#endif
|
|
}
|
|
|
|
void ArchiveJob::LoadFile(String file)
|
|
{
|
|
FileMapping mapping;
|
|
if(!mapping.Open(file))
|
|
throw Exc(NFormat("%s: file failed to open", file));
|
|
objects[AddObject(file, mapping.GetData(0, (dword)mapping.GetFileSize()), mapping.GetTime())].ReadObject();
|
|
}
|
|
|
|
int ArchiveJob::AddObject(String filename, String object_data, Time filetime)
|
|
{
|
|
return objects.Add(new Object(*this, objects.GetCount(), filename, object_data,
|
|
ToLibraryTime(filetime), -1, true)).index;
|
|
}
|
|
|
|
void ArchiveJob::LoadLibrary(String libfile)
|
|
{
|
|
FileMapping mapping;
|
|
if(!mapping.Open(libfile))
|
|
{
|
|
if(archive_must_exist)
|
|
throw Exc(NFormat("%s: archive file failed to open", libfile));
|
|
if(use_existing_archive && verbose)
|
|
PutStdOut(NFormat("%s: archive not found", libfile));
|
|
return;
|
|
}
|
|
if(memcmp(~mapping, "!<arch>\n", 8))
|
|
throw Exc(NFormat("%s: not a valid library file (missing !<arch>\\n header)", libfile));
|
|
if(verbose)
|
|
PutStdOut(NFormat("%s: reading archive (%d B)", libfile, mapping.GetFileSize()));
|
|
|
|
int objcount = -1;
|
|
const byte *ptr = mapping.GetIter(8);
|
|
const byte *end = mapping.End();
|
|
const byte *longptr = NULL;
|
|
while(ptr + HEADER_SIZE <= end)
|
|
{
|
|
// if(*ptr == '\n')
|
|
// ptr++;
|
|
int offset = int(ptr - mapping.Begin());
|
|
COFF_IMAGE_ARCHIVE_MEMBER_HEADER hdr;
|
|
memcpy(&hdr, ptr, sizeof(hdr));
|
|
ptr += sizeof(hdr);
|
|
int size = atoi((const char *)hdr.Size);
|
|
const byte *brk = ptr + ((size + 1) & -2);
|
|
if(hdr.Name[0] == '/' && hdr.Name[1] == '/')
|
|
{ // longnames
|
|
longptr = ptr;
|
|
}
|
|
else if(hdr.Name[0] != '/' || hdr.Name[1] != ' ')
|
|
{ // object
|
|
const byte *nameptr, *namelim;
|
|
if(hdr.Name[0] != '/')
|
|
{
|
|
nameptr = hdr.Name;
|
|
namelim = nameptr + __countof(hdr.Name);
|
|
}
|
|
else
|
|
{
|
|
int loff = atoi((const char *)hdr.Name + 1);
|
|
nameptr = longptr + loff;
|
|
namelim = end;
|
|
}
|
|
const byte *p = nameptr;
|
|
while(p < namelim && *p && *p != '/')
|
|
p++;
|
|
String objname(nameptr, int(p - nameptr));
|
|
String objdata(ptr, size);
|
|
char temp[13];
|
|
memcpy(temp, hdr.Date, 12);
|
|
temp[12] = 0;
|
|
double membertime = ScanDouble(temp);
|
|
const COFF_IMPORT_OBJECT_HEADER *imphdr = (const COFF_IMPORT_OBJECT_HEADER *)ptr;
|
|
int oindex = objects.GetCount();
|
|
if(skip_duplicates && FindObject(objname) >= 0)
|
|
{
|
|
if(verbose)
|
|
PutStdOut(NFormat("%s: skipping object '%s' (inserting object with the same name)", libfile, objname));
|
|
}
|
|
else
|
|
{
|
|
if(imphdr->Sig1 == 0 && imphdr->Sig2 == 0xFFFF)
|
|
{
|
|
if(imphdr->Machine != machine)
|
|
throw Exc(NFormat("%s:%s: invalid machine type (%d) in import library module",
|
|
libfile, objname, imphdr->Machine));
|
|
const char *pname = (const char *)ptr + sizeof(COFF_IMPORT_OBJECT_HEADER);
|
|
AddGlobal(oindex, pname);
|
|
objects.Add(new Object(*this, oindex, objname, objdata, membertime, offset, false));
|
|
}
|
|
else
|
|
objects.Add(new Object(*this, oindex, objname, objdata, membertime, offset, false)).ReadObject();
|
|
}
|
|
}
|
|
ptr = brk;
|
|
}
|
|
}
|
|
|
|
String ArchiveJob::TrimObjectName(String objname) const
|
|
{
|
|
String out = objname;
|
|
if(!keep_object_paths)
|
|
out = GetFileName(out);
|
|
out = ToLower(out);
|
|
if(trim_longname_objects && out.GetLength() > 15)
|
|
{
|
|
for(int f; out.GetLength() > 15 && ((f = out.Find('/')) >= 0 || (f = out.Find('\\')) >= 0); out.Remove(0, f + 1))
|
|
;
|
|
if(out.GetLength() > 15)
|
|
out.Trim(15);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
int ArchiveJob::FindObject(String objname) const
|
|
{
|
|
return FindFieldIndex(objects, &Object::trimmed_name, TrimObjectName(objname));
|
|
}
|
|
|
|
void ArchiveJob::BuildLibrary(String libfile, bool keep_archive_backups)
|
|
{
|
|
SortGlobals();
|
|
BuildSymbolIndex();
|
|
BuildLongNameIndex();
|
|
CalcArchiveOffsets();
|
|
WriteArchive(libfile, keep_archive_backups);
|
|
}
|
|
|
|
void ArchiveJob::AddGlobal(int object, String symbol)
|
|
{
|
|
if(symbol_names.FindAdd(symbol) >= symbol_objects.GetCount())
|
|
symbol_objects.Add(object);
|
|
}
|
|
|
|
void ArchiveJob::SortGlobals()
|
|
{
|
|
Vector<String> symn = symbol_names.PickKeys();
|
|
IndexSort(symn, symbol_objects, StdLess<String>());
|
|
symbol_names = symn;
|
|
}
|
|
|
|
void ArchiveJob::BuildLongNameIndex()
|
|
{
|
|
for(int i = 0; i < objects.GetCount(); i++)
|
|
{
|
|
String n = objects[i].trimmed_name;
|
|
if(n.GetLength() >= 16)
|
|
{
|
|
objects[i].longname_offset = longnames.GetLength();
|
|
longnames.Cat(n, n.GetLength() + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ArchiveJob::BuildSymbolIndex()
|
|
{
|
|
for(int i = 0; i < symbol_names.GetCount(); i++)
|
|
symbolnames.Cat(symbol_names[i], symbol_names[i].GetLength() + 1);
|
|
}
|
|
|
|
void ArchiveJob::CalcArchiveOffsets()
|
|
{
|
|
first_index_offset = 8; // header length
|
|
first_index_datasize = 4 + 4 * symbol_names.GetCount() + symbolnames.GetLength();
|
|
second_index_offset = first_index_offset + HEADER_SIZE + ((first_index_datasize + 1) & -2);
|
|
second_index_datasize = 4 + 4 * objects.GetCount() + 4 + 2 * symbol_names.GetCount() + symbolnames.GetLength();
|
|
longnames_offset = second_index_offset + HEADER_SIZE + ((second_index_datasize + 1) & -2);
|
|
archive_length = longnames_offset + HEADER_SIZE + ((longnames.GetLength() + 1) & -2);
|
|
for(int i = 0; i < objects.GetCount(); i++)
|
|
{
|
|
objects[i].header_offset = archive_length;
|
|
archive_length += HEADER_SIZE + ((objects[i].object_data.GetLength() + 1) & -2);
|
|
}
|
|
}
|
|
|
|
static void WriteMemberHeader(byte *out, String name, int longname_offset, double time, int size)
|
|
{
|
|
memset(out, ' ', sizeof(COFF_IMAGE_ARCHIVE_MEMBER_HEADER));
|
|
if(longname_offset < 0)
|
|
{
|
|
memcpy(out, name, name.GetLength());
|
|
out[name.GetLength()] = '/';
|
|
}
|
|
else
|
|
{
|
|
String tmp = FormatInt(longname_offset);
|
|
out[0] = '/';
|
|
memcpy(out + 1, tmp, tmp.GetLength());
|
|
}
|
|
String tmp = FormatDoubleFix(time, 0);
|
|
memcpy(out + 16, tmp, min(tmp.GetLength(), 12));
|
|
out[28] = '0';
|
|
out[34] = '0';
|
|
memcpy(out + 40, "100666", 6);
|
|
tmp = FormatInt(size);
|
|
memcpy(out + 48, tmp, tmp.GetLength());
|
|
out[58] = COFF_IMAGE_ARCHIVE_END1;
|
|
out[59] = COFF_IMAGE_ARCHIVE_END2;
|
|
if(size & 1)
|
|
out[HEADER_SIZE + size] = COFF_IMAGE_ARCHIVE_PAD;
|
|
}
|
|
|
|
void ArchiveJob::WriteArchive(String libraryfile, bool keep_archive_backups)
|
|
{
|
|
Vector<byte> archive;
|
|
archive.SetCount(archive_length);
|
|
memcpy(archive.Begin(), "!<arch>\n", 8);
|
|
double nulltime = ToLibraryTime(GetSysTime());
|
|
WriteMemberHeader(archive.GetIter(first_index_offset), "", -1, nulltime, first_index_datasize);
|
|
WriteMemberHeader(archive.GetIter(second_index_offset), "", -1, nulltime, second_index_datasize);
|
|
WriteIndex(archive.GetIter(first_index_offset + HEADER_SIZE), archive.GetIter(second_index_offset + HEADER_SIZE));
|
|
WriteMemberHeader(archive.GetIter(longnames_offset), "/", -1, nulltime, longnames.GetLength());
|
|
memcpy(archive.GetIter(longnames_offset + HEADER_SIZE), longnames, longnames.GetLength());
|
|
for(int i = 0; i < objects.GetCount(); i++)
|
|
{
|
|
const Object& o = objects[i];
|
|
WriteMemberHeader(archive.GetIter(o.header_offset), o.trimmed_name, o.longname_offset, o.filetime, o.object_data.GetLength());
|
|
memcpy(archive.GetIter(o.header_offset + HEADER_SIZE), o.object_data, o.object_data.GetLength());
|
|
}
|
|
String tmpfile = libraryfile + ".tmp";
|
|
String oldfile = libraryfile + ".old";
|
|
FileOut fo;
|
|
if(!fo.Open(tmpfile))
|
|
throw Exc(NFormat("%s: failed to create file", tmpfile));
|
|
if(verbose)
|
|
PutStdOut(NFormat("%s: saving updated archive (%d B)", libraryfile, archive_length));
|
|
fo.Put(archive.Begin(), archive_length);
|
|
fo.Close();
|
|
if(fo.IsError())
|
|
{
|
|
FileDelete(tmpfile);
|
|
throw Exc(NFormat("%s: error writing file", tmpfile));
|
|
}
|
|
FileDelete(oldfile);
|
|
FileMove(libraryfile, oldfile);
|
|
if(!FileMove(tmpfile, libraryfile))
|
|
throw Exc(NFormat("%s: error replacing %s", tmpfile, libraryfile));
|
|
if(!keep_archive_backups)
|
|
FileDelete(oldfile);
|
|
}
|
|
|
|
void ArchiveJob::WriteIndex(byte *first, byte *second)
|
|
{
|
|
int nsym = symbol_names.GetCount();
|
|
Poke32be(first, nsym);
|
|
first += 4;
|
|
for(int i = 0; i < nsym; i++, first += 4)
|
|
Poke32be(first, objects[symbol_objects[i]].header_offset);
|
|
memcpy(first, symbolnames, symbolnames.GetLength());
|
|
Poke32le(second, objects.GetCount());
|
|
second += 4;
|
|
for(int i = 0; i < objects.GetCount(); i++, second += 4)
|
|
Poke32le(second, objects[i].header_offset);
|
|
Poke32le(second, nsym);
|
|
second += 4;
|
|
for(int i = 0; i < symbol_names.GetCount(); i++, second += 2)
|
|
Poke16le(second, symbol_objects[i] + 1);
|
|
memcpy(second, symbolnames, symbolnames.GetLength());
|
|
}
|
|
|
|
#endif
|
|
|
|
END_UPP_NAMESPACE
|