1 /****************************    omf.cpp    *********************************
2 * Author:        Agner Fog
3 * Date created:  2007-01-29
4 * Last modified: 2018-05-26
5 * Project:       objconv
6 * Module:        omf.cpp
7 * Description:
8 * Module for reading OMF files
9 *
10 * Class COMF is used for reading, interpreting and dumping OMF files.
11 *
12 * Copyright 2007-2018 GNU General Public License http://www.gnu.org/licenses
13 *****************************************************************************/
14 #include "stdafx.h"
15 
16 // OMF Record type names
17 SIntTxt OMFRecordTypeNames[] = {
18    {OMF_THEADR,      "Translator Header"},
19    {OMF_LHEADR,      "Library Module Header"},
20    {OMF_COMENT,      "Comment"},
21    {OMF_MODEND,      "Module End"},
22    {OMF_EXTDEF,      "External Names Definition"},
23    {OMF_PUBDEF,      "Public Names Definition"},
24    {OMF_LINNUM,      "Line Numbers"},
25    {OMF_LNAMES,      "List of Names"},
26    {OMF_SEGDEF,      "Segment Definition"},
27    {OMF_GRPDEF,      "Group Definition"},
28    {OMF_FIXUPP,      "Fixup"},
29    {OMF_LEDATA,      "Enumerated Data"},
30    {OMF_LIDATA,      "Iterated Data"},
31    {OMF_COMDEF,      "Communal Names Definition"},
32    {OMF_BAKPAT,      "Backpatch"},
33    {OMF_LEXTDEF,     "Local External Names"},
34    {OMF_LPUBDEF,     "Local Public Names"},
35    {OMF_LCOMDEF,     "Local Communal Names"},
36    {OMF_CEXTDEF,     "COMDAT External Names"},
37    {OMF_COMDAT,      "Initialized Communal Data"},
38    {OMF_LINSYM,      "Symbol Line Numbers"},
39    {OMF_ALIAS,       "Alias Definition"},
40    {OMF_NBKPAT,      "Named Backpatch"},
41    {OMF_LLNAMES,     "Local Logical Names"},
42    {OMF_VERNUM,      "OMF Version Number"},
43    {OMF_VENDEXT,     "Vendor-specific OMF Extension"},
44    {OMF_LIBHEAD,     "Library Header"},
45    {OMF_LIBEND,      "Library End"}
46 };
47 
48 // Segment combination names
49 SIntTxt OMFSegmentCombinationNames[] = {
50    {0,     "Private"},
51    {1,     "Invalid"},
52    {2,     "Public"},
53    {3,     "Invalid"},
54    {4,     "Public 4"},
55    {5,     "Stack"},
56    {6,     "Common"},
57    {7,     "Public 7"}
58 };
59 
60 // Relocation mode names
61 static SIntTxt OMFRelocationModeNames[] = {
62    {0,     "Relatv"},
63    {1,     "Direct"}
64 };
65 
66 
67 // Fixup location names
68 static SIntTxt OMFFixupLocationNames[] = {
69    {OMF_Fixup_8bit,        "8 bit"},                       // 0
70    {OMF_Fixup_16bit,       "16 bit"},                      // 1
71    {OMF_Fixup_Segment,     "segment selector, 16 bit"},    // 2
72    {OMF_Fixup_Far,         "far pointer 16+16 bit"},       // 3
73    {OMF_Fixup_Hi8bit,      "high 8 bit of 16 bits"},       // 4
74    {OMF_Fixup_16bitLoader, "16 bit loader resolved"},      // 5
75    {OMF_Fixup_Pharlab48,   "farword 48 bit, Pharlab only"},// 6
76    {OMF_Fixup_32bit,       "32 bit"},                      // 9
77    {OMF_Fixup_Farword,     "farword 32+16 bit"},           // 11
78    {OMF_Fixup_32bitLoader, "32 bit loader resolved"}       // 13
79 };
80 
81 // Alignment value translation table
82 static const uint32_t OMFAlignTranslate[8] = {0,1,2,16,256,4,0,0};
83 
84 
85 // Class COMF members:
86 // Constructor
87 
COMF()88 COMF::COMF() {
89    // Default constructor
90    memset(this, 0, sizeof(*this));     // reset everything
91 }
92 
93 
ParseFile()94 void COMF::ParseFile() {
95    // Parse file buffer
96    //uint8_t  RecordType;                            // Type of current record
97    uint32_t Checksum;                              // Record checksum
98    uint32_t ChecksumZero = 0;                      // Count number of records with zero checksum
99    SOMFRecordPointer rec;                        // Current record pointer
100 
101    // Make first entry zero in name lists
102    LocalNameOffset.PushZero();
103    SegmentNameOffset.PushZero();
104    GroupNameOffset.PushZero();
105    SymbolNameOffset.PushZero();
106 
107    // Initialize record pointer
108    rec.Start(Buf(), 0, GetDataSize());
109 
110    // Loop through records to set record pointers and store names
111    do {
112       // Read record
113       //RecordType = rec.Type2;                    // First byte of record = type
114 
115       // Compute checksum
116       Checksum = 0;  rec.Index = 0;
117       while (rec.Index < rec.End) Checksum += rec.GetByte();
118       uint32_t CheckByte = rec.GetByte();
119       if ((Checksum + CheckByte) & 0xFF) {
120          // Checksum failed
121          if (CheckByte == 0) {
122             ChecksumZero++;
123          }
124          else err.submit(1202);                  // Checksum error
125       }
126 
127       // Store record pointer
128       rec.Index = 3;                             // Offset to current byte while parsing
129       Records.Push(rec);                         // Store record pointer in list
130 
131       if (rec.Type2 == OMF_LNAMES) {
132          // LNAMES record. Store local names by name index
133          // Loop through strings in record
134          while (rec.Index < rec.End) {
135             char * LocalName = rec.GetString();
136             uint32_t LocalNameIndex = NameBuffer.PushString(LocalName); // Store local name
137             LocalNameOffset.Push(LocalNameIndex);// Store local name index
138          }
139          if (rec.Index != rec.End) err.submit(1203);  // Check for consistency
140       }
141 
142       if (rec.Type2 == OMF_SEGDEF) {
143          // SEGDEF record. Store segment names by segment index
144          OMF_SAttrib Attributes;
145          if (rec.Type2 == OMF_SEGDEF) {
146             Attributes.b = rec.GetByte();     // Read attributes
147             if (Attributes.u.A == 0) {
148                // Frame and Offset only included if A = 0
149                rec.GetWord();  rec.GetByte();
150             }
151             rec.GetNumeric(); // Length
152          }
153          uint32_t NameIndex  = rec.GetIndex();
154          if (NameIndex < LocalNameOffset.GetNumEntries()) {
155             SegmentNameOffset.Push(LocalNameOffset[NameIndex]); // List by segment index
156          }
157       }
158 
159       if (rec.Type2 == OMF_GRPDEF) {
160          // GRPDEF record. Store group name
161          uint32_t NameIndex  = rec.GetIndex();
162          if (NameIndex < LocalNameOffset.GetNumEntries()) {
163             GroupNameOffset.Push(LocalNameOffset[NameIndex]); // List by group index
164          }
165       }
166 
167       if (rec.Type2 == OMF_EXTDEF) {
168          // EXTDEF record. Store external symbol names
169          // Loop through strings in record
170          while (rec.Index < rec.End) {
171             char * symbolname = rec.GetString();
172             rec.GetIndex();
173             uint32_t SymbolNameIndex = NameBuffer.PushString(symbolname); // Store external name
174             SymbolNameOffset.Push(SymbolNameIndex);   // Save in name index table
175          }
176          if (rec.Index != rec.End) err.submit(1203);  // Check for consistency
177       }
178 
179       if (rec.Type2 == OMF_CEXTDEF) {
180          // CEXTDEF record. Store communal symbol names
181          // Loop through entries in record
182          uint32_t SymbolNameIndex;                 // Index into NameBuffer
183          while (rec.Index < rec.End) {
184             uint32_t LIndex = rec.GetIndex();      // Index into preceding LNAMES
185             rec.GetIndex();                      // Type index. Ignore
186             // Get name from LocalNameOffset and put into SymbolNameOffset.
187             if (LIndex < LocalNameOffset.GetNumEntries()) {
188                SymbolNameIndex = LocalNameOffset[LIndex];
189             }
190             else SymbolNameIndex = 0;
191             SymbolNameOffset.Push(SymbolNameIndex);   // Save in name index table
192          }
193          if (rec.Index != rec.End) err.submit(1203);  // Check for consistency
194       }
195    }  // Point to next record
196    while (rec.GetNext());                        // End of loop through records
197 
198    NumRecords = Records.GetNumEntries();         // Number of records
199 
200    if (ChecksumZero) printf("\nChecksums are zero"); // This is taken out of the loop to report it only once
201 }
202 
203 
Dump(int options)204 void COMF::Dump(int options) {
205    // Dump file
206    if (options & DUMP_FILEHDR) DumpRecordTypes(); // Dump summary of record types
207 
208    if (options & DUMP_STRINGTB) DumpNames(); // Dump names records
209 
210    if (options & DUMP_SYMTAB) DumpSymbols(); // Dump public/external name records
211 
212    if (options & DUMP_SECTHDR) DumpSegments(); // Dump segment records
213 
214    if (options & DUMP_RELTAB) DumpRelocations(); // Dump fixup records
215 
216    if (options & DUMP_COMMENT) DumpComments(); // Dump coment records
217 }
218 
DumpRecordTypes()219 void COMF::DumpRecordTypes() {
220    // Dump summary of records
221    printf("\nSummary of records:");
222    for (uint32_t i = 0; i < NumRecords; i++) {
223       // Print record type
224       printf("\n  Record %02X, %s%s, total length %i", Records[i].Type,
225          Lookup(OMFRecordTypeNames, Records[i].Type2),
226          (Records[i].Type & 1) ? ".32" : "",
227          Records[i].End+1);
228    }
229 }
230 
231 
DumpNames()232 void COMF::DumpNames() {
233    // Dump local names records
234    uint32_t i;           // Record index
235    uint32_t ln = 0;     // Local name index
236    printf("\n\nLocal names:");
237    for (i = 0; i < NumRecords; i++) {
238       if (Records[i].Type2 == OMF_LNAMES) {
239          // LNAMES record. There should be only one
240          // Loop through strings in record
241          while (Records[i].Index < Records[i].End) {
242             printf("\n  %2i  %s", ++ln, Records[i].GetString());
243          }
244          if (Records[i].Index != Records[i].End) err.submit(1203);   // Check for consistency
245       }
246 
247       if (Records[i].Type2 == OMF_THEADR || Records[i].Type2 == OMF_LHEADR) {
248          // Module header record
249          // Loop through strings in record
250          while (Records[i].Index < Records[i].End) {
251             printf("\n  Module: %s\n", Records[i].GetString());
252          }
253          if (Records[i].Index != Records[i].End) err.submit(1203);   // Check for consistency
254       }
255       if (Records[i].Type2 == OMF_COMDEF) {
256          // COMDEF record. Communal names
257          uint32_t DType, DSize, DNum;
258          printf("\n\n Communal names:");
259 
260          // Loop through strings in record
261          while (Records[i].Index < Records[i].End) {
262             printf("\n  \"%s\":", Records[i].GetString());
263             printf(" %i", Records[i].GetByte()); // Type index, should be 0
264             DType = Records[i].GetByte(); // Data type
265             switch (DType) {
266             case 0x61:
267                DNum  = Records[i].GetLength();
268                DSize = Records[i].GetLength();
269                printf(" FAR: %i*%i bytes", DNum, DSize);
270                break;
271             case 0x62:
272                DSize = Records[i].GetLength();
273                printf(" NEAR: 0x%X bytes", DSize);
274                break;
275             default:
276                DSize = Records[i].GetLength();
277                if (DType < 0x60) { // Borland segment index
278                   printf(" segment %i, size 0x%X", DType, DSize);
279                   break;
280                }
281                printf(" unknown type %i, size 0x%X", DType, DSize);
282                break;
283             }
284          }
285          if (Records[i].Index != Records[i].End) err.submit(1203);   // Check for consistency
286       }
287    }
288 }
289 
DumpSymbols()290 void COMF::DumpSymbols() {
291    // Dump public, external and communal names records
292    uint32_t i;          // Record index
293    uint32_t xn = 0;     // External name index
294    char * string;
295    uint32_t TypeIndex;
296    uint32_t Group;
297    uint32_t Segment;
298    uint32_t BaseFrame;
299    uint32_t Offset;
300 
301    for (i = 0; i < NumRecords; i++) {
302       if (Records[i].Type2 == OMF_EXTDEF) {
303          // EXTDEF record.
304          Records[i].Index = 3;
305          printf("\n\nExternal names:");
306 
307          // Loop through strings in record
308          while (Records[i].Index < Records[i].End) {
309             string = Records[i].GetString();
310             TypeIndex = Records[i].GetIndex();
311             printf("\n  %2i  %s, Type %i", ++xn, string, TypeIndex);
312          }
313          if (Records[i].Index != Records[i].End) err.submit(1203);   // Check for consistency
314       }
315 
316       if (Records[i].Type2 == OMF_PUBDEF) {
317          // PUBDEF record.
318          printf("\n\nPublic names:");
319          Records[i].Index = 3;
320          Group = Records[i].GetIndex();
321          Segment = Records[i].GetIndex();
322          BaseFrame = 0;
323          if (Segment == 0) BaseFrame = Records[i].GetWord();
324          // Loop through strings in record
325          while (Records[i].Index < Records[i].End) {
326             string = Records[i].GetString();
327             Offset = Records[i].GetNumeric();
328             TypeIndex = Records[i].GetIndex();
329             printf("\n  %s, Segment %s, Group %s, Offset 0x%X, Type %i",
330                string, GetSegmentName(Segment), GetGroupName(Group), Offset, TypeIndex);
331             if (BaseFrame) printf(", Frame %i", BaseFrame);
332          }
333          if (Records[i].Index != Records[i].End) err.submit(1203);   // Check for consistency
334       }
335 
336       if (Records[i].Type2 == OMF_CEXTDEF) {
337          // CEXTDEF record.
338          printf("\n\nCommunal names:");
339          Records[i].Index = 3;
340          while (Records[i].Index < Records[i].End) {
341             uint32_t LIndex = Records[i].GetIndex();    // Index into preceding LNAMES
342             uint32_t Type = Records[i].GetIndex();      // Type index. Ignored
343             printf("\n  %2i  %s, Type %i", ++xn, GetLocalName(LIndex), Type);
344          }
345          if (Records[i].Index != Records[i].End) err.submit(1203);   // Check for consistency
346       }
347    }
348 }
349 
350 
DumpSegments()351 void COMF::DumpSegments() {
352    // Dump all segment records
353 
354    // Define structure of attributes
355    OMF_SAttrib Attributes;
356 
357    // Record values
358    uint32_t Frame, Offset, SegLength, NameIndex, ClassIndex, OverlayIndex;
359 
360    uint32_t i;                                     // Record number
361    uint32_t SegNum = 0;                            // Segment number
362 
363    printf("\n\nSegment records:");
364    for (i = 0; i < NumRecords; i++) {
365       if (Records[i].Type2 == OMF_SEGDEF) {
366          // SEGDEF record
367          Records[i].Index = 3;
368          // Loop through entries in record. There should be only 1
369          while (Records[i].Index < Records[i].End) {
370             Attributes.b = Records[i].GetByte();     // Read attributes
371             if (Attributes.u.A == 0) {
372                // Frame and Offset only included if A = 0
373                Frame = Records[i].GetWord();  Offset = Records[i].GetByte();
374             }
375             else Frame = Offset = 0;
376             SegLength    = Records[i].GetNumeric();
377             NameIndex    = Records[i].GetIndex();
378             ClassIndex   = Records[i].GetIndex();
379             OverlayIndex = Records[i].GetIndex();
380 
381             printf("\n  Segment %2i, Name %s, Class %s, Align %i, %s, %i bit",
382                ++SegNum, GetLocalName(NameIndex), GetLocalName(ClassIndex),
383                OMFAlignTranslate[Attributes.u.A],
384                Lookup(OMFSegmentCombinationNames, Attributes.u.C),
385                Attributes.u.P ? 32 : 16);
386             if (Attributes.u.B) printf(", big");
387             if (Attributes.u.A == 0) printf(", Frame %i, Offset 0x%X", Frame, Offset);
388             printf(", Length %i", SegLength);
389             if (OverlayIndex) printf("\n   Overlay %i", OverlayIndex);
390          }
391          if (Records[i].Index != Records[i].End) err.submit(1203);   // Check for consistency
392       }
393    }
394    printf("\n\nGroup records:");
395    for (i = 0; i < NumRecords; i++) {
396       if (Records[i].Type2 == OMF_GRPDEF) {
397          // GRPDEF record
398          Records[i].Index = 3;
399          ClassIndex = Records[i].GetIndex();
400          printf("\n  Group: %s\n   Segments:", GetLocalName(ClassIndex));
401 
402          // Loop through remaining entries in record
403          while (Records[i].Index < Records[i].End) {
404             uint8_t Type = Records[i].GetByte();
405             if (Type != 0xFF) printf(" Type=%X:", Type);
406             NameIndex =  Records[i].GetIndex();
407             printf(" %s", GetSegmentName(NameIndex));
408          }
409          if (Records[i].Index != Records[i].End) err.submit(1203);   // Check for consistency
410       }
411    }
412 }
413 
414 
DumpRelocations()415 void COMF::DumpRelocations() {
416    // Dump all LEDATA, LIDATA, COMDAT and FIXUPP records
417    //uint32_t LastDataRecord = 0;          // Index to the data record that relocations refer to
418    uint32_t LastDataRecordSize = 0;      // Size of the data record that relocations refer to
419    int8_t * LastDataRecordPointer = 0;   // Pointer to data in the data record that relocations refer to
420    uint32_t i;                           // Loop counter
421    uint32_t Segment, Offset, Size;       // Contents of LEDATA or LIDATA record
422    uint32_t LastOffset = 0;              // Offset of last LEDATA or LIDATA record
423    uint32_t Frame, Target, TargetDisplacement; // Contents of FIXUPP record
424    uint8_t byte1, byte2;                 // First two bytes of subrecord
425 
426    // Bitfields in subrecords
427    OMF_SLocat Locat;         // Structure of first two bytes of FIXUP subrecord swapped = Locat field
428    OMF_SFixData FixData;     // Structure of FixData field in FIXUP subrecord of FIXUPP record
429    OMF_STrdDat TrdDat;       // Structure of Thread Data field in THREAD subrecord of FIXUPP record
430 
431    printf("\n\nLEDATA, LIDATA, COMDAT and FIXUPP records:");
432    for (i = 0; i < NumRecords; i++) {
433       if (Records[i].Type2 == OMF_LEDATA) {
434          // LEDATA record
435          Segment = Records[i].GetIndex();             // Read segment and offset
436          Offset  = Records[i].GetNumeric();
437          Size    = Records[i].End - Records[i].Index; // Calculate size of data
438          //LastDataRecord = i;                          // Save for later FIXUPP that refers to this record
439          LastDataRecordSize = Size;
440          LastDataRecordPointer = Records[i].buffer + Records[i].FileOffset + Records[i].Index;
441          if (Segment < 0x4000) {
442             printf("\n  LEDATA: segment %s, Offset 0x%X, Size 0x%X", // Dump segment, offset, size
443                GetSegmentName(Segment), Offset, Size);
444             LastOffset = Offset;
445          }
446          else { // Undocumented Borland communal section
447             printf("\n  LEDATA communal section %i, Offset 0x%X, Size 0x%X", // Dump segment, offset, size
448                (Segment & ~0x4000), Offset, Size);
449             LastOffset = Offset;
450          }
451       }
452 
453       if (Records[i].Type2 == OMF_LIDATA) {
454          // LIDATA record
455          Segment = Records[i].GetIndex();
456          Offset  = Records[i].GetNumeric();
457          //LastDataRecord = i;
458          LastDataRecordSize = Records[i].End - Records[i].Index; // Size before expansion of repeat blocks
459          LastDataRecordPointer = Records[i].buffer + Records[i].FileOffset + Records[i].Index;
460          printf("\n  LIDATA: segment %s, Offset 0x%X, Size ",
461             GetSegmentName(Segment), Offset);
462          // Call recursive function to interpret repeat data block
463          Size = Records[i].InterpretLIDATABlock();
464          printf(" = 0x%X", Size);
465          LastOffset = Offset;
466       }
467 
468       if (Records[i].Type2 == OMF_COMDAT) {
469          // COMDAT record
470          //uint32_t Flags = Records[i].GetByte(); // 1 = continuation, 2 = iterated, 4 = local, 8 = data in code segment
471          uint32_t Attributes = Records[i].GetByte();
472          uint32_t Base = 0;
473          // 0 = explicit, 1 = far code, 2 = far data, 3 = code32, 4 = data32
474          // 0x00 = no match, 0x10 = pick any, 0x20 = same size, 0x30 = exact match
475          uint32_t Align = Records[i].GetByte(); // Alignment
476          Offset  = Records[i].GetNumeric();    // Offset
477          uint32_t TypeIndex = Records[i].GetIndex(); // Type index
478          if ((Attributes & 0x0F) == 0) {
479             Base = Records[i].GetIndex(); // Public base
480          }
481          uint32_t NameIndex = Records[i].GetIndex(); // LNAMES index
482          Size    = Records[i].End - Records[i].Index; // Calculate size of data
483 
484          printf("\n  COMDAT: name %s, Offset 0x%X, Size 0x%X, Attrib 0x%02X, Align %i, Type %i, Base %i",
485             GetLocalName(NameIndex), Offset, Size, Attributes, Align, TypeIndex, Base);
486          LastOffset = Offset;
487       }
488 
489       if (Records[i].Type2 == OMF_FIXUPP) {
490          // FIXUPP record
491          printf("\n  FIXUPP:");
492          Records[i].Index = 3;
493 
494          // Loop through entries in record
495          while (Records[i].Index < Records[i].End) {
496 
497             // Read first byte
498             byte1 = Records[i].GetByte();
499             if (byte1 & 0x80) {
500                // This is a FIXUP subrecord
501                Frame = 0; Target = 0; TargetDisplacement = 0;
502 
503                // read second byte
504                byte2 = Records[i].GetByte();
505                // swap bytes and put into byte12 bitfield
506                Locat.bytes[1] = byte1;
507                Locat.bytes[0] = byte2;
508                // Read FixData
509                FixData.b = Records[i].GetByte();
510 
511                // print mode and location
512                printf("\n   %s %s, Offset 0x%X",
513                   Lookup(OMFRelocationModeNames, Locat.s.M),
514                   Lookup(OMFFixupLocationNames, Locat.s.Location),
515                   Locat.s.Offset + LastOffset);
516 
517                // Read conditional fields
518                if (FixData.s.F == 0) {
519                   if (FixData.s.Frame < 4) {
520                      Frame = Records[i].GetIndex();
521                   }
522                   else Frame = 0;
523 
524                   switch (FixData.s.Frame) { // Frame method
525                   case 0:  // F0: segment
526                      printf(", segment %s", GetSegmentName(Frame));  break;
527 
528                   case 1:  // F1: group
529                      printf(", group %s", GetGroupName(Frame));  break;
530 
531                   case 2:  // F2: external symbol
532                      printf(", external frame %s", GetSymbolName(Frame));  break;
533 
534                   case 4:  // F4: frame = source,
535                            // or Borland floating point emulation record (undocumented?)
536                      printf(", frame = source; or Borland f.p. emulation record");
537                      break;
538 
539                   case 5:  // F5: frame = target
540                      printf(", frame = target");  break;
541 
542                   default:
543                      printf(", target frame %i method F%i", Frame, FixData.s.Frame);
544                   }
545                }
546                else {
547                   printf(", frame uses thread %i", FixData.s.Frame);
548                }
549 
550                if (FixData.s.T == 0) {
551                   // Target specified
552                   Target = Records[i].GetIndex();
553                   uint32_t TargetMethod = FixData.s.Target + FixData.s.P * 4;
554 
555                   switch (FixData.s.Target) { // = Target method modulo 4
556                   case 0: // T0 and T4: Target = segment
557                   case 1: // T1 and T5: Target = segment group
558                      printf(". Segment %s (T%i)",
559                         GetSegmentName(Target), TargetMethod);
560                      break;
561                   case 2: // T2 and T6: Target = external symbol
562                      printf(". Symbol %s (T%i)",
563                         GetSymbolName(Target), TargetMethod);
564                      break;
565                   default: // Unknown method
566                      printf(", target %i unknown method T%i", Target, TargetMethod);
567                   }
568                }
569                else {
570                   // Target specified in previous thread
571                   printf(", target uses thread %i", FixData.s.Target);
572                }
573 
574                if (FixData.s.P == 0) {
575                   TargetDisplacement = Records[i].GetNumeric();
576                   printf("\n    target displacement %i", TargetDisplacement);
577                }
578                // Get inline addend
579                if (LastDataRecordPointer && Locat.s.Offset < LastDataRecordSize) {
580                   int8_t * inlinep = LastDataRecordPointer + Locat.s.Offset;
581                   switch (Locat.s.Location) {
582                   case 0: case 4:  // 8 bit
583                      printf(", inline 0x%X", *inlinep);  break;
584 
585                   case 1: case 2: case 5: // 16 bit
586                      printf(", inline 0x%X", *(int16_t*)inlinep);  break;
587 
588                   case 3: // 16+16 bit
589                      printf(", inline 0x%X:0x%X", *(int16_t*)(inlinep+2), *(int16_t*)inlinep);  break;
590 
591                   case 9: case 13: // 32 bit
592                      printf(", inline 0x%X", *(int32_t*)inlinep);  break;
593 
594                   case 6: case 11: // 16+32 bit
595                      printf(", inline 0x%X:0x%X", *(int16_t*)(inlinep+4), *(int32_t*)inlinep);  break;
596                   }
597                }
598             }
599             else {
600                // This is a THREAD subrecord
601                TrdDat.b = byte1;                 // Put byte into bitfield
602 
603                uint32_t Index  = 0;
604                if (TrdDat.s.Method < 4) {
605                   Index  = Records[i].GetIndex(); // has index field if method < 4 ?
606                }
607                printf("\n   %s Thread %i. Method %s%i, index %i",
608                   (TrdDat.s.D ? "Frame" : "Target"), TrdDat.s.Thread,
609                   (TrdDat.s.D ? "F" : "T"), TrdDat.s.Method, Index);
610             }
611          } // Finished loop through subrecords
612          if (Records[i].Index != Records[i].End) err.submit(1203);   // Check for consistency
613       }
614    } // Finished loop through records
615 }
616 
617 
DumpComments()618 void COMF::DumpComments() {
619    // Dump COMENT records
620    uint32_t i;           // Record index
621    int startindex;
622    printf("\n");
623    for (i = 0; i < NumRecords; i++) {
624       if (Records[i].Type2 == OMF_COMENT) {
625          // COMENT record
626          printf("\nCOMENT record:\n");
627          startindex = Records[i].Index;
628          // Print as hex
629          while (Records[i].Index < Records[i].End) {
630             printf("%02X ", Records[i].GetByte());
631          }
632          // Print again as string
633          Records[i].Index = startindex;
634          printf("\n");
635          while (Records[i].Index < Records[i].End) {
636             printf("%c ", Records[i].GetByte());
637          }
638          printf("\n");
639          if (Records[i].Index != Records[i].End) err.submit(1203);   // Check for consistency
640       }
641    }
642 }
643 
644 
PublicNames(CMemoryBuffer * Strings,CSList<SStringEntry> * Index,int m)645 void COMF::PublicNames(CMemoryBuffer * Strings, CSList<SStringEntry> * Index, int m) {
646    // Make list of public names
647    // Strings will receive ASCIIZ strings
648    // Index will receive records of type SStringEntry with Member = m
649    SOMFRecordPointer rec;                        // Current OMF record
650    char * string;                                // Symbol name
651    SStringEntry se;                              // String entry record to save
652 
653    // Initialize record pointer
654    rec.Start(Buf(), 0, GetDataSize());
655 
656    // Loop through records and search for PUBDEF records
657    do {
658       // Read record
659       if (rec.Type2 == OMF_PUBDEF) {
660 
661          // Public symbols definition found
662          rec.GetIndex(); // Read group
663          uint32_t Segment = rec.GetIndex(); // Read segment
664          if (Segment == 0) rec.GetWord(); // Read base frame
665          // Loop through strings in record
666          while (rec.Index < rec.End) {
667             string = rec.GetString();  // Read name
668             rec.GetNumeric(); // Read offset
669             rec.GetIndex(); // Read type
670             // Make SStringEntry record
671             se.Member = m;
672             // Store name
673             se.String = Strings->PushString(string);
674             // Store name index
675             Index->Push(se);
676          }
677          if (rec.Index != rec.End) err.submit(1203);   // Check for consistency
678       }
679       if (rec.Type2 == OMF_CEXTDEF) {
680          // CEXTDEF record. Store communal symbol names
681          // Loop through entries in record
682          while (rec.Index < rec.End) {
683             uint32_t LIndex = rec.GetIndex() - 1;  // Index into preceding LNAMES
684             rec.GetIndex();                      // Type index. Ignore
685             // Check if index valid
686             if (LIndex < LocalNameOffset.GetNumEntries()) {
687                // Make SStringEntry record
688                se.Member = m;
689                // Get name
690                char * name = GetLocalName(LIndex);
691                if (strlen(name) > 0) {
692                   // Store name
693                   se.String = Strings->PushString(name);
694                   // Store name index
695                   Index->Push(se);
696                }
697             }
698          }
699          if (rec.Index != rec.End) err.submit(1203);  // Check for consistency
700       }
701       if (rec.Type2 == OMF_LNAMES) {
702          // LNAMES record. Check if file has been parsed
703          if (Records.GetNumEntries() == 0) {
704             // ParseFile has not been called. We need to store LNAMES table because
705             // these names may be needed by subsequent EXTDEF records.
706             // Loop through strings in record
707             while (rec.Index < rec.End) {
708                char * LocalName = rec.GetString();
709                uint32_t LocalNameIndex = NameBuffer.PushString(LocalName); // Store local name
710                LocalNameOffset.Push(LocalNameIndex);// Store local name index
711             }
712             if (rec.Index != rec.End) err.submit(1203);  // Check for consistency
713          }
714       }
715    } // Get next record
716    while (rec.GetNext());                        // End of loop through records
717 }
718 
GetLocalName(uint32_t i)719 char * COMF::GetLocalName(uint32_t i) {
720    // Get section name or class name by name index
721     if (i == 0 || i >= LocalNameOffset.GetNumEntries())  {
722         i = NameBuffer.PushString("null");
723         return (char*)NameBuffer.Buf() + i;
724     }
725     return (char*)NameBuffer.Buf() + LocalNameOffset[i];
726 }
727 
GetLocalNameO(uint32_t i)728 uint32_t COMF::GetLocalNameO(uint32_t i) {
729    // Get section name or class by converting name index offset into NameBuffer
730    if (i > 0 && i < LocalNameOffset.GetNumEntries()) {
731       return LocalNameOffset[i];
732    }
733    return 0;
734 }
735 
GetSegmentName(uint32_t i)736 const char * COMF::GetSegmentName(uint32_t i) {
737    // Get section name by segment index
738    if (i == 0) return "none";
739    if ((i & 0xC000) == 0x4000) {
740       // Borland communal section
741       static char text[32];
742       sprintf(text, "communal section %i", i - 0x4000);
743       return text;
744    }
745    if (i <= NumRecords) {
746       return(char*) NameBuffer.Buf() + SegmentNameOffset[i];
747    }
748    return "?";
749 }
750 
751 
GetSymbolName(uint32_t i)752 const char * COMF::GetSymbolName(uint32_t i) {
753    // Get external symbol name by index
754    if (i == 0) return "null";
755    if (i < SymbolNameOffset.GetNumEntries()) {
756       return (char*)NameBuffer.Buf() + SymbolNameOffset[i];
757    }
758    // return "?";
759    // index out of range
760    static char temp[100];
761    sprintf(temp, "Unknown index %i", i);
762    return temp;
763 }
764 
GetGroupName(uint32_t i)765 const char * COMF::GetGroupName(uint32_t i) {
766    // Get group name by index
767    if (i == 0) return "none";
768    if (i <= NumRecords) {
769       return (char*)NameBuffer.Buf() + GroupNameOffset[i];
770    }
771    return "?";
772 }
773 
GetRecordTypeName(uint32_t i)774 const char * COMF::GetRecordTypeName(uint32_t i) {
775    // Get record type name
776    return Lookup(OMFRecordTypeNames, i);
777 }
778 
779 // Member functions for parsing SOMFRecordPointer
GetByte()780 uint8_t  SOMFRecordPointer::GetByte() {
781    // Read next byte from buffer
782    return *(buffer + FileOffset + Index++);
783 }
784 
GetWord()785 uint16_t SOMFRecordPointer::GetWord() {
786    // Read next 16 bit word from buffer
787    uint16_t x = *(uint16_t*)(buffer + FileOffset + Index);
788    Index += 2;
789    return x;
790 }
791 
GetDword()792 uint32_t SOMFRecordPointer::GetDword() {
793    // Read next 32 bit dword from buffer
794    uint32_t x = *(uint32_t*)(buffer + FileOffset + Index);
795    Index += 4;
796    return x;
797 }
798 
GetIndex()799 uint32_t SOMFRecordPointer::GetIndex() {
800    // Read byte or word, depending on sign of first byte
801    uint32_t byte1, byte2;
802    byte1 = GetByte();
803    if (byte1 & 0x80) {
804       // Two byte index
805       byte2 = GetByte();
806       return ((byte1 & 0x7F) << 8) | byte2;
807    }
808    else {
809       // One byte index
810       return byte1;
811    }
812 }
813 
GetNumeric()814 uint32_t SOMFRecordPointer::GetNumeric(){
815    // Read word or dword, depending on record type even or odd
816    if (Type & 1) {
817       // Odd record type. Number is 32 bits
818       return GetDword();
819    }
820    else {
821       // Even record type. Number is 16 bit s
822       return GetWord();
823    }
824 }
825 
GetLength()826 uint32_t SOMFRecordPointer::GetLength() {
827    // Read 1, 2, 3 or 4 bytes, depending on value of first byte
828    uint32_t x = GetByte();
829    switch (x) {
830    case 0x81: // 16-bit value
831       return GetWord();
832    case 0x82: // 24-bit value
833       x = GetWord();
834       return (GetByte() << 16) + x;
835    case 0x84: // 32-bit value
836       return GetDword();
837    default: // 8-bit value
838       if (x > 0x80) err.submit(1203);
839       return x;
840    }
841 }
842 
GetString()843 char * SOMFRecordPointer::GetString() {
844    // Read string and return as ASCIIZ string in static buffer
845    static char String[256];
846    uint8_t Length = GetByte();
847    if (Length == 0 /*|| Length >= sizeof(String)*/) {
848       String[0] = 0;
849    }
850    else {
851       // Copy string
852       memcpy(String, buffer + FileOffset + Index, Length);
853       // Terminate by 0
854       String[Length] = 0;
855    }
856    // Point to next
857    Index += Length;
858 
859    return String;
860 }
861 
Start(int8_t * Buffer,uint32_t FileOffset,uint32_t FileEnd)862 void SOMFRecordPointer::Start(int8_t * Buffer, uint32_t FileOffset, uint32_t FileEnd) {
863    // Start scanning through records
864    this->buffer = Buffer;
865    this->FileOffset = FileOffset;
866    this->FileEnd = FileEnd;
867    Index = 0;
868    Type = GetByte();
869    Type2 = Type;  if (Type2 < OMF_LIBHEAD) Type2 &= ~1; // Make even
870    uint16_t RecordSize = GetWord();
871    End = Index + RecordSize - 1;
872    if (FileOffset + RecordSize + 3 > FileEnd) err.submit(2301); // Extends beyond end of file
873 }
874 
GetNext(uint32_t align)875 uint8_t SOMFRecordPointer::GetNext(uint32_t align) {
876    // Get next record. Returns record type, made even. Returns 0 if finished
877    // align = alignment after MODEND records = page size. Applies to lib files only
878    FileOffset += End + 1;
879 
880    // Check if alignment needed
881    if (align > 1 && Type2 == OMF_MODEND) {
882       // Align after MODEND record in library
883       FileOffset = (FileOffset + align - 1) & - (int32_t)align;
884    }
885    if (FileOffset >= FileEnd) return 0;          // End of file
886    Index = 0;                                    // Start reading record
887    Type = GetByte();                             // Get record type
888    Type2 = Type;  if (Type2 < OMF_LIBHEAD) Type2 &= ~1; // Make even
889    uint16_t RecordSize = GetWord();                // Get record size
890    End = Index + RecordSize - 1;                 // Point to checksum byte
891    if ((uint64_t)FileOffset + RecordSize + 3 > FileEnd) err.submit(2301); // Extends beyond end of file
892    return Type2;
893 }
894 
InterpretLIDATABlock()895 uint32_t SOMFRecordPointer::InterpretLIDATABlock() {
896    // Interpret Data block in LIDATA record recursively
897    // Prints repeat count and returns total size
898    uint32_t RepeatCount = GetNumeric();
899    uint32_t BlockCount  = GetWord();
900    uint32_t Size = 0;
901    printf("%i * ", RepeatCount);
902    if (BlockCount == 0) {
903       Size = GetByte();
904       Index += Size;
905       printf("%i", Size);
906       return RepeatCount * Size;
907    }
908    // Nested repeat blocks
909    printf("(");
910    for (uint32_t i = 0; i < BlockCount; i++) {
911       // Recursion
912       Size += InterpretLIDATABlock();
913       if (i+1 < BlockCount) printf(" + ");
914    }
915    printf(")");
916    return RepeatCount * Size;
917 }
918 
919 
UnpackLIDATABlock(int8_t * destination,uint32_t MaxSize)920 uint32_t SOMFRecordPointer::UnpackLIDATABlock(int8_t * destination, uint32_t MaxSize) {
921    // Unpack Data block in LIDATA record recursively and store data at destination
922    uint32_t RepeatCount = GetNumeric();            // Outer repeat count
923    uint32_t BlockCount  = GetWord();               // Inner repeat count
924    uint32_t Size = 0;                              // Size of data expanded so far
925    uint32_t RSize;                                 // Size of recursively expanded data
926    uint32_t SaveIndex;                             // Save Index for repetition
927    uint32_t i, j;                                  // Loop counters
928    if (BlockCount == 0) {
929       // Contains one repeated block
930       Size = GetByte();                          // Size of repeated block
931       if (RepeatCount * Size > MaxSize) {
932          // Data outside allowed area
933          err.submit(2310);                       // Error message
934          Index += Size;                          // Point to after block
935          return 0;                               // No data stored
936       }
937 
938       // Loop RepeatCount times
939       for (i = 0; i < RepeatCount; i++) {
940          // copy data block into destination
941          memcpy(destination, buffer + FileOffset + Index, Size);
942          destination += Size;
943       }
944       Index += Size;                             // Point to after block
945       return RepeatCount * Size;                 // Size of expanded data
946    }
947    // Nested repeat blocks
948    SaveIndex = Index;
949    // Loop RepeatCount times
950    for (i = 0; i < RepeatCount; i++) {
951       // Go back and repeat unpacking
952       Index = SaveIndex;
953       // Loop BlockCount times
954       for (j = 0; j < BlockCount; j++) {
955          // Recursion
956          RSize = UnpackLIDATABlock(destination, MaxSize);
957          destination += RSize;
958          MaxSize -= RSize;
959          Size += RSize;
960       }
961    }
962    return Size;
963 }
964 
965 
966 // Members of COMFFileBuilder, class for building OMF files
COMFFileBuilder()967 COMFFileBuilder::COMFFileBuilder() {
968    // Constructor
969    Index = 0;
970 }
971 
StartRecord(uint8_t type)972 void COMFFileBuilder::StartRecord(uint8_t type) {
973    // Start building new record
974    this->Type = type;                            // Save type
975    RecordStart = Index = GetDataSize();          // Remember start position
976    PutByte(Type);                                // Put type into record
977    PutWord(0);                                   // Reserve space for size, put in later
978 }
979 
EndRecord()980 void COMFFileBuilder::EndRecord() {
981    // Finish building current record
982    // Update length
983    Get<uint16_t>(RecordStart + 1) = GetSize() + 1;
984 
985    // Make checksum
986    int8_t checksum = 0;
987    for (uint32_t i = RecordStart; i < Index; i++) checksum += Buf()[i];
988    PutByte(-checksum);
989 
990    // Check size limit
991    if (GetSize() > 0x407) {
992       err.submit(9005);
993    }
994 }
995 
PutByte(uint8_t x)996 void COMFFileBuilder::PutByte(uint8_t x) {
997    // Put byte into buffer
998    Push(&x, 1);
999    Index++;
1000 }
1001 
PutWord(uint16_t x)1002 void COMFFileBuilder::PutWord(uint16_t x) {
1003    // Put 16 bit word into buffer
1004    Push(&x, 2);
1005    Index += 2;
1006 }
1007 
PutDword(uint32_t x)1008 void COMFFileBuilder::PutDword(uint32_t x) {
1009    // Put 32 bit dword into buffer
1010    Push(&x, 4);
1011    Index += 4;
1012 }
1013 
PutIndex(uint32_t x)1014 void COMFFileBuilder::PutIndex(uint32_t x) {
1015    // Put byte or word into buffer (word if > 0x7F)
1016    if (x < 0x80) {
1017       // One byte
1018       PutByte(x);
1019    }
1020    else {
1021       // Two bytes
1022       if (x > 0x7fff) {
1023          err.submit(2303);             // Index out of range
1024       }
1025       PutByte((uint8_t)(x >> 8) | 0x80); // First byte = high byte | 0x80
1026       PutByte(uint8_t(x));               // Second byte = low byte
1027    }
1028 }
1029 
PutNumeric(uint32_t x)1030 void COMFFileBuilder::PutNumeric(uint32_t x) {
1031    // Put word or dword into buffer, depending on type being even or odd
1032    if (Type & 1) {
1033       PutDword(x);                     // Type is odd, put 32 bits
1034    }
1035    else {
1036       if (x > 0xffff) err.submit(2304);// Index out of range
1037       PutWord(uint16_t(x));              // Type is even, put 16 bits
1038    }
1039 }
1040 
PutString(const char * s)1041 void COMFFileBuilder::PutString(const char * s) {
1042    // Put ASCII string into buffer, preceded by size
1043    uint32_t len = (uint32_t)strlen(s);     // Check length
1044    if (len > 255) {
1045       // String too long
1046       err.submit(1204, s);             // Issue warning
1047       len = 255;                       // Truncate string to 255 characters
1048    }
1049    PutByte(uint8_t(len));                // Store length
1050    Push(s, len);                       // Store len bytes
1051    Index += len;                       // Update index
1052 }
1053 
PutBinary(void * p,uint32_t Size)1054 void COMFFileBuilder::PutBinary(void * p, uint32_t Size) {
1055    // Put binary data of any length
1056    if (Size > 1024) {err.submit(9000); Size = 1024;}  // 1024 bytes size limit
1057    Push(p, Size);
1058    Index += Size;
1059 }
1060 
GetSize()1061 uint32_t COMFFileBuilder::GetSize() {
1062    // Get size of data added so far
1063    if (Index <= RecordStart + 3) return 0;
1064    return Index - RecordStart - 3;     // Type and size fields not included in size
1065 }
1066