1 /****************************  library.cpp  **********************************
2 * Author:        Agner Fog
3 * Date created:  2006-08-27
4 * Last modified: 2017-07-27
5 * Project:       objconv
6 * Module:        library.cpp
7 * Description:
8 * This module contains code for reading, writing and manipulating function
9 * libraries (archives) of the UNIX type and OMF type.
10 *
11 * Copyright 2006-2017 GNU General Public License http://www.gnu.org/licenses
12 *****************************************************************************/
13 
14 #include "stdafx.h"
15 
16 // OMF Library flag names
17 SIntTxt OMFLibraryFlags[] = {
18     {0,      "Flag = 0"},
19     {1,      "Case sensitive"}
20 };
21 
22 
CLibrary()23 CLibrary::CLibrary() {
24     // Constructor
25     CurrentOffset = 0;
26     CurrentNumber = 0;
27     LongNames = 0;
28     LongNamesSize = 0;
29     AlignBy = 0;
30     MemberFileType = 0;
31     RepressWarnings = 0;
32     PageSize = 16;
33 }
34 
35 
Go()36 void CLibrary::Go() {
37     // Do to library whatever the command line says
38     char const * MemberName1 = 0;  // Name of library member
39     char const * MemberName2 = 0;  // Modified name of library member
40     int action = 0;                // Action to take on member
41     int FileType1 = 0;             // File type of current member
42     int WordSize1 = 0;             // Word size of current member
43 
44     if (cmd.DumpOptions && !(cmd.LibraryOptions & CMDL_LIBRARY_EXTRACTMEM)) {
45         // Dump library, but not its members
46         Dump();
47         return;
48     }
49 
50     // Remove path form member names and check member type before extracting or adding members
51     AlignBy = 2;
52     if (GetDataSize()) FixNames();
53 
54     if (err.Number()) return; // Stop if error
55 
56     // check LibraryOptions and whether an output file is specified
57     if (cmd.FileOptions & CMDL_FILE_OUTPUT) {
58         // Output is a library file
59         if ((cmd.OutputFile == 0 || cmd.OutputFile[0] == 0) && !(cmd.LibraryOptions & CMDL_LIBRARY_ADDMEMBER)) {
60             err.submit(2503); // Output file name missing
61             return;
62         }
63         // Check extension
64         if (strstr(cmd.OutputFile, ".lib") && strstr(cmd.OutputFile, ".LIB") && strstr(cmd.OutputFile, ".a")) {
65             err.submit(1101); // Warning wrong extension
66         }
67         if (cmd.OutputType >= IMPORT_LIBRARY_MEMBER) {
68             // Wrong output type
69             if (cmd.OutputType == FILETYPE_ASM) {
70                 // Attempt to disassemble whole library
71                 err.submit(2620);
72             }
73             else {
74                 err.submit(2621);
75             }
76             return;
77         }
78     }
79 
80     // Desired alignment = 2 for COFF and ELF, 8 for Mach-O
81     AlignBy = 2;
82     if (cmd.OutputType == FILETYPE_MACHO_LE) AlignBy = 8;
83 
84     // Determine library type and subtype
85     if (cmd.LibrarySubtype == 0) {
86         switch (cmd.OutputType) {
87         case FILETYPE_OMF: case FILETYPE_OMFLIBRARY:
88             cmd.LibrarySubtype = LIBTYPE_OMF;  break;
89         case FILETYPE_COFF: case FILETYPE_DOS:
90             cmd.LibrarySubtype = LIBTYPE_WINDOWS;  break;
91         case FILETYPE_ELF:
92             cmd.LibrarySubtype = LIBTYPE_LINUX;  break;
93         case FILETYPE_MACHO_LE: case FILETYPE_MACHO_BE:
94             cmd.LibrarySubtype = LIBTYPE_BSD_MAC;  break;
95         case FILETYPE_LIBRARY:
96             switch (cmd.InputType) {
97             case FILETYPE_COFF: case FILETYPE_DOS:
98                 cmd.LibrarySubtype = LIBTYPE_WINDOWS;  break;
99             case FILETYPE_ELF:
100                 cmd.LibrarySubtype = LIBTYPE_LINUX;  break;
101             case FILETYPE_MACHO_LE: case FILETYPE_MACHO_BE:
102                 cmd.LibrarySubtype = LIBTYPE_BSD_MAC;  break;
103             }
104             break;
105         default:
106             cmd.LibrarySubtype = LIBTYPE_SHORTNAMES;
107         }
108     }
109 
110     // Reserve space for data buffer
111     DataBuffer.SetSize(GetBufferSize());
112 
113     // Check options
114     if (!cmd.LibraryOptions) cmd.LibraryOptions = CMDL_LIBRARY_CONVERT;
115 
116     if (cmd.Verbose) {
117         // Tell what we are doing
118         if ((cmd.LibraryOptions & CMDL_LIBRARY_ADDMEMBER) && GetDataSize() == 0) {
119             // Creating library
120             printf("\nCreating library %s (%s)", FileName, GetFileFormatName(cmd.OutputType));
121         }
122         else if (OutputFileName) {
123             // Print input file name
124             printf("\nInput library: %s", FileName);
125             if (cmd.InputType != cmd.OutputType) {
126                 // Converting library type. Print input file type
127                 int InType = cmd.InputType;
128                 if (InType == FILETYPE_LIBRARY || InType == FILETYPE_OMFLIBRARY) InType = cmd.MemberType;
129                 printf(", Format: %s", GetFileFormatName(InType));
130                 if (cmd.DesiredWordSize) printf("%i", cmd.DesiredWordSize);
131             }
132             // Print output file name and type
133             printf(", Output: %s, Format: %s", OutputFileName, GetFileFormatName(cmd.OutputType));
134             if (cmd.DesiredWordSize) printf("%i", cmd.DesiredWordSize);
135         }
136         else {
137             printf("\nExtracting from library file: %s", FileName);
138         }
139     }
140 
141     // Convert library or extract or add or dump all members
142     StartExtracting();                           // Initialize before ExtractMember()
143 
144     // Loop through input library
145     while ((MemberName1 = ExtractMember(&MemberBuffer)) != 0) {
146 
147         // Check if any specific action required for this member
148         action = cmd.SymbolChange(MemberName1, &MemberName2, SYMT_LIBRARYMEMBER);
149         /*
150         if ((action != SYMA_CHANGE_NAME && action != SYMA_EXTRACT_MEMBER) || MemberName2 == 0) {
151             MemberName2 = MemberName1; // No name change
152         } */
153         MemberBuffer.FileName = MemberName1;
154         MemberBuffer.OutputFileName = MemberName2 ? MemberName2 : MemberName1;
155 
156         if (action == SYMA_DELETE_MEMBER) {
157             // Remove this member from library
158             if (cmd.Verbose) {
159                 printf("\nRemoving member %s from library", MemberName1);
160             }
161             continue;
162         }
163         if (action == SYMA_ADD_MEMBER) {
164             // Replace this member with new file
165             // (Message comes later when adding new member)
166             continue;
167         }
168 
169         // Check type of this member
170         FileType1 = MemberBuffer.GetFileType();
171         if (FileType1 == 0) continue;
172         WordSize1 = MemberBuffer.WordSize;
173 
174         if (!(cmd.LibraryOptions & (CMDL_LIBRARY_EXTRACTMEM | CMDL_LIBRARY_ADDMEMBER))) {
175             // Not adding or extracting members. Apply conversion options to all members
176             if (cmd.SymbolChangesRequested() || FileType1 != cmd.OutputType) {
177 
178                 // Check file type before conversion
179                 int FileType0 = MemberBuffer.GetFileType();
180                 // Conversion or name change requested
181                 MemberBuffer.Go();                   // Do required conversion
182                 if (err.Number()) break;             // Stop if error
183                 // Check type again after conversion
184                 FileType1 = MemberBuffer.GetFileType();
185                 if (MemberBuffer.OutputFileName == 0 || FileType1 != FileType0) {
186                     MemberBuffer.OutputFileName = MemberBuffer.SetFileNameExtension(MemberBuffer.FileName);
187                 }
188                 MemberBuffer.FileName = MemberBuffer.OutputFileName;
189             }
190         }
191         if (MemberFileType == 0) MemberFileType = FileType1;
192         if (WordSize == 0) WordSize = WordSize1;
193         if (FileType1 != MemberFileType || WordSize1 != WordSize) {
194             if (WordSize1 == 0) {
195                 // Library member has no wordsize
196                 err.submit(1109, MemberBuffer.FileName);
197             }
198             else {
199                 // Library members have different type or word size
200                 err.submit(1102);
201             }
202         }
203 
204         if (cmd.LibraryOptions & CMDL_LIBRARY_EXTRACTMEM) {
205             // Extract member(s)
206             if (action == SYMA_EXTRACT_MEMBER || cmd.LibraryOptions == CMDL_LIBRARY_EXTRACTALL) {
207                 // Extract this member
208                 if (cmd.DumpOptions == 0 && cmd.OutputType != CMDL_OUTPUT_DUMP) {
209                     // Write this member to file
210                     if (err.Number()) return; // Check first if error
211 
212                     if (cmd.SymbolChangesRequested() || FileType1 != cmd.OutputType) {
213                         // Conversion or name change requested
214 
215                         // Check type before conversion
216                         int FileType0 = MemberBuffer.GetFileType();
217                         MemberBuffer.Go();
218                         if (err.Number()) return; // Stop if error
219                         // Check type after conversion
220                         FileType1 = MemberBuffer.GetFileType();
221                         if (MemberBuffer.OutputFileName == 0 /*|| FileType1 != FileType0*/) {
222                             MemberBuffer.OutputFileName = MemberBuffer.SetFileNameExtension(MemberBuffer.FileName);
223                         }
224                     }
225                     if (MemberBuffer.OutputFileName == 0) {
226                         MemberBuffer.OutputFileName = MemberBuffer.FileName;
227                     }
228                     if (cmd.Verbose) {
229                         // Tell what we are doing
230                         if (MemberName1 == MemberName2) {
231                             printf("\nExtracting file %s from library", MemberName1);
232                         }
233                         else {
234                             printf("\nExtracting library member %s to file %s", MemberName1, MemberBuffer.OutputFileName);
235                         }
236                     }
237                     if (WordSize1 == 0) {
238                         err.submit(1109, MemberName1);
239                     }
240                     // Write this member to file
241                     MemberBuffer.Write();
242                 }
243                 else {
244                     // Dump this member
245                     MemberBuffer.Go();
246                 }
247             }
248         }
249         else if (cmd.DumpOptions == 0) {
250             // Add member to new library
251             if (MemberName2 == 0) MemberName2 = MemberName1;
252             if (cmd.Verbose) {
253                 // Tell what we are doing
254                 if (strcmp(MemberName1, MemberName2) != 0) {
255                     printf("\nRenaming member %s to %s", MemberName1, MemberName2);
256                 }
257             }
258             // Put into new library
259             InsertMember(&MemberBuffer);
260         }
261     } // End of loop through library
262     // Stop if error
263     if (err.Number()) return;
264 
265     if (cmd.LibraryOptions & CMDL_LIBRARY_ADDMEMBER) {
266         // Add object files to library
267         SSymbolChange const * sym;
268         // Loop through file names to add
269         while ((sym = cmd.GetMemberToAdd()) != 0) {
270             MemberBuffer.Reset();                     // Reset MemberBuffer
271             // Name of object file
272             MemberBuffer.FileName = sym->Name2;       // Name of object file
273             MemberBuffer.OutputFileName = sym->Name1; // Name of new member
274             // Read object file
275             MemberBuffer.Read();
276             // Stop if read failed
277             if (err.Number()) continue;
278             // Detect file type
279             int NewMemberType = MemberBuffer.GetFileType();
280             if (cmd.Verbose) {
281                 // Tell what we are doing
282                 if (sym->Done) {
283                     printf("\nReplacing member %s with file %s", sym->Name1, sym->Name2);
284                 }
285                 else {
286                     printf("\nAdding member %s from file %s", sym->Name1, sym->Name2);
287                 }
288                 if (NewMemberType != cmd.OutputType) {
289                     // Converting type
290                     printf(". Converting from %s.", GetFileFormatName(NewMemberType));
291                 }
292             }
293             // Do any conversion required
294             MemberBuffer.Go();
295 
296             // Stop if error
297             if (err.Number()) continue;
298 
299             // Check if file type is right after conversion
300             MemberFileType = MemberBuffer.FileType;
301             if (WordSize == 0) WordSize = MemberBuffer.WordSize;
302             if (MemberFileType != cmd.OutputType) {
303                 // Library members have different type
304                 err.submit(2504, GetFileFormatName(MemberBuffer.FileType)); continue;
305             }
306             if (MemberBuffer.WordSize != WordSize) {
307                 // Library members have different word size
308                 err.submit(2505, MemberBuffer.WordSize); continue;
309             }
310             // Put into library
311             MemberBuffer.FileName = MemberBuffer.OutputFileName;
312             InsertMember(&MemberBuffer);
313         } // End of loop through object file names
314     }
315     // Stop if error
316     if (err.Number()) return;
317 
318     if (cmd.LibraryOptions & CMDL_LIBRARY_EXTRACTMEM) {
319         cmd.CheckExtractSuccess();  // Check if members to extract were found
320     }
321 
322     if (cmd.FileOptions & CMDL_FILE_OUTPUT) {
323         // Make output library
324         MakeBinaryFile();
325         // Take over OutFile buffer
326         *this << OutFile;
327     }
328     else {
329         // No output library
330         OutputFileName = 0;
331     }
332 }
333 
FixNames()334 void CLibrary::FixNames() {
335     // Rebuild library or fix member names
336     // Dispatch according to library type
337     switch (cmd.InputType) {
338     case FILETYPE_OMF: case FILETYPE_OMFLIBRARY:
339         // Rebuild OMF style library and fix long member names
340         RebuildOMF();  break;
341 
342     case FILETYPE_LIBRARY:
343     default:
344         // Fix names in UNIX style library
345         StripMemberNamesUNIX();  break;
346     }
347 }
348 
349 /*  Unused:
350 void CLibrary::Rebuild() {
351     // Rebuild library: fix member names
352     // Dispatch according to library type
353     switch (cmd.InputType) {
354     case FILETYPE_OMF: case FILETYPE_OMFLIBRARY:
355         // Rebuild OMF style library
356         RebuildOMF();  break;
357 
358     case FILETYPE_LIBRARY:
359     default:
360         // Rebuild UNIX style library
361         RebuildUNIX();  break;
362     }
363 }
364 
365 void CLibrary::RebuildUNIX() {
366     // Rebuild UNIX style library
367     // Make member names unique and without path. Rebuild symbol table
368     char const * MemberName1 = 0;  // Name of library member
369     uint32_t OutputFileType;
370 
371     // Save OutputType before changing it
372     OutputFileType = cmd.OutputType;
373 
374     // Loop through input library
375     CurrentOffset = 8;  CurrentNumber = 0;
376     while ((MemberName1 = ExtractMember(&MemberBuffer)) != 0) {
377         // Properties of member
378         MemberBuffer.FileName = MemberBuffer.OutputFileName = MemberName1;
379         // Check if import library
380         if (MemberBuffer.Get<uint32_t>(0) == 0xFFFF0000) {
381             // Import library. Cannot do anything sensible
382             err.submit(2507, cmd.InputFile);  return;
383         }
384         // Remember member type
385         cmd.MemberType = MemberBuffer.GetFileType();
386         // Temporarily set OutputType to MemberType
387         cmd.OutputType = cmd.MemberType;
388         // Put into new library
389         InsertMember(&MemberBuffer);
390     }
391     // Avoid getting warnings twice for duplicate symbols
392     RepressWarnings = 1;
393     // Make library header etc. and add all members to OutFile
394     MakeBinaryFile();
395     RepressWarnings = 0;
396 
397     // Restore OutputType
398     cmd.OutputType = OutputFileType;
399 
400     // Replace file buffer by OutFile
401     *this << OutFile;
402 
403     // Clear buffers used for building OutFile
404     StringBuffer.SetSize(0);
405     StringEntries.SetNum(0);
406     Indexes.SetNum(0);
407     DataBuffer.SetSize(0);
408 }
409 */
410 
RebuildOMF()411 void CLibrary::RebuildOMF() {
412     // Rebuild OMF style library.
413     // Removes paths from member names, truncates to 16 characters, and makes unique.
414     // Symbol table is removed, not remade
415 
416     SOMFRecordPointer rec;                        // Current OMF record
417     char * ModuleName;                            // Module name
418     SStringEntry se;                              // String entry record to save
419     COMFFileBuilder NewBuffer;                    // Buffer for rebuilt library
420     uint32_t Align;                                 // Alignment
421 
422     // Remember member file type
423     cmd.MemberType = FILETYPE_OMF;
424 
425     // Initialize record pointer
426     rec.Start(Buf(), 0, GetDataSize());
427 
428     // Read header
429     if (rec.Type2 != OMF_LIBHEAD) {err.submit(2500); return;} // Does not start with library header
430 
431     // Read library header
432     DictionaryOffset = rec.GetDword();
433     DictionarySize = rec.GetWord();
434     if ((uint64_t)DictionaryOffset + DictionarySize >= GetDataSize()) {err.submit(2035); return;}
435 
436     rec.GetByte(); // Ignore flag
437     // Page size / alignment for members
438     PageSize = rec.End + 1;
439     Align = 1 << FloorLog2(PageSize);       // Make power of 2
440     if (PageSize != Align) {
441         err.submit(2601, PageSize);          // Error: not a power of 2
442     }
443 
444     // Copy library header to new buffer
445     NewBuffer.Push(Buf(), PageSize);
446 
447     // Reset record loop end when DictionaryOffset is known
448     rec.FileEnd = DictionaryOffset;
449 
450     // Next record is start of first module
451     rec.GetNext();
452     if (rec.Type2 != OMF_THEADR) err.submit(2500);  // Member does not start with THEADR
453 
454     // Loop through the records of all OMF modules
455     do {
456         // Check record type
457         switch (rec.Type2) {
458 
459         case OMF_THEADR:     // Start of module
460 
461             // Get name
462             ModuleName = rec.GetString();
463 
464             // Remove path, truncate name and make unique
465             ModuleName = ShortenMemberName(ModuleName);
466 
467             // Remember name to check for duplicates
468             // Not needed any more:
469             se.String = StringBuffer.PushString(ModuleName);
470             se.Member = NewBuffer.GetDataSize();
471             StringEntries.Push(se);
472 
473             // Make new THEADR record
474             NewBuffer.StartRecord(OMF_THEADR);
475             NewBuffer.PutString(ModuleName);
476             NewBuffer.EndRecord();
477             break;
478 
479         case OMF_MODEND: case OMF_LIBEND:   // End of module or library
480             // Copy MODEND record
481             NewBuffer.Push(Buf() + rec.FileOffset, rec.End + 1);
482 
483             // Align output file by PageSize
484             NewBuffer.Align(PageSize);
485             break;
486 
487         default:   // Any other record in module
488             // Copy record unchanged
489             NewBuffer.Push(Buf() + rec.FileOffset, rec.End + 1);
490             break;
491         }
492     }  // Point to next record
493     while (rec.GetNext(PageSize));                // End of loop through records
494 
495     // Put dictionary offset in LIBHEAD record
496     DictionaryOffset = NewBuffer.GetDataSize();
497     NewBuffer.Get<uint32_t>(3) = DictionaryOffset;
498 
499     // Take over modified library file
500     *this << NewBuffer;
501 
502     // Empty used buffers
503     StringEntries.SetNum(0);
504     StringBuffer.SetSize(0);
505 }
506 
507 
StripMemberNamesUNIX()508 void CLibrary::StripMemberNamesUNIX() {
509     // Remove path from member names, set extension to default
510     char * MemberName1 = 0;  // Name of library member
511 
512     // Loop through input library
513     CurrentOffset = 8;  CurrentNumber = 0;
514     while ((MemberName1 = ExtractMember(&MemberBuffer)) != 0) {
515         // Properties of member
516         // Check if import library
517         if (MemberBuffer.Get<uint32_t>(0) == 0xFFFF0000) {
518             // Import library. Cannot do anything sensible
519             err.submit(2507, cmd.InputFile);  return;
520         }
521         if (MemberName1[0] == '/') continue;  // names record
522         // remember member type
523         if (cmd.MemberType == 0) {
524             cmd.MemberType = MemberBuffer.GetFileType();
525         }
526         // Fix name
527         StripMemberName(MemberName1);
528         // Check word size
529         if (cmd.DesiredWordSize == 0) {
530             cmd.DesiredWordSize = MemberBuffer.WordSize;
531         }
532         else if (cmd.DesiredWordSize != MemberBuffer.WordSize && MemberBuffer.WordSize != 0) {
533             err.submit(2012, MemberBuffer.WordSize, cmd.DesiredWordSize);  // wrong word size
534             return;
535         }
536         if (cmd.OutputType == FILETYPE_LIBRARY || cmd.OutputType == FILETYPE_OMFLIBRARY) {
537             cmd.OutputType = cmd.MemberType;
538         }
539     }
540 }
541 
542 
Dump()543 void CLibrary::Dump() {
544     // Print contents of library
545 
546     // Dispatch according to library type
547     switch (cmd.InputType) {
548     case FILETYPE_LIBRARY:
549         DumpUNIX();  break;                        // Print contents of UNIX style library
550 
551     case FILETYPE_OMFLIBRARY:
552         DumpOMF();   break;                        // Print contents of OMF style library
553 
554     default:
555         err.submit(9000);                          // Should not occur
556     }
557 }
558 
DumpOMF()559 void CLibrary::DumpOMF() {
560     // Print contents of OMF style library
561     uint8_t  Flags;                                 // Dictionary flags
562     uint32_t i;                                     // Loop counter
563     uint32_t Align;                                 // Member alignment
564     uint32_t RecordEnd;                             // End of OMF record
565     SOMFRecordPointer rec;                        // Current OMF record
566     char * MemberName;                            // Name of library member
567     char * SymbolName;                            // Name of public symbol in member
568     uint32_t MemberStart = 0;                       // Start of member
569     uint32_t MemberEnd;                             // End of member
570     uint32_t MemberNum = 0;                         // Member number
571     uint32_t FirstPublic;                           // Index to first public name of current member
572     CMemoryBuffer Strings;                        // Local string buffer
573     CSList<SStringEntry> MemberIndex;             // Local member index buffer
574     COMF Member;                                  // Local buffer for member
575 
576     DictionaryOffset = GetDataSize();             // Loop end. This value is changed when library header is read
577     rec.Start(Buf(), 0, DictionaryOffset);        // Initialize record pointer
578 
579     PageSize = 0;
580 
581     printf("\nDump of library %s\nExported symbols by member:\n", cmd.InputFile);
582 
583     // Loop through the records of all OMF modules
584     do {
585         // Check record type
586         switch (rec.Type2) {
587 
588         case OMF_LIBHEAD:  // Library header. This should be the first record
589             if (PageSize || rec.FileOffset > 0) {
590                 err.submit(2600);  break;            // More than one header
591             }
592             // Read library header
593             DictionaryOffset = rec.GetDword();
594             DictionarySize = rec.GetWord();
595             if ((uint64_t)DictionaryOffset + DictionarySize >= GetDataSize()) {err.submit(2035); return;}
596             Flags = rec.GetByte();
597             // Page size / alignment for members
598             PageSize = rec.End + 1;
599             Align = 1 << FloorLog2(PageSize);       // Make power of 2
600             if (PageSize != Align) {
601                 err.submit(2601, PageSize);          // Error: not a power of 2
602             }
603             // Reset record loop end when DictionaryOffset is known
604             rec.FileEnd = DictionaryOffset;
605 
606             // Print values from LIBHEAD
607             printf("\nOMF Library. Page size %i. %s.",
608                 PageSize, Lookup(OMFLibraryFlags, Flags));
609             break;
610 
611         case OMF_THEADR: // Module header. Member starts here
612             MemberName = rec.GetString();           // Get name
613             MemberStart = rec.FileOffset;           // Get start address
614             printf("\nMember %s Offset 0x%X", MemberName, MemberStart);// Print member name
615             break;
616 
617         case OMF_MODEND: // Member ends here.
618             RecordEnd = rec.FileOffset + rec.End + 1;// End of record
619             MemberEnd = RecordEnd;                   // = member end address
620 
621             // Store member in temporary buffer
622             Member.SetSize(0);
623             Member.Push(Buf() + MemberStart, MemberEnd - MemberStart);
624 
625             // Get public names from member
626             FirstPublic = MemberIndex.GetNumEntries();
627             Member.PublicNames(&Strings, &MemberIndex, ++MemberNum);
628 
629             // Print public names
630             for (i = FirstPublic; i < MemberIndex.GetNumEntries(); i++) {
631                 SymbolName = (char*)Strings.Buf() + MemberIndex[i].String;
632                 printf("\n  %s", SymbolName);
633             }
634             // Align next member by PageSize;
635             MemberEnd = (MemberEnd + PageSize - 1) & - (int32_t)PageSize;
636             rec.End = MemberEnd - rec.FileOffset - 1;
637             break;
638 
639         case OMF_LIBEND: // Last member should end here
640             RecordEnd = rec.FileOffset + rec.End + 1;// End of record
641             if (RecordEnd != DictionaryOffset) err.submit(2602);
642             break;
643         }
644     }  // Go to next record
645     while (rec.GetNext());                        // End of loop through records
646 
647     // Check hash table integrity
648     CheckOMFHash(Strings, MemberIndex);
649 
650     // Check if there is an extended library dictionary
651     uint32_t ExtendedDictionaryOffset = DictionaryOffset + DictionarySize * 512;
652 
653     if (ExtendedDictionaryOffset > GetDataSize()) {
654         err.submit(2500);                          // File is truncated
655     }
656     if (ExtendedDictionaryOffset < GetDataSize()) {
657         // Library contains extended dictionary
658         uint32_t ExtendedDictionarySize = GetDataSize() - ExtendedDictionaryOffset;
659         uint8_t DictionaryType = Get<uint8_t>(ExtendedDictionaryOffset); // Read first byte of extended dictionary
660         if (DictionaryType == OMF_LIBEXT) {
661             // Extended dictionary in the official format
662             printf("\nExtended dictionary IBM/MS format. size %i", ExtendedDictionarySize);
663         }
664         else if (ExtendedDictionarySize >= 10 && (DictionaryType == 0xAD || Get<uint16_t>(ExtendedDictionaryOffset + 2) == MemberNum)) {
665             // Extended dictionary in the proprietary Borland format, documented only in US Patent 5408665, 1995
666             printf("\nExtended dictionary Borland format. size %i", ExtendedDictionarySize);
667         }
668         else {
669             // Unknown format
670             printf("\nExtended dictionary size %i, unknown type 0x%02X",
671                 ExtendedDictionarySize, DictionaryType);
672         }
673     }
674 }
675 
DumpUNIX()676 void CLibrary::DumpUNIX() {
677     // Print contents of UNIX style library
678 
679     const char * MemberName = 0;
680     CurrentOffset = 8;  CurrentNumber = 0;
681 
682     printf("\nDump of library %s", cmd.InputFile);
683 
684     if (cmd.DumpOptions & DUMP_SECTHDR) {
685         // dump headers
686         SUNIXLibraryHeader * Header = 0;    // Member header
687         uint32_t MemberSize = 0;              // Size of member
688         //uint32_t HeaderExtra = 0;             // Extra added to size of header
689         //uint32_t NameIndex;                 // Index into long names member
690         char * Name = 0;                    // Name of member
691         int symindex = 0;                   // count symbol index records
692         int i;                              // loop counter
693 
694         // Search for member
695         while (CurrentOffset) {
696             //HeaderExtra = 0;
697             // Extract next library member from input library
698             Header = &Get<SUNIXLibraryHeader>(CurrentOffset);
699             // Size of member
700             MemberSize = (uint32_t)atoi(Header->FileSize);
701             // Member name
702             Name = Header->Name;
703             // Terminate name
704             for (i = 0; i < 15; i++) {
705                 if (Name[i] == ' ') {
706                     Name[i+1] = 0;  break;
707                 }
708             }
709             if (i == 16) Name[i] = 0;
710 
711             if (strncmp(Name, "//", 2) == 0) {
712                 // This is the long names member.
713                 printf("\nLongnames header \"%s\". Offset 0x%X, size 0x%X", Name,
714                     CurrentOffset + (uint32_t)sizeof(SUNIXLibraryHeader), MemberSize);
715             }
716             else if (Name[0] == '/' && Name[1] <= ' ') {
717                 // Symbol index
718                 printf("\nSymbol index %i, \"%s\"", ++symindex, Name);
719             }
720             else if (strncmp(Name, "__.SYMDEF", 9) == 0) {
721                 // Mac/BSD Symbol index
722                 printf("\nSymbol index %i, \"%s\"", ++symindex, Name);
723             }
724             else if (strncmp(Name, "#1/", 3) == 0) {
725                 // Name refers to long name after the header
726                 // This variant is used by Mac and some versions of BSD
727                 //HeaderExtra = atoi(Name+3);
728                 Name += sizeof(SUNIXLibraryHeader);
729                 if (strncmp(Name, "__.SYMDEF", 9) == 0) {
730                     // Symbol table "__.SYMDEF SORTED" as long name
731                     printf("\nSymbol index %i, \"%s\"", ++symindex, Name);
732                 }
733             }
734             else break;
735             // Point to next member
736             CurrentOffset = NextHeader(CurrentOffset);
737         }
738         CurrentOffset = 8;  CurrentNumber = 0;
739     }
740 
741     printf("\n\nExported symbols by member:\n");
742 
743     // Loop through library
744     while (CurrentOffset + sizeof(SUNIXLibraryHeader) < DataSize) {
745 
746         // Reset buffers
747         StringBuffer.SetSize(0);
748         MemberBuffer.Reset();
749         StringEntries.SetNum(0);
750 
751         // Get member name
752         MemberName = ExtractMember(&MemberBuffer);
753         if (MemberName == 0) break;
754         printf("\nMember %s", MemberName);
755         MemberBuffer.FileName = MemberName;
756 
757         // Detect file type of member
758         MemberFileType = MemberBuffer.GetFileType();
759 
760         WordSize = MemberBuffer.WordSize;
761         printf (" - %s", GetFileFormatName(MemberFileType));
762         if (WordSize) {
763             printf("-%i", MemberBuffer.WordSize);
764         }
765         else {
766             printf(". Type not specified. Possibly alias record");
767         }
768 
769         // Get symbol table for specific file type
770         switch (MemberFileType) {
771         case FILETYPE_ELF:
772             if (WordSize == 32) {
773                 // Make instance of file parser, 32 bit template
774                 CELF<ELF32STRUCTURES> elf;
775                 MemberBuffer >> elf;        // Transfer MemberBuffer to elf object
776                 elf.PublicNames(&StringBuffer, &StringEntries, 0);
777                 break;
778             }
779             else {
780                 // Make instance of file parser, 64 bit template
781                 CELF<ELF64STRUCTURES> elf;
782                 MemberBuffer >> elf;        // Transfer MemberBuffer to elf object
783                 elf.PublicNames(&StringBuffer, &StringEntries, 0);
784                 break;
785             }
786 
787         case FILETYPE_COFF: {
788             CCOFF coff;
789             MemberBuffer >> coff;       // Transfer MemberBuffer to coff object
790             coff.PublicNames(&StringBuffer, &StringEntries, 0);
791             break;}
792 
793         case FILETYPE_MACHO_LE:
794             if (WordSize == 32) {
795                 // Make instance of file parser, 32 bit template
796                 CMACHO<MAC32STRUCTURES> mac;
797                 MemberBuffer >> mac;       // Transfer MemberBuffer to coff object
798                 mac.PublicNames(&StringBuffer, &StringEntries, 0);
799                 break;
800             }
801             else {
802                 // Make instance of file parser, 64 bit template
803                 CMACHO<MAC64STRUCTURES> mac;
804                 MemberBuffer >> mac;       // Transfer MemberBuffer to coff object
805                 mac.PublicNames(&StringBuffer, &StringEntries, 0);
806                 break;
807             }
808 
809         case IMPORT_LIBRARY_MEMBER: {
810             // This is an import library
811             char * name1 = (char*)MemberBuffer.Buf() + 20;
812             printf("\n  Import %s from %s", name1, name1 + strlen(name1) + 1);
813             break;}
814 
815         default:
816             printf("\n   Cannot extract symbol names from this file type");
817             break;
818         }
819 
820         // Loop through table of public names
821         for (uint32_t i = 0; i < StringEntries.GetNumEntries(); i++) {
822             uint32_t j = StringEntries[i].String;
823             printf("\n   %s", StringBuffer.Buf() + j);
824         }
825     }
826 }
827 
828 
NextHeader(uint32_t Offset)829 uint32_t CLibrary::NextHeader(uint32_t Offset) {
830 
831     // Loop through library headers.
832     // Input = current offset. Output = next offset
833     SUNIXLibraryHeader * Header;   // Member header
834     int32_t MemberSize;          // Size of member
835     //uint32_t HeaderExtra = 0;    // Extra added to size of header
836     uint32_t NextOffset;         // Offset of next header
837 
838     if (Offset + sizeof(SUNIXLibraryHeader) >= DataSize) {
839         // No more members
840         return 0;
841     }
842     // Find header
843     Header = &Get<SUNIXLibraryHeader>(Offset);
844 
845     // Size of member
846     MemberSize = atoi(Header->FileSize);
847     if (MemberSize < 0 || MemberSize + Offset + sizeof(SUNIXLibraryHeader) > DataSize) {
848         err.submit(2500);  // Points outside file
849         return 0;
850     }
851     if (strncmp(Header->Name, "#1/", 3) == 0) {
852         // Name refers to long name after the header
853         // This variant is used by Mac and some versions of BSD.
854         // HeaderExtra is included in MemberSize:
855         // HeaderExtra = atoi(Header->Name+3);
856     }
857 
858     // Get next offset
859     NextOffset = Offset + sizeof(SUNIXLibraryHeader) + MemberSize;
860     // Round up to align by 2
861     NextOffset = (NextOffset + 1) & ~ 1;
862     // Check if last
863     if (NextOffset >= DataSize) NextOffset = 0;
864     return NextOffset;
865 }
866 
867 
StartExtracting()868 void CLibrary::StartExtracting() {
869     // Initialize before ExtractMember()
870     if (cmd.InputType == FILETYPE_OMFLIBRARY) {
871         SOMFRecordPointer rec;                     // OMF records
872         rec.Start(Buf(), 0, GetDataSize());        // Initialize record pointer
873         if (rec.Type2 != OMF_LIBHEAD) {
874             err.submit(2500);  return;              // This should not happen
875         }
876         // Read library header
877         DictionaryOffset = rec.GetDword();         // Read dictionary offset
878         DictionarySize   = rec.GetWord();          // Read dictionary size
879         rec.GetByte();                             // Read flag
880         // Page size / alignment for members
881         PageSize = rec.End + 1;
882         uint32_t align = 1 << FloorLog2(PageSize);   // Make power of 2
883         if (PageSize != align) {
884             err.submit(2601, PageSize);             // Error: not a power of 2
885         }
886         CurrentOffset = PageSize;                  // Offset to first library member
887     }
888     else {
889         // Unix style library. First header at offset 8
890         CurrentOffset = 8;
891     }
892     // Current member number
893     CurrentNumber = 0;
894 }
895 
896 
ExtractMember(CFileBuffer * Destination)897 char * CLibrary::ExtractMember(CFileBuffer * Destination) {
898     // Extract library member
899     // Dispatch according to library type
900     if (cmd.InputType == FILETYPE_OMFLIBRARY || cmd.InputType == FILETYPE_OMF) {
901         return ExtractMemberOMF(Destination);
902     }
903     else {
904         return ExtractMemberUNIX(Destination);
905     }
906 }
907 
908 
ExtractMemberOMF(CFileBuffer * Destination)909 char * CLibrary::ExtractMemberOMF(CFileBuffer * Destination) {
910     // Extract member of OMF style library
911 
912     uint32_t RecordEnd;                             // End of OMF record
913     SOMFRecordPointer rec;                        // Current OMF record
914     char * MemberName = 0;                        // Name of library member
915     uint32_t MemberStart = 0;                       // Start of member
916     uint32_t MemberEnd = 0;                         // End of member
917 
918     if (CurrentOffset >= DictionaryOffset) return 0;// No more members
919 
920     rec.Start(Buf(), CurrentOffset, DictionaryOffset);// Initialize record pointer
921 
922     // Loop through the records of all OMF modules
923     do {
924         // Check record type
925         switch (rec.Type2) {
926 
927         case OMF_THEADR: // Module header. Member starts here
928             MemberName  = rec.GetString();          // Get name
929             MemberStart = rec.FileOffset;           // Get start address
930             break;
931 
932         case OMF_MODEND: // Member ends here.
933             RecordEnd = rec.FileOffset + rec.End +1;// End of record
934             MemberEnd = RecordEnd;                  // = member end address
935 
936             // Save member as raw data
937             if (Destination) {
938                 Destination->SetSize(0);             // Make sure destination buffer is empty
939                 Destination->FileType = Destination->WordSize = 0;
940                 Destination->Push(Buf() + MemberStart, MemberEnd - MemberStart);
941             }
942 
943             // Align next member by PageSize;
944             rec.GetNext(PageSize);
945             CurrentOffset = rec.FileOffset;
946 
947             // Check name
948             //!!if (MemberName[0] == 0) MemberName = (char*)"NoName!";
949 
950             // Return member name
951             return MemberName;
952 
953         case OMF_LIBEND: // Last member should end here
954             RecordEnd = rec.FileOffset + rec.End + 1;// End of record
955             if (RecordEnd != DictionaryOffset) err.submit(2602);
956 
957             // No more members:
958             return 0;
959         }
960     }  // Go to next record
961     while (rec.GetNext());                        // End of loop through records
962 
963     err.submit(2610);                             // Library end record not found
964     return 0;
965 }
966 
967 
ExtractMemberUNIX(CFileBuffer * Destination)968 char * CLibrary::ExtractMemberUNIX(CFileBuffer * Destination) {
969     // Extract member of UNIX style library
970     // This function is called repeatedly to get each member of library/archive
971     SUNIXLibraryHeader * Header = 0;     // Member header
972     uint32_t MemberSize = 0;              // Size of member
973     uint32_t HeaderExtra = 0;             // Extra added to size of header
974     uint32_t NameIndex;                   // Index into long names member
975     char * Name = 0;                    // Name of member
976     int Skip = 1;                       // Skip record and search for next
977     int i;                              // Loop counter
978     char * p;                           // Used for loop through string
979 
980     if (CurrentOffset == 0 || CurrentOffset + sizeof(SUNIXLibraryHeader) >= DataSize) {
981         // No more members
982         return 0;
983     }
984 
985     // Search for member
986     while (Skip && CurrentOffset) {
987         HeaderExtra = 0;
988         // Extract next library member from input library
989         Header = &Get<SUNIXLibraryHeader>(CurrentOffset);
990         // Size of member
991         MemberSize = (uint32_t)atoi(Header->FileSize);
992         if (MemberSize + CurrentOffset + sizeof(SUNIXLibraryHeader) > DataSize) {
993             err.submit(2500);  // Points outside file
994             return 0;
995         }
996         // Member name
997         Name = Header->Name;
998         if (strncmp(Name, "// ", 3) == 0) {
999             // This is the long names member. Remember its position
1000             LongNames = CurrentOffset + sizeof(SUNIXLibraryHeader);
1001             LongNamesSize = MemberSize;
1002             // The long names are terminated by '/' or 0, depending on system,
1003             // but may contain non-terminating '/'. Find out which type we have:
1004             // Pointer to LongNames record
1005             p = (char*)Buf() + LongNames;
1006             // Find out whether we have terminating zeroes:
1007             if ((LongNamesSize > 1 && p[LongNamesSize-1] == '/') || (p[LongNamesSize-1] <= ' ' && p[LongNamesSize-2] == '/')) {
1008                 // Names are terminated by '/'. Replace all '/' by 0 in the longnames record
1009                 for (uint32_t j = 0; j < LongNamesSize; j++, p++) {
1010                     if (*p == '/') *p = 0;
1011                 }
1012             }
1013         }
1014         else if (strncmp(Name, "/ ", 2) == 0
1015             || strncmp(Name, "__.SYMDEF", 9) == 0) {
1016                 // This is a symbol index member.
1017                 // The symbol index is not used because we are always building a new symbol index.
1018         }
1019         else if (Name[0] == '/' && Name[1] >= '0' && Name[1] <= '9' && LongNames) {
1020             // Name contains index into LongNames record
1021             NameIndex = atoi(Name+1);
1022             if (NameIndex < LongNamesSize) {
1023                 Name = (char*)Buf() + LongNames + NameIndex;
1024             }
1025             else {
1026                 Name = (char*)"NoName!";
1027             }
1028             Skip = 0;
1029         }
1030         else if (strncmp(Name, "#1/", 3) == 0) {
1031             // Name refers to long name after the header
1032             // This variant is used by Mac and some versions of BSD
1033             HeaderExtra = atoi(Name+3);
1034             Name += sizeof(SUNIXLibraryHeader);
1035             if (MemberSize > HeaderExtra) {
1036                 // The length of the name, HeaderExtra, is included in the
1037                 // Header->FileSize field. Subtract to get the real file size
1038                 MemberSize -= HeaderExtra;
1039             }
1040             if (strncmp(Name, "__.SYMDEF", 9) == 0) {
1041                 // Symbol table "__.SYMDEF SORTED" as long name
1042                 Skip = 1;
1043             }
1044             else {
1045                 Skip = 0;
1046             }
1047         }
1048         else {
1049             // Ordinary short name
1050             // Name may be terminated by '/' or space. Replace termination char by 0
1051             for (i = 15; i >= 0; i--) {
1052                 if (Name[i] == ' ' || Name[i] == '/') Name[i] = 0;
1053                 else break;
1054             }
1055             // Terminate name with max length by overwriting Date field, which we are not using
1056             Name[16] = 0;
1057             Skip = 0;
1058         }
1059         // Point to next member
1060         CurrentOffset = NextHeader(CurrentOffset);
1061         // Increment number
1062         CurrentNumber += !Skip;
1063     }  // End of while loop
1064 
1065     // Save member as raw data
1066     if (Destination) {
1067         Destination->SetSize(0);       // Make sure destination buffer is empty
1068         Destination->FileType = Destination->WordSize = 0;
1069         Destination->Push((int8_t*)Header + sizeof(SUNIXLibraryHeader) + HeaderExtra, MemberSize);
1070     }
1071 
1072     // Check name
1073     if (Name[0] == 0) Name = (char*)"NoName!";
1074 
1075     // Return member name
1076     return Name;
1077 }
1078 
InsertMember(CFileBuffer * member)1079 void CLibrary::InsertMember(CFileBuffer * member) {
1080     // Add member to output library
1081     if (cmd.OutputType == FILETYPE_OMF) {
1082         InsertMemberOMF(member);                   // OMF style output library
1083     }
1084     else {
1085         InsertMemberUNIX(member);                  // UNIX style output library
1086     }
1087 }
1088 
1089 
InsertMemberOMF(CFileBuffer * member)1090 void CLibrary::InsertMemberOMF(CFileBuffer * member) {
1091     // Add member to OMF library
1092 
1093     // Check file type
1094     if (member->GetFileType() != FILETYPE_OMF) err.submit(9000);
1095 
1096     // Get word size
1097     WordSize = member->WordSize;
1098 
1099     // Store offset
1100     uint32_t offset = DataBuffer.GetDataSize();
1101     Indexes.Push(offset);
1102 
1103     // Store member
1104     DataBuffer.Push(member->Buf(), member->GetDataSize());
1105     DataBuffer.Align(PageSize);
1106 
1107     // Member index
1108     uint32_t mindex = Indexes.GetNumEntries() - 1;
1109 
1110     // Get public string table
1111     COMF omf;
1112     *member >> omf;     // Translate member to class OMF
1113     omf.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names
1114     *member << omf;     // Return buffer to member
1115 }
1116 
1117 
InsertMemberUNIX(CFileBuffer * member)1118 void CLibrary::InsertMemberUNIX(CFileBuffer * member) {
1119     // Add next library member to output library
1120     uint32_t RawSize = 0;                 // Size of binary file
1121     uint32_t AlignmentPadding = 0;        // Padding after file
1122     char * name = 0;                    // Name of member
1123     int NameLength = 0;                 // length of name
1124     int NameAfter = 0;                  // length of name after MachO header
1125     int i;                              // loop counter, index
1126 
1127     // Get word size
1128     WordSize = member->WordSize;
1129 
1130     // Make member header
1131     SUNIXLibraryHeader header;
1132     memset(&header, ' ', sizeof(SUNIXLibraryHeader));  // Fill with spaces
1133 
1134     // Name of member
1135     if (member->OutputFileName == 0 || *member->OutputFileName == 0) member->OutputFileName = member->FileName;
1136 
1137     if (cmd.LibrarySubtype == LIBTYPE_SHORTNAMES) {
1138         // Make short name
1139         name = ShortenMemberName(member->OutputFileName);
1140     }
1141     else {
1142         // Remove path from library member name. Original long name is overwritten
1143         name = StripMemberName((char*)(member->OutputFileName));
1144     }
1145     NameLength = (int)strlen(name);
1146 
1147     if (cmd.OutputType == FILETYPE_MACHO_LE && cmd.LibrarySubtype != LIBTYPE_SHORTNAMES) {
1148         // Mach-O library stores name after header record.
1149         // Name is zero padded to length 4 modulo 8 to align by 8
1150         int pad = 8 - ((NameLength + 4) & 7);
1151         NameAfter = NameLength + pad;
1152         sprintf(header.Name, "#1/%i ", NameAfter);
1153     }
1154     else {
1155         // ELF and COFF library store names < 16 characters in the name field
1156         if (NameLength < 16) {
1157             // (Don't use sprintf to write header.Name here: It seems that Gnu sprintf checks the size of the
1158             // buffer it is writing to. Gives buffer overrun error when termniating zero goes beyond the name field)
1159             memset(header.Name, ' ', 16);
1160             memcpy(header.Name, name, NameLength);
1161             header.Name[NameLength] = '/';
1162         }
1163         else {
1164             // store in LongNamesBuffer
1165             if (cmd.OutputType == FILETYPE_COFF) {
1166                 // COFF: Name is zero-terminated
1167                 i = LongNamesBuffer.PushString(name);
1168             }
1169             else {
1170                 // ELF: Name terminated by "/\n"
1171                 i = LongNamesBuffer.Push(name, NameLength);
1172                 LongNamesBuffer.Push("/\n", 2);
1173             }
1174             // store index into long names member
1175             sprintf(header.Name, "/%i ", i);
1176         }
1177     }
1178 
1179     // Date
1180     sprintf(header.Date, "%u ", (uint32_t)time(0));
1181 
1182     // User and group id
1183     header.UserID[0] = '0';
1184     header.GroupID[0] = '0';
1185     // File mode
1186     strcpy(header.FileMode, "100666");
1187     // Size of binary file
1188     RawSize = member->GetDataSize();
1189     // Calculate alignment padding
1190     if (AlignBy) {
1191         AlignmentPadding = uint32_t(-int32_t(RawSize)) & (AlignBy-1);
1192     }
1193 
1194     // File size including name string
1195     sprintf(header.FileSize, "%u ", NameAfter + RawSize + AlignmentPadding);
1196 
1197     // Header end
1198     header.HeaderEnd[0] = '`';
1199     header.HeaderEnd[1] = '\n';
1200 
1201     // Remove terminating zeroes
1202     for (uint32_t i = 0; i < sizeof(SUNIXLibraryHeader); i++) {
1203         if (((char*)&header)[i] == 0) ((char*)&header)[i] = ' ';
1204     }
1205 
1206     // Store offset
1207     uint32_t offset = DataBuffer.GetDataSize();
1208     Indexes.Push(offset);
1209 
1210     // Store member header
1211     DataBuffer.Push(&header, sizeof(header));
1212 
1213     if (cmd.OutputType == FILETYPE_MACHO_LE) {
1214         // Store member name after header if Mach-O
1215         if (NameAfter) {
1216             // Mach-O library stores name after header record.
1217             DataBuffer.PushString(name);
1218         }
1219         DataBuffer.Align(AlignBy);
1220     }
1221 
1222     // Store member
1223     DataBuffer.Push(member->Buf(), RawSize);
1224 
1225     // Align by padding with '\n'
1226     for (uint32_t i = 0; i < AlignmentPadding; i++) {
1227         DataBuffer.Push("\n", 1);
1228     }
1229 
1230     // Member index
1231     uint32_t mindex = Indexes.GetNumEntries() - 1;
1232 
1233     // Get public string table
1234     switch(member->GetFileType()) {
1235     case FILETYPE_COFF: {
1236         CCOFF coff;
1237         *member >> coff;     // Translate member to type COFF
1238         coff.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names
1239         *member << coff;     // Return buffer to member
1240         break;}
1241 
1242     case FILETYPE_OMF: {
1243         COMF omf;
1244         *member >> omf;      // Translate member to type COFF
1245         omf.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names
1246         *member << omf;      // Return buffer to member
1247         break;}
1248 
1249     case FILETYPE_ELF:
1250         if (WordSize == 32) {
1251             // Make instance of file parser, 32 bit template
1252             CELF<ELF32STRUCTURES> elf;
1253             *member >> elf;      // Translate member to type ELF
1254             elf.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names
1255             *member << elf;      // Return buffer to member
1256             break;
1257         }
1258         else {
1259             // Make instance of file parser, 64 bit template
1260             CELF<ELF64STRUCTURES> elf;
1261             *member >> elf;      // Translate member to type ELF
1262             elf.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names
1263             *member << elf;      // Return buffer to member
1264             break;
1265         }
1266 
1267     case FILETYPE_MACHO_LE:
1268         if (WordSize == 32) {
1269             // Make instance of file parser, 32 bit template
1270             CMACHO<MAC32STRUCTURES> mac;
1271             *member >> mac;      // Translate member to type ELF
1272             mac.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names
1273             *member << mac;      // Return buffer to member
1274             break;
1275         }
1276         else {
1277             // Make instance of file parser, 64 bit template
1278             CMACHO<MAC64STRUCTURES> mac;
1279             *member >> mac;      // Translate member to type ELF
1280             mac.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names
1281             *member << mac;      // Return buffer to member
1282             break;
1283         }
1284 
1285     default:                // Type not supported
1286         err.submit(2501, GetFileFormatName(member->GetFileType()));
1287         break;
1288     }
1289 }
1290 
1291 /*   unused:
1292 char * CLibrary::TruncateMemberName(char const * name) {
1293     // Remove path and truncate object file name to 15 characters
1294     // This function removes any path from the member name,
1295     // changes the extension to the default for the the output file type,
1296     // changes any spaces to underscores, and
1297     // truncates the member name to 15 characters for the sake of compatibility.
1298     // The return value is an ASCII string in a static buffer
1299     static char TruncName[32];          // Truncated name
1300     int maxlen;                         // Max length, not including extension
1301     char const * p1;                    // Point to start of name without path
1302     char const * extension;             // Default extension for file type
1303     int i;                              // Loop counter
1304     int len;                            // String length
1305     static int DummyNumber = 0;         // Count invalid/null names
1306     int FileType;                       // File type
1307 
1308     // Remove path
1309     len = (int)strlen(name);  p1 = name;
1310     for (i = len-1; i >= 0; i--) {
1311         if (name[i] == '/' || name[i] == '\\' || name[i] == ':') {
1312             p1 = name + i + 1; break;
1313         }
1314     }
1315     // Remove extension
1316     len = (int)strlen(p1);
1317     for (i = len-1; i >= 0; i--) {
1318         if (p1[i] == '.') {
1319             len = i;  break;
1320         }
1321     }
1322 
1323     // Check if any name remains
1324     if (len == 0) {     // No name. Make one
1325         sprintf(TruncName, "NoName%i", ++DummyNumber);
1326         p1 = TruncName;  len = (int)strlen(p1);
1327     }
1328 
1329     // Get file type
1330     FileType = cmd.OutputType;
1331     if (FileType == CMDL_OUTPUT_DUMP || FileType == 0) FileType = cmd.InputType;
1332     if (FileType >= FILETYPE_LIBRARY) FileType = cmd.MemberType;
1333 
1334     // Get default extension and max length of name without extension
1335     if (FileType == FILETYPE_COFF || FileType == FILETYPE_OMF) {
1336         maxlen = 11;  extension = ".obj";
1337     }
1338     else {
1339         maxlen = 13;  extension = ".o";
1340     }
1341     if (len > maxlen) len = maxlen;
1342 
1343     // Truncate name
1344     strncpy(TruncName, p1, len);
1345     TruncName[len] = 0;
1346 
1347     // Remove any spaces or other illegal characters
1348     len = (int)strlen(TruncName);
1349     for (i = 0; i < len; i++) {
1350         if ((uint8_t)TruncName[i] <= 0x20) TruncName[i] = '_';
1351     }
1352 
1353     // Add default extension
1354     strcpy(TruncName+strlen(TruncName), extension);
1355 
1356     // Terminate
1357     TruncName[15] = 0;
1358     return TruncName;
1359 }
1360 */
1361 
StripMemberName(char * name)1362 char * CLibrary::StripMemberName(char * name) {
1363     // Remove path from zero-terminated library member name and set extension to default.
1364     // Note: Original long name is overwritten
1365     char * p1;                          // Point to start of name without path
1366     const char * extension = 0;         // Default extension for file type
1367     int i;                              // Loop counter
1368     int len0;                           // Original string length
1369     int len;                            // String length
1370     int nlen;                           // length of name without extension
1371     int elen = 0;                       // length of extension
1372     static int DummyNumber = 0;         // Count invalid/null names
1373     int FileType;                       // File type
1374 
1375     // Length
1376     len0 = len = (int)strlen(name);
1377 
1378     // Remove path
1379     p1 = name;
1380     for (i = len-1; i >= 0; i--) {
1381         if (name[i] == '/' || name[i] == '\\' || name[i] == ':') {
1382             p1 = name + i + 1; break;
1383         }
1384     }
1385     // move to begin of buffer
1386     if (p1 > name) {
1387         strcpy(name, p1);
1388     }
1389 
1390     // Get file type
1391     if (cmd.MemberType) {
1392         FileType = cmd.MemberType;
1393     }
1394     else if (cmd.LibraryOptions & CMDL_LIBRARY_EXTRACTMEM) {
1395         FileType = cmd.InputType;
1396     }
1397     else {
1398         FileType = cmd.OutputType;
1399     }
1400     if (FileType == CMDL_OUTPUT_DUMP || FileType == 0) FileType = cmd.InputType;
1401     if (FileType >= FILETYPE_LIBRARY) FileType = cmd.MemberType;
1402 
1403     // Get default extension and max length of name without extension
1404     if (FileType == FILETYPE_COFF || FileType == FILETYPE_OMF) {
1405         extension = ".obj";  elen = 4;
1406     }
1407     else if (FileType == FILETYPE_ELF || FileType == FILETYPE_MACHO_LE || FileType == FILETYPE_MACHO_BE) {
1408         extension = ".o";  elen = 2;
1409     }
1410 
1411     // find extension
1412     len = (int)strlen(name);
1413     for (nlen = len-1; nlen >= 0; nlen--) {
1414         if (name[nlen] == '.') {
1415             break;
1416         }
1417     }
1418 
1419     // Remove any spaces or other illegal characters
1420     len = (int)strlen(name);
1421     for (i = 0; i < nlen; i++) {
1422         if ((uint8_t)name[i] <= 0x20 || name[i] == '.') name[i] = '_';
1423     }
1424 
1425     // Check if any name remains
1426     if ((len == 0 && len0 > 12) || nlen == 0) {     // No name. Make one
1427         sprintf(name, "NoName%i", ++DummyNumber);
1428         len = (int)strlen(name);
1429     }
1430 
1431     // Replace extension
1432     if (len + elen <= len0 && extension != 0) {
1433         strcpy(name + nlen, extension);
1434     }
1435     // Terminate
1436     return name;
1437 }
1438 
1439 
ShortenMemberName(char const * name)1440 char * CLibrary::ShortenMemberName(char const *name) {
1441     // Truncate library member name to 15 characters and make unique
1442     // The path is removed and the extension set to default.
1443     // The original long name is not overwritten
1444     static char fixedName[32];          // Modified name
1445     char const * p1;                    // Point to start of name without path
1446     char const * extension;             // Default extension for file type
1447     int i;                              // Loop counter
1448     int len;                            // Filename length
1449     int len0;                           // Filename length without extension
1450     int elen;                           // length of extension
1451     static int RunningNumber = 0;       // Enumerate truncated names
1452     int FileType;                       // File type
1453 
1454     // Length
1455     len = (int)strlen(name);
1456 
1457     // Skip path
1458     p1 = name;
1459     for (i = len-1; i >= 0; i--) {
1460         if (name[i] == '/' || name[i] == '\\' || name[i] == ':') {
1461             p1 = name + i + 1; break;
1462         }
1463     }
1464     len = (int)strlen(name);
1465 
1466     // move to static buffer
1467     if (len > 15) len = 15;
1468     memcpy(fixedName, p1, len);
1469     fixedName[len] = 0;
1470 
1471     // find extension
1472     for (i = len-1; i >= 0; i--) {
1473         if (fixedName[i] == '.') {
1474             fixedName[i] = 0;  break;
1475         }
1476     }
1477     // length without extension
1478     len0 = (int)strlen(fixedName);
1479 
1480     // Remove any spaces or other illegal characters
1481     for (i = 0; i < len0; i++) {
1482         if ((uint8_t)fixedName[i] <= 0x20 || fixedName[i] == '.') fixedName[i] = '_';
1483     }
1484 
1485     // Check if any name remains
1486     if (len0 == 0) {     // No name. Make one
1487         sprintf(fixedName, "NoName_%X", RunningNumber++);
1488         len0 = (int)strlen(fixedName);
1489     }
1490 
1491     // Get file type
1492     FileType = cmd.OutputType;
1493     if (FileType == CMDL_OUTPUT_DUMP || FileType == 0) FileType = cmd.InputType;
1494     if (FileType >= FILETYPE_LIBRARY) FileType = cmd.MemberType;
1495 
1496     // Get default extension and max length of name without extension
1497     if (FileType == FILETYPE_COFF || FileType == FILETYPE_OMF) {
1498         extension = ".obj";  elen = 4;
1499     }
1500     else {
1501         extension = ".o";  elen = 2;
1502     }
1503 
1504     // Make unique and add extension
1505     if (len0 + elen >= 15) {
1506         // Name is truncated or possibly identical to some other truncated name.
1507         // Insert 2-, 3- or 4-digit running hexadecimal number.
1508         if (RunningNumber < 0x100) {
1509             sprintf(fixedName + 12 - elen, "_%02X%s", RunningNumber++, extension);
1510         }
1511         else if (RunningNumber < 0x1000) {
1512             sprintf(fixedName + 12 - elen, "%03X%s", RunningNumber++, extension);
1513         }
1514         else {
1515             sprintf(fixedName + 11 - elen, "%04X%s", (RunningNumber++ & 0xFFFF), extension);
1516         }
1517     }
1518     else {
1519         // Short name. Just add extension
1520         strcpy(fixedName + len0, extension);
1521     }
1522 
1523     // Return static name buffer
1524     return fixedName;
1525 }
1526 
1527 /*  Unused:
1528 int CLibrary::MemberNameExistsUNIX(char * name) {
1529     // Check if DataBuffer contains a member with this name
1530     char Name1[20], Name2[20];
1531     uint32_t i, j;
1532 
1533     // Terminate name without extension
1534     memcpy(Name1, name, 16);
1535     for (j = 0; j < 16; j++) {
1536         if (Name1[j] == '.' || Name1[j] == '/') Name1[j] = 0;
1537     }
1538 
1539     // Loop through previous members in DataBuffer
1540     for (i = 0; i < Indexes.GetNumEntries(); i++) {
1541         uint32_t offset = Indexes[i];
1542         // Copy name of member i
1543         memcpy(Name2, DataBuffer.Buf() + offset, 16);
1544         // Terminate name2
1545         for (j = 0; j < 16; j++) {
1546             if (Name2[j] == '.' || Name2[j] == '/') Name2[j] = 0;
1547         }
1548         // Case-insensitive compare of names
1549         if (stricmp(Name1, Name2) == 0) {
1550             // Identical name found
1551             return i + 1;
1552         }
1553     }
1554     // No identical name found
1555     return 0;
1556 }*/
1557 
1558 
SortStringTable()1559 void CLibrary::SortStringTable() {
1560     // Sort the string table in ASCII order
1561 
1562     // Length of table
1563     int32_t n = StringEntries.GetNumEntries();
1564     if (n <= 0) return;
1565 
1566     // Point to table of SStringEntry records
1567     SStringEntry * Table = &StringEntries[0];
1568     // String pointers
1569     char * s1, * s2;
1570     // Temporary record for swapping
1571     SStringEntry temp;
1572 
1573     // Simple Bubble sort:
1574     int32_t i, j;
1575     for (i = 0; i < n; i++) {
1576         for (j = 0; j < n - i - 1; j++) {
1577             s1 = (char*)StringBuffer.Buf() + Table[j].String;
1578             s2 = (char*)StringBuffer.Buf() + Table[j+1].String;
1579             if (strcmp(s1, s2) > 0) {
1580                 // Swap records
1581                 temp = Table[j];
1582                 Table[j] = Table[j+1];
1583                 Table[j+1] = temp;
1584             }
1585         }
1586     }
1587     // Now StringEntries has been sorted. Reorder StringBuffer to the sort order.
1588     CMemoryBuffer SortedStringBuffer;    // Temporary buffer for strings in sort order
1589     for (i = 0; i < n; i++) {
1590         // Pointer to old string
1591         s1 = (char*)StringBuffer.Buf() + Table[i].String;
1592         // Update table to point to new string
1593         Table[i].String = SortedStringBuffer.GetDataSize();
1594         // Put string into SortedStringBuffer
1595         SortedStringBuffer.PushString(s1);
1596     }
1597     if (SortedStringBuffer.GetDataSize() != StringBuffer.GetDataSize()) {
1598         // The two string buffers should be same size
1599         err.submit(9000); return;
1600     }
1601     // Copy SortedStringBuffer into StringBuffer
1602     memcpy(StringBuffer.Buf(), SortedStringBuffer.Buf(), StringBuffer.GetDataSize());
1603 
1604     // Check for duplicate symbols
1605     for (i = 0; i < n-1; i++) {
1606         s1 = (char*)StringBuffer.Buf() + Table[i].String;
1607         for (j = i + 1; j < n; j++) {
1608             s2 = (char*)StringBuffer.Buf() + Table[j].String;
1609             if (strcmp(s1,s2) == 0) {
1610                 // Duplicate found
1611                 // Compose error string "Modulename1 and Modulename2"
1612                 uint32_t errstring = LongNamesBuffer.GetDataSize();
1613                 LongNamesBuffer.PushString(GetModuleName(Table[i].Member));
1614                 LongNamesBuffer.SetSize(LongNamesBuffer.GetDataSize()-1); // remove terminating zero
1615                 LongNamesBuffer.Push(" and ", 5);
1616                 LongNamesBuffer.PushString(GetModuleName(Table[j].Member));
1617                 err.submit(1214, s1, (char*)LongNamesBuffer.Buf() + errstring);
1618                 LongNamesBuffer.SetSize(errstring);  // remove string again
1619                 i++;  // Prevent excessive messages
1620             }
1621             else {
1622                 break;
1623             }
1624         }
1625     }
1626 }
1627 
1628 
EndianChange(uint32_t n)1629 uint32_t EndianChange(uint32_t n) {
1630     // Convert little-endian to big-endian number, or vice versa
1631     return (n << 24) | ((n & 0x0000FF00) << 8) | ((n & 0x00FF0000) >> 8) | (n >> 24);
1632 }
1633 
1634 
RoundEven(uint32_t n)1635 uint32_t RoundEven(uint32_t n) {
1636     // Round up number to nearest even
1637     return (n + 1) & uint32_t(-2);
1638 }
1639 
1640 
Round4(uint32_t n)1641 uint32_t Round4(uint32_t n) {
1642     // Round up number to nearest multiple of 4
1643     return (n + 3) & uint32_t(-4);
1644 }
1645 
1646 
MakeSymbolTableUnix()1647 void CLibrary::MakeSymbolTableUnix() {
1648     // Make symbol table for COFF, ELF or MACHO library
1649     // Uses UNIX archive format for COFF, BSD and Mac
1650     uint32_t i;                              // Loop counter
1651     uint32_t MemberOffset;                   // Offset to member
1652     uint32_t LongNameSize = 0;               // Length of symbol table name if stored after record
1653 
1654     int SymbolTableType = cmd.OutputType;  // FILETYPE_COFF       = 1: COFF
1655     // FILETYPE_ELF        = 3: ELF
1656     // FILETYPE_MACHO_LE   = 4: Mac, unsorted
1657     //              0x10000004: Mac, sorted
1658     // Newer Mac tools require the sorted type, unless there are multiple publics with same name
1659     if (SymbolTableType == FILETYPE_MACHO_LE) SymbolTableType |= 0x10000000;
1660 
1661     // Make symbol table header
1662     SUNIXLibraryHeader SymTab;             // Symbol table header
1663     memset(&SymTab, ' ', sizeof(SymTab));  // Fill with spaces
1664     SymTab.Name[0] = '/';                  // Name = '/'
1665     // The silly Mac linker requires that the symbol table has a date stamp not
1666     // older than the .a file. Fix this by post-dating the symbol table:
1667     uint32_t PostDate = 0;
1668     if (SymbolTableType & 0x10000000) PostDate = 100; // Post-date if mac sorted symbol table
1669     sprintf(SymTab.Date, "%u ", (uint32_t)time(0) + PostDate); // Date stamp for symbol table
1670 
1671     SymTab.UserID[0] = '0';                // UserID = 0  (may be omitted in COFF)
1672     SymTab.GroupID[0] = '0';               // GroupID = 0 (may be omitted in COFF)
1673     strcpy(SymTab.FileMode, "100666");     // FileMode = 0100666 (may be 0 in COFF)
1674 
1675     SymTab.HeaderEnd[0] = '`';             // End with "`\n"
1676     SymTab.HeaderEnd[1] = '\n';
1677 
1678     // File header
1679     OutFile.Push("!<arch>\n", 8);
1680 
1681     uint32_t NumMembers = Indexes.GetNumEntries();        // Number of members
1682     uint32_t NumStrings = StringEntries.GetNumEntries();  // Number of symbol names
1683     uint32_t StringsLen = StringBuffer.GetDataSize();     // Size of string table
1684 
1685     // Calculate sizes of string index records, not including header
1686     // Unsorted index, used in ELF and COFF libraries
1687     uint32_t Index1Size = (NumStrings+1)*4 + StringsLen;
1688     // Sorted index, used in COFF libraries as second member
1689     uint32_t Index2Size = (NumMembers+2)*4 + NumStrings*2 + StringsLen;
1690     // Sorted index, used in Mach-O libraries
1691     uint32_t Index3Size = Round4(NumStrings*8 + 8 + StringsLen);
1692     // Longnames member
1693     uint32_t LongnamesMemberSize = 0;
1694     // Official MS COFF reference says that the "//" longnames member must be present,
1695     // even if it is unused, but MS LIB does not make it unless it is needed.
1696     // Here, we will include the longnames member only if it is needed
1697     if ((SymbolTableType == FILETYPE_COFF || SymbolTableType == FILETYPE_ELF) && LongNamesBuffer.GetDataSize()) {
1698         LongnamesMemberSize = sizeof(SUNIXLibraryHeader) + LongNamesBuffer.GetDataSize();
1699     }
1700 
1701     // Offset to first member
1702     uint32_t FirstMemberOffset = 0;
1703     switch (SymbolTableType) {
1704     case FILETYPE_COFF:
1705         FirstMemberOffset = 8 + 2*sizeof(SUNIXLibraryHeader) + RoundEven(Index1Size)
1706             + RoundEven(Index2Size) + RoundEven(LongnamesMemberSize);
1707         break;
1708     case FILETYPE_ELF:
1709         FirstMemberOffset = 8 + sizeof(SUNIXLibraryHeader) + RoundEven(Index1Size)
1710             + RoundEven(LongnamesMemberSize);
1711         break;
1712     case FILETYPE_MACHO_LE:
1713         FirstMemberOffset = 8 + sizeof(SUNIXLibraryHeader) + Index3Size;
1714         break;
1715     case FILETYPE_MACHO_LE | 0x10000000: // Mac, sorted
1716         LongNameSize = 20;
1717         FirstMemberOffset = 8 + sizeof(SUNIXLibraryHeader) + Index3Size + LongNameSize;
1718         break;
1719     default:
1720         err.submit(2501, GetFileFormatName(cmd.OutputType));
1721     }
1722 
1723     // Make unsorted symbol table for COFF or ELF output
1724     if (SymbolTableType == FILETYPE_COFF || SymbolTableType == FILETYPE_ELF) {
1725 
1726         // Put file size into symbol table header
1727         sprintf(SymTab.FileSize, "%u ", Index1Size);
1728         // Remove terminating zeroes
1729         for (i = 0; i < sizeof(SymTab); i++) {
1730             if (((char*)&SymTab)[i] == 0) ((char*)&SymTab)[i] = ' ';
1731         }
1732 
1733         // Store header
1734         OutFile.Push(&SymTab, sizeof(SymTab));
1735 
1736         // Store table of offsets
1737         uint32_t BigEndian;             // Number converted to big-endian
1738         BigEndian = EndianChange(NumStrings);
1739         OutFile.Push(&BigEndian, sizeof(BigEndian));  // Number of symbols
1740 
1741         // Loop through strings
1742         for (i = 0; i < NumStrings; i++) {
1743             // Get record in temporary symbol table
1744             SStringEntry * psym = &StringEntries[i];
1745             // Get offset of member in DataBuffer
1746             MemberOffset = Indexes[psym->Member];
1747             // Add size of headers to compute member offset in final file
1748             BigEndian = EndianChange(MemberOffset + FirstMemberOffset);
1749             // Store offset as big endian number
1750             OutFile.Push(&BigEndian, sizeof(BigEndian));
1751         }
1752 
1753         // Store strings
1754         OutFile.Push(StringBuffer.Buf(), StringBuffer.GetDataSize());
1755         // Align by 2
1756         if (OutFile.GetDataSize() & 1) {
1757             OutFile.Push("\n", 1);
1758         }
1759     }
1760 
1761     // Sort string table
1762     if (!RepressWarnings && SymbolTableType != FILETYPE_MACHO_LE) SortStringTable();
1763 
1764     // Make sorted symbol table, COFF style
1765     if (SymbolTableType == FILETYPE_COFF) {
1766         if (NumMembers > 0xFFFF) err.submit(2502);  // Too many members
1767 
1768         // Reuse symbol table header, change size entry
1769         sprintf(SymTab.FileSize, "%u ", Index2Size);
1770 
1771         // Remove terminating zeroes
1772         for (i = 0; i < sizeof(SymTab); i++) {
1773             if (((char*)&SymTab)[i] == 0) ((char*)&SymTab)[i] = ' ';
1774         }
1775         // Store header
1776         OutFile.Push(&SymTab, sizeof(SymTab));
1777 
1778         // Store number of members
1779         OutFile.Push(&NumMembers, sizeof(NumMembers));
1780 
1781         // Store member offsets
1782         for (i = 0; i < NumMembers; i++) {
1783             MemberOffset = Indexes[i] + FirstMemberOffset;
1784             OutFile.Push(&MemberOffset, sizeof(MemberOffset));
1785         }
1786 
1787         // Store number of symbols
1788         OutFile.Push(&NumStrings, sizeof(NumStrings));
1789 
1790         // Store member index for each string
1791         // Loop through strings
1792         for (i = 0; i < NumStrings; i++) {
1793             // Get record in temporary symbol table
1794             SStringEntry * psym = &StringEntries[i];
1795             // Get member index, 16 bits
1796             uint16_t MemberI = (uint16_t)(psym->Member + 1);
1797             OutFile.Push(&MemberI, sizeof(MemberI));
1798         }
1799 
1800         // Store strings
1801         OutFile.Push(StringBuffer.Buf(), StringBuffer.GetDataSize());
1802         // Align by 2
1803         if (OutFile.GetDataSize() & 1) {
1804             OutFile.Push("\n", 1);
1805         }
1806     }
1807 
1808     // Make longnames table member for COFF or ELF output
1809     // (The decision whether to include a "//" longnames member is taken above)
1810     if (LongnamesMemberSize) {
1811         // reuse SymTab
1812         strcpy(SymTab.Name, "//       ");    // Name = "//"
1813         memset(SymTab.FileSize, ' ', 10);
1814         sprintf(SymTab.FileSize, "%u", LongNamesBuffer.GetDataSize());
1815 
1816         // Remove terminating zeroes
1817         for (i = 0; i < sizeof(SymTab); i++) {
1818             if (((char*)&SymTab)[i] == 0) ((char*)&SymTab)[i] = ' ';
1819         }
1820         // Store header
1821         OutFile.Push(&SymTab, sizeof(SymTab));
1822         // Store data
1823         OutFile.Push(LongNamesBuffer.Buf(), LongNamesBuffer.GetDataSize());
1824         // Align by 2
1825         if (OutFile.GetDataSize() & 1) {
1826             OutFile.Push("\n", 1);
1827         }
1828     }
1829 
1830     // Make sorted or unsorted symbol table, Mach-O style
1831     if ((SymbolTableType & 0xFFFF) == FILETYPE_MACHO_LE) {
1832 
1833         if (SymbolTableType & 0x10000000) {
1834             // Sorted table. "__.SYMDEF SORTED" stored as long name
1835             memcpy(SymTab.Name, "#1/20           ", 16);
1836             // Put file size into symbol table header, including long name length
1837             sprintf(SymTab.FileSize, "%u ", Index3Size + LongNameSize);
1838         }
1839         else {
1840             // Unsorted table. "__.SYMDEF" stored as short name
1841             memcpy(SymTab.Name, "__.SYMDEF       ", 16);
1842             // Put file size into symbol table header
1843             sprintf(SymTab.FileSize, "%u ", Index3Size);
1844         }
1845 
1846         // Remove terminating zeroes
1847         for (i = 0; i < sizeof(SymTab); i++) {
1848             if (((char*)&SymTab)[i] == 0) ((char*)&SymTab)[i] = ' ';
1849         }
1850 
1851         // Store header
1852         OutFile.Push(&SymTab, sizeof(SymTab));
1853 
1854         if (SymbolTableType & 0x10000000) {
1855             // Store long name "__.SYMDEF SORTED"
1856             OutFile.Push("__.SYMDEF SORTED\0\0\0\0", LongNameSize);
1857         }
1858 
1859         // Store an array of records of string index and member offsets
1860         // Store length first
1861         uint32_t ArrayLength = NumStrings * sizeof(SStringEntry);
1862         OutFile.Push(&ArrayLength, sizeof(ArrayLength));
1863 
1864         // Loop through strings
1865         for (i = 0; i < NumStrings; i++) {
1866             // Get record in temporary symbol table
1867             SStringEntry * psym = &StringEntries[i];
1868             SStringEntry Record;
1869             Record.String = psym->String;
1870             Record.Member = Indexes[psym->Member] + FirstMemberOffset;
1871             // Store symbol record
1872             OutFile.Push(&Record, sizeof(Record));
1873         }
1874 
1875         // Store length of string table
1876         StringsLen = Round4(StringsLen);             // Round up to align by 4
1877         OutFile.Push(&StringsLen, sizeof(StringsLen));
1878         // Store strings
1879         OutFile.Push(StringBuffer.Buf(), StringBuffer.GetDataSize());
1880         // Align by 4
1881         OutFile.Align(4);
1882         // Cross check precalculated size (8 is the size of "!<arch>\n" file identifier)
1883         if (OutFile.GetDataSize() != Index3Size + sizeof(SymTab) + 8 + LongNameSize) err.submit(9000);
1884     }
1885 }
1886 
1887 
MakeBinaryFile()1888 void CLibrary::MakeBinaryFile() {
1889     if (cmd.OutputType == FILETYPE_OMF) {
1890         MakeBinaryFileOMF();                       // OMF style output library
1891     }
1892     else {
1893         MakeBinaryFileUNIX();                      // UNIX style output library
1894     }
1895 }
1896 
1897 
MakeBinaryFileOMF()1898 void CLibrary::MakeBinaryFileOMF() {
1899     // Make OMF library
1900     uint32_t PageSize;                              // Page size / alignment for output library
1901     uint32_t temp;                                  // Temporary
1902     uint16_t temp16;                                // Temporary
1903     uint8_t  temp8;                                 // Temporary
1904     uint32_t MemberI;                               // Member number
1905     uint32_t MemberOffset;                          // File offset of member in output file
1906     uint32_t MemberStart;                           // Start of member in DataBuffer
1907     uint32_t MemberEnd;                             // End of member in DataBuffer
1908     uint32_t SymbolI;                               // Public symbol number
1909     uint32_t DictionaryOffset2;                     // Offset to hash table
1910     CSList<uint32_t> MemberPageIndex;               // Remember page index of each member
1911 
1912     // Check number of entries
1913     if (DataBuffer.GetNumEntries() >= 0x8000) {
1914         err.submit(2606);  return;                 // Error: too big
1915     }
1916 
1917     // Find optimal page size
1918     PageSize = DataBuffer.GetDataSize() / (0x8000 - DataBuffer.GetNumEntries());
1919     // Make power of 2, minimum 16
1920     temp = FloorLog2(PageSize) + 1;
1921     if (temp < 4) temp = 4;
1922     PageSize = 1 << temp;
1923 
1924     // Make library header
1925     temp8 = OMF_LIBHEAD;
1926     OutFile.Push(&temp8, 1);                      // Library header type byte
1927     temp16 = PageSize - 3;
1928     OutFile.Push(&temp16, 2);                     // Record length
1929     OutFile.Push(0, 6);                           // Dictionary offset and size: insert later
1930     temp8 = 1;
1931     OutFile.Push(&temp8, 1);                      // Flag: case sensitive
1932     OutFile.Align(PageSize);                      // Align for first member
1933 
1934     // Allocate MemberPageIndex
1935     MemberPageIndex.SetNum(Indexes.GetNumEntries());
1936 
1937     // Insert members
1938     for (MemberI = 0; MemberI < Indexes.GetNumEntries(); MemberI++) {
1939 
1940         // Find member in DataBuffer
1941         MemberStart = Indexes[MemberI];            // Start of member in DataBuffer
1942         if (MemberI+1 < Indexes.GetNumEntries()) {
1943             // Not last member
1944             MemberEnd = Indexes[MemberI+1];         // End of member in DataBuffer = start of next member
1945         }
1946         else {
1947             // Last member
1948             MemberEnd = DataBuffer.GetDataSize();   // End of member in DataBuffer = end of DataBuffer
1949         }
1950 
1951         // Put member into output file
1952         MemberOffset = OutFile.Push(DataBuffer.Buf() + MemberStart, MemberEnd - MemberStart);
1953 
1954         // Align next member
1955         OutFile.Align(PageSize);
1956 
1957         // Member page index
1958         MemberPageIndex[MemberI] = MemberOffset / PageSize;
1959     }
1960 
1961     // Change member index to member page index in StringEntries
1962     // Loop through StringEntries
1963     for (SymbolI = 0; SymbolI < StringEntries.GetNumEntries(); SymbolI++) {
1964         // Member index
1965         MemberI = StringEntries[SymbolI].Member;
1966         if (MemberI < MemberPageIndex.GetNumEntries()) {
1967             // Change to member page
1968             StringEntries[SymbolI].Member = MemberPageIndex[MemberI];
1969         }
1970     }
1971 
1972     // Make OMF_LIBEND record
1973     temp8 = OMF_LIBEND;
1974     OutFile.Push(&temp8, 1);                      // Library header type byte
1975     temp16 = PageSize - 3;                        // Length of rest of record
1976     OutFile.Push(&temp16, 2);                     // Record length
1977     OutFile.Align(PageSize);                      // Align
1978 
1979     // Offset to hash table
1980     DictionaryOffset2 = OutFile.GetDataSize();
1981 
1982     // Make hash table for public symbols
1983     COMFHashTable HashTable;
1984     HashTable.MakeHashTable(StringEntries, StringBuffer, OutFile, this); // Make hash table
1985 
1986     // Insert missing values in library header
1987     // Hash table offset
1988     OutFile.Get<uint32_t>(3) = DictionaryOffset2;
1989 
1990     // Hash table size
1991     OutFile.Get<uint16_t>(7) = (OutFile.GetDataSize() - DictionaryOffset2) / OMFBlockSize;
1992 }
1993 
1994 
MakeBinaryFileUNIX()1995 void CLibrary::MakeBinaryFileUNIX() {
1996     // Make UNIX library
1997     // Combine string index and members into binary file
1998 
1999     // Reserve file buffer for output file
2000     OutFile.SetSize(GetBufferSize());
2001 
2002     if (cmd.OutputType == FILETYPE_COFF || cmd.OutputType == FILETYPE_ELF || cmd.OutputType == FILETYPE_MACHO_LE) {
2003         // COFF, ELF and MAach-O libraries all use Unix-style archive with
2004         // differences in symbol table format
2005 
2006         // Make symbol table
2007         MakeSymbolTableUnix();
2008 
2009         // Store all members
2010         OutFile.Push(DataBuffer.Buf(), DataBuffer.GetDataSize());
2011     }
2012     else {
2013         err.submit(2501, GetFileFormatName(cmd.OutputType));
2014     }
2015 }
2016 
2017 
CheckOMFHash(CMemoryBuffer & stringbuf,CSList<SStringEntry> & index)2018 void CLibrary::CheckOMFHash(CMemoryBuffer &stringbuf, CSList<SStringEntry> &index) {
2019     // Check if OMF library hash table has correct entries for all symbol names
2020     uint32_t i;                                     // Loop counter
2021     char * Name;                                    // Public symbol name
2022     COMFHashTable HashTab;                          // OMF hash table interpreter
2023     uint32_t NString;                               // Number of occurrences of Name in hash table
2024     uint32_t Module;                                // Module with first occurrence of Name
2025     uint32_t Conf, ConfSum = 0;                     // Count number of conflicting entries in hash table
2026 
2027     // Initialize hash table interpreter
2028     HashTab.Init(&Get<SOMFHashBlock>(DictionaryOffset), DictionarySize);
2029 
2030     // Loop through public symbol names
2031     for (i = 0; i < index.GetNumEntries(); i++) {
2032         // Get public name
2033         Name = (char*)stringbuf.Buf() + index[i].String;
2034         // Make hash
2035         HashTab.MakeHash(Name);
2036         // Search for string
2037         NString = HashTab.FindString(Module, Conf);
2038         // Count conflicting strings
2039         ConfSum += Conf;
2040 
2041         // Make error message if not 1 occurrence of Name
2042         if (NString == 0) err.submit(2603, Name);  // Error if not found
2043         if (NString >  1) err.submit(1213, NString, Name);  // Warning more than one occurrence
2044 
2045         //printf("\n%i occurence of %s, module offset %i", NString, Name, Module);
2046     }
2047     printf("\n\nHash table %i blocks x 37 buckets at offet 0x%X.\n Efficiency: %i conflicts for %i entries",
2048         DictionarySize, DictionaryOffset, ConfSum, index.GetNumEntries());
2049 }
2050 
2051 
GetModuleName(uint32_t Index)2052 const char * CLibrary::GetModuleName(uint32_t Index) {
2053     // Get name of module from index (UNIX) or page index (OMF)
2054     static char name[32];
2055     if (cmd.OutputType == FILETYPE_OMF || cmd.OutputType == FILETYPE_OMFLIBRARY) {
2056         // Get name of module in OMF library
2057         if (Index * PageSize < OutFile.GetDataSize() && OutFile.Get<uint8_t>(Index * PageSize) == OMF_THEADR) {
2058             SOMFRecordPointer rec;                     // Record pointer
2059             rec.Start(OutFile.Buf(), Index * PageSize, OutFile.GetDataSize());
2060             if (rec.Type2 == OMF_THEADR) {
2061                 // Get module name from THEADR record
2062                 strncpy(name, rec.GetString(), 16);
2063                 // Make sure name is not too long
2064                 name[16] = 0;
2065                 // Return name
2066                 return name;
2067             }
2068         }
2069         // No module starts here
2070         return "?";
2071     }
2072     // UNIX style library.
2073     if (Index < Indexes.GetNumEntries()) {
2074         // Get offset from Index
2075         uint32_t Offset = Indexes[Index];
2076         if (Offset < DataBuffer.GetDataSize()) {
2077             // Copy name from header
2078             memcpy(name, DataBuffer.Buf() + Offset, 16);
2079             // Check for long name
2080             if (strncmp(name, "#1/", 3) == 0) {
2081                 // Long name after record
2082                 memcpy(name, DataBuffer.Buf()+Offset+sizeof(SUNIXLibraryHeader), 16);
2083             }
2084             else if (name[0] == '/') {
2085                 // Long name in longnames record
2086                 uint32_t NameIndex = atoi(name+1);
2087                 if (NameIndex < LongNamesSize) {
2088                     return (char*)Buf() + LongNames + NameIndex;
2089                 }
2090                 else {
2091                     return "?";
2092                 }
2093             }
2094 
2095             // Find terminating '/'
2096             for (int i = 0; i < 16; i++) if (name[i] == '/') name[i] = 0;
2097             // Make sure name is not too long
2098             name[16] = 0;
2099             // return name
2100             return name;
2101         }
2102     }
2103     // Error
2104     return "?";
2105 }
2106