1 /**
2  * A library in the OMF format, a legacy format for 32-bit Windows.
3  *
4  * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/libomf.d, _libomf.d)
8  * Documentation:  https://dlang.org/phobos/dmd_libomf.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/libomf.d
10  */
11 
12 module dmd.libomf;
13 
14 version(Windows):
15 
16 import core.stdc.stdio;
17 import core.stdc.string;
18 import core.stdc.stdlib;
19 
20 import dmd.globals;
21 import dmd.utils;
22 import dmd.lib;
23 
24 import dmd.root.array;
25 import dmd.root.file;
26 import dmd.root.filename;
27 import dmd.root.rmem;
28 import dmd.root.outbuffer;
29 import dmd.root.string;
30 import dmd.root.stringtable;
31 
32 import dmd.scanomf;
33 
34 // Entry point (only public symbol in this module).
35 extern (C++) Library LibOMF_factory()
36 {
37     return new LibOMF();
38 }
39 
40 private: // for the remainder of this module
41 
42 enum LOG = false;
43 
44 struct OmfObjSymbol
45 {
46     char* name;
47     OmfObjModule* om;
48 
49     /// Predicate for `Array.sort`for name comparison
50     static int name_pred (scope const OmfObjSymbol** ppe1, scope const OmfObjSymbol** ppe2) nothrow @nogc pure
51     {
52         return strcmp((**ppe1).name, (**ppe2).name);
53     }
54 }
55 
56 alias OmfObjModules = Array!(OmfObjModule*);
57 alias OmfObjSymbols = Array!(OmfObjSymbol*);
58 
59 extern (C) uint _rotl(uint value, int shift);
60 extern (C) uint _rotr(uint value, int shift);
61 
62 final class LibOMF : Library
63 {
64     OmfObjModules objmodules; // OmfObjModule[]
65     OmfObjSymbols objsymbols; // OmfObjSymbol[]
66     StringTable!(OmfObjSymbol*) tab;
67 
68     extern (D) this()
69     {
70         tab._init(14_000);
71     }
72 
73     /***************************************
74      * Add object module or library to the library.
75      * Examine the buffer to see which it is.
76      * If the buffer is NULL, use module_name as the file name
77      * and load the file.
78      */
79     override void addObject(const(char)[] module_name, const ubyte[] buffer)
80     {
81         static if (LOG)
82         {
83             printf("LibOMF::addObject(%.*s)\n", cast(int)module_name.length,
84                    module_name.ptr);
85         }
86 
87         void corrupt(int reason)
88         {
89             error("corrupt OMF object module %.*s %d",
90                   cast(int)module_name.length, module_name.ptr, reason);
91         }
92 
93         auto buf = buffer.ptr;
94         auto buflen = buffer.length;
95         if (!buf)
96         {
97             assert(module_name.length, "No module nor buffer provided to `addObject`");
98             // read file and take buffer ownership
99             auto data = readFile(Loc.initial, module_name).extractSlice();
100             buf = data.ptr;
101             buflen = data.length;
102         }
103         uint g_page_size;
104         ubyte* pstart = cast(ubyte*)buf;
105         bool islibrary = false;
106         /* See if it's an OMF library.
107          * Don't go by file extension.
108          */
109         struct LibHeader
110         {
111         align(1):
112             ubyte recTyp; // 0xF0
113             ushort pagesize;
114             uint lSymSeek;
115             ushort ndicpages;
116         }
117 
118         /* Determine if it is an OMF library, an OMF object module,
119          * or something else.
120          */
121         if (buflen < (LibHeader).sizeof)
122             return corrupt(__LINE__);
123         const lh = cast(const(LibHeader)*)buf;
124         if (lh.recTyp == 0xF0)
125         {
126             /* OMF library
127              * The modules are all at buf[g_page_size .. lh.lSymSeek]
128              */
129             islibrary = 1;
130             g_page_size = lh.pagesize + 3;
131             buf = cast(ubyte*)(pstart + g_page_size);
132             if (lh.lSymSeek > buflen || g_page_size > buflen)
133                 return corrupt(__LINE__);
134             buflen = lh.lSymSeek - g_page_size;
135         }
136         else if (lh.recTyp == '!' && memcmp(lh, "!<arch>\n".ptr, 8) == 0)
137         {
138             error("COFF libraries not supported");
139             return;
140         }
141         else
142         {
143             // Not a library, assume OMF object module
144             g_page_size = 16;
145         }
146         bool firstmodule = true;
147 
148         void addOmfObjModule(char* name, void* base, size_t length)
149         {
150             auto om = new OmfObjModule();
151             om.base = cast(ubyte*)base;
152             om.page = cast(ushort)((om.base - pstart) / g_page_size);
153             om.length = cast(uint)length;
154             /* Determine the name of the module
155              */
156             if (firstmodule && module_name && !islibrary)
157             {
158                 // Remove path and extension
159                 om.name = FileName.removeExt(FileName.name(module_name));
160             }
161             else
162             {
163                 /* Use THEADR name as module name,
164                  * removing path and extension.
165                  */
166                 om.name = FileName.removeExt(FileName.name(name.toDString()));
167             }
168             firstmodule = false;
169             this.objmodules.push(om);
170         }
171 
172         if (scanOmfLib(&addOmfObjModule, cast(void*)buf, buflen, g_page_size))
173             return corrupt(__LINE__);
174     }
175 
176     /*****************************************************************************/
177 
178     void addSymbol(OmfObjModule* om, const(char)[] name, int pickAny = 0)
179     {
180         assert(name.length == strlen(name.ptr));
181         static if (LOG)
182         {
183             printf("LibOMF::addSymbol(%.*s, %.*s, %d)\n",
184                 cast(int)om.name.length, om.name.ptr,
185                 cast(int)name.length, name.ptr, pickAny);
186         }
187         if (auto s = tab.insert(name, null))
188         {
189             auto os = new OmfObjSymbol();
190             os.name = cast(char*)Mem.check(strdup(name.ptr));
191             os.om = om;
192             s.value = os;
193             objsymbols.push(os);
194         }
195         else
196         {
197             // already in table
198             if (!pickAny)
199             {
200                 const s2 = tab.lookup(name);
201                 assert(s2);
202                 const os = s2.value;
203                 error("multiple definition of %.*s: %.*s and %.*s: %s",
204                     cast(int)om.name.length, om.name.ptr,
205                     cast(int)name.length, name.ptr,
206                     cast(int)os.om.name.length, os.om.name.ptr, os.name);
207             }
208         }
209     }
210 
211 private:
212     /************************************
213      * Scan single object module for dictionary symbols.
214      * Send those symbols to LibOMF::addSymbol().
215      */
216     void scanObjModule(OmfObjModule* om)
217     {
218         static if (LOG)
219         {
220             printf("LibMSCoff::scanObjModule(%s)\n", om.name.ptr);
221         }
222 
223         extern (D) void addSymbol(const(char)[] name, int pickAny)
224         {
225             this.addSymbol(om, name, pickAny);
226         }
227 
228         scanOmfObjModule(&addSymbol, om.base[0 .. om.length], om.name.ptr, loc);
229     }
230 
231     /***********************************
232      * Calculates number of pages needed for dictionary
233      * Returns:
234      *      number of pages
235      */
236     ushort numDictPages(uint padding)
237     {
238         ushort ndicpages;
239         ushort bucksForHash;
240         ushort bucksForSize;
241         uint symSize = 0;
242         foreach (s; objsymbols)
243         {
244             symSize += (strlen(s.name) + 4) & ~1;
245         }
246         foreach (om; objmodules)
247         {
248             size_t len = om.name.length;
249             if (len > 0xFF)
250                 len += 2; // Digital Mars long name extension
251             symSize += (len + 4 + 1) & ~1;
252         }
253         bucksForHash = cast(ushort)((objsymbols.dim + objmodules.dim + HASHMOD - 3) / (HASHMOD - 2));
254         bucksForSize = cast(ushort)((symSize + BUCKETSIZE - padding - padding - 1) / (BUCKETSIZE - padding));
255         ndicpages = (bucksForHash > bucksForSize) ? bucksForHash : bucksForSize;
256         //printf("ndicpages = %u\n",ndicpages);
257         // Find prime number greater than ndicpages
258         __gshared uint* primes =
259         [
260             1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,
261             47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103,
262             107, 109, 113, 127, 131, 137, 139, 149, 151, 157,
263             163, 167, 173, 179, 181, 191, 193, 197, 199, 211,
264             223, 227, 229, 233, 239, 241, 251, 257, 263, 269,
265             271, 277, 281, 283, 293, 307, 311, 313, 317, 331,
266             337, 347, 349, 353, 359, 367, 373, 379, 383, 389,
267             397, 401, 409, 419, 421, 431, 433, 439, 443, 449,
268             457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
269             //521,523,541,547,
270             0
271         ];
272         for (size_t i = 0; 1; i++)
273         {
274             if (primes[i] == 0)
275             {
276                 // Quick and easy way is out.
277                 // Now try and find first prime number > ndicpages
278                 uint prime;
279                 for (prime = (ndicpages + 1) | 1; 1; prime += 2)
280                 {
281                     // Determine if prime is prime
282                     for (uint u = 3; u < prime / 2; u += 2)
283                     {
284                         if ((prime / u) * u == prime)
285                             goto L1;
286                     }
287                     break;
288                 L1:
289                 }
290                 ndicpages = cast(ushort)prime;
291                 break;
292             }
293             if (primes[i] > ndicpages)
294             {
295                 ndicpages = cast(ushort)primes[i];
296                 break;
297             }
298         }
299         return ndicpages;
300     }
301 
302     /*******************************************
303      * Write the module and symbol names to the dictionary.
304      * Returns:
305      *      false   failure
306      */
307     bool FillDict(ubyte* bucketsP, ushort ndicpages)
308     {
309         // max size that will fit in dictionary
310         enum LIBIDMAX = (512 - 0x25 - 3 - 4);
311         ubyte[4 + LIBIDMAX + 2 + 1] entry;
312         //printf("FillDict()\n");
313         // Add each of the module names
314         foreach (om; objmodules)
315         {
316             ushort n = cast(ushort)om.name.length;
317             if (n > 255)
318             {
319                 entry[0] = 0xFF;
320                 entry[1] = 0;
321                 *cast(ushort*)(entry.ptr + 2) = cast(ushort)(n + 1);
322                 memcpy(entry.ptr + 4, om.name.ptr, n);
323                 n += 3;
324             }
325             else
326             {
327                 entry[0] = cast(ubyte)(1 + n);
328                 memcpy(entry.ptr + 1, om.name.ptr, n);
329             }
330             entry[n + 1] = '!';
331             *(cast(ushort*)(n + 2 + entry.ptr)) = om.page;
332             if (n & 1)
333                 entry[n + 2 + 2] = 0;
334             if (!EnterDict(bucketsP, ndicpages, entry.ptr, n + 1))
335                 return false;
336         }
337         // Sort the symbols
338         objsymbols.sort!(OmfObjSymbol.name_pred);
339         // Add each of the symbols
340         foreach (os; objsymbols)
341         {
342             ushort n = cast(ushort)strlen(os.name);
343             if (n > 255)
344             {
345                 entry[0] = 0xFF;
346                 entry[1] = 0;
347                 *cast(ushort*)(entry.ptr + 2) = n;
348                 memcpy(entry.ptr + 4, os.name, n);
349                 n += 3;
350             }
351             else
352             {
353                 entry[0] = cast(ubyte)n;
354                 memcpy(entry.ptr + 1, os.name, n);
355             }
356             *(cast(ushort*)(n + 1 + entry.ptr)) = os.om.page;
357             if ((n & 1) == 0)
358                 entry[n + 3] = 0;
359             if (!EnterDict(bucketsP, ndicpages, entry.ptr, n))
360             {
361                 return false;
362             }
363         }
364         return true;
365     }
366 
367     /**********************************************
368      * Create and write library to libbuf.
369      * The library consists of:
370      *      library header
371      *      object modules...
372      *      dictionary header
373      *      dictionary pages...
374      */
375     protected override void WriteLibToBuffer(OutBuffer* libbuf)
376     {
377         /* Scan each of the object modules for symbols
378          * to go into the dictionary
379          */
380         foreach (om; objmodules)
381         {
382             scanObjModule(om);
383         }
384         uint g_page_size = 16;
385         /* Calculate page size so that the number of pages
386          * fits in 16 bits. This is because object modules
387          * are indexed by page number, stored as an unsigned short.
388          */
389         while (1)
390         {
391         Lagain:
392             static if (LOG)
393             {
394                 printf("g_page_size = %d\n", g_page_size);
395             }
396             uint offset = g_page_size;
397             foreach (om; objmodules)
398             {
399                 uint page = offset / g_page_size;
400                 if (page > 0xFFFF)
401                 {
402                     // Page size is too small, double it and try again
403                     g_page_size *= 2;
404                     goto Lagain;
405                 }
406                 offset += OMFObjSize(om.base, om.length, om.name.ptr);
407                 // Round the size of the file up to the next page size
408                 // by filling with 0s
409                 uint n = (g_page_size - 1) & offset;
410                 if (n)
411                     offset += g_page_size - n;
412             }
413             break;
414         }
415         /* Leave one page of 0s at start as a dummy library header.
416          * Fill it in later with the real data.
417          */
418         libbuf.fill0(g_page_size);
419         /* Write each object module into the library
420          */
421         foreach (om; objmodules)
422         {
423             uint page = cast(uint)(libbuf.length / g_page_size);
424             assert(page <= 0xFFFF);
425             om.page = cast(ushort)page;
426             // Write out the object module om
427             writeOMFObj(libbuf, om.base, om.length, om.name.ptr);
428             // Round the size of the file up to the next page size
429             // by filling with 0s
430             uint n = (g_page_size - 1) & libbuf.length;
431             if (n)
432                 libbuf.fill0(g_page_size - n);
433         }
434         // File offset of start of dictionary
435         uint offset = cast(uint)libbuf.length;
436         // Write dictionary header, then round it to a BUCKETPAGE boundary
437         ushort size = (BUCKETPAGE - (cast(short)offset + 3)) & (BUCKETPAGE - 1);
438         libbuf.writeByte(0xF1);
439         libbuf.writeword(size);
440         libbuf.fill0(size);
441         // Create dictionary
442         ubyte* bucketsP = null;
443         ushort ndicpages;
444         ushort padding = 32;
445         for (;;)
446         {
447             ndicpages = numDictPages(padding);
448             static if (LOG)
449             {
450                 printf("ndicpages = %d\n", ndicpages);
451             }
452             // Allocate dictionary
453             if (bucketsP)
454                 bucketsP = cast(ubyte*)Mem.check(realloc(bucketsP, ndicpages * BUCKETPAGE));
455             else
456                 bucketsP = cast(ubyte*)Mem.check(malloc(ndicpages * BUCKETPAGE));
457             memset(bucketsP, 0, ndicpages * BUCKETPAGE);
458             for (uint u = 0; u < ndicpages; u++)
459             {
460                 // 'next available' slot
461                 bucketsP[u * BUCKETPAGE + HASHMOD] = (HASHMOD + 1) >> 1;
462             }
463             if (FillDict(bucketsP, ndicpages))
464                 break;
465             padding += 16; // try again with more margins
466         }
467         // Write dictionary
468         libbuf.write(bucketsP[0 .. ndicpages * BUCKETPAGE]);
469         if (bucketsP)
470             free(bucketsP);
471         // Create library header
472         struct Libheader
473         {
474         align(1):
475             ubyte recTyp;
476             ushort recLen;
477             uint trailerPosn;
478             ushort ndicpages;
479             ubyte flags;
480             uint filler;
481         }
482 
483         Libheader libHeader;
484         memset(&libHeader, 0, (Libheader).sizeof);
485         libHeader.recTyp = 0xF0;
486         libHeader.recLen = 0x0D;
487         libHeader.trailerPosn = offset + (3 + size);
488         libHeader.recLen = cast(ushort)(g_page_size - 3);
489         libHeader.ndicpages = ndicpages;
490         libHeader.flags = 1; // always case sensitive
491         // Write library header at start of buffer
492         memcpy(cast(void*)(*libbuf)[].ptr, &libHeader, (libHeader).sizeof);
493     }
494 }
495 
496 /*****************************************************************************/
497 /*****************************************************************************/
498 struct OmfObjModule
499 {
500     ubyte* base; // where are we holding it in memory
501     uint length; // in bytes
502     ushort page; // page module starts in output file
503     const(char)[] name; // module name, with terminating 0
504 }
505 
506 enum HASHMOD = 0x25;
507 enum BUCKETPAGE = 512;
508 enum BUCKETSIZE = (BUCKETPAGE - HASHMOD - 1);
509 
510 /*******************************************
511  * Write a single entry into dictionary.
512  * Returns:
513  *      false   failure
514  */
515 bool EnterDict(ubyte* bucketsP, ushort ndicpages, ubyte* entry, uint entrylen)
516 {
517     ushort uStartIndex;
518     ushort uStep;
519     ushort uStartPage;
520     ushort uPageStep;
521     ushort uIndex;
522     ushort uPage;
523     ushort n;
524     uint u;
525     uint nbytes;
526     ubyte* aP;
527     ubyte* zP;
528     aP = entry;
529     zP = aP + entrylen; // point at last char in identifier
530     uStartPage = 0;
531     uPageStep = 0;
532     uStartIndex = 0;
533     uStep = 0;
534     u = entrylen;
535     while (u--)
536     {
537         uStartPage = cast(ushort)_rotl(uStartPage, 2) ^ (*aP | 0x20);
538         uStep = cast(ushort)_rotr(uStep, 2) ^ (*aP++ | 0x20);
539         uStartIndex = cast(ushort)_rotr(uStartIndex, 2) ^ (*zP | 0x20);
540         uPageStep = cast(ushort)_rotl(uPageStep, 2) ^ (*zP-- | 0x20);
541     }
542     uStartPage %= ndicpages;
543     uPageStep %= ndicpages;
544     if (uPageStep == 0)
545         uPageStep++;
546     uStartIndex %= HASHMOD;
547     uStep %= HASHMOD;
548     if (uStep == 0)
549         uStep++;
550     uPage = uStartPage;
551     uIndex = uStartIndex;
552     // number of bytes in entry
553     nbytes = 1 + entrylen + 2;
554     if (entrylen > 255)
555         nbytes += 2;
556     while (1)
557     {
558         aP = &bucketsP[uPage * BUCKETPAGE];
559         uStartIndex = uIndex;
560         while (1)
561         {
562             if (0 == aP[uIndex])
563             {
564                 // n = next available position in this page
565                 n = aP[HASHMOD] << 1;
566                 assert(n > HASHMOD);
567                 // if off end of this page
568                 if (n + nbytes > BUCKETPAGE)
569                 {
570                     aP[HASHMOD] = 0xFF;
571                     break;
572                     // next page
573                 }
574                 else
575                 {
576                     aP[uIndex] = cast(ubyte)(n >> 1);
577                     memcpy((aP + n), entry, nbytes);
578                     aP[HASHMOD] += (nbytes + 1) >> 1;
579                     if (aP[HASHMOD] == 0)
580                         aP[HASHMOD] = 0xFF;
581                     return true;
582                 }
583             }
584             uIndex += uStep;
585             uIndex %= 0x25;
586             /*if (uIndex > 0x25)
587              uIndex -= 0x25;*/
588             if (uIndex == uStartIndex)
589                 break;
590         }
591         uPage += uPageStep;
592         if (uPage >= ndicpages)
593             uPage -= ndicpages;
594         if (uPage == uStartPage)
595             break;
596     }
597     return false;
598 }