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 _scanmach.d)
9  */
10 
11 module ddmd.scanmach;
12 
13 import core.stdc..string;
14 import core.stdc.stdint;
15 import core.sys.osx.mach.loader;
16 import ddmd.globals;
17 import ddmd.errors;
18 
19 enum LOG = false;
20 
21 /*****************************************
22  * Reads an object module from base[] and passes the names
23  * of any exported symbols to (*pAddSymbol)().
24  * Params:
25  *      pAddSymbol =  function to pass the names to
26  *      base =        array of contents of object module
27  *      module_name = name of the object module (used for error messages)
28  *      loc =         location to use for error printing
29  */
30 void scanMachObjModule(void delegate(const(char)[] name, int pickAny) pAddSymbol,
31         const(ubyte)[] base, const(char)* module_name, Loc loc)
32 {
33     static if (LOG)
34     {
35         printf("scanMachObjModule(%s)\n", module_name);
36     }
37 
38     void corrupt(int reason)
39     {
40         error(loc, "corrupt Mach-O object module %s %d", module_name, reason);
41     }
42 
43     const buf = base.ptr;
44     const buflen = base.length;
45     uint32_t ncmds;
46     mach_header* header = cast(mach_header*)buf;
47     mach_header_64* header64 = null;
48     /* First do sanity checks on object file
49      */
50     if (buflen < mach_header.sizeof)
51         return corrupt(__LINE__);
52 
53     if (header.magic == MH_MAGIC)
54     {
55         if (header.cputype != CPU_TYPE_I386)
56         {
57             error(loc, "Mach-O object module %s has cputype = %d, should be %d", module_name, header.cputype, CPU_TYPE_I386);
58             return;
59         }
60         if (header.filetype != MH_OBJECT)
61         {
62             error(loc, "Mach-O object module %s has file type = %d, should be %d", module_name, header.filetype, MH_OBJECT);
63             return;
64         }
65         if (buflen < mach_header.sizeof + header.sizeofcmds)
66             return corrupt(__LINE__);
67         ncmds = header.ncmds;
68     }
69     else if (header.magic == MH_MAGIC_64)
70     {
71         header64 = cast(mach_header_64*)buf;
72         if (buflen < mach_header_64.sizeof)
73             return corrupt(__LINE__);
74         if (header64.cputype != CPU_TYPE_X86_64)
75         {
76             error(loc, "Mach-O object module %s has cputype = %d, should be %d", module_name, header64.cputype, CPU_TYPE_X86_64);
77             return;
78         }
79         if (header64.filetype != MH_OBJECT)
80         {
81             error(loc, "Mach-O object module %s has file type = %d, should be %d", module_name, header64.filetype, MH_OBJECT);
82             return;
83         }
84         if (buflen < mach_header_64.sizeof + header64.sizeofcmds)
85             return corrupt(__LINE__);
86         ncmds = header64.ncmds;
87     }
88     else
89         return corrupt(__LINE__);
90 
91     segment_command* segment_commands = null;
92     segment_command_64* segment_commands64 = null;
93     symtab_command* symtab_commands = null;
94     dysymtab_command* dysymtab_commands = null;
95     // Commands immediately follow mach_header
96     char* commands = cast(char*)buf + (header.magic == MH_MAGIC_64 ? mach_header_64.sizeof : mach_header.sizeof);
97     for (uint32_t i = 0; i < ncmds; i++)
98     {
99         load_command* command = cast(load_command*)commands;
100         //printf("cmd = 0x%02x, cmdsize = %u\n", command->cmd, command->cmdsize);
101         switch (command.cmd)
102         {
103         case LC_SEGMENT:
104             segment_commands = cast(segment_command*)command;
105             break;
106         case LC_SEGMENT_64:
107             segment_commands64 = cast(segment_command_64*)command;
108             break;
109         case LC_SYMTAB:
110             symtab_commands = cast(symtab_command*)command;
111             break;
112         case LC_DYSYMTAB:
113             dysymtab_commands = cast(dysymtab_command*)command;
114             break;
115         default:
116             break;
117         }
118         commands += command.cmdsize;
119     }
120     if (symtab_commands)
121     {
122         // Get pointer to string table
123         char* strtab = cast(char*)buf + symtab_commands.stroff;
124         if (buflen < symtab_commands.stroff + symtab_commands.strsize)
125             return corrupt(__LINE__);
126 
127         if (header.magic == MH_MAGIC_64)
128         {
129             // Get pointer to symbol table
130             nlist_64* symtab = cast(nlist_64*)(cast(char*)buf + symtab_commands.symoff);
131             if (buflen < symtab_commands.symoff + symtab_commands.nsyms * nlist_64.sizeof)
132                 return corrupt(__LINE__);
133 
134             // For each symbol
135             for (int i = 0; i < symtab_commands.nsyms; i++)
136             {
137                 nlist_64* s = symtab + i;
138                 const(char)* name = strtab + s.n_strx;
139                 const namelen = strlen(name);
140                 if (s.n_type & N_STAB)
141                 {
142                     // values in /usr/include/mach-o/stab.h
143                     //printf(" N_STAB");
144                 }
145                 else
146                 {
147                     version (none)
148                     {
149                         if (s.n_type & N_PEXT)
150                         {
151                         }
152                         if (s.n_type & N_EXT)
153                         {
154                         }
155                     }
156                     switch (s.n_type & N_TYPE)
157                     {
158                     case N_UNDF:
159                         if (s.n_type & N_EXT && s.n_value != 0) // comdef
160                             pAddSymbol(name[0 .. namelen], 1);
161                         break;
162                     case N_ABS:
163                         break;
164                     case N_SECT:
165                         if (s.n_type & N_EXT) /*&& !(s->n_desc & N_REF_TO_WEAK)*/
166                             pAddSymbol(name[0 .. namelen], 1);
167                         break;
168                     case N_PBUD:
169                         break;
170                     case N_INDR:
171                         break;
172                     default:
173                         break;
174                     }
175                 }
176             }
177         }
178         else
179         {
180             // Get pointer to symbol table
181             nlist* symtab = cast(nlist*)(cast(char*)buf + symtab_commands.symoff);
182             if (buflen < symtab_commands.symoff + symtab_commands.nsyms * nlist.sizeof)
183                 return corrupt(__LINE__);
184 
185             // For each symbol
186             for (int i = 0; i < symtab_commands.nsyms; i++)
187             {
188                 nlist* s = symtab + i;
189                 const(char)* name = strtab + s.n_strx;
190                 const namelen = strlen(name);
191                 if (s.n_type & N_STAB)
192                 {
193                     // values in /usr/include/mach-o/stab.h
194                     //printf(" N_STAB");
195                 }
196                 else
197                 {
198                     version (none)
199                     {
200                         if (s.n_type & N_PEXT)
201                         {
202                         }
203                         if (s.n_type & N_EXT)
204                         {
205                         }
206                     }
207                     switch (s.n_type & N_TYPE)
208                     {
209                     case N_UNDF:
210                         if (s.n_type & N_EXT && s.n_value != 0) // comdef
211                             pAddSymbol(name[0 .. namelen], 1);
212                         break;
213                     case N_ABS:
214                         break;
215                     case N_SECT:
216                         if (s.n_type & N_EXT) /*&& !(s->n_desc & N_REF_TO_WEAK)*/
217                             pAddSymbol(name[0 .. namelen], 1);
218                         break;
219                     case N_PBUD:
220                         break;
221                     case N_INDR:
222                         break;
223                     default:
224                         break;
225                     }
226                 }
227             }
228         }
229     }
230 }
231 
232 enum CPU_TYPE_I386 = 7;
233 enum CPU_TYPE_X86_64 = CPU_TYPE_I386 | 0x1000000;
234 
235 enum MH_OBJECT = 0x1;
236 
237 struct segment_command
238 {
239     uint32_t cmd;
240     uint32_t cmdsize;
241     char[16] segname;
242     uint32_t vmaddr;
243     uint32_t vmsize;
244     uint32_t fileoff;
245     uint32_t filesize;
246     int32_t  maxprot;
247     int32_t  initprot;
248     uint32_t nsects;
249     uint32_t flags;
250 }
251 
252 struct segment_command_64
253 {
254     uint32_t cmd;
255     uint32_t cmdsize;
256     char[16] segname;
257     uint64_t vmaddr;
258     uint64_t vmsize;
259     uint64_t fileoff;
260     uint64_t filesize;
261     int32_t  maxprot;
262     int32_t  initprot;
263     uint32_t nsects;
264     uint32_t flags;
265 }
266 
267 struct symtab_command
268 {
269     uint32_t cmd;
270     uint32_t cmdsize;
271     uint32_t symoff;
272     uint32_t nsyms;
273     uint32_t stroff;
274     uint32_t strsize;
275 }
276 
277 struct dysymtab_command
278 {
279     uint32_t cmd;
280     uint32_t cmdsize;
281     uint32_t ilocalsym;
282     uint32_t nlocalsym;
283     uint32_t iextdefsym;
284     uint32_t nextdefsym;
285     uint32_t iundefsym;
286     uint32_t nundefsym;
287     uint32_t tocoff;
288     uint32_t ntoc;
289     uint32_t modtaboff;
290     uint32_t nmodtab;
291     uint32_t extrefsymoff;
292     uint32_t nextrefsyms;
293     uint32_t indirectsymoff;
294     uint32_t nindirectsyms;
295     uint32_t extreloff;
296     uint32_t nextrel;
297     uint32_t locreloff;
298     uint32_t nlocrel;
299 }
300 
301 enum LC_SEGMENT    = 1;
302 enum LC_SYMTAB     = 2;
303 enum LC_DYSYMTAB   = 11;
304 enum LC_SEGMENT_64 = 0x19;
305 
306 struct load_command
307 {
308     uint32_t cmd;
309     uint32_t cmdsize;
310 }
311 
312 enum N_EXT  = 1;
313 enum N_STAB = 0xE0;
314 enum N_PEXT = 0x10;
315 enum N_TYPE = 0x0E;
316 enum N_UNDF = 0;
317 enum N_ABS  = 2;
318 enum N_INDR = 10;
319 enum N_PBUD = 12;
320 enum N_SECT = 14;
321 
322 struct nlist
323 {
324     int32_t n_strx;
325     uint8_t n_type;
326     uint8_t n_sect;
327     int16_t n_desc;
328     uint32_t n_value;
329 }
330 
331 struct nlist_64
332 {
333     uint32_t n_strx;
334     uint8_t n_type;
335     uint8_t n_sect;
336     uint16_t n_desc;
337     uint64_t n_value;
338 }