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