forked from KolibriOS/kolibrios
876 lines
41 KiB
C++
876 lines
41 KiB
C++
|
/**************************** omf2cof.cpp *********************************
|
||
|
* Author: Agner Fog
|
||
|
* Date created: 2007-02-08
|
||
|
* Last modified: 2018-08-15
|
||
|
* Project: objconv
|
||
|
* Module: omf2cof.cpp
|
||
|
* Description:
|
||
|
* Module for converting OMF file to PE/COFF file
|
||
|
*
|
||
|
* Copyright 2007-2018 GNU General Public License http://www.gnu.org/licenses
|
||
|
*****************************************************************************/
|
||
|
#include "stdafx.h"
|
||
|
|
||
|
// Alignment value translation table
|
||
|
static const uint32 OMFAlignTranslate[8] = {0,1,2,16,256,4,0,0};
|
||
|
|
||
|
COMF2COF::COMF2COF() {
|
||
|
// Constructor
|
||
|
memset(this, 0, sizeof(*this)); // Reset everything
|
||
|
}
|
||
|
|
||
|
void COMF2COF::Convert() {
|
||
|
// Do the conversion
|
||
|
// Allocate variable size buffers
|
||
|
//NewSectIndex.SetNum(this->NSections);// Allocate section translation table
|
||
|
//NewSectIndex.SetZero(); // Initialize
|
||
|
|
||
|
// Call the subfunctions
|
||
|
ToFile.SetFileType(FILETYPE_COFF); // Set type of to file
|
||
|
MakeFileHeader(); // Make file header
|
||
|
MakeSymbolTable1(); // Make symbol table and string table entries for file and segments
|
||
|
MakeSymbolTable2(); // Make symbol table and string table entries for external symbols
|
||
|
MakeSymbolTable3(); // Make symbol table and string table entries for public symbols
|
||
|
MakeSymbolTable4(); // Make symbol table and string table entries for communal symbols
|
||
|
MakeSymbolTable5(); // Make symbol table and string table entries for local symbols
|
||
|
MakeSections(); // Make sections and relocation tables
|
||
|
CheckUnsupportedRecords(); // Make warnings if file containes unsupported record types
|
||
|
MakeBinaryFile(); // Put sections together
|
||
|
*this << ToFile; // Take over new file buffer
|
||
|
}
|
||
|
|
||
|
|
||
|
void COMF2COF::MakeFileHeader() {
|
||
|
// Convert subfunction: File header
|
||
|
// Make PE file header
|
||
|
NewFileHeader.Machine = PE_MACHINE_I386;
|
||
|
NewFileHeader.TimeDateStamp = (uint32)time(0);
|
||
|
NewFileHeader.SizeOfOptionalHeader = 0;
|
||
|
NewFileHeader.Flags = 0;
|
||
|
|
||
|
// Values inserted later:
|
||
|
NewFileHeader.NumberOfSections = 0;
|
||
|
NewFileHeader.PSymbolTable = 0;
|
||
|
NewFileHeader.NumberOfSymbols = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
void COMF2COF::MakeSymbolTable1() {
|
||
|
// Make symbol table string table and section table entries for file and segments
|
||
|
SCOFF_SymTableEntry sym; // Symbol table entry
|
||
|
SCOFF_SectionHeader sec; // Section header entry
|
||
|
char * ClassName; // Old segment class name
|
||
|
|
||
|
// Initialize new string table. make space for 4-bytes size
|
||
|
NewStringTable.Push(0, 4);
|
||
|
|
||
|
// Allocate SegmentTranslation buffer
|
||
|
SegmentTranslation.SetNum(SegmentNameOffset.GetNumEntries());
|
||
|
|
||
|
// Make symbol table entry for file name
|
||
|
memset(&sym, 0, SIZE_SCOFF_SymTableEntry);
|
||
|
strcpy(sym.s.Name, ".file");
|
||
|
sym.s.SectionNumber = COFF_SECTION_DEBUG;
|
||
|
sym.s.StorageClass = COFF_CLASS_FILE;
|
||
|
char * ShortFileName = CLibrary::ShortenMemberName(OutputFileName);
|
||
|
sym.s.NumAuxSymbols = 1; // File name is truncated so it will fit into the 18 bytes of SIZE_SCOFF_SymTableEntry
|
||
|
NewSymbolTable.Push(sym); // Store symbol table entry
|
||
|
// Needs auxiliary entry:
|
||
|
memset(&sym, 0, SIZE_SCOFF_SymTableEntry);
|
||
|
if (strlen(ShortFileName) < SIZE_SCOFF_SymTableEntry) {
|
||
|
strcpy(sym.s.Name, ShortFileName);
|
||
|
}
|
||
|
NewSymbolTable.Push(sym); // Store auxiliary symbol table entry
|
||
|
|
||
|
// Define structure of attributes
|
||
|
OMF_SAttrib Attributes;
|
||
|
// Other segment properties
|
||
|
uint32 SegLength, NameIndex, ClassIndex;
|
||
|
//uint32 Offset;
|
||
|
const char * sname; // Segment/section name
|
||
|
uint32 SegNum = 0; // Segment/section number
|
||
|
uint32 StringI; // New sting table index
|
||
|
uint32 i; // Record number
|
||
|
int32 j, n; // Temporary
|
||
|
|
||
|
// Loop through segments of old file
|
||
|
for (i = 0; i < NumRecords; i++) {
|
||
|
if (Records[i].Type2 == OMF_SEGDEF) {
|
||
|
// SEGDEF record
|
||
|
Records[i].Index = 3;
|
||
|
// Loop through entries in record. There should be only 1
|
||
|
while (Records[i].Index < Records[i].End) {
|
||
|
Attributes.b = Records[i].GetByte(); // Read attributes
|
||
|
if (Attributes.u.A == 0) {
|
||
|
// Frame and Offset only included if A = 0
|
||
|
Records[i].GetWord(); // Frame ignored
|
||
|
Records[i].GetByte();
|
||
|
}
|
||
|
//else Offset = 0;
|
||
|
SegLength = Records[i].GetNumeric();
|
||
|
NameIndex = Records[i].GetIndex();
|
||
|
ClassIndex = Records[i].GetIndex(); // Class index
|
||
|
Records[i].GetIndex(); // Overlay index ignored
|
||
|
sname = GetLocalName(NameIndex); // Segment name = new section name
|
||
|
|
||
|
if (Attributes.u.B) {
|
||
|
// Segment is big
|
||
|
if (Attributes.u.P) {
|
||
|
// 32 bit segment. Big means 2^32 bytes!
|
||
|
err.submit(2306);
|
||
|
}
|
||
|
else {
|
||
|
// 16 bit segment. Big means 2^16 bytes
|
||
|
SegLength = 0x10000;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// make symbol table entry
|
||
|
memset(&sym, 0, SIZE_SCOFF_SymTableEntry);
|
||
|
|
||
|
// Put name into string table
|
||
|
StringI = NewStringTable.PushString(sname);
|
||
|
|
||
|
// Put name into symbol table
|
||
|
//COFF_PutNameInSymbolTable(sym, sname, NewStringTable);
|
||
|
((uint32*)(sym.s.Name))[1] = StringI;
|
||
|
|
||
|
sym.s.SectionNumber = ++SegNum; // Count section number
|
||
|
sym.s.StorageClass = COFF_CLASS_STATIC;
|
||
|
sym.s.NumAuxSymbols = 1; // Needs 1 aux record
|
||
|
|
||
|
// Remember NewSymbolTable index
|
||
|
SegmentTranslation[SegNum] = NewSymbolTable.GetNumEntries();
|
||
|
NewSymbolTable.Push(sym); // Store symbol table entry
|
||
|
|
||
|
// Make auxiliary entry
|
||
|
memset(&sym, 0, SIZE_SCOFF_SymTableEntry);
|
||
|
|
||
|
// Insert section size
|
||
|
sym.section.Length = SegLength;
|
||
|
|
||
|
// Remember to insert NumberOfRelocations here later
|
||
|
|
||
|
// Store auxiliary symbol table entry
|
||
|
NewSymbolTable.Push(sym);
|
||
|
|
||
|
// Make section header
|
||
|
memset(&sec, 0, sizeof(sec)); // Reset section header
|
||
|
|
||
|
// Put name into section header
|
||
|
sprintf(sec.Name, "/%i", StringI);
|
||
|
|
||
|
// Put size into section header
|
||
|
sec.SizeOfRawData = SegLength;
|
||
|
|
||
|
// Alignment
|
||
|
switch (Attributes.u.A) {
|
||
|
case 0: // Absolute segment
|
||
|
err.submit(2307); break;
|
||
|
case 1: // Byte
|
||
|
sec.Flags |= PE_SCN_ALIGN_1; break;
|
||
|
case 2: // Word
|
||
|
sec.Flags |= PE_SCN_ALIGN_2; break;
|
||
|
case 3: // Paragraph
|
||
|
sec.Flags |= PE_SCN_ALIGN_16; break;
|
||
|
case 4: // Page. May be 256 or 4096, depending on system
|
||
|
// If we use 4096 where the source intended 256, we may get
|
||
|
// size and symbol offsets wrong!
|
||
|
sec.Flags |= PE_SCN_ALIGN_256; break;
|
||
|
case 5: // Dword
|
||
|
sec.Flags |= PE_SCN_ALIGN_4; break;
|
||
|
default: // Unknown alignment
|
||
|
err.submit(2308, Attributes.u.A);
|
||
|
sec.Flags |= PE_SCN_ALIGN_16; break;
|
||
|
}
|
||
|
|
||
|
// Get further attributes from class name
|
||
|
ClassName = GetLocalName(ClassIndex);
|
||
|
|
||
|
// Convert class name to upper case
|
||
|
n = (int32)strlen(ClassName);
|
||
|
for (j = 0; j < n; j++) ClassName[j] &= ~0x20;
|
||
|
|
||
|
// Search for known class names.
|
||
|
// Standard names are CODE, DATA, BSS, CONST, STACK
|
||
|
if (strstr(ClassName, "CODE") || strstr(ClassName, "TEXT")) {
|
||
|
// Code segment
|
||
|
sec.Flags |= PE_SCN_CNT_CODE | PE_SCN_MEM_EXECUTE | PE_SCN_MEM_READ;
|
||
|
}
|
||
|
else if (strstr(ClassName, "DATA")) {
|
||
|
// Data segment
|
||
|
sec.Flags |= PE_SCN_CNT_INIT_DATA | PE_SCN_MEM_READ | PE_SCN_MEM_WRITE;
|
||
|
}
|
||
|
else if (strstr(ClassName, "BSS")) {
|
||
|
// Unitialized data segment
|
||
|
sec.Flags |= PE_SCN_CNT_UNINIT_DATA | PE_SCN_MEM_READ | PE_SCN_MEM_WRITE;
|
||
|
}
|
||
|
else if (strstr(ClassName, "CONST")) {
|
||
|
// Constant data segment
|
||
|
sec.Flags |= PE_SCN_CNT_INIT_DATA | PE_SCN_MEM_READ;
|
||
|
}
|
||
|
else if (strstr(ClassName, "STACK")) {
|
||
|
// Stack segment. Ignore
|
||
|
sec.Flags |= PE_SCN_LNK_REMOVE;
|
||
|
err.submit(1206); // Warning: ignored
|
||
|
}
|
||
|
else {
|
||
|
// Unknown/user defined class. Assume data segment
|
||
|
sec.Flags |= PE_SCN_CNT_INIT_DATA | PE_SCN_MEM_READ | PE_SCN_MEM_WRITE;
|
||
|
}
|
||
|
|
||
|
// Insert pointers to relocations and raw data later
|
||
|
// Store section header
|
||
|
NewSectionHeaders.Push(sec);
|
||
|
}
|
||
|
if (Records[i].Index != Records[i].End) err.submit(1203); // Check for consistency
|
||
|
}
|
||
|
if (Records[i].Type2 == OMF_COMDAT || Records[i].Type2 == OMF_COMDEF) {
|
||
|
// Communal sections
|
||
|
err.submit(1055);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void COMF2COF::MakeSymbolTable2() {
|
||
|
// Make symbol table and string table entries for external symbols
|
||
|
uint32 i;
|
||
|
SCOFF_SymTableEntry sym; // new symbol table entry
|
||
|
uint32 NumExtSym = SymbolNameOffset.GetNumEntries(); // Number of external symbols
|
||
|
ExtdefTranslation.SetNum(NumExtSym+1); // Allocate space in translation table
|
||
|
|
||
|
// Loop through external symbol names
|
||
|
for (i = 1; i < NumExtSym; i++) {
|
||
|
// Reset symbol table entry
|
||
|
memset(&sym, 0, SIZE_SCOFF_SymTableEntry);
|
||
|
|
||
|
// Insert name
|
||
|
COFF_PutNameInSymbolTable(sym, GetSymbolName(i), NewStringTable);
|
||
|
|
||
|
// Insert storage class
|
||
|
sym.s.StorageClass = COFF_CLASS_EXTERNAL;
|
||
|
|
||
|
// Store symbol table entry
|
||
|
NewSymbolTable.Push(sym);
|
||
|
|
||
|
// Update table for translating old EXTDEF number (1-based) to new symbol table index (0-based)
|
||
|
ExtdefTranslation[i] = NewSymbolTable.GetNumEntries() - 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void COMF2COF::MakeSymbolTable3() {
|
||
|
// Make symbol table and string table entries for public symbols
|
||
|
SCOFF_SymTableEntry sym; // new symbol table entry
|
||
|
uint32 i; // Record index
|
||
|
char * string; // Symbol name
|
||
|
uint32 Segment; // Segment
|
||
|
uint32 Offset; // Offset
|
||
|
uint32 Namei; // Index into symbol table
|
||
|
SOMFLocalSymbol localsym; // Entry into LocalSymbols
|
||
|
|
||
|
// Search for PUBDEF records
|
||
|
for (i = 0; i < NumRecords; i++) {
|
||
|
if (Records[i].Type2 == OMF_PUBDEF) {
|
||
|
// PUBDEF record
|
||
|
|
||
|
Records[i].Index = 3;
|
||
|
Records[i].GetIndex(); // Group. Ignore
|
||
|
Segment = Records[i].GetIndex(); // Segment
|
||
|
if (Segment == 0) Records[i].GetWord(); // Base frame. Ignore
|
||
|
|
||
|
// Loop through strings in record
|
||
|
while (Records[i].Index < Records[i].End) {
|
||
|
string = Records[i].GetString(); // Symbol name
|
||
|
Offset = Records[i].GetNumeric(); // Offset to segment
|
||
|
Records[i].GetIndex(); // Type index. Ignore
|
||
|
|
||
|
// Reset symbol table entry
|
||
|
memset(&sym, 0, SIZE_SCOFF_SymTableEntry);
|
||
|
|
||
|
// Insert name
|
||
|
Namei = COFF_PutNameInSymbolTable(sym, string, NewStringTable);
|
||
|
|
||
|
// Insert storage class
|
||
|
sym.s.StorageClass = COFF_CLASS_EXTERNAL;
|
||
|
|
||
|
// Store offset
|
||
|
sym.s.Value = Offset;
|
||
|
|
||
|
// Section number = segment number
|
||
|
if (Segment == 0) sym.s.SectionNumber = COFF_SECTION_ABSOLUTE;
|
||
|
else sym.s.SectionNumber = (int16)Segment;
|
||
|
|
||
|
// Store symbol table entry
|
||
|
NewSymbolTable.Push(sym);
|
||
|
|
||
|
// Make entry into LocalSymbols to indicate that this symbol has a name
|
||
|
if (Segment > 0) {
|
||
|
// Make index into NewStringTable if we don't allready have one
|
||
|
if (Namei == 0) Namei = NewStringTable.PushString(string);
|
||
|
localsym.Offset = Offset;
|
||
|
localsym.Segment = Segment;
|
||
|
localsym.Name = Namei;
|
||
|
localsym.NewSymtabIndex = NewSymbolTable.GetNumEntries() - 1; // 0-based index into new symbol table
|
||
|
LocalSymbols.PushUnique(localsym);
|
||
|
}
|
||
|
}
|
||
|
if (Records[i].Index != Records[i].End) err.submit(1203); // Check for consistency
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void COMF2COF::MakeSymbolTable4() {
|
||
|
// Make symbol table and string table entries for communal symbols
|
||
|
// Warning: Currently not supported!
|
||
|
}
|
||
|
|
||
|
void COMF2COF::MakeSymbolTable5() {
|
||
|
// Make symbol table and string table entries for local symbols.
|
||
|
// There is no table for local symbols in OMF files. We have to search
|
||
|
// through all FIXUPP records for relocation targets and assign arbitrary
|
||
|
// names to them.
|
||
|
|
||
|
uint32 i; // Loop counter
|
||
|
uint32 Target, TargetDisplacement; // Contents of FIXUPP record
|
||
|
uint8 byte1, byte2; // First two bytes of FIXUPP subrecord
|
||
|
// Bitfields in subrecords
|
||
|
OMF_SLocat Locat; // Structure of first two bytes of FIXUP subrecord swapped = Locat field
|
||
|
OMF_SFixData FixData; // Structure of FixData field in FIXUP subrecord of FIXUPP record
|
||
|
OMF_STrdDat TrdDat; // Structure of Thread Data field in THREAD subrecord of FIXUPP record
|
||
|
int8 * LastDataRecordPointer = 0; // Pointer to data in the data record that relocations refer to
|
||
|
uint32 LastDataRecordSize = 0; // Size of the data record that relocations refer to
|
||
|
SOMFLocalSymbol localsym; // Entry into LocalSymbols
|
||
|
uint32 LocalSymNum = 0; // Number of unnamed local symbols
|
||
|
char NewName[32]; // Buffer for making new name
|
||
|
SCOFF_SymTableEntry sym; // New symbol table entry
|
||
|
|
||
|
// Search for FIXUPP records and data records
|
||
|
for (i = 0; i < NumRecords; i++) {
|
||
|
|
||
|
if (Records[i].Type2 == OMF_LEDATA) {
|
||
|
// LEDATA record. Remember pointer to binary data in order to read inline offset
|
||
|
Records[i].Index = 3; // Initialize record reading
|
||
|
Records[i].GetIndex(); // Read segment and offset
|
||
|
Records[i].GetNumeric();
|
||
|
LastDataRecordSize = Records[i].End - Records[i].Index; // Calculate size of data
|
||
|
LastDataRecordPointer = Records[i].buffer + Records[i].FileOffset + Records[i].Index;
|
||
|
}
|
||
|
|
||
|
if (Records[i].Type2 == OMF_FIXUPP) {
|
||
|
// FIXUPP record
|
||
|
Records[i].Index = 3; // Initialize record reading
|
||
|
|
||
|
// Loop through entries in record
|
||
|
while (Records[i].Index < Records[i].End) {
|
||
|
|
||
|
// Read first byte
|
||
|
byte1 = Records[i].GetByte();
|
||
|
|
||
|
if (byte1 & 0x80) {
|
||
|
// This is a FIXUP subrecord
|
||
|
|
||
|
Target = 0; TargetDisplacement = 0;
|
||
|
// read second byte
|
||
|
byte2 = Records[i].GetByte();
|
||
|
// swap bytes and put into byte12 bitfield
|
||
|
Locat.bytes[1] = byte1;
|
||
|
Locat.bytes[0] = byte2;
|
||
|
// Read FixData
|
||
|
FixData.b = Records[i].GetByte();
|
||
|
// Read conditional fields
|
||
|
if (FixData.s.F == 0) {
|
||
|
if (FixData.s.Frame < 4) {
|
||
|
Records[i].GetIndex(); // Frame. Ignore
|
||
|
}
|
||
|
}
|
||
|
if (FixData.s.T == 0) {
|
||
|
// Target specified
|
||
|
Target = Records[i].GetIndex();
|
||
|
//uint32 TargetMethod = FixData.s.Target + FixData.s.P * 4;
|
||
|
}
|
||
|
if (FixData.s.P == 0) {
|
||
|
TargetDisplacement = Records[i].GetNumeric();
|
||
|
}
|
||
|
// Get inline addend
|
||
|
if (LastDataRecordPointer && Locat.s.Offset < LastDataRecordSize) {
|
||
|
int8 * inlinep = LastDataRecordPointer + Locat.s.Offset;
|
||
|
if (Locat.s.Location == 9 || Locat.s.Location == 13) {
|
||
|
TargetDisplacement += *(int32*)inlinep;
|
||
|
}
|
||
|
}
|
||
|
if (FixData.s.T == 0 && (FixData.s.Target == 0 || FixData.s.Target == 1)) {
|
||
|
// Target is local symbol
|
||
|
|
||
|
// Make entry in LocalSymbols
|
||
|
localsym.Offset = TargetDisplacement; // Offset to segment
|
||
|
localsym.Segment = Target; // Target segment
|
||
|
localsym.Name = 0; // Has no name yet
|
||
|
localsym.NewSymtabIndex = 0; // Not in new symbol table yet
|
||
|
// Make entry in LocalSymbols.
|
||
|
// PushUnique will not make an entry if this target address already
|
||
|
// has an entry in LocalSymbols and possibly a public name
|
||
|
LocalSymbols.PushUnique(localsym);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// This is a THREAD subrecord. Read and ignore
|
||
|
TrdDat.b = byte1; // Put byte into bitfield
|
||
|
if (TrdDat.s.Method < 4) {
|
||
|
Records[i].GetIndex(); // has index field if method < 4 ?
|
||
|
}
|
||
|
}
|
||
|
} // Finished loop through subrecords
|
||
|
if (Records[i].Index != Records[i].End) err.submit(1203); // Check for consistency
|
||
|
}
|
||
|
} // Finished loop through records
|
||
|
|
||
|
// Now check LocalSymbols for unnamed symbols
|
||
|
for (i = 0; i < LocalSymbols.GetNumEntries(); i++) {
|
||
|
if (LocalSymbols[i].Name == 0) {
|
||
|
|
||
|
// Unnamed symbol. Give it a name
|
||
|
sprintf(NewName, "?NoName%02i", ++LocalSymNum);
|
||
|
|
||
|
// Make index in new symbol table
|
||
|
// Reset symbol table entry
|
||
|
memset(&sym, 0, SIZE_SCOFF_SymTableEntry);
|
||
|
|
||
|
// Insert name
|
||
|
LocalSymbols[i].Name = COFF_PutNameInSymbolTable(sym, NewName, NewStringTable);
|
||
|
// if (LocalSymbols[i].Name == 0) LocalSymbols[i].Name = NewStringTable.PushString(NewName);
|
||
|
|
||
|
// Store offset
|
||
|
sym.s.Value = LocalSymbols[i].Offset;
|
||
|
|
||
|
// Section number = segment number
|
||
|
sym.s.SectionNumber = LocalSymbols[i].Segment;
|
||
|
|
||
|
// Storage class
|
||
|
sym.s.StorageClass = COFF_CLASS_STATIC;
|
||
|
|
||
|
// Store symbol table entry
|
||
|
NewSymbolTable.Push(sym);
|
||
|
|
||
|
// Store index into new symbol table (0 - based)
|
||
|
LocalSymbols[i].NewSymtabIndex = NewSymbolTable.GetNumEntries() - 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void COMF2COF::MakeSections() {
|
||
|
// Make sections and relocation tables
|
||
|
uint32 SegNum; // Index into NewSectionHeaders = segment - 1
|
||
|
uint32 DesiredSegment; // Old segment number = new section number
|
||
|
uint32 RecNum; // Old record number
|
||
|
CMemoryBuffer TempBuf; // Temporary buffer for building raw data
|
||
|
CMemoryBuffer RelocationTable; // Temporary buffer for building new relocation table
|
||
|
SCOFF_Relocation rel; // New relocation table record
|
||
|
uint32 LastDataRecord = 0; // Index to the data record that relocations refer to
|
||
|
uint32 LastDataRecordSize = 0; // Size of the data record that relocations refer to
|
||
|
int8 * LastDataRecordPointer = 0; // Pointer to data in the data record that relocations refer to
|
||
|
uint32 Segment = 0; // Segment of last LEDATA, LIDATA or COMDEF record
|
||
|
uint32 Offset; // Offset of LEDATA or LIDATA record to segment
|
||
|
uint32 Size; // Size of data in LEDATA or LIDATA record
|
||
|
uint32 SegmentSize; // Total size of segment
|
||
|
uint32 LastOffset; // Offset after last LEDATA into segment
|
||
|
uint32 FileOffsetData; // File offset of first raw data and relocations in new file
|
||
|
uint32 FileOffset; // File offset of current raw data or relocations
|
||
|
|
||
|
// File offset of first data = size of file header and section headers
|
||
|
FileOffsetData = sizeof(SCOFF_FileHeader) + NewSectionHeaders.GetNumEntries() * sizeof(SCOFF_SectionHeader);
|
||
|
|
||
|
// Loop through segments
|
||
|
for (SegNum = 0; SegNum < NewSectionHeaders.GetNumEntries(); SegNum++) {
|
||
|
|
||
|
DesiredSegment = SegNum + 1; // Search for records referring to this segment
|
||
|
|
||
|
SegmentSize = NewSectionHeaders[SegNum].SizeOfRawData;
|
||
|
if (SegmentSize == 0) continue; // Empty segment
|
||
|
|
||
|
// Allocate temporary data buffer and reset it
|
||
|
TempBuf.SetSize(SegmentSize + 16);
|
||
|
int FillByte = 0; // Byte to fill memory with
|
||
|
if (NewSectionHeaders[SegNum].Flags & PE_SCN_CNT_CODE) {
|
||
|
// Code segment. Fill any unused bytes with NOP opcode = 0x90
|
||
|
FillByte = 0x90;
|
||
|
}
|
||
|
memset(TempBuf.Buf(), FillByte, SegmentSize + 16);// Reset to all 0 or NOP
|
||
|
|
||
|
// Reset relocation table buffer
|
||
|
RelocationTable.SetSize(0);
|
||
|
|
||
|
LastOffset = 0; LastDataRecordSize = 0;
|
||
|
|
||
|
// Search for LEDATA, LIDATA and FIXUPP records for this segment
|
||
|
for (RecNum = 0; RecNum < NumRecords; RecNum++) {
|
||
|
if (Records[RecNum].Type2 == OMF_LEDATA) {
|
||
|
|
||
|
// LEDATA record
|
||
|
Records[RecNum].Index = 3; // Initialize record reading
|
||
|
Segment = Records[RecNum].GetIndex();// Read segment number
|
||
|
|
||
|
if (Segment != DesiredSegment) continue; // Does not refer to this segment
|
||
|
|
||
|
Offset = Records[RecNum].GetNumeric();// Read offset
|
||
|
Size = Records[RecNum].End - Records[RecNum].Index; // Calculate size of data
|
||
|
LastDataRecord = RecNum; // Save for later FIXUPP that refers to this record
|
||
|
|
||
|
// Check if data within segment
|
||
|
if (Offset + Size > SegmentSize) {
|
||
|
err.submit(2309, GetSegmentName(Segment));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (Offset < LastOffset + LastDataRecordSize && LastOffset < Offset + Size) {
|
||
|
// Overlapping data records
|
||
|
if (Offset + 8 < LastOffset + LastDataRecordSize || !(NewSectionHeaders[SegNum].Flags & PE_SCN_CNT_CODE)) {
|
||
|
// Overlapping data by more than 7 bytes or not executable code
|
||
|
err.submit(1207);
|
||
|
}
|
||
|
else {
|
||
|
// Possibly backpatched code
|
||
|
err.submit(1208); // Warning
|
||
|
err.ClearError(1208); // Report only once
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LastDataRecordSize = Size;
|
||
|
LastDataRecordPointer = Records[RecNum].buffer + Records[RecNum].FileOffset + Records[RecNum].Index;
|
||
|
LastOffset = Offset; // Save offset for subsequent FIXUPP records
|
||
|
|
||
|
/*// Check if data within segment
|
||
|
if (Offset + Size > SegmentSize) {
|
||
|
err.submit(2309, GetSegmentName(Segment));
|
||
|
continue;
|
||
|
} */
|
||
|
|
||
|
// Put raw data into temporary buffer
|
||
|
memcpy(TempBuf.Buf() + Offset, LastDataRecordPointer, Size);
|
||
|
|
||
|
} // Finished with LEDATA record
|
||
|
|
||
|
if (Records[RecNum].Type2 == OMF_LIDATA) {
|
||
|
// LIDATA record
|
||
|
Records[RecNum].Index = 3; // Initialize record reading
|
||
|
Segment = Records[RecNum].GetIndex();
|
||
|
|
||
|
if (Segment != DesiredSegment) continue; // Does not refer to this segment
|
||
|
|
||
|
LastDataRecord = RecNum; // Save for later FIXUPP that refers to this record
|
||
|
|
||
|
Offset = Records[RecNum].GetNumeric();// Read offset
|
||
|
|
||
|
if (Offset > SegmentSize) {
|
||
|
err.submit(2310); return; // Error: outside bounds
|
||
|
}
|
||
|
|
||
|
// Unpack LIDATA blocks recursively
|
||
|
Size = Records[RecNum].UnpackLIDATABlock(TempBuf.Buf() + Offset, SegmentSize - Offset);
|
||
|
|
||
|
if (Offset < LastOffset + LastDataRecordSize && LastOffset < Offset + Size) {
|
||
|
// Overlapping data records
|
||
|
err.submit(1207); // Warning
|
||
|
}
|
||
|
LastDataRecordSize = Size; // Save data size
|
||
|
LastOffset = Offset; // Save offset for subsequent FIXUPP records
|
||
|
|
||
|
} // Finished with LIDATA record
|
||
|
|
||
|
if (Records[RecNum].Type2 == OMF_COMDAT) {
|
||
|
// COMDAT record. Currently not supported by objconv
|
||
|
LastDataRecord = RecNum; // Save for later FIXUPP that refers to this record
|
||
|
Segment = 0; // Ignore any relocation referring to this
|
||
|
}
|
||
|
|
||
|
if (Records[RecNum].Type2 == OMF_FIXUPP) {
|
||
|
// FIXUPP record
|
||
|
|
||
|
if (Segment != DesiredSegment) continue; // Does not refer to this segment
|
||
|
|
||
|
uint32 Target, TargetDisplacement; // Contents of FIXUPP record
|
||
|
//uint32 Frame; // Contents of FIXUPP record
|
||
|
uint8 byte1, byte2; // First two bytes of subrecord
|
||
|
|
||
|
// Bitfields in subrecords
|
||
|
OMF_SLocat Locat; // Structure of first two bytes of FIXUP subrecord swapped = Locat field
|
||
|
OMF_SFixData FixData; // Structure of FixData field in FIXUP subrecord of FIXUPP record
|
||
|
OMF_STrdDat TrdDat; // Structure of Thread Data field in THREAD subrecord of FIXUPP record
|
||
|
|
||
|
Records[RecNum].Index = 3;
|
||
|
|
||
|
if (Records[LastDataRecord].Type2 != OMF_LEDATA && Records[RecNum].Index < Records[RecNum].End) {
|
||
|
// Non-empty FIXUPP record does not refer to LEDATA record
|
||
|
if (Records[LastDataRecord].Type2 == OMF_COMDAT) {
|
||
|
// COMDAT currently not supported. Ignore!
|
||
|
}
|
||
|
else if (Records[LastDataRecord].Type2 == OMF_LIDATA) {
|
||
|
err.submit(2311); // Error: Relocation of iterated data not supported
|
||
|
}
|
||
|
else {
|
||
|
err.submit(2312); // Does not refer to data record
|
||
|
}
|
||
|
continue; // Ignore this FIXUPP record
|
||
|
}
|
||
|
|
||
|
// Loop through entries in record
|
||
|
while (Records[RecNum].Index < Records[RecNum].End) {
|
||
|
|
||
|
// Read first byte
|
||
|
byte1 = Records[RecNum].GetByte();
|
||
|
if (byte1 & 0x80) {
|
||
|
|
||
|
// This is a FIXUP subrecord
|
||
|
//Frame = 0;
|
||
|
Target = 0; TargetDisplacement = 0;
|
||
|
|
||
|
// read second byte
|
||
|
byte2 = Records[RecNum].GetByte();
|
||
|
// swap bytes and put into byte12 bitfield
|
||
|
Locat.bytes[1] = byte1;
|
||
|
Locat.bytes[0] = byte2;
|
||
|
// Read FixData
|
||
|
FixData.b = Records[RecNum].GetByte();
|
||
|
|
||
|
// Read conditional fields
|
||
|
if (FixData.s.F == 0) {
|
||
|
if (FixData.s.Frame < 4) {
|
||
|
Records[RecNum].GetIndex();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (FixData.s.T == 0) {
|
||
|
// Target specified
|
||
|
Target = Records[RecNum].GetIndex();
|
||
|
//uint32 TargetMethod = FixData.s.Target + FixData.s.P * 4;
|
||
|
}
|
||
|
else {
|
||
|
// Target specified in previous thread
|
||
|
// Does anybody still use compression of repeated fixup targets?
|
||
|
// I don't care to support this if it is never used
|
||
|
err.submit(2313); // Error message: not supported
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (FixData.s.P == 0) {
|
||
|
TargetDisplacement = Records[RecNum].GetNumeric();
|
||
|
}
|
||
|
|
||
|
// Get inline addend and check relocation method
|
||
|
if (LastDataRecordPointer && Locat.s.Offset < LastDataRecordSize) {
|
||
|
// Pointer to relocation source inline in raw data:
|
||
|
int8 * inlinep = LastDataRecordPointer + Locat.s.Offset;
|
||
|
|
||
|
switch (Locat.s.Location) { // Relocation method
|
||
|
|
||
|
case 9: case 13: // 32 bit
|
||
|
// The OMF format may indicate a relocation target by an
|
||
|
// offset stored inline in the relocation source.
|
||
|
// We prefer to store the target address explicitly in a
|
||
|
// symbol table entry.
|
||
|
|
||
|
// Add the inline offset to the explicit offset
|
||
|
TargetDisplacement += *(uint32*)inlinep;
|
||
|
|
||
|
// Remove the inline addend to avoid adding it twice:
|
||
|
// We have to do this in the new buffer TempBuf because
|
||
|
// the data have already been copied to TempBuf
|
||
|
if (*(uint32*)(TempBuf.Buf() + LastOffset + Locat.s.Offset) != *(uint32*)inlinep) {
|
||
|
// Check that the data in Buf() and TempBuf.Buf() are the same
|
||
|
err.submit(9000);
|
||
|
}
|
||
|
// Remove the inline addend to avoid adding it twice
|
||
|
*(uint32*)(TempBuf.Buf() + LastOffset + Locat.s.Offset) = 0;
|
||
|
break;
|
||
|
|
||
|
case 0: case 4: // 8 bit. Not supported
|
||
|
err.submit(2316, "8 bit"); break;
|
||
|
|
||
|
case 1: case 2: case 5: // 16 bit. Not supported
|
||
|
err.submit(2316, "16 bit"); break;
|
||
|
|
||
|
case 3: // 16+16 bit. Not supported
|
||
|
err.submit(2316, "16+16 bit far"); break;
|
||
|
|
||
|
case 6: case 11: // 16+32 bit. Not supported
|
||
|
err.submit(2316, "16+32 bit far"); break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Make relocation record
|
||
|
// Offset of relocation source
|
||
|
rel.VirtualAddress = Locat.s.Offset + LastOffset;
|
||
|
|
||
|
SOMFLocalSymbol locsym; // Symbol record for search in LocalSymbols table
|
||
|
int32 LocalSymbolsIndex; // Index into LocalSymbols table
|
||
|
|
||
|
// Relocation type: direct or EIP-relative
|
||
|
// (The displacement between relocation source and EIP for
|
||
|
// self-relative relocations is implicit in both OMF and COFF
|
||
|
// files. No need for correction)
|
||
|
rel.Type = Locat.s.M ? COFF32_RELOC_DIR32 : COFF32_RELOC_REL32;
|
||
|
|
||
|
switch (FixData.s.Target) { // = Target method modulo 4
|
||
|
case 0: // T0 and T4: Target = segment
|
||
|
|
||
|
// Local or public symbol. Search in LocalSymbols table
|
||
|
locsym.Segment = Target; // Target segment
|
||
|
locsym.Offset = TargetDisplacement; // Target offset including inline displacement
|
||
|
// Find in LocalSymbols table
|
||
|
LocalSymbolsIndex = LocalSymbols.Exists(locsym);
|
||
|
if (LocalSymbolsIndex < 0) {err.submit(9000); continue;} // Not found
|
||
|
|
||
|
// Get index into new symbol table
|
||
|
rel.SymbolTableIndex = LocalSymbols[LocalSymbolsIndex].NewSymtabIndex;
|
||
|
break;
|
||
|
|
||
|
case 1: // T1 and T5: Target = segment group
|
||
|
// Don't know how to handle group-relative relocation. Make error message
|
||
|
err.submit(2315, GetLocalName(Target));
|
||
|
continue;
|
||
|
|
||
|
case 2: // T2 and T6: Target = external symbol
|
||
|
|
||
|
// Translate old EXTDEF index to new symbol table index
|
||
|
if (Target >= ExtdefTranslation.GetNumEntries()) {
|
||
|
Target = 0; err.submit(2312);
|
||
|
continue;
|
||
|
}
|
||
|
rel.SymbolTableIndex = ExtdefTranslation[Target];
|
||
|
|
||
|
// Put addend inline in new file
|
||
|
if (LastOffset + Locat.s.Offset < SegmentSize) {
|
||
|
*(uint32*)(TempBuf.Buf() + LastOffset + Locat.s.Offset) = TargetDisplacement;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default: // Unknown method
|
||
|
err.submit(2314, FixData.s.Target + FixData.s.P * 4);
|
||
|
}
|
||
|
|
||
|
// Store in temporary relocation table
|
||
|
RelocationTable.Push(&rel, SIZE_SCOFF_Relocation);
|
||
|
|
||
|
}
|
||
|
else {
|
||
|
// This is a THREAD subrecord.
|
||
|
// I don't think this feature for compressing fixup data is
|
||
|
// used any more, if it ever was. I am not supporting it here.
|
||
|
// Frame threads can be safely ignored. A target thread cannot
|
||
|
// be ignored if there is any reference to it. The error is
|
||
|
// reported above at the reference to a target thread, not here.
|
||
|
TrdDat.b = byte1; // Put byte into bitfield
|
||
|
if (TrdDat.s.Method < 4) { // Make sure we read this correctly, even if ignored
|
||
|
Records[RecNum].GetIndex(); // has index field if method < 4 ?
|
||
|
}
|
||
|
}
|
||
|
} // Finished loop through subrecords
|
||
|
|
||
|
if (Records[RecNum].Index != Records[RecNum].End) err.submit(1203); // Check for consistency
|
||
|
}
|
||
|
} // End of loop to search for LEDATA, LIDATA and FIXUPP records for this segment
|
||
|
|
||
|
// Transfer raw data from TempBuf to NewData buffer
|
||
|
FileOffset = NewData.Push(TempBuf.Buf(), SegmentSize);
|
||
|
|
||
|
// Put file offset of raw data into section header
|
||
|
NewSectionHeaders[SegNum].PRawData = FileOffsetData + FileOffset;
|
||
|
|
||
|
// Align relocation table by 4
|
||
|
NewData.Align(4);
|
||
|
|
||
|
// Transfer relocation table from RelocationTable to NewData buffer
|
||
|
FileOffset = NewData.Push(RelocationTable.Buf(), RelocationTable.GetDataSize());
|
||
|
|
||
|
// Put file offset of relocations into section header
|
||
|
NewSectionHeaders[SegNum].PRelocations = FileOffsetData + FileOffset;
|
||
|
|
||
|
// Put number of relocations into section header
|
||
|
NewSectionHeaders[SegNum].NRelocations = (uint16)(RelocationTable.GetNumEntries());
|
||
|
|
||
|
// Put number of relocations into symbol table auxiliary entry.
|
||
|
// Search for the symbol table entry for this section:
|
||
|
for (uint32 sym = 0; sym < NewSymbolTable.GetNumEntries(); sym++) {
|
||
|
if ((uint32)NewSymbolTable[sym].s.SectionNumber == DesiredSegment
|
||
|
&& NewSymbolTable[sym].s.StorageClass == COFF_CLASS_STATIC
|
||
|
&& NewSymbolTable[sym].s.NumAuxSymbols == 1) {
|
||
|
// Found right symbol table entry. Insert NumberOfRelocations
|
||
|
NewSymbolTable[sym+1].section.NumberOfRelocations = NewSectionHeaders[SegNum].NRelocations;
|
||
|
break; // No need to search further
|
||
|
}
|
||
|
}
|
||
|
} // End of loop through segments
|
||
|
}
|
||
|
|
||
|
|
||
|
void COMF2COF::CheckUnsupportedRecords() {
|
||
|
// Make warnings if file containes unsupported record types
|
||
|
uint32 RecNum; // Record number
|
||
|
uint32 NumComdat = 0; // Number of COMDAT records
|
||
|
uint32 NumComent = 0; // Number of COMENT records
|
||
|
|
||
|
// Loop through all records
|
||
|
for (RecNum = 0; RecNum < NumRecords; RecNum++) {
|
||
|
// Check record type
|
||
|
switch (Records[RecNum].Type2) {
|
||
|
case OMF_THEADR: case OMF_MODEND: case OMF_EXTDEF: case OMF_PUBDEF:
|
||
|
case OMF_LNAMES: case OMF_SEGDEF: case OMF_GRPDEF: case OMF_FIXUPP:
|
||
|
case OMF_LEDATA: case OMF_LIDATA: case OMF_COMDEF: case OMF_VERNUM:
|
||
|
// These record types are supported or can safely be ignored
|
||
|
break;
|
||
|
|
||
|
case OMF_LINNUM: case OMF_LINSYM:
|
||
|
// Debug records
|
||
|
cmd.CountDebugRemoved(); break;
|
||
|
|
||
|
case OMF_COMDAT: case OMF_LCOMDEF: case OMF_CEXTDEF:
|
||
|
NumComdat++; break; // Count COMDAT records
|
||
|
|
||
|
case OMF_COMENT:
|
||
|
NumComent++; break; // Count COMENT records
|
||
|
|
||
|
default: // Warning for unknown record type
|
||
|
err.submit(1212, COMF::GetRecordTypeName(Records[RecNum].Type2));
|
||
|
}
|
||
|
}
|
||
|
// Report number of unsupported sections found
|
||
|
if (NumComdat) err.submit(2305, NumComdat);
|
||
|
if (NumComent) err.submit(1211, NumComent);
|
||
|
}
|
||
|
|
||
|
|
||
|
void COMF2COF::MakeBinaryFile() {
|
||
|
// Putting sections together
|
||
|
uint32 i;
|
||
|
|
||
|
// Get number of symbols and sections into file header
|
||
|
NewFileHeader.NumberOfSymbols = NewSymbolTable.GetNumEntries();
|
||
|
NewFileHeader.NumberOfSections = NewSectionHeaders.GetNumEntries();
|
||
|
|
||
|
// Put file header into new file
|
||
|
ToFile.Push(&NewFileHeader, sizeof(NewFileHeader));
|
||
|
|
||
|
// Put section headers into new file
|
||
|
if (NewSectionHeaders.GetNumEntries()) {
|
||
|
ToFile.Push(&NewSectionHeaders[0], NewSectionHeaders.GetNumEntries() * sizeof(SCOFF_SectionHeader));
|
||
|
}
|
||
|
|
||
|
// Put raw data and relocation tables into new file
|
||
|
ToFile.Push(NewData.Buf(), NewData.GetDataSize());
|
||
|
|
||
|
// Get address of symbol table into file header
|
||
|
ToFile.Get<SCOFF_FileHeader>(0).PSymbolTable = ToFile.GetDataSize();
|
||
|
|
||
|
// Put symbol table into new file
|
||
|
for (i = 0; i < NewSymbolTable.GetNumEntries(); i++) {
|
||
|
ToFile.Push(&NewSymbolTable[i], SIZE_SCOFF_SymTableEntry);
|
||
|
}
|
||
|
|
||
|
// Insert string table size
|
||
|
NewStringTable.Get<uint32>(0) = NewStringTable.GetDataSize();
|
||
|
|
||
|
// Put string table into new file
|
||
|
ToFile.Push(NewStringTable.Buf(), NewStringTable.GetDataSize());
|
||
|
}
|