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