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