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