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 _libmscoff.d) 9 */ 10 11 module ddmd.libmscoff; 12 13 import core.stdc.stdlib; 14 import core.stdc..string; 15 import core.stdc.time; 16 import core.stdc.stdio; 17 import core.stdc..string; 18 19 import core.sys.windows.windows; 20 import core.sys.windows.stat; 21 22 import ddmd.globals; 23 import ddmd.lib; 24 import ddmd.utils; 25 26 import ddmd.root.array; 27 import ddmd.root.file; 28 import ddmd.root.filename; 29 import ddmd.root.outbuffer; 30 import ddmd.root.port; 31 import ddmd.root.rmem; 32 import ddmd.root.stringtable; 33 34 import ddmd.scanmscoff; 35 36 enum LOG = false; 37 38 alias stat_t = struct_stat; 39 40 struct MSCoffObjSymbol 41 { 42 const(char)[] name; // still has a terminating 0 43 MSCoffObjModule* om; 44 } 45 46 /********* 47 * Do lexical comparison of MSCoffObjSymbol's for qsort() 48 */ 49 extern (C) int MSCoffObjSymbol_cmp(const(void*) p, const(void*) q) 50 { 51 MSCoffObjSymbol* s1 = *cast(MSCoffObjSymbol**)p; 52 MSCoffObjSymbol* s2 = *cast(MSCoffObjSymbol**)q; 53 return strcmp(s1.name.ptr, s2.name.ptr); 54 } 55 56 alias MSCoffObjModules = Array!(MSCoffObjModule*); 57 alias MSCoffObjSymbols = Array!(MSCoffObjSymbol*); 58 59 final class LibMSCoff : Library 60 { 61 MSCoffObjModules objmodules; // MSCoffObjModule[] 62 MSCoffObjSymbols objsymbols; // MSCoffObjSymbol[] 63 StringTable tab; 64 65 extern (D) this() 66 { 67 tab._init(14000); 68 } 69 70 /*************************************** 71 * Add object module or library to the library. 72 * Examine the buffer to see which it is. 73 * If the buffer is NULL, use module_name as the file name 74 * and load the file. 75 */ 76 override void addObject(const(char)* module_name, const ubyte[] buffer) 77 { 78 if (!module_name) 79 module_name = ""; 80 static if (LOG) 81 { 82 printf("LibMSCoff::addObject(%s)\n", module_name); 83 } 84 85 void corrupt(int reason) 86 { 87 error("corrupt MS Coff object module %s %d", module_name, reason); 88 } 89 90 int fromfile = 0; 91 auto buf = buffer.ptr; 92 auto buflen = buffer.length; 93 if (!buf) 94 { 95 assert(module_name[0]); 96 File* file = File.create(cast(char*)module_name); 97 readFile(Loc(), file); 98 buf = file.buffer; 99 buflen = file.len; 100 file._ref = 1; 101 fromfile = 1; 102 } 103 int reason = 0; 104 if (buflen < 16) 105 { 106 static if (LOG) 107 { 108 printf("buf = %p, buflen = %d\n", buf, buflen); 109 } 110 return corrupt(__LINE__); 111 } 112 if (memcmp(buf, cast(char*)"!<arch>\n", 8) == 0) 113 { 114 /* It's a library file. 115 * Pull each object module out of the library and add it 116 * to the object module array. 117 */ 118 static if (LOG) 119 { 120 printf("archive, buf = %p, buflen = %d\n", buf, buflen); 121 } 122 MSCoffLibHeader* flm = null; // first linker member 123 MSCoffLibHeader* slm = null; // second linker member 124 uint number_of_members = 0; 125 uint* member_file_offsets = null; 126 uint number_of_symbols = 0; 127 ushort* indices = null; 128 char* string_table = null; 129 size_t string_table_length = 0; 130 MSCoffLibHeader* lnm = null; // longname member 131 char* longnames = null; 132 size_t longnames_length = 0; 133 size_t offset = 8; 134 char* symtab = null; 135 uint symtab_size = 0; 136 size_t mstart = objmodules.dim; 137 while (1) 138 { 139 offset = (offset + 1) & ~1; // round to even boundary 140 if (offset >= buflen) 141 break; 142 if (offset + MSCoffLibHeader.sizeof >= buflen) 143 return corrupt(__LINE__); 144 MSCoffLibHeader* header = cast(MSCoffLibHeader*)(cast(ubyte*)buf + offset); 145 offset += MSCoffLibHeader.sizeof; 146 char* endptr = null; 147 uint size = strtoul(cast(char*)header.file_size, &endptr, 10); 148 if (endptr >= header.file_size.ptr + 10 || *endptr != ' ') 149 return corrupt(__LINE__); 150 if (offset + size > buflen) 151 return corrupt(__LINE__); 152 //printf("header->object_name = '%.*s'\n", MSCOFF_OBJECT_NAME_SIZE, header->object_name); 153 if (memcmp(cast(char*)header.object_name, cast(char*)"/ ", MSCOFF_OBJECT_NAME_SIZE) == 0) 154 { 155 if (!flm) 156 { 157 // First Linker Member, which is ignored 158 flm = header; 159 } 160 else if (!slm) 161 { 162 // Second Linker Member, which we require even though the format doesn't require it 163 slm = header; 164 if (size < 4 + 4) 165 return corrupt(__LINE__); 166 number_of_members = Port.readlongLE(cast(char*)buf + offset); 167 member_file_offsets = cast(uint*)(cast(char*)buf + offset + 4); 168 if (size < 4 + number_of_members * 4 + 4) 169 return corrupt(__LINE__); 170 number_of_symbols = Port.readlongLE(cast(char*)buf + offset + 4 + number_of_members * 4); 171 indices = cast(ushort*)(cast(char*)buf + offset + 4 + number_of_members * 4 + 4); 172 string_table = cast(char*)(cast(char*)buf + offset + 4 + number_of_members * 4 + 4 + number_of_symbols * 2); 173 if (size <= (4 + number_of_members * 4 + 4 + number_of_symbols * 2)) 174 return corrupt(__LINE__); 175 string_table_length = size - (4 + number_of_members * 4 + 4 + number_of_symbols * 2); 176 /* The number of strings in the string_table must be number_of_symbols; check it 177 * The strings must also be in ascending lexical order; not checked. 178 */ 179 size_t i = 0; 180 for (uint n = 0; n < number_of_symbols; n++) 181 { 182 while (1) 183 { 184 if (i >= string_table_length) 185 return corrupt(__LINE__); 186 if (!string_table[i++]) 187 break; 188 } 189 } 190 if (i != string_table_length) 191 return corrupt(__LINE__); 192 } 193 } 194 else if (memcmp(cast(char*)header.object_name, cast(char*)"// ", MSCOFF_OBJECT_NAME_SIZE) == 0) 195 { 196 if (!lnm) 197 { 198 lnm = header; 199 longnames = cast(char*)buf + offset; 200 longnames_length = size; 201 } 202 } 203 else 204 { 205 if (!slm) 206 return corrupt(__LINE__); 207 version (none) 208 { 209 // Microsoft Spec says longnames member must appear, but Microsoft Lib says otherwise 210 if (!lnm) 211 return corrupt(__LINE__); 212 } 213 auto om = new MSCoffObjModule(); 214 // Include MSCoffLibHeader in base[0..length], so we don't have to repro it 215 om.base = cast(ubyte*)buf + offset - MSCoffLibHeader.sizeof; 216 om.length = cast(uint)(size + MSCoffLibHeader.sizeof); 217 om.offset = 0; 218 if (header.object_name[0] == '/') 219 { 220 /* Pick long name out of longnames[] 221 */ 222 uint foff = strtoul(cast(char*)header.object_name + 1, &endptr, 10); 223 uint i; 224 for (i = 0; 1; i++) 225 { 226 if (foff + i >= longnames_length) 227 return corrupt(__LINE__); 228 char c = longnames[foff + i]; 229 if (c == 0) 230 break; 231 } 232 char* oname = cast(char*)malloc(i + 1); 233 assert(oname); 234 memcpy(oname, longnames + foff, i); 235 oname[i] = 0; 236 om.name = oname[0 .. i]; 237 //printf("\tname = '%s'\n", om->name); 238 } 239 else 240 { 241 /* Pick short name out of header 242 */ 243 char* oname = cast(char*)malloc(MSCOFF_OBJECT_NAME_SIZE); 244 assert(oname); 245 int i; 246 for (i = 0; 1; i++) 247 { 248 if (i == MSCOFF_OBJECT_NAME_SIZE) 249 return corrupt(__LINE__); 250 char c = header.object_name[i]; 251 if (c == '/') 252 { 253 oname[i] = 0; 254 break; 255 } 256 oname[i] = c; 257 } 258 om.name = oname[0 .. i]; 259 } 260 om.file_time = strtoul(cast(char*)header.file_time, &endptr, 10); 261 om.user_id = strtoul(cast(char*)header.user_id, &endptr, 10); 262 om.group_id = strtoul(cast(char*)header.group_id, &endptr, 10); 263 om.file_mode = strtoul(cast(char*)header.file_mode, &endptr, 8); 264 om.scan = 0; // don't scan object module for symbols 265 objmodules.push(om); 266 } 267 offset += size; 268 } 269 if (offset != buflen) 270 return corrupt(__LINE__); 271 /* Scan the library's symbol table, and insert it into our own. 272 * We use this instead of rescanning the object module, because 273 * the library's creator may have a different idea of what symbols 274 * go into the symbol table than we do. 275 * This is also probably faster. 276 */ 277 if (!slm) 278 return corrupt(__LINE__); 279 char* s = string_table; 280 for (uint i = 0; i < number_of_symbols; i++) 281 { 282 const(char)[] name = s[0 .. strlen(s)]; 283 s += name.length + 1; 284 uint memi = indices[i] - 1; 285 if (memi >= number_of_members) 286 return corrupt(__LINE__); 287 uint moff = member_file_offsets[memi]; 288 for (size_t m = mstart; 1; m++) 289 { 290 if (m == objmodules.dim) 291 return corrupt(__LINE__); // didn't find it 292 MSCoffObjModule* om = objmodules[m]; 293 //printf("\tom offset = x%x\n", (char *)om->base - (char *)buf); 294 if (moff == cast(char*)om.base - cast(char*)buf) 295 { 296 addSymbol(om, name, 1); 297 //if (mstart == m) 298 // mstart++; 299 break; 300 } 301 } 302 } 303 return; 304 } 305 /* It's an object module 306 */ 307 auto om = new MSCoffObjModule(); 308 om.base = cast(ubyte*)buf; 309 om.length = cast(uint)buflen; 310 om.offset = 0; 311 const(char)* n = global.params.preservePaths ? module_name : FileName.name(module_name); // remove path, but not extension 312 om.name = n[0 .. strlen(n)]; 313 om.scan = 1; 314 if (fromfile) 315 { 316 stat_t statbuf; 317 int i = stat(cast(char*)module_name, &statbuf); 318 if (i == -1) // error, errno is set 319 return corrupt(__LINE__); 320 om.file_time = statbuf.st_ctime; 321 om.user_id = statbuf.st_uid; 322 om.group_id = statbuf.st_gid; 323 om.file_mode = statbuf.st_mode; 324 } 325 else 326 { 327 /* Mock things up for the object module file that never was 328 * actually written out. 329 */ 330 time_t file_time = 0; 331 time(&file_time); 332 om.file_time = cast(long)file_time; 333 om.user_id = 0; // meaningless on Windows 334 om.group_id = 0; // meaningless on Windows 335 om.file_mode = (1 << 15) | (6 << 6) | (4 << 3) | (4 << 0); // 0100644 336 } 337 objmodules.push(om); 338 } 339 340 /*****************************************************************************/ 341 342 void addSymbol(MSCoffObjModule* om, const(char)[] name, int pickAny = 0) 343 { 344 static if (LOG) 345 { 346 printf("LibMSCoff::addSymbol(%s, %s, %d)\n", om.name.ptr, name, pickAny); 347 } 348 auto os = new MSCoffObjSymbol(); 349 os.name = xarraydup(name); 350 os.om = om; 351 objsymbols.push(os); 352 } 353 354 private: 355 /************************************ 356 * Scan single object module for dictionary symbols. 357 * Send those symbols to LibMSCoff::addSymbol(). 358 */ 359 void scanObjModule(MSCoffObjModule* om) 360 { 361 static if (LOG) 362 { 363 printf("LibMSCoff::scanObjModule(%s)\n", om.name.ptr); 364 } 365 366 extern (D) void addSymbol(const(char)[] name, int pickAny) 367 { 368 this.addSymbol(om, name, pickAny); 369 } 370 371 scanMSCoffObjModule(&addSymbol, om.base[0 .. om.length], om.name.ptr, loc); 372 } 373 374 /*****************************************************************************/ 375 /*****************************************************************************/ 376 /********************************************** 377 * Create and write library to libbuf. 378 * The library consists of: 379 * !<arch>\n 380 * header 381 * 1st Linker Member 382 * Header 383 * 2nd Linker Member 384 * Header 385 * Longnames Member 386 * object modules... 387 */ 388 protected override void WriteLibToBuffer(OutBuffer* libbuf) 389 { 390 static if (LOG) 391 { 392 printf("LibElf::WriteLibToBuffer()\n"); 393 } 394 assert(MSCoffLibHeader.sizeof == 60); 395 /************* Scan Object Modules for Symbols ******************/ 396 for (size_t i = 0; i < objmodules.dim; i++) 397 { 398 MSCoffObjModule* om = objmodules[i]; 399 if (om.scan) 400 { 401 scanObjModule(om); 402 } 403 } 404 /************* Determine longnames size ******************/ 405 /* The longnames section is where we store long file names. 406 */ 407 uint noffset = 0; 408 for (size_t i = 0; i < objmodules.dim; i++) 409 { 410 MSCoffObjModule* om = objmodules[i]; 411 size_t len = om.name.length; 412 if (len >= MSCOFF_OBJECT_NAME_SIZE) 413 { 414 om.name_offset = noffset; 415 noffset += len + 1; 416 } 417 else 418 om.name_offset = -1; 419 } 420 static if (LOG) 421 { 422 printf("\tnoffset = x%x\n", noffset); 423 } 424 /************* Determine string table length ******************/ 425 size_t slength = 0; 426 for (size_t i = 0; i < objsymbols.dim; i++) 427 { 428 MSCoffObjSymbol* os = objsymbols[i]; 429 slength += os.name.length + 1; 430 } 431 /************* Offset of first module ***********************/ 432 size_t moffset = 8; // signature 433 size_t firstLinkerMemberOffset = moffset; 434 moffset += MSCoffLibHeader.sizeof + 4 + objsymbols.dim * 4 + slength; // 1st Linker Member 435 moffset += moffset & 1; 436 size_t secondLinkerMemberOffset = moffset; 437 moffset += MSCoffLibHeader.sizeof + 4 + objmodules.dim * 4 + 4 + objsymbols.dim * 2 + slength; 438 moffset += moffset & 1; 439 size_t LongnamesMemberOffset = moffset; 440 moffset += MSCoffLibHeader.sizeof + noffset; // Longnames Member size 441 static if (LOG) 442 { 443 printf("\tmoffset = x%x\n", moffset); 444 } 445 /************* Offset of each module *************************/ 446 for (size_t i = 0; i < objmodules.dim; i++) 447 { 448 MSCoffObjModule* om = objmodules[i]; 449 moffset += moffset & 1; 450 om.offset = cast(uint)moffset; 451 if (om.scan) 452 moffset += MSCoffLibHeader.sizeof + om.length; 453 else 454 moffset += om.length; 455 } 456 libbuf.reserve(moffset); 457 /************* Write the library ******************/ 458 libbuf.write("!<arch>\n".ptr, 8); 459 MSCoffObjModule om; 460 om.name_offset = -1; 461 om.base = null; 462 om.length = cast(uint)(4 + objsymbols.dim * 4 + slength); 463 om.offset = 8; 464 om.name = ""; 465 time_t file_time = 0; 466 .time(&file_time); 467 om.file_time = cast(long)file_time; 468 om.user_id = 0; 469 om.group_id = 0; 470 om.file_mode = 0; 471 /*** Write out First Linker Member ***/ 472 assert(libbuf.offset == firstLinkerMemberOffset); 473 MSCoffLibHeader h; 474 MSCoffOmToHeader(&h, &om); 475 libbuf.write(&h, h.sizeof); 476 char[4] buf; 477 Port.writelongBE(cast(uint)objsymbols.dim, buf.ptr); 478 libbuf.write(buf.ptr, 4); 479 // Sort objsymbols[] in module offset order 480 qsort(objsymbols.data, objsymbols.dim, (objsymbols.data[0]).sizeof, &MSCoffObjSymbol_offset_cmp); 481 uint lastoffset; 482 for (size_t i = 0; i < objsymbols.dim; i++) 483 { 484 MSCoffObjSymbol* os = objsymbols[i]; 485 //printf("objsymbols[%d] = '%s', offset = %u\n", i, os->name, os->om->offset); 486 if (i) 487 { 488 // Should be sorted in module order 489 assert(lastoffset <= os.om.offset); 490 } 491 lastoffset = os.om.offset; 492 Port.writelongBE(lastoffset, buf.ptr); 493 libbuf.write(buf.ptr, 4); 494 } 495 for (size_t i = 0; i < objsymbols.dim; i++) 496 { 497 MSCoffObjSymbol* os = objsymbols[i]; 498 libbuf.writestring(os.name); 499 libbuf.writeByte(0); 500 } 501 /*** Write out Second Linker Member ***/ 502 if (libbuf.offset & 1) 503 libbuf.writeByte('\n'); 504 assert(libbuf.offset == secondLinkerMemberOffset); 505 om.length = cast(uint)(4 + objmodules.dim * 4 + 4 + objsymbols.dim * 2 + slength); 506 MSCoffOmToHeader(&h, &om); 507 libbuf.write(&h, h.sizeof); 508 Port.writelongLE(cast(uint)objmodules.dim, buf.ptr); 509 libbuf.write(buf.ptr, 4); 510 for (size_t i = 0; i < objmodules.dim; i++) 511 { 512 MSCoffObjModule* om2 = objmodules[i]; 513 om2.index = cast(ushort)i; 514 Port.writelongLE(om2.offset, buf.ptr); 515 libbuf.write(buf.ptr, 4); 516 } 517 Port.writelongLE(cast(uint)objsymbols.dim, buf.ptr); 518 libbuf.write(buf.ptr, 4); 519 // Sort objsymbols[] in lexical order 520 qsort(objsymbols.data, objsymbols.dim, (objsymbols.data[0]).sizeof, &MSCoffObjSymbol_cmp); 521 for (size_t i = 0; i < objsymbols.dim; i++) 522 { 523 MSCoffObjSymbol* os = objsymbols[i]; 524 Port.writelongLE(os.om.index + 1, buf.ptr); 525 libbuf.write(buf.ptr, 2); 526 } 527 for (size_t i = 0; i < objsymbols.dim; i++) 528 { 529 MSCoffObjSymbol* os = objsymbols[i]; 530 libbuf.writestring(os.name); 531 libbuf.writeByte(0); 532 } 533 /*** Write out longnames Member ***/ 534 if (libbuf.offset & 1) 535 libbuf.writeByte('\n'); 536 //printf("libbuf %x longnames %x\n", (int)libbuf->offset, (int)LongnamesMemberOffset); 537 assert(libbuf.offset == LongnamesMemberOffset); 538 // header 539 memset(&h, ' ', MSCoffLibHeader.sizeof); 540 h.object_name[0] = '/'; 541 h.object_name[1] = '/'; 542 size_t len = sprintf(h.file_size.ptr, "%u", noffset); 543 assert(len < 10); 544 h.file_size[len] = ' '; 545 h.trailer[0] = '`'; 546 h.trailer[1] = '\n'; 547 libbuf.write(&h, h.sizeof); 548 for (size_t i = 0; i < objmodules.dim; i++) 549 { 550 MSCoffObjModule* om2 = objmodules[i]; 551 if (om2.name_offset >= 0) 552 { 553 libbuf.writestring(om2.name); 554 libbuf.writeByte(0); 555 } 556 } 557 /* Write out each of the object modules 558 */ 559 for (size_t i = 0; i < objmodules.dim; i++) 560 { 561 MSCoffObjModule* om2 = objmodules[i]; 562 if (libbuf.offset & 1) 563 libbuf.writeByte('\n'); // module alignment 564 //printf("libbuf %x om %x\n", (int)libbuf->offset, (int)om2->offset); 565 assert(libbuf.offset == om2.offset); 566 if (om2.scan) 567 { 568 MSCoffOmToHeader(&h, om2); 569 libbuf.write(&h, h.sizeof); // module header 570 libbuf.write(om2.base, om2.length); // module contents 571 } 572 else 573 { 574 // Header is included in om->base[0..length] 575 libbuf.write(om2.base, om2.length); // module contents 576 } 577 } 578 static if (LOG) 579 { 580 printf("moffset = x%x, libbuf->offset = x%x\n", cast(uint)moffset, cast(uint)libbuf.offset); 581 } 582 assert(libbuf.offset == moffset); 583 } 584 } 585 586 extern (C++) Library LibMSCoff_factory() 587 { 588 return new LibMSCoff(); 589 } 590 591 /*****************************************************************************/ 592 /*****************************************************************************/ 593 struct MSCoffObjModule 594 { 595 ubyte* base; // where are we holding it in memory 596 uint length; // in bytes 597 uint offset; // offset from start of library 598 ushort index; // index in Second Linker Member 599 const(char)[] name; // module name (file name) terminated with 0 600 int name_offset; // if not -1, offset into string table of name 601 long file_time; // file time 602 uint user_id; 603 uint group_id; 604 uint file_mode; 605 int scan; // 1 means scan for symbols 606 } 607 608 /********* 609 * Do module offset comparison of MSCoffObjSymbol's for qsort() 610 */ 611 extern (C) int MSCoffObjSymbol_offset_cmp(const(void*) p, const(void*) q) 612 { 613 MSCoffObjSymbol* s1 = *cast(MSCoffObjSymbol**)p; 614 MSCoffObjSymbol* s2 = *cast(MSCoffObjSymbol**)q; 615 return s1.om.offset - s2.om.offset; 616 } 617 618 enum MSCOFF_OBJECT_NAME_SIZE = 16; 619 620 struct MSCoffLibHeader 621 { 622 char[MSCOFF_OBJECT_NAME_SIZE] object_name; 623 char[12] file_time; 624 char[6] user_id; 625 char[6] group_id; 626 char[8] file_mode; // in octal 627 char[10] file_size; 628 char[2] trailer; 629 } 630 631 extern (C++) void MSCoffOmToHeader(MSCoffLibHeader* h, MSCoffObjModule* om) 632 { 633 size_t len; 634 if (om.name_offset == -1) 635 { 636 len = om.name.length; 637 memcpy(h.object_name.ptr, om.name.ptr, len); 638 h.object_name[len] = '/'; 639 } 640 else 641 { 642 len = sprintf(h.object_name.ptr, "/%d", om.name_offset); 643 h.object_name[len] = ' '; 644 } 645 assert(len < MSCOFF_OBJECT_NAME_SIZE); 646 memset(h.object_name.ptr + len + 1, ' ', MSCOFF_OBJECT_NAME_SIZE - (len + 1)); 647 /* In the following sprintf's, don't worry if the trailing 0 648 * that sprintf writes goes off the end of the field. It will 649 * write into the next field, which we will promptly overwrite 650 * anyway. (So make sure to write the fields in ascending order.) 651 */ 652 len = sprintf(h.file_time.ptr, "%llu", cast(long)om.file_time); 653 assert(len <= 12); 654 memset(h.file_time.ptr + len, ' ', 12 - len); 655 // Match what MS tools do (set to all blanks) 656 memset(h.user_id.ptr, ' ', (h.user_id).sizeof); 657 memset(h.group_id.ptr, ' ', (h.group_id).sizeof); 658 len = sprintf(h.file_mode.ptr, "%o", om.file_mode); 659 assert(len <= 8); 660 memset(h.file_mode.ptr + len, ' ', 8 - len); 661 len = sprintf(h.file_size.ptr, "%u", om.length); 662 assert(len <= 10); 663 memset(h.file_size.ptr + len, ' ', 10 - len); 664 h.trailer[0] = '`'; 665 h.trailer[1] = '\n'; 666 }