804 lines
36 KiB
C++
804 lines
36 KiB
C++
|
/**************************** cof2omf.cpp ********************************
|
||
|
* Author: Agner Fog
|
||
|
* Date created: 2007-02-03
|
||
|
* Last modified: 2007-02-03
|
||
|
* Project: objconv
|
||
|
* Module: cof2omf.cpp
|
||
|
* Description:
|
||
|
* Module for converting 32 bit PE/COFF file to OMF file
|
||
|
*
|
||
|
* Copyright 2007-2008 GNU General Public License http://www.gnu.org/licenses
|
||
|
*****************************************************************************/
|
||
|
#include "stdafx.h"
|
||
|
|
||
|
CCOF2OMF::CCOF2OMF () {
|
||
|
// Constructor
|
||
|
memset(this, 0, sizeof(*this));
|
||
|
}
|
||
|
|
||
|
|
||
|
void CCOF2OMF::Convert() {
|
||
|
// Do the conversion
|
||
|
if (WordSize != 32) {
|
||
|
err.submit(2317, WordSize); // Wrong word size
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Allocate variable size buffers
|
||
|
SectionBuffer.SetNum(NSections + 2); // Allocate buffer for section translation list
|
||
|
SectionBuffer.SetZero(); // Initialize
|
||
|
SymbolBuffer.SetNum(NumberOfSymbols + 1); // Allocate buffer for symbol translation list
|
||
|
SymbolBuffer.SetZero(); // Initialize
|
||
|
NameBuffer.Push(0, 1); // Make first entry in NameBuffer empty
|
||
|
|
||
|
// Call the subfunctions
|
||
|
ToFile.SetFileType(FILETYPE_OMF); // Set type of output file
|
||
|
MakeSegmentList(); // Make temporary segment conversion list
|
||
|
MakeSymbolList(); // Make temporary symbol conversion list
|
||
|
MakeRelocationsList(); // Make temporary list of relocations (fixups) and sort it
|
||
|
MakeLNAMES(); // Make THEADR and LNAMES records
|
||
|
MakeSEGDEF(); // Make SEGDEF and GRPDEF records
|
||
|
MakeEXTDEF(); // Make EXTDEF records
|
||
|
MakePUBDEF(); // Make PUBDEF records
|
||
|
MakeLEDATA(); // Make LEDATA, LIDATA and FIXUPP records
|
||
|
MakeMODEND(); // Finish output file
|
||
|
*this << ToFile; // Take over new file buffer containing the converted file
|
||
|
}
|
||
|
|
||
|
|
||
|
void CCOF2OMF::MakeSegmentList() {
|
||
|
// Make temporary segment conversion list
|
||
|
const char * oldname; // Old name of section
|
||
|
uint32 namei; // Name index into NameBuffer
|
||
|
uint32 align; // Segment alignment = 2^align
|
||
|
int32 align2; // align2 = 2^align
|
||
|
uint32 flags; // Old flags
|
||
|
int i, j; // Loop counters
|
||
|
int oldsec; // Old section number
|
||
|
|
||
|
// Loop through old sections
|
||
|
for (j = 0; j < NSections; j++) {
|
||
|
// Old section number
|
||
|
oldsec = j + 1;
|
||
|
|
||
|
// Get old section header
|
||
|
SCOFF_SectionHeader * pSectionHeader = &SectionHeaders[j];
|
||
|
|
||
|
// Get name
|
||
|
oldname = GetSectionName(pSectionHeader->Name);
|
||
|
|
||
|
// Check for debug sections
|
||
|
if (strnicmp(oldname,"debug",5) == 0 || strnicmp(oldname+1,"debug",5) == 0) {
|
||
|
// This is a debug section
|
||
|
if (cmd.DebugInfo == CMDL_DEBUG_STRIP) {
|
||
|
// Remove debug info
|
||
|
SectionBuffer[oldsec].NewNumber = 0;
|
||
|
cmd.CountDebugRemoved();
|
||
|
continue;
|
||
|
}
|
||
|
else if (cmd.InputType != cmd.OutputType) {
|
||
|
err.submit(1029); // Warn that debug information is incompatible
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check for directive sections
|
||
|
if (strnicmp(oldname,".drectve",8) == 0 || (pSectionHeader->Flags & (PE_SCN_LNK_INFO | PE_SCN_LNK_REMOVE))) {
|
||
|
// This is a directive section
|
||
|
if (cmd.ExeptionInfo) {
|
||
|
// Remove directive section
|
||
|
SectionBuffer[oldsec].NewNumber = 0;
|
||
|
cmd.CountExceptionRemoved();
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get alignment
|
||
|
align = (pSectionHeader->Flags & PE_SCN_ALIGN_MASK) / PE_SCN_ALIGN_1;
|
||
|
if (align > 0) align--; // Alignment = 2^align
|
||
|
align2 = 1 << align; // 2^align
|
||
|
|
||
|
// Check for previous sections with same name
|
||
|
for (i = 0; i < SectionBufferNum; i++) {
|
||
|
if (strcmp(oldname, NameBuffer.Buf() + SectionBuffer[i].OldName) == 0) break; // Found same name
|
||
|
}
|
||
|
if (i < SectionBufferNum) {
|
||
|
// Previous section with same name found.
|
||
|
// i = first section with this name, oldsec = current section with this name
|
||
|
SectionBuffer[oldsec] = SectionBuffer[i]; // Copy record
|
||
|
SectionBuffer[oldsec].NewNameI = 0; // Indicate this is not the first record
|
||
|
|
||
|
// Check if alignment is the same
|
||
|
if (align != SectionBuffer[i].Align) {
|
||
|
err.submit(1060, oldname); // Warning different alignments
|
||
|
if (align > SectionBuffer[i].Align) SectionBuffer[i].Align = align; // Use highest alignment
|
||
|
}
|
||
|
|
||
|
// Get section header
|
||
|
SectionBuffer[oldsec].psechdr = pSectionHeader;
|
||
|
|
||
|
// Get size of this section
|
||
|
SectionBuffer[oldsec].Size = pSectionHeader->SizeOfRawData;
|
||
|
|
||
|
// Get offset relative to first section with same name
|
||
|
SectionBuffer[oldsec].Offset = SectionBuffer[i].Offset + SectionBuffer[i].SegmentSize;
|
||
|
|
||
|
// Align this section (We are aligning each section in the segment)
|
||
|
SectionBuffer[oldsec].Offset = (SectionBuffer[oldsec].Offset + align2 - 1) & (- align2);
|
||
|
|
||
|
// Update total size of all sections with same name
|
||
|
SectionBuffer[i].SegmentSize = SectionBuffer[oldsec].Offset + SectionBuffer[oldsec].Size;
|
||
|
}
|
||
|
else {
|
||
|
// No previous section found with same name. Make SOMFSegmentList record
|
||
|
SectionBufferNum = oldsec + 1; // End of entries in SectionBuffer
|
||
|
|
||
|
// Assign a number to this segment
|
||
|
SectionBuffer[oldsec].NewNumber = ++NumSegments;
|
||
|
SectionBuffer[oldsec].NewNameI = NumSegments + OMF_LNAME_LAST;
|
||
|
|
||
|
// Point to old section header
|
||
|
SectionBuffer[oldsec].psechdr = pSectionHeader;
|
||
|
|
||
|
// Give it a name
|
||
|
namei = NameBuffer.PushString(oldname); // Save name in buffer, because it is volatile
|
||
|
SectionBuffer[oldsec].OldName = namei; // Index to name
|
||
|
// Segment names like .text and _TEXT are both common. No need to convert the name
|
||
|
// Only restriction is length < 256.
|
||
|
// Do we need a unique segment name if the alignment is different from segments
|
||
|
// with same name in another module?
|
||
|
if (strlen(oldname) > 255) {
|
||
|
// Segment name too long. This is very unlikely
|
||
|
namei = NameBuffer.Push(oldname, 255); // Make truncated name
|
||
|
NameBuffer.Push(0, 1); // Terminate by zero
|
||
|
}
|
||
|
SectionBuffer[oldsec].NewName = namei; // Index to name
|
||
|
|
||
|
// Size
|
||
|
SectionBuffer[oldsec].Size = pSectionHeader->SizeOfRawData;
|
||
|
SectionBuffer[oldsec].SegmentSize = pSectionHeader->SizeOfRawData;
|
||
|
SectionBuffer[oldsec].Offset = 0;
|
||
|
|
||
|
// Alignment
|
||
|
SectionBuffer[oldsec].Align = align;
|
||
|
|
||
|
// Segment type
|
||
|
flags = pSectionHeader->Flags;
|
||
|
|
||
|
// Get segment class
|
||
|
if (flags & (PE_SCN_CNT_CODE | PE_SCN_MEM_EXECUTE)) {
|
||
|
// Code segment
|
||
|
SectionBuffer[oldsec].Class = OMF_LNAME_CODE;
|
||
|
}
|
||
|
else if (flags & PE_SCN_CNT_UNINIT_DATA) {
|
||
|
// Uninitialized data
|
||
|
SectionBuffer[oldsec].Class = OMF_LNAME_BSS;
|
||
|
}
|
||
|
else if (!(flags & PE_SCN_MEM_WRITE)) {
|
||
|
// Read only
|
||
|
SectionBuffer[oldsec].Class = OMF_LNAME_CONST;
|
||
|
}
|
||
|
else {
|
||
|
// Normal data
|
||
|
SectionBuffer[oldsec].Class = OMF_LNAME_DATA;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Add 1 to section count because new section numbers are 1-based
|
||
|
SectionBufferNum = NSections + 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CCOF2OMF::MakeSymbolList() {
|
||
|
// Make temporary symbol conversion list
|
||
|
int isym = 0; // current symbol table entry
|
||
|
//int jsym = 0; // auxiliary entry number
|
||
|
union { // Pointer to symbol table
|
||
|
SCOFF_SymTableEntry * p; // Normal pointer
|
||
|
int8 * b; // Used for address calculation
|
||
|
} Symtab;
|
||
|
|
||
|
Symtab.p = SymbolTable; // Set pointer to begin of SymbolTable
|
||
|
|
||
|
// Loop through symbol table
|
||
|
while (isym < NumberOfSymbols) {
|
||
|
// Check scope
|
||
|
if (Symtab.p->s.StorageClass == COFF_CLASS_EXTERNAL) {
|
||
|
// Scope is public or external
|
||
|
|
||
|
if (Symtab.p->s.SectionNumber > 0) {
|
||
|
|
||
|
// Symbol is public
|
||
|
SymbolBuffer[isym].Scope = S_PUBLIC; // Scope = public
|
||
|
SymbolBuffer[isym].NewIndex = ++NumPublicSymbols; // Public symbol number
|
||
|
// Get name
|
||
|
SymbolBuffer[isym].Name = NameBuffer.PushString(GetSymbolName(Symtab.p->s.Name));
|
||
|
|
||
|
// Find section in SectionBuffer
|
||
|
uint32 OldSection = Symtab.p->s.SectionNumber;
|
||
|
SymbolBuffer[isym].Segment = SectionBuffer[OldSection].NewNumber; // New segment number
|
||
|
|
||
|
// Calculate offset = offset into old section + offset of old section to first section with same name
|
||
|
SymbolBuffer[isym].Offset = Symtab.p->s.Value + SectionBuffer[OldSection].Offset;
|
||
|
}
|
||
|
else if (Symtab.p->s.SectionNumber == 0) {
|
||
|
|
||
|
// Symbol is external
|
||
|
SymbolBuffer[isym].Scope = S_EXTERNAL; // Scope = external
|
||
|
SymbolBuffer[isym].NewIndex = ++NumExternalSymbols; // External symbol number
|
||
|
SymbolBuffer[isym].Name = NameBuffer.PushString(GetSymbolName(Symtab.p->s.Name));
|
||
|
}
|
||
|
else if (Symtab.p->s.SectionNumber == COFF_SECTION_ABSOLUTE) {
|
||
|
|
||
|
// Symbol is public, absolute
|
||
|
SymbolBuffer[isym].Scope = S_PUBLIC; // Scope = public
|
||
|
SymbolBuffer[isym].NewIndex = ++NumPublicSymbols; // Public symbol number
|
||
|
// Get name
|
||
|
SymbolBuffer[isym].Name = NameBuffer.PushString(GetSymbolName(Symtab.p->s.Name));
|
||
|
|
||
|
SymbolBuffer[isym].Segment = 0; // 0 indicates absolute
|
||
|
|
||
|
SymbolBuffer[isym].Offset = Symtab.p->s.Value; // Store value in Offset
|
||
|
}
|
||
|
else {
|
||
|
// COFF_SECTION_DEBUG, COFF_SECTION_N_TV, COFF_SECTION_P_TV
|
||
|
// Ignore
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Skip auxiliary symbol table entries and increment pointer
|
||
|
isym += Symtab.p->s.NumAuxSymbols + 1;
|
||
|
Symtab.b += (Symtab.p->s.NumAuxSymbols + 1) * SIZE_SCOFF_SymTableEntry;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CCOF2OMF::MakeRelocationsList() {
|
||
|
// Make temporary list of relocations (fixups) and sort it
|
||
|
uint32 i; // Relocation number in old file
|
||
|
int j; // Section number of relocation source in old file
|
||
|
int isym; // Symbol table index in old file
|
||
|
//int32 * paddend = 0; // Pointer to inline addend
|
||
|
uint32 TargetOldSection; // Section number of relocation target in old file
|
||
|
|
||
|
SOMFRelocation NewRel; // Entry in RelocationBuffer
|
||
|
|
||
|
union { // Pointer to symbol table
|
||
|
SCOFF_SymTableEntry * p; // Normal pointer
|
||
|
int8 * b; // Used for address calculation
|
||
|
} Symtab;
|
||
|
|
||
|
union { // Pointer to relocation entry
|
||
|
SCOFF_Relocation * p; // pointer to record
|
||
|
int8 * b; // used for address calculation and incrementing
|
||
|
} Reloc;
|
||
|
|
||
|
// Loop through section headers of old file
|
||
|
for (j = 0; j < NSections; j++) {
|
||
|
SCOFF_SectionHeader * SectionHeaderp = &SectionHeaders[j];
|
||
|
|
||
|
// Pointer to first relocation entry in section
|
||
|
Reloc.b = Buf() + SectionHeaderp->PRelocations;
|
||
|
|
||
|
// Loop through relocations in section
|
||
|
for (i = 0; i < SectionHeaderp->NRelocations; i++) {
|
||
|
|
||
|
// Find symbol table entry
|
||
|
isym = Reloc.p->SymbolTableIndex;
|
||
|
if ((uint32)isym >= (uint32)NumberOfSymbols) {
|
||
|
err.submit(2040); // SymbolTableIndex points outside Symbol Table
|
||
|
isym = 0;
|
||
|
}
|
||
|
Symtab.p = SymbolTable; // Set pointer to begin of SymbolTable
|
||
|
Symtab.b += SIZE_SCOFF_SymTableEntry * isym; // Calculate address of entry isym
|
||
|
|
||
|
// Find inline addend
|
||
|
if (Reloc.p->Type < COFF32_RELOC_SEG12) {
|
||
|
//paddend = (int32*)(Buf()+SectionHeaderp->PRawData+Reloc.p->VirtualAddress);
|
||
|
}
|
||
|
//else paddend = 0;
|
||
|
|
||
|
// Make entry in RelocationBuffer
|
||
|
// Relocation type
|
||
|
switch (Reloc.p->Type) {
|
||
|
case COFF32_RELOC_DIR32: // Direct 32 bit
|
||
|
NewRel.Mode = 1; break; // 0 = EIP-relative, 1 = direct
|
||
|
|
||
|
case COFF32_RELOC_REL32: // 32 bit EIP relative
|
||
|
NewRel.Mode = 0; break; // 0 = EIP-relative, 1 = direct
|
||
|
|
||
|
case COFF32_RELOC_ABS: // Ignore
|
||
|
continue;
|
||
|
|
||
|
default: // Other. Not supported
|
||
|
NewRel.Mode = -1; // -1 = unsupported.
|
||
|
// Postpone error message in case it refers to a debug section that is being removed
|
||
|
NewRel.TargetOffset = Reloc.p->Type; // Remember relocation type
|
||
|
//err.submit(2030, Reloc.p->Type); continue; // Unsupported relocation type
|
||
|
break;
|
||
|
}
|
||
|
// Get source
|
||
|
NewRel.Section = j + 1; // Section number in old file
|
||
|
NewRel.SourceOffset = Reloc.p->VirtualAddress;// Offset of source relative to section
|
||
|
|
||
|
// Get target
|
||
|
if (Symtab.p->s.SectionNumber > 0) {
|
||
|
// Local
|
||
|
NewRel.Scope = S_LOCAL; // 0 = local, 2 = external
|
||
|
TargetOldSection = Symtab.p->s.SectionNumber; // Target section
|
||
|
if (TargetOldSection > uint32(NSections)) {
|
||
|
// SectionNumber out of range
|
||
|
err.submit(2035); continue;
|
||
|
}
|
||
|
// Segment index of target in new file:
|
||
|
NewRel.TargetSegment = SectionBuffer[TargetOldSection].NewNumber;
|
||
|
// Offset relative to old section
|
||
|
NewRel.TargetOffset = Symtab.p->s.Value;
|
||
|
// Add offset relative to first section with same name to get offset relative to new segment
|
||
|
NewRel.TargetOffset += SectionBuffer[TargetOldSection].Offset;
|
||
|
}
|
||
|
else {
|
||
|
// External
|
||
|
NewRel.Scope = S_EXTERNAL; // 0 = local, 2 = external
|
||
|
NewRel.TargetOffset = 0; // Any addend is inline
|
||
|
// Find EXTDEF index in SymbolBuffer
|
||
|
NewRel.TargetSegment = SymbolBuffer[isym].NewIndex;
|
||
|
}
|
||
|
|
||
|
// Put NewRel into RelocationBuffer
|
||
|
RelocationBuffer.Push(NewRel);
|
||
|
|
||
|
// Increment pointer to relocation record
|
||
|
Reloc.b += SIZE_SCOFF_Relocation; // Next relocation record
|
||
|
}
|
||
|
}
|
||
|
// Sort RelocationBuffer
|
||
|
RelocationBuffer.Sort();
|
||
|
|
||
|
// Store number of relocations
|
||
|
NumRelocations = RelocationBuffer.GetNumEntries();
|
||
|
|
||
|
// Check for overlapping relocation sources
|
||
|
for (uint32 i = 1; i < RelocationBuffer.GetNumEntries(); i++) {
|
||
|
if (RelocationBuffer[i].Section == RelocationBuffer[i-1].Section
|
||
|
&& RelocationBuffer[i].SourceOffset >= RelocationBuffer[i-1].SourceOffset
|
||
|
&& RelocationBuffer[i].SourceOffset < RelocationBuffer[i-1].SourceOffset + 4
|
||
|
&& (RelocationBuffer[i].Mode == 0 || RelocationBuffer[i].Mode == 1)) {
|
||
|
err.submit(2210); // Error: overlapping relocation sources
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CCOF2OMF::MakeLNAMES() {
|
||
|
// Make THEADR and LNAMES records
|
||
|
int Sec; // Loop counter
|
||
|
uint32 NameI; // Name index
|
||
|
|
||
|
// Make first record in output file = Translator header
|
||
|
ToFile.StartRecord(OMF_THEADR);
|
||
|
// Remove path from file name and limit length
|
||
|
char * ShortName = CLibrary::ShortenMemberName(OutputFileName);
|
||
|
ToFile.PutString(ShortName);
|
||
|
ToFile.EndRecord();
|
||
|
|
||
|
// Make LNAMES record containing names of segments, groups and classes
|
||
|
ToFile.StartRecord(OMF_LNAMES);
|
||
|
|
||
|
// Store default group and class names
|
||
|
ToFile.PutString("FLAT"); // 1: FLAT = group name
|
||
|
ToFile.PutString("CODE"); // 2: CODE = class name for code
|
||
|
ToFile.PutString("DATA"); // 3: DATA = class name for data segment
|
||
|
ToFile.PutString("BSS"); // 4: BSS = class name for uninitialized data
|
||
|
ToFile.PutString("CONST"); // 5: CONST = class name for readonly data
|
||
|
|
||
|
NameI = OMF_LNAME_LAST + 1; // Index of next name
|
||
|
|
||
|
// Get segment names
|
||
|
for (Sec = 0; Sec < SectionBufferNum; Sec++) {
|
||
|
if (SectionBuffer[Sec].NewNameI == NameI) {
|
||
|
// Found next segment name to add
|
||
|
// Check if current record too big
|
||
|
if (ToFile.GetSize() >= 1024 - 256) { // Max size = 1024
|
||
|
ToFile.EndRecord(); // End current record
|
||
|
ToFile.StartRecord(OMF_LNAMES); // Start new LNAMES record
|
||
|
}
|
||
|
// Store name of this segment
|
||
|
ToFile.PutString(NameBuffer.Buf() + SectionBuffer[Sec].NewName);
|
||
|
NameI++; // Ready for next name
|
||
|
}
|
||
|
}
|
||
|
// End LNAMES record
|
||
|
ToFile.EndRecord();
|
||
|
}
|
||
|
|
||
|
|
||
|
void CCOF2OMF::MakeSEGDEF() {
|
||
|
// Make SEGDEF and GRPDEF records
|
||
|
int Sec; // Index into SectionBuffer
|
||
|
uint32 SegNum = 0; // Segment number in new file
|
||
|
OMF_SAttrib Attr; // Segment attributes bitfield
|
||
|
uint32 align; // Alignment in new file
|
||
|
|
||
|
// Loop through SectionBuffer
|
||
|
for (Sec = 0; Sec < SectionBufferNum; Sec++) {
|
||
|
|
||
|
if (SectionBuffer[Sec].NewNumber == SegNum+1 && SectionBuffer[Sec].NewNameI) {
|
||
|
|
||
|
// New segment found
|
||
|
SegNum++; // Segment number
|
||
|
|
||
|
// Make a SEGDEF record for this segment
|
||
|
ToFile.StartRecord(OMF_SEGDEF + 1); // Odd record number = 32 bit
|
||
|
|
||
|
// Attributes bitfield
|
||
|
Attr.u.P = 1; // Indicate 32 bit segment
|
||
|
Attr.u.B = 0; // 1 indicates 4 Gbytes
|
||
|
Attr.u.C = 2; // Indicates public combination
|
||
|
|
||
|
// Translate alignment
|
||
|
switch(SectionBuffer[Sec].Align) {
|
||
|
case 0: // Align by 1
|
||
|
align = 1; break;
|
||
|
|
||
|
case 1: // Align by 2
|
||
|
align = 2; break;
|
||
|
|
||
|
case 2: // Align by 4
|
||
|
align = 5; break;
|
||
|
|
||
|
case 3: // Align by 8 not supported, use 16
|
||
|
case 4: // Align by 16
|
||
|
align = 3; break;
|
||
|
|
||
|
default: // 32 or higher. Use 'page' alignment
|
||
|
// Note: this gives 256 on some systems, 4096 on other systems
|
||
|
align = 4;
|
||
|
if (SectionBuffer[Sec].Align > 8) {
|
||
|
err.submit(1205, 1 << SectionBuffer[Sec].Align); // Warning: alignment not supported
|
||
|
}
|
||
|
}
|
||
|
Attr.u.A = align; // Put alignment into bitfield
|
||
|
|
||
|
ToFile.PutByte(Attr.b); // Save attributes bitfield
|
||
|
|
||
|
// Segment length
|
||
|
ToFile.PutNumeric(SectionBuffer[Sec].SegmentSize);
|
||
|
|
||
|
// Segment name given by index into LNAMES record
|
||
|
ToFile.PutIndex(SectionBuffer[Sec].NewNameI);
|
||
|
|
||
|
// Class name index
|
||
|
ToFile.PutIndex(SectionBuffer[Sec].Class);
|
||
|
|
||
|
// Overlay index (ignored)
|
||
|
ToFile.PutIndex(0);
|
||
|
|
||
|
// End SEGDEF record
|
||
|
ToFile.EndRecord();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Make GRPDEF record for the FLAT group
|
||
|
ToFile.StartRecord(OMF_GRPDEF); // Strart GRPDEF record
|
||
|
ToFile.PutIndex(OMF_LNAME_FLAT); // Index of name "FLAT"
|
||
|
// No need to put segment indices into the GRPDEF record when group is FLAT
|
||
|
// End GRPDEF record
|
||
|
ToFile.EndRecord();
|
||
|
}
|
||
|
|
||
|
|
||
|
void CCOF2OMF::MakeEXTDEF() {
|
||
|
// Make EXTDEF records
|
||
|
uint32 j; // SymbolBuffer entry index
|
||
|
uint32 ExtSymNum = 0; // External symbol number
|
||
|
|
||
|
if (NumExternalSymbols > 0) { // Are there any external symbols?
|
||
|
|
||
|
// Make EXTDEF record for one or more symbols
|
||
|
ToFile.StartRecord(OMF_EXTDEF); // Start record
|
||
|
|
||
|
// Loop through SymbolBuffer
|
||
|
for (j = 0; j < SymbolBuffer.GetNumEntries(); j++) {
|
||
|
if (SymbolBuffer[j].Scope == S_EXTERNAL && SymbolBuffer[j].NewIndex == ExtSymNum+1) {
|
||
|
// Found external symbol
|
||
|
ExtSymNum++; // Symbol number
|
||
|
|
||
|
// Check if current record too big
|
||
|
if (ToFile.GetSize() >= 1024 - 257) {// Max size = 1024
|
||
|
ToFile.EndRecord(); // End current record
|
||
|
ToFile.StartRecord(OMF_EXTDEF); // Start new EXTDEF record
|
||
|
}
|
||
|
// Put symbol name in record
|
||
|
ToFile.PutString(NameBuffer.Buf() + SymbolBuffer[j].Name);
|
||
|
|
||
|
// Type index
|
||
|
ToFile.PutIndex(0); // Not used any more
|
||
|
}
|
||
|
}
|
||
|
ToFile.EndRecord(); // End EXTDEF record
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CCOF2OMF::MakePUBDEF() {
|
||
|
// Make PUBDEF records
|
||
|
uint32 j; // SymbolBuffer entry index
|
||
|
uint32 PubSymNum = 0; // Public symbol number
|
||
|
|
||
|
// Loop through SymbolBuffer
|
||
|
for (j = 0; j < SymbolBuffer.GetNumEntries(); j++) {
|
||
|
if (SymbolBuffer[j].Scope == S_PUBLIC && SymbolBuffer[j].NewIndex == PubSymNum+1) {
|
||
|
// Found public symbol
|
||
|
PubSymNum++; // Symbol number
|
||
|
|
||
|
// Make PUBDEF record for this symbol
|
||
|
ToFile.StartRecord(OMF_PUBDEF + 1); // Start new PUBDEF record, 32 bit
|
||
|
|
||
|
// Group index
|
||
|
uint32 Group = SymbolBuffer[j].Segment ? OMF_LNAME_FLAT : 0; // Group = FLAT, except for absolute symbols
|
||
|
ToFile.PutIndex(Group); // Group name index
|
||
|
|
||
|
// Segment index
|
||
|
ToFile.PutIndex(SymbolBuffer[j].Segment);
|
||
|
|
||
|
// Base frame field if segment = 0
|
||
|
if (SymbolBuffer[j].Segment == 0) ToFile.PutWord(0);
|
||
|
|
||
|
// Put symbol name in record
|
||
|
ToFile.PutString(NameBuffer.Buf() + SymbolBuffer[j].Name);
|
||
|
|
||
|
// Offset relative to segment
|
||
|
ToFile.PutNumeric(SymbolBuffer[j].Offset);
|
||
|
|
||
|
// Type index
|
||
|
ToFile.PutIndex(0); // Not used any more
|
||
|
|
||
|
// End record
|
||
|
ToFile.EndRecord(); // End PUBDEF record
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CCOF2OMF::MakeLEDATA() {
|
||
|
/*
|
||
|
This function makes both LEDATA records, containing binary data, and FIXUPP
|
||
|
records, containing relocations.
|
||
|
|
||
|
The function is quite complicated because the LEDATA and FIXUPP records are
|
||
|
mutually interdependent. Some explanation is in place here.
|
||
|
|
||
|
I am using the word segment for the collection of all sections in the old file
|
||
|
having the same name. A section is a record of binary data in the old file.
|
||
|
Each section is stored as one or more LEDATA records in the new file.
|
||
|
A segment may thus be split into multiple sections, which again may be split
|
||
|
into multiple LEDATA records. The sections must be aligned according to the
|
||
|
specified alignment for the segment. The LEDATA records need not be aligned,
|
||
|
and they may be misaligned for reasons explained below.
|
||
|
|
||
|
Each LEDATA record is followed by a FIXUPP record containing all relocations
|
||
|
referring to a source in the LEDATA record, if any.
|
||
|
|
||
|
The size of a LEDATA record is limited to 1024 bytes because each entry in
|
||
|
the FIXUPP record has only 10 bits for addressing it. Some linkers impose
|
||
|
the 1024 bytes size limit to all OMF records, although this limitation is
|
||
|
strictly necessary only for LEDATA records. If the code has many relocations
|
||
|
then it may be necessary to make a LEDATA record smaller than 1024 bytes
|
||
|
in order to avoid that the corresponding FIXUPP record becomes bigger than
|
||
|
1024 bytes. Furthermore, the size of a LEDATA record may need adjustment to
|
||
|
avoid that a relocation source crosses a LEDATA record boundary.
|
||
|
|
||
|
I have stored all relocations in a temporary list RelocationBuffer which is
|
||
|
sorted by relocation source address in order to make it easier to find all
|
||
|
relocations with a source address in the current LEDATA record.
|
||
|
*/
|
||
|
int Segment; // Segment index in new file
|
||
|
int OldSection; // Section index in old file
|
||
|
uint32 SegOffset; // Offset of section relative to segment
|
||
|
uint32 SectOffset; // Offset of LEDATA record relative to section
|
||
|
uint32 CutOff; // Size limit for splitting section into multiple LEDATA records
|
||
|
uint32 RelFirst; // Index into RelocationBuffer of first relocation for section/record
|
||
|
uint32 RelLast; // Index into RelocationBuffer of last relocation for record + 1
|
||
|
uint32 Rel; // Current index into RelocationBuffer
|
||
|
uint32 FrameDatum; // Frame datum field in FIXUPP record
|
||
|
uint32 TargetDatum; // Target datum field in FIXUPP record
|
||
|
uint32 TargetDisplacement; // Target displacement field in FIXUPP record
|
||
|
uint32 AlignmentFiller; // Number of unused bytes from end of one section to begin of next section due to alignment
|
||
|
|
||
|
SOMFRelocation Reloc0; // Reference relocation record for compare and search
|
||
|
OMF_SLocat Locat; // Locat bitfield for FIXUPP record
|
||
|
OMF_SFixData FixData; // FixData bitfield for FIXUPP record
|
||
|
|
||
|
// Loop through segment numbers
|
||
|
for (Segment = 1; Segment <= NumSegments; Segment++) {
|
||
|
SegOffset = 0; // SegOffset = 0 for first section in segment
|
||
|
|
||
|
// Search through SectionBuffer for old sections contributing to this segment
|
||
|
for (OldSection = 0; OldSection < SectionBufferNum; OldSection++) {
|
||
|
|
||
|
if (SectionBuffer[OldSection].NewNumber == (uint32)Segment && SectionBuffer[OldSection].Size) {
|
||
|
// This section contributes to Segment. Make LEDATA record(s)
|
||
|
|
||
|
if (SectionBuffer[OldSection].Offset > SegOffset) {
|
||
|
// Fillers needed for alignment after previous section
|
||
|
AlignmentFiller = SectionBuffer[OldSection].Offset - SegOffset;
|
||
|
}
|
||
|
else {
|
||
|
AlignmentFiller = 0;
|
||
|
}
|
||
|
SectOffset = 0; // Offset of LEDATA record relative to section start
|
||
|
|
||
|
if (AlignmentFiller > 0
|
||
|
&& AlignmentFiller < 4096 && AlignmentFiller < (1u << SectionBuffer[OldSection].Align)
|
||
|
&& SectionBuffer[OldSection].Class == OMF_LNAME_CODE) {
|
||
|
// This is a code segment and there is a space from previous section
|
||
|
// Fill the alignment space with NOP's
|
||
|
// Make LIDATA record with NOP's
|
||
|
ToFile.StartRecord(OMF_LIDATA); // Start new LEDATA record
|
||
|
ToFile.PutIndex(Segment); // Segment index
|
||
|
ToFile.PutNumeric(SegOffset); // Offset of LIDATA relative to segment
|
||
|
ToFile.PutNumeric(AlignmentFiller); // Repeat count
|
||
|
ToFile.PutWord(0); // Block count
|
||
|
ToFile.PutByte(1); // Byte count
|
||
|
ToFile.PutByte(0x90); // NOP opcode
|
||
|
ToFile.EndRecord(); // End LIDATA record
|
||
|
}
|
||
|
|
||
|
SegOffset = SectionBuffer[OldSection].Offset; // Offset of section to segment
|
||
|
|
||
|
// Search for relocations for this section
|
||
|
Reloc0.Section = OldSection;
|
||
|
Reloc0.SourceOffset = 0;
|
||
|
RelFirst = RelocationBuffer.FindFirst(Reloc0); // Points to first relocation for this section
|
||
|
RelLast = RelFirst;
|
||
|
|
||
|
// Loop for possibly more than one LEDATA records for this section
|
||
|
while (SectOffset < SectionBuffer[OldSection].Size) {
|
||
|
|
||
|
CutOff = SectionBuffer[OldSection].Size - SectOffset; // Size of rest of section
|
||
|
if (CutOff > 1024 && SectionBuffer[OldSection].Class != OMF_LNAME_BSS) {
|
||
|
CutOff = 1024; // Maximum LEDATA size
|
||
|
}
|
||
|
|
||
|
// Search for last relocation entry
|
||
|
while (RelLast < NumRelocations) {
|
||
|
if (RelocationBuffer[RelLast].Section != (uint32)OldSection) {
|
||
|
break; // Reached end of relocations for this section
|
||
|
}
|
||
|
if (RelocationBuffer[RelLast].SourceOffset >= CutOff + SectOffset) {
|
||
|
break; // Reached size limit of LEDATA record
|
||
|
}
|
||
|
if (RelocationBuffer[RelLast].SourceOffset + 4 > CutOff + SectOffset) {
|
||
|
// Relocation crosses LEDATA boundary.
|
||
|
// Reduce limit of LEDATA to before this relocation source
|
||
|
CutOff = RelocationBuffer[RelLast].SourceOffset - SectOffset;
|
||
|
if (CutOff == 0) {
|
||
|
err.submit(2302); // Relocation source extends beyond end of section.
|
||
|
CutOff = 4; // Prevent infinite loop
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
if (RelLast - RelFirst > 100) {
|
||
|
// FIXUPP record will become too big. End LEDATA record here
|
||
|
CutOff = RelocationBuffer[RelLast].SourceOffset - SectOffset;
|
||
|
break;
|
||
|
}
|
||
|
RelLast++;
|
||
|
} // End of search for last relocation entry for this LEDATA
|
||
|
|
||
|
if (SectionBuffer[OldSection].Class == OMF_LNAME_BSS) {
|
||
|
// BSS: Unitialized data section needs no LEDATA record and no FIXUPP
|
||
|
if (RelLast > RelFirst) {
|
||
|
// Error: Relocation of uninitialized data
|
||
|
err.submit(2041);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// Section contains initialized data. Needs LEDATA and FIXUPP records
|
||
|
|
||
|
// Make LEDATA record for section data
|
||
|
ToFile.StartRecord(OMF_LEDATA + 1);// Start new LEDATA record, 32 bit
|
||
|
|
||
|
// Segment index
|
||
|
ToFile.PutIndex(Segment);
|
||
|
|
||
|
// Offset of LEDATA relative to segment
|
||
|
ToFile.PutNumeric(SegOffset + SectOffset);
|
||
|
|
||
|
// Binary data
|
||
|
ToFile.PutBinary(Buf() + SectionBuffer[OldSection].psechdr->PRawData + SectOffset, CutOff);
|
||
|
|
||
|
// End LEDATA record
|
||
|
ToFile.EndRecord();
|
||
|
|
||
|
if (RelLast > RelFirst) { // If there are any relocations
|
||
|
|
||
|
// Make FIXUPP record with (RelLast-RelFirst) relocation entries
|
||
|
ToFile.StartRecord(OMF_FIXUPP + 1); // Start FIXUPP record, 32 bit
|
||
|
|
||
|
// Loop through relocations
|
||
|
for (Rel = RelFirst; Rel < RelLast; Rel++) {
|
||
|
|
||
|
if (RelocationBuffer[Rel].Mode < 0) {
|
||
|
// Unsupported mode. Make error message
|
||
|
err.submit(2030, RelocationBuffer[Rel].TargetOffset); // TargetOffset contains COFF relocation mode
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Make Locat word bitfield
|
||
|
Locat.s.one = 1; // Indicates FIXUP subrecord
|
||
|
Locat.s.M = RelocationBuffer[Rel].Mode; // Direct / EIP-relative
|
||
|
Locat.s.Location = 9; // Indicates 32-bit offset
|
||
|
|
||
|
// Offset of source relative to section (10 bits)
|
||
|
uint32 RelocOffset = RelocationBuffer[Rel].SourceOffset - SectOffset; // Offset of relocation source to begin of LEDATA record
|
||
|
if (RelocOffset >= 1024) err.submit(9000); // Check that it fits into 10 bits
|
||
|
Locat.s.Offset = RelocOffset;
|
||
|
|
||
|
// Make FixData byte bitfield
|
||
|
FixData.b = 0; // Start with all bits 0
|
||
|
|
||
|
if (RelocationBuffer[Rel].Scope == S_LOCAL) {
|
||
|
// Local target
|
||
|
FixData.s.Target = 0; // Target method T0 or T4: Target = segment
|
||
|
FixData.s.Frame = 1; // Frame method F1: specified by a group index (FLAT)
|
||
|
FrameDatum = OMF_LNAME_FLAT; // Target frame = FLAT group
|
||
|
TargetDatum = RelocationBuffer[Rel].TargetSegment; // Fixup target = segment
|
||
|
TargetDisplacement = RelocationBuffer[Rel].TargetOffset; // Displacement or addend (?)
|
||
|
}
|
||
|
else {
|
||
|
// External symbol target
|
||
|
FixData.s.Frame = 5; // Frame method F5: Frame specified by target, not here
|
||
|
FixData.s.Target = 2; // Target method T2 or T6: Target specified by EXTDEF index
|
||
|
TargetDatum = RelocationBuffer[Rel].TargetSegment; // Index into EXTDEF
|
||
|
TargetDisplacement = RelocationBuffer[Rel].TargetOffset; // This is zero. Any addend is inline
|
||
|
}
|
||
|
if (TargetDisplacement) {
|
||
|
FixData.s.P = 0; // Displacement field present
|
||
|
}
|
||
|
else {
|
||
|
FixData.s.P = 1; // Displacement field absent
|
||
|
}
|
||
|
|
||
|
// Put these data into record
|
||
|
// Locat bytes in reverse order:
|
||
|
ToFile.PutByte(Locat.bytes[1]);
|
||
|
ToFile.PutByte(Locat.bytes[0]);
|
||
|
// FixData byte
|
||
|
ToFile.PutByte(FixData.b);
|
||
|
// Frame datum field only if FixData.F = 0 and Frame < 4
|
||
|
if (FixData.s.Frame < 4) ToFile.PutIndex(FrameDatum);
|
||
|
// Target datum field if FixData.T = 0, which it is here
|
||
|
ToFile.PutIndex(TargetDatum);
|
||
|
// Target displacement field if FixData.P = 0
|
||
|
if (FixData.s.P == 0) ToFile.PutNumeric(TargetDisplacement);
|
||
|
|
||
|
} // End of loop through relocation for last LEDATA
|
||
|
|
||
|
// End FIXUPP record
|
||
|
ToFile.EndRecord();
|
||
|
}
|
||
|
}
|
||
|
// Update pointers to after this LEDATA
|
||
|
SectOffset += CutOff;
|
||
|
RelFirst = RelLast;
|
||
|
|
||
|
} // End of loop for multiple LEDATA records for one section
|
||
|
|
||
|
// Find end of section
|
||
|
SegOffset += SectionBuffer[OldSection].Size; // End of section
|
||
|
|
||
|
} // End of if section in segment
|
||
|
} // End of loop through multiple sections for same segment
|
||
|
} // End of loop through segments
|
||
|
}
|
||
|
|
||
|
|
||
|
void CCOF2OMF::MakeMODEND() {
|
||
|
// Make MODEND record and finish file
|
||
|
ToFile.StartRecord(OMF_MODEND); // Start MODEND record
|
||
|
ToFile.PutByte(0); // Module type field. 0 if not a startup module with start address
|
||
|
ToFile.EndRecord(); // End MODEND record
|
||
|
}
|