1 /**
2  * Extract symbols from a COFF 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/scanmscoff.d, _scanmscoff.d)
8  * Documentation:  https://dlang.org/phobos/dmd_scanmscoff.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/scanmscoff.d
10  */
11 
12 module dmd.scanmscoff;
13 
14 version(Windows):
15 
16 import core.stdc..string, core.stdc.stdlib, core.sys.windows.winnt;
17 
18 import dmd.root.rmem;
19 import dmd.root..string;
20 
21 import dmd.globals, dmd.errors;
22 
23 private enum LOG = false;
24 
25 /*****************************************
26  * Reads an object module from base[] and passes the names
27  * of any exported symbols to (*pAddSymbol)().
28  * Params:
29  *      pAddSymbol =  function to pass the names to
30  *      base =        array of contents of object module
31  *      module_name = name of the object module (used for error messages)
32  *      loc =         location to use for error printing
33  */
34 void scanMSCoffObjModule(void delegate(const(char)[] name, int pickAny) pAddSymbol,
35         const(ubyte)[] base, const(char)* module_name, Loc loc)
36 {
37     static if (LOG)
38     {
39         printf("scanMSCoffObjModule(%s)\n", module_name);
40     }
41 
42     void corrupt(int reason)
43     {
44         error(loc, "corrupt MS-Coff object module `%s` %d", module_name, reason);
45     }
46 
47     const buf = base.ptr;
48     const buflen = base.length;
49     /* First do sanity checks on object file
50      */
51     if (buflen < BIGOBJ_HEADER.sizeof)
52         return corrupt(__LINE__);
53 
54     BIGOBJ_HEADER* header = cast(BIGOBJ_HEADER*)buf;
55     char is_old_coff = false;
56     if (header.Sig2 != 0xFFFF && header.Version != 2)
57     {
58         is_old_coff = true;
59         IMAGE_FILE_HEADER* header_old;
60         header_old = cast(IMAGE_FILE_HEADER*)Mem.check(malloc(IMAGE_FILE_HEADER.sizeof));
61         memcpy(header_old, buf, IMAGE_FILE_HEADER.sizeof);
62         header = cast(BIGOBJ_HEADER*)Mem.check(malloc(BIGOBJ_HEADER.sizeof));
63         *header = BIGOBJ_HEADER.init;
64         header.Machine = header_old.Machine;
65         header.NumberOfSections = header_old.NumberOfSections;
66         header.TimeDateStamp = header_old.TimeDateStamp;
67         header.PointerToSymbolTable = header_old.PointerToSymbolTable;
68         header.NumberOfSymbols = header_old.NumberOfSymbols;
69         free(header_old);
70     }
71     switch (header.Machine)
72     {
73     case IMAGE_FILE_MACHINE_UNKNOWN:
74     case IMAGE_FILE_MACHINE_I386:
75     case IMAGE_FILE_MACHINE_AMD64:
76         break;
77     default:
78         if (buf[0] == 0x80)
79             error(loc, "Object module `%s` is 32 bit OMF, but it should be 64 bit MS-Coff", module_name);
80         else
81             error(loc, "MS-Coff object module `%s` has magic = %x, should be %x", module_name, header.Machine, IMAGE_FILE_MACHINE_AMD64);
82         return;
83     }
84     // Get string table:  string_table[0..string_len]
85     size_t off = header.PointerToSymbolTable;
86     if (off == 0)
87     {
88         error(loc, "MS-Coff object module `%s` has no string table", module_name);
89         return;
90     }
91     off += header.NumberOfSymbols * (is_old_coff ? SymbolTable.sizeof : SymbolTable32.sizeof);
92     if (off + 4 > buflen)
93         return corrupt(__LINE__);
94 
95     uint string_len = *cast(uint*)(buf + off);
96     char* string_table = cast(char*)(buf + off + 4);
97     if (off + string_len > buflen)
98         return corrupt(__LINE__);
99 
100     string_len -= 4;
101     for (int i = 0; i < header.NumberOfSymbols; i++)
102     {
103         SymbolTable32* n;
104         char[8 + 1] s;
105         char* p;
106         static if (LOG)
107         {
108             printf("Symbol %d:\n", i);
109         }
110         off = header.PointerToSymbolTable + i * (is_old_coff ? SymbolTable.sizeof : SymbolTable32.sizeof);
111         if (off > buflen)
112             return corrupt(__LINE__);
113 
114         n = cast(SymbolTable32*)(buf + off);
115         if (is_old_coff)
116         {
117             SymbolTable* n2;
118             n2 = cast(SymbolTable*)Mem.check(malloc(SymbolTable.sizeof));
119             memcpy(n2, (buf + off), SymbolTable.sizeof);
120             n = cast(SymbolTable32*)Mem.check(malloc(SymbolTable32.sizeof));
121             memcpy(n, n2, (n2.Name).sizeof);
122             n.Value = n2.Value;
123             n.SectionNumber = n2.SectionNumber;
124             n.Type = n2.Type;
125             n.StorageClass = n2.StorageClass;
126             n.NumberOfAuxSymbols = n2.NumberOfAuxSymbols;
127             free(n2);
128         }
129         if (n.Zeros)
130         {
131             strncpy(s.ptr, cast(const(char)*)n.Name, 8);
132             s[SYMNMLEN] = 0;
133             p = s.ptr;
134         }
135         else
136             p = string_table + n.Offset - 4;
137         i += n.NumberOfAuxSymbols;
138         static if (LOG)
139         {
140             printf("n_name    = '%s'\n", p);
141             printf("n_value   = x%08lx\n", n.Value);
142             printf("n_scnum   = %d\n", n.SectionNumber);
143             printf("n_type    = x%04x\n", n.Type);
144             printf("n_sclass  = %d\n", n.StorageClass);
145             printf("n_numaux  = %d\n", n.NumberOfAuxSymbols);
146         }
147         switch (n.SectionNumber)
148         {
149         case IMAGE_SYM_DEBUG:
150             continue;
151         case IMAGE_SYM_ABSOLUTE:
152             if (strcmp(p, "@comp.id") == 0)
153                 continue;
154             break;
155         case IMAGE_SYM_UNDEFINED:
156             // A non-zero value indicates a common block
157             if (n.Value)
158                 break;
159             continue;
160         default:
161             break;
162         }
163         switch (n.StorageClass)
164         {
165         case IMAGE_SYM_CLASS_EXTERNAL:
166             break;
167         case IMAGE_SYM_CLASS_STATIC:
168             if (n.Value == 0) // if it's a section name
169                 continue;
170             continue;
171         case IMAGE_SYM_CLASS_FUNCTION:
172         case IMAGE_SYM_CLASS_FILE:
173         case IMAGE_SYM_CLASS_LABEL:
174             continue;
175         default:
176             continue;
177         }
178         pAddSymbol(p.toDString(), 1);
179     }
180 }
181 
182 private: // for the remainder of this module
183 
184 align(1)
185 struct BIGOBJ_HEADER
186 {
187     WORD Sig1;                  // IMAGE_FILE_MACHINE_UNKNOWN
188     WORD Sig2;                  // 0xFFFF
189     WORD Version;               // 2
190     WORD Machine;               // identifies type of target machine
191     DWORD TimeDateStamp;        // creation date, number of seconds since 1970
192     BYTE[16]  UUID;             //  { '\xc7', '\xa1', '\xba', '\xd1', '\xee', '\xba', '\xa9', '\x4b',
193                                 //    '\xaf', '\x20', '\xfa', '\xf6', '\x6a', '\xa4', '\xdc', '\xb8' };
194     DWORD[4] unused;            // { 0, 0, 0, 0 }
195     DWORD NumberOfSections;     // number of sections
196     DWORD PointerToSymbolTable; // file offset of symbol table
197     DWORD NumberOfSymbols;      // number of entries in the symbol table
198 }
199 
200 align(1)
201 struct IMAGE_FILE_HEADER
202 {
203     WORD  Machine;
204     WORD  NumberOfSections;
205     DWORD TimeDateStamp;
206     DWORD PointerToSymbolTable;
207     DWORD NumberOfSymbols;
208     WORD  SizeOfOptionalHeader;
209     WORD  Characteristics;
210 }
211 
212 enum SYMNMLEN = 8;
213 
214 enum IMAGE_FILE_MACHINE_UNKNOWN = 0;            // applies to any machine type
215 enum IMAGE_FILE_MACHINE_I386    = 0x14C;        // x86
216 enum IMAGE_FILE_MACHINE_AMD64   = 0x8664;       // x86_64
217 
218 enum IMAGE_SYM_DEBUG     = -2;
219 enum IMAGE_SYM_ABSOLUTE  = -1;
220 enum IMAGE_SYM_UNDEFINED = 0;
221 
222 enum IMAGE_SYM_CLASS_EXTERNAL = 2;
223 enum IMAGE_SYM_CLASS_STATIC   = 3;
224 enum IMAGE_SYM_CLASS_LABEL    = 6;
225 enum IMAGE_SYM_CLASS_FUNCTION = 101;
226 enum IMAGE_SYM_CLASS_FILE     = 103;
227 
228 align(1) struct SymbolTable32
229 {
230     union
231     {
232         BYTE[SYMNMLEN] Name;
233         struct
234         {
235             DWORD Zeros;
236             DWORD Offset;
237         }
238     }
239 
240     DWORD Value;
241     DWORD SectionNumber;
242     WORD Type;
243     BYTE StorageClass;
244     BYTE NumberOfAuxSymbols;
245 }
246 
247 align(1) struct SymbolTable
248 {
249     BYTE[SYMNMLEN] Name;
250     DWORD Value;
251     WORD SectionNumber;
252     WORD Type;
253     BYTE StorageClass;
254     BYTE NumberOfAuxSymbols;
255 }