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