1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved
6  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
7  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8  * Source:      $(DMDSRC _scanomf.d)
9  */
10 
11 module ddmd.scanomf;
12 
13 import core.stdc..string;
14 import core.stdc.stdlib;
15 import ddmd.globals;
16 import ddmd.root.outbuffer;
17 import ddmd.arraytypes;
18 import ddmd.errors;
19 
20 enum LOG = false;
21 
22 /**************************
23  * Record types:
24  */
25 enum RHEADR = 0x6E;
26 enum REGINT = 0x70;
27 enum REDATA = 0x72;
28 enum RIDATA = 0x74;
29 enum OVLDEF = 0x76;
30 enum ENDREC = 0x78;
31 enum BLKDEF = 0x7A;
32 enum BLKEND = 0x7C;
33 enum DEBSYM = 0x7E;
34 enum THEADR = 0x80;
35 enum LHEADR = 0x82;
36 enum PEDATA = 0x84;
37 enum PIDATA = 0x86;
38 enum COMENT = 0x88;
39 enum MODEND = 0x8A;
40 enum M386END = 0x8B; /* 32 bit module end record */
41 enum EXTDEF = 0x8C;
42 enum TYPDEF = 0x8E;
43 enum PUBDEF = 0x90;
44 enum PUB386 = 0x91;
45 enum LOCSYM = 0x92;
46 enum LINNUM = 0x94;
47 enum LNAMES = 0x96;
48 enum SEGDEF = 0x98;
49 enum GRPDEF = 0x9A;
50 enum FIXUPP = 0x9C;
51 /*#define (none)        0x9E    */
52 enum LEDATA = 0xA0;
53 enum LIDATA = 0xA2;
54 enum LIBHED = 0xA4;
55 enum LIBNAM = 0xA6;
56 enum LIBLOC = 0xA8;
57 enum LIBDIC = 0xAA;
58 enum COMDEF = 0xB0;
59 enum LEXTDEF = 0xB4;
60 enum LPUBDEF = 0xB6;
61 enum LCOMDEF = 0xB8;
62 enum CEXTDEF = 0xBC;
63 enum COMDAT = 0xC2;
64 enum LINSYM = 0xC4;
65 enum ALIAS = 0xC6;
66 enum LLNAMES = 0xCA;
67 enum LIBIDMAX = (512 - 0x25 - 3 - 4);
68 
69 // max size that will fit in dictionary
70 extern (C++) void parseName(const(ubyte)** pp, char* name)
71 {
72     auto p = *pp;
73     uint len = *p++;
74     if (len == 0xFF && *p == 0) // if long name
75     {
76         len = p[1] & 0xFF;
77         len |= cast(uint)p[2] << 8;
78         p += 3;
79         assert(len <= LIBIDMAX);
80     }
81     memcpy(name, p, len);
82     name[len] = 0;
83     *pp = p + len;
84 }
85 
86 extern (C++) static ushort parseIdx(const(ubyte)** pp)
87 {
88     auto p = *pp;
89     const c = *p++;
90     ushort idx = (0x80 & c) ? ((0x7F & c) << 8) + *p++ : c;
91     *pp = p;
92     return idx;
93 }
94 
95 // skip numeric field of a data type of a COMDEF record
96 extern (C++) static void skipNumericField(const(ubyte)** pp)
97 {
98     const(ubyte)* p = *pp;
99     const c = *p++;
100     if (c == 0x81)
101         p += 2;
102     else if (c == 0x84)
103         p += 3;
104     else if (c == 0x88)
105         p += 4;
106     else
107         assert(c <= 0x80);
108     *pp = p;
109 }
110 
111 // skip data type of a COMDEF record
112 extern (C++) static void skipDataType(const(ubyte)** pp)
113 {
114     auto p = *pp;
115     const c = *p++;
116     if (c == 0x61)
117     {
118         // FAR data
119         skipNumericField(&p);
120         skipNumericField(&p);
121     }
122     else if (c == 0x62)
123     {
124         // NEAR data
125         skipNumericField(&p);
126     }
127     else
128     {
129         assert(1 <= c && c <= 0x5f); // Borland segment indices
130     }
131     *pp = p;
132 }
133 
134 /*****************************************
135  * Reads an object module from base[] and passes the names
136  * of any exported symbols to (*pAddSymbol)().
137  * Params:
138  *      pAddSymbol =  function to pass the names to
139  *      base =        array of contents of object module
140  *      module_name = name of the object module (used for error messages)
141  *      loc =         location to use for error printing
142  */
143 void scanOmfObjModule(void delegate(const(char)[] name, int pickAny) pAddSymbol,
144         const(ubyte)[] base, const(char)* module_name, Loc loc)
145 {
146     static if (LOG)
147     {
148         printf("scanOmfObjModule(%s)\n", module_name);
149     }
150     const buf = base.ptr;
151     const buflen = base.length;
152     int easyomf;
153     ubyte result = 0;
154     char[LIBIDMAX + 1] name;
155     Strings names;
156     names.push(null); // don't use index 0
157     easyomf = 0; // assume not EASY-OMF
158     auto pend = cast(const(ubyte)*)base.ptr + buflen;
159     const(ubyte)* pnext;
160     for (auto p = cast(const(ubyte)*)base.ptr; 1; p = pnext)
161     {
162         assert(p < pend);
163         ubyte recTyp = *p++;
164         ushort recLen = *cast(ushort*)p;
165         p += 2;
166         pnext = p + recLen;
167         recLen--; // forget the checksum
168         switch (recTyp)
169         {
170         case LNAMES:
171         case LLNAMES:
172             while (p + 1 < pnext)
173             {
174                 parseName(&p, name.ptr);
175                 names.push(strdup(name.ptr));
176             }
177             break;
178         case PUBDEF:
179             if (easyomf)
180                 recTyp = PUB386; // convert to MS format
181             goto case;
182         case PUB386:
183             if (!(parseIdx(&p) | parseIdx(&p)))
184                 p += 2; // skip seg, grp, frame
185             while (p + 1 < pnext)
186             {
187                 parseName(&p, name.ptr);
188                 p += (recTyp == PUBDEF) ? 2 : 4; // skip offset
189                 parseIdx(&p); // skip type index
190                 pAddSymbol(name[0 .. strlen(name.ptr)], 0);
191             }
192             break;
193         case COMDAT:
194             if (easyomf)
195                 recTyp = COMDAT + 1; // convert to MS format
196             goto case;
197         case COMDAT + 1:
198             {
199                 int pickAny = 0;
200                 if (*p++ & 5) // if continuation or local comdat
201                     break;
202                 ubyte attr = *p++;
203                 if (attr & 0xF0) // attr: if multiple instances allowed
204                     pickAny = 1;
205                 p++; // align
206                 p += 2; // enum data offset
207                 if (recTyp == COMDAT + 1)
208                     p += 2; // enum data offset
209                 parseIdx(&p); // type index
210                 if ((attr & 0x0F) == 0) // if explicit allocation
211                 {
212                     parseIdx(&p); // base group
213                     parseIdx(&p); // base segment
214                 }
215                 uint idx = parseIdx(&p); // public name index
216                 if (idx == 0 || idx >= names.dim)
217                 {
218                     //debug(printf("[s] name idx=%d, uCntNames=%d\n", idx, uCntNames));
219                     error(loc, "corrupt COMDAT");
220                     return;
221                 }
222                 //printf("[s] name='%s'\n",name);
223                 const(char)* n = names[idx];
224                 pAddSymbol(n[0 .. strlen(name.ptr)], pickAny);
225                 break;
226             }
227         case COMDEF:
228             {
229                 while (p + 1 < pnext)
230                 {
231                     parseName(&p, name.ptr);
232                     parseIdx(&p); // type index
233                     skipDataType(&p); // data type
234                     pAddSymbol(name[0 .. strlen(name.ptr)], 1);
235                 }
236                 break;
237             }
238         case ALIAS:
239             while (p + 1 < pnext)
240             {
241                 parseName(&p, name.ptr);
242                 pAddSymbol(name[0 .. strlen(name.ptr)], 0);
243                 parseName(&p, name.ptr);
244             }
245             break;
246         case MODEND:
247         case M386END:
248             result = 1;
249             goto Ret;
250         case COMENT:
251             // Recognize Phar Lap EASY-OMF format
252             {
253                 static __gshared ubyte* omfstr1 = [0x80, 0xAA, '8', '0', '3', '8', '6'];
254                 if (recLen == (omfstr1).sizeof)
255                 {
256                     for (uint i = 0; i < (omfstr1).sizeof; i++)
257                         if (*p++ != omfstr1[i])
258                             goto L1;
259                     easyomf = 1;
260                     break;
261                 L1:
262                 }
263             }
264             // Recognize .IMPDEF Import Definition Records
265             {
266                 static __gshared ubyte* omfstr2 = [0, 0xA0, 1];
267                 if (recLen >= 7)
268                 {
269                     p++;
270                     for (uint i = 1; i < (omfstr2).sizeof; i++)
271                         if (*p++ != omfstr2[i])
272                             goto L2;
273                     p++; // skip OrdFlag field
274                     parseName(&p, name.ptr);
275                     pAddSymbol(name[0 .. strlen(name.ptr)], 0);
276                     break;
277                 L2:
278                 }
279             }
280             break;
281         default:
282             // ignore
283         }
284     }
285 Ret:
286     for (size_t u = 1; u < names.dim; u++)
287         free(cast(void*)names[u]);
288 }
289 
290 /*************************************************
291  * Scan a block of memory buf[0..buflen], pulling out each
292  * OMF object module in it and sending the info in it to (*pAddObjModule).
293  * Returns:
294  *      true for corrupt OMF data
295  */
296 bool scanOmfLib(void delegate(char* name, void* base, size_t length) pAddObjModule, void* buf, size_t buflen, uint pagesize)
297 {
298     /* Split up the buffer buf[0..buflen] into multiple object modules,
299      * each aligned on a pagesize boundary.
300      */
301     bool first_module = true;
302     const(ubyte)* base = null;
303     char[LIBIDMAX + 1] name;
304     auto p = cast(const(ubyte)*)buf;
305     auto pend = p + buflen;
306     const(ubyte)* pnext;
307     for (; p < pend; p = pnext) // for each OMF record
308     {
309         if (p + 3 >= pend)
310             return true; // corrupt
311         ubyte recTyp = *p;
312         ushort recLen = *cast(const(ushort)*)(p + 1);
313         pnext = p + 3 + recLen;
314         if (pnext > pend)
315             return true; // corrupt
316         recLen--; // forget the checksum
317         switch (recTyp)
318         {
319         case LHEADR:
320         case THEADR:
321             if (!base)
322             {
323                 base = p;
324                 p += 3;
325                 parseName(&p, name.ptr);
326                 if (name[0] == 'C' && name[1] == 0) // old C compilers did this
327                     base = pnext; // skip past THEADR
328             }
329             break;
330         case MODEND:
331         case M386END:
332             {
333                 if (base)
334                 {
335                     pAddObjModule(name.ptr, cast(ubyte*)base, pnext - base);
336                     base = null;
337                 }
338                 // Round up to next page
339                 uint t = cast(uint)(pnext - cast(const(ubyte)*)buf);
340                 t = (t + pagesize - 1) & ~cast(uint)(pagesize - 1);
341                 pnext = cast(const(ubyte)*)buf + t;
342                 break;
343             }
344         default:
345             // ignore
346         }
347     }
348     return (base !is null); // missing MODEND record
349 }
350 
351 uint OMFObjSize(const(void)* base, uint length, const(char)* name)
352 {
353     ubyte c = *cast(const(ubyte)*)base;
354     if (c != THEADR && c != LHEADR)
355     {
356         size_t len = strlen(name);
357         assert(len <= LIBIDMAX);
358         length += len + 5;
359     }
360     return length;
361 }
362 
363 void writeOMFObj(OutBuffer* buf, const(void)* base, uint length, const(char)* name)
364 {
365     ubyte c = *cast(const(ubyte)*)base;
366     if (c != THEADR && c != LHEADR)
367     {
368         const len = strlen(name);
369         assert(len <= LIBIDMAX);
370         ubyte[4 + LIBIDMAX + 1] header;
371         header[0] = THEADR;
372         header[1] = cast(ubyte)(2 + len);
373         header[2] = 0;
374         header[3] = cast(ubyte)len;
375         assert(len <= 0xFF - 2);
376         memcpy(4 + header.ptr, name, len);
377         // Compute and store record checksum
378         uint n = cast(uint)(len + 4);
379         ubyte checksum = 0;
380         ubyte* p = header.ptr;
381         while (n--)
382         {
383             checksum -= *p;
384             p++;
385         }
386         *p = checksum;
387         buf.write(header.ptr, len + 5);
388     }
389     buf.write(base, length);
390 }