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