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 _link.d) 9 */ 10 11 module ddmd.link; 12 13 import core.stdc.ctype; 14 import core.stdc.stdio; 15 import core.stdc..string; 16 import core.sys.posix.stdio; 17 import core.sys.posix.stdlib; 18 import core.sys.posix.unistd; 19 import ddmd.errors; 20 import ddmd.globals; 21 import ddmd.root.file; 22 import ddmd.root.filename; 23 import ddmd.root.outbuffer; 24 import ddmd.root.rmem; 25 import ddmd.utils; 26 27 version (Posix) extern (C) int pipe(int*); 28 version (Windows) extern (C) int putenv(const char*); 29 version (Windows) extern (C) int spawnlp(int, const char*, const char*, const char*, const char*); 30 version (Windows) extern (C) int spawnl(int, const char*, const char*, const char*, const char*); 31 version (Windows) extern (C) int spawnv(int, const char*, const char**); 32 version (CRuntime_Microsoft) extern (Windows) uint GetShortPathNameA(const char* lpszLongPath, char* lpszShortPath, uint cchBuffer); 33 34 /**************************************** 35 * Write filename to cmdbuf, quoting if necessary. 36 */ 37 private void writeFilename(OutBuffer* buf, const(char)* filename, size_t len) 38 { 39 /* Loop and see if we need to quote 40 */ 41 for (size_t i = 0; i < len; i++) 42 { 43 char c = filename[i]; 44 if (isalnum(c) || c == '_') 45 continue; 46 /* Need to quote 47 */ 48 buf.writeByte('"'); 49 buf.write(filename, len); 50 buf.writeByte('"'); 51 return; 52 } 53 /* No quoting necessary 54 */ 55 buf.write(filename, len); 56 } 57 58 private void writeFilename(OutBuffer* buf, const(char)* filename) 59 { 60 writeFilename(buf, filename, strlen(filename)); 61 } 62 63 version (Posix) 64 { 65 /***************************** 66 * As it forwards the linker error message to stderr, checks for the presence 67 * of an error indicating lack of a main function (NME_ERR_MSG). 68 * 69 * Returns: 70 * 1 if there is a no main error 71 * -1 if there is an IO error 72 * 0 otherwise 73 */ 74 private int findNoMainError(int fd) 75 { 76 version (OSX) 77 { 78 static __gshared const(char)* nmeErrorMessage = "\"__Dmain\", referenced from:"; 79 } 80 else 81 { 82 static __gshared const(char)* nmeErrorMessage = "undefined reference to `_Dmain'"; 83 } 84 FILE* stream = fdopen(fd, "r"); 85 if (stream is null) 86 return -1; 87 const(size_t) len = 64 * 1024 - 1; 88 char[len + 1] buffer; // + '\0' 89 size_t beg = 0, end = len; 90 bool nmeFound = false; 91 for (;;) 92 { 93 // read linker output 94 const(size_t) n = fread(&buffer[beg], 1, len - beg, stream); 95 if (beg + n < len && ferror(stream)) 96 return -1; 97 buffer[(end = beg + n)] = '\0'; 98 // search error message, stop at last complete line 99 const(char)* lastSep = strrchr(buffer.ptr, '\n'); 100 if (lastSep) 101 buffer[(end = lastSep - &buffer[0])] = '\0'; 102 if (strstr(&buffer[0], nmeErrorMessage)) 103 nmeFound = true; 104 if (lastSep) 105 buffer[end++] = '\n'; 106 if (fwrite(&buffer[0], 1, end, stderr) < end) 107 return -1; 108 if (beg + n < len && feof(stream)) 109 break; 110 // copy over truncated last line 111 memcpy(&buffer[0], &buffer[end], (beg = len - end)); 112 } 113 return nmeFound ? 1 : 0; 114 } 115 } 116 117 /***************************** 118 * Run the linker. Return status of execution. 119 */ 120 public int runLINK() 121 { 122 version (Windows) 123 { 124 if (global.params.mscoff) 125 { 126 OutBuffer cmdbuf; 127 cmdbuf.writestring("/NOLOGO "); 128 for (size_t i = 0; i < global.params.objfiles.dim; i++) 129 { 130 if (i) 131 cmdbuf.writeByte(' '); 132 const(char)* p = (*global.params.objfiles)[i]; 133 const(char)* basename = FileName.removeExt(FileName.name(p)); 134 const(char)* ext = FileName.ext(p); 135 if (ext && !strchr(basename, '.')) 136 { 137 // Write name sans extension (but not if a double extension) 138 writeFilename(&cmdbuf, p, ext - p - 1); 139 } 140 else 141 writeFilename(&cmdbuf, p); 142 FileName.free(basename); 143 } 144 if (global.params.resfile) 145 { 146 cmdbuf.writeByte(' '); 147 writeFilename(&cmdbuf, global.params.resfile); 148 } 149 cmdbuf.writeByte(' '); 150 if (global.params.exefile) 151 { 152 cmdbuf.writestring("/OUT:"); 153 writeFilename(&cmdbuf, global.params.exefile); 154 } 155 else 156 { 157 /* Generate exe file name from first obj name. 158 * No need to add it to cmdbuf because the linker will default to it. 159 */ 160 const(char)* n = (*global.params.objfiles)[0]; 161 n = FileName.name(n); 162 global.params.exefile = cast(char*)FileName.forceExt(n, "exe"); 163 } 164 // Make sure path to exe file exists 165 ensurePathToNameExists(Loc(), global.params.exefile); 166 cmdbuf.writeByte(' '); 167 if (global.params.mapfile) 168 { 169 cmdbuf.writestring("/MAP:"); 170 writeFilename(&cmdbuf, global.params.mapfile); 171 } 172 else if (global.params.map) 173 { 174 const(char)* fn = FileName.forceExt(global.params.exefile, "map"); 175 const(char)* path = FileName.path(global.params.exefile); 176 const(char)* p; 177 if (path[0] == '\0') 178 p = FileName.combine(global.params.objdir, fn); 179 else 180 p = fn; 181 cmdbuf.writestring("/MAP:"); 182 writeFilename(&cmdbuf, p); 183 } 184 for (size_t i = 0; i < global.params.libfiles.dim; i++) 185 { 186 cmdbuf.writeByte(' '); 187 cmdbuf.writestring("/DEFAULTLIB:"); 188 writeFilename(&cmdbuf, (*global.params.libfiles)[i]); 189 } 190 if (global.params.deffile) 191 { 192 cmdbuf.writeByte(' '); 193 cmdbuf.writestring("/DEF:"); 194 writeFilename(&cmdbuf, global.params.deffile); 195 } 196 if (global.params.symdebug) 197 { 198 cmdbuf.writeByte(' '); 199 cmdbuf.writestring("/DEBUG"); 200 // in release mode we need to reactivate /OPT:REF after /DEBUG 201 if (global.params.release) 202 cmdbuf.writestring(" /OPT:REF"); 203 } 204 if (global.params.dll) 205 { 206 cmdbuf.writeByte(' '); 207 cmdbuf.writestring("/DLL"); 208 } 209 for (size_t i = 0; i < global.params.linkswitches.dim; i++) 210 { 211 cmdbuf.writeByte(' '); 212 cmdbuf.writestring((*global.params.linkswitches)[i]); 213 } 214 /* Append the path to the VC lib files, and then the SDK lib files 215 */ 216 const(char)* vcinstalldir = getenv("VCINSTALLDIR"); 217 if (vcinstalldir) 218 { 219 cmdbuf.writestring(" /LIBPATH:\""); 220 cmdbuf.writestring(vcinstalldir); 221 if (global.params.is64bit) 222 cmdbuf.writestring("\\lib\\amd64\""); 223 else 224 cmdbuf.writestring("\\lib\""); 225 } 226 const(char)* windowssdkdir = getenv("WindowsSdkDir"); 227 if (windowssdkdir) 228 { 229 cmdbuf.writestring(" /LIBPATH:\""); 230 cmdbuf.writestring(windowssdkdir); 231 if (global.params.is64bit) 232 cmdbuf.writestring("\\lib\\x64\""); 233 else 234 cmdbuf.writestring("\\lib\""); 235 } 236 cmdbuf.writeByte(' '); 237 const(char)* lflags; 238 if (detectVS14(cmdbuf.peekString())) 239 { 240 lflags = getenv("LFLAGS_VS14"); 241 if (!lflags) 242 lflags = "legacy_stdio_definitions.lib"; 243 // environment variables UniversalCRTSdkDir and UCRTVersion set 244 // when running vcvarsall.bat x64 245 if (const(char)* UniversalCRTSdkDir = getenv("UniversalCRTSdkDir")) 246 if (const(char)* UCRTVersion = getenv("UCRTVersion")) 247 { 248 cmdbuf.writestring(" /LIBPATH:\""); 249 cmdbuf.writestring(UniversalCRTSdkDir); 250 cmdbuf.writestring("\\lib\\"); 251 cmdbuf.writestring(UCRTVersion); 252 if (global.params.is64bit) 253 cmdbuf.writestring("\\ucrt\\x64\""); 254 else 255 cmdbuf.writestring("\\ucrt\\x86\""); 256 } 257 } 258 else 259 { 260 lflags = getenv("LFLAGS_VS12"); 261 } 262 if (lflags) 263 { 264 cmdbuf.writeByte(' '); 265 cmdbuf.writestring(lflags); 266 } 267 char* p = cmdbuf.peekString(); 268 const(char)* lnkfilename = null; 269 size_t plen = strlen(p); 270 if (plen > 7000) 271 { 272 lnkfilename = FileName.forceExt(global.params.exefile, "lnk"); 273 auto flnk = File(lnkfilename); 274 flnk.setbuffer(p, plen); 275 flnk._ref = 1; 276 if (flnk.write()) 277 error(Loc(), "error writing file %s", lnkfilename); 278 if (strlen(lnkfilename) < plen) 279 sprintf(p, "@%s", lnkfilename); 280 } 281 const(char)* linkcmd = getenv(global.params.is64bit ? "LINKCMD64" : "LINKCMD"); 282 if (!linkcmd) 283 linkcmd = getenv("LINKCMD"); // backward compatible 284 if (!linkcmd) 285 { 286 if (vcinstalldir) 287 { 288 OutBuffer linkcmdbuf; 289 linkcmdbuf.writestring(vcinstalldir); 290 if (global.params.is64bit) 291 linkcmdbuf.writestring("\\bin\\amd64\\link"); 292 else 293 linkcmdbuf.writestring("\\bin\\link"); 294 linkcmd = linkcmdbuf.extractString(); 295 } 296 else 297 linkcmd = "optlink"; 298 } 299 int status = executecmd(linkcmd, p); 300 if (lnkfilename) 301 { 302 remove(lnkfilename); 303 FileName.free(lnkfilename); 304 } 305 return status; 306 } 307 else 308 { 309 OutBuffer cmdbuf; 310 global.params.libfiles.push("user32"); 311 global.params.libfiles.push("kernel32"); 312 for (size_t i = 0; i < global.params.objfiles.dim; i++) 313 { 314 if (i) 315 cmdbuf.writeByte('+'); 316 const(char)* p = (*global.params.objfiles)[i]; 317 const(char)* basename = FileName.removeExt(FileName.name(p)); 318 const(char)* ext = FileName.ext(p); 319 if (ext && !strchr(basename, '.')) 320 { 321 // Write name sans extension (but not if a double extension) 322 writeFilename(&cmdbuf, p, ext - p - 1); 323 } 324 else 325 writeFilename(&cmdbuf, p); 326 FileName.free(basename); 327 } 328 cmdbuf.writeByte(','); 329 if (global.params.exefile) 330 writeFilename(&cmdbuf, global.params.exefile); 331 else 332 { 333 /* Generate exe file name from first obj name. 334 * No need to add it to cmdbuf because the linker will default to it. 335 */ 336 const(char)* n = (*global.params.objfiles)[0]; 337 n = FileName.name(n); 338 global.params.exefile = cast(char*)FileName.forceExt(n, "exe"); 339 } 340 // Make sure path to exe file exists 341 ensurePathToNameExists(Loc(), global.params.exefile); 342 cmdbuf.writeByte(','); 343 if (global.params.mapfile) 344 writeFilename(&cmdbuf, global.params.mapfile); 345 else if (global.params.map) 346 { 347 const(char)* fn = FileName.forceExt(global.params.exefile, "map"); 348 const(char)* path = FileName.path(global.params.exefile); 349 const(char)* p; 350 if (path[0] == '\0') 351 p = FileName.combine(global.params.objdir, fn); 352 else 353 p = fn; 354 writeFilename(&cmdbuf, p); 355 } 356 else 357 cmdbuf.writestring("nul"); 358 cmdbuf.writeByte(','); 359 for (size_t i = 0; i < global.params.libfiles.dim; i++) 360 { 361 if (i) 362 cmdbuf.writeByte('+'); 363 writeFilename(&cmdbuf, (*global.params.libfiles)[i]); 364 } 365 if (global.params.deffile) 366 { 367 cmdbuf.writeByte(','); 368 writeFilename(&cmdbuf, global.params.deffile); 369 } 370 /* Eliminate unnecessary trailing commas */ 371 while (1) 372 { 373 size_t i = cmdbuf.offset; 374 if (!i || cmdbuf.data[i - 1] != ',') 375 break; 376 cmdbuf.offset--; 377 } 378 if (global.params.resfile) 379 { 380 cmdbuf.writestring("/RC:"); 381 writeFilename(&cmdbuf, global.params.resfile); 382 } 383 if (global.params.map || global.params.mapfile) 384 cmdbuf.writestring("/m"); 385 version (none) 386 { 387 if (debuginfo) 388 cmdbuf.writestring("/li"); 389 if (codeview) 390 { 391 cmdbuf.writestring("/co"); 392 if (codeview3) 393 cmdbuf.writestring(":3"); 394 } 395 } 396 else 397 { 398 if (global.params.symdebug) 399 cmdbuf.writestring("/co"); 400 } 401 cmdbuf.writestring("/noi"); 402 for (size_t i = 0; i < global.params.linkswitches.dim; i++) 403 { 404 cmdbuf.writestring((*global.params.linkswitches)[i]); 405 } 406 cmdbuf.writeByte(';'); 407 char* p = cmdbuf.peekString(); 408 const(char)* lnkfilename = null; 409 size_t plen = strlen(p); 410 if (plen > 7000) 411 { 412 lnkfilename = FileName.forceExt(global.params.exefile, "lnk"); 413 auto flnk = File(lnkfilename); 414 flnk.setbuffer(p, plen); 415 flnk._ref = 1; 416 if (flnk.write()) 417 error(Loc(), "error writing file %s", lnkfilename); 418 if (strlen(lnkfilename) < plen) 419 sprintf(p, "@%s", lnkfilename); 420 } 421 const(char)* linkcmd = getenv("LINKCMD"); 422 if (!linkcmd) 423 linkcmd = "link"; 424 int status = executecmd(linkcmd, p); 425 if (lnkfilename) 426 { 427 remove(lnkfilename); 428 FileName.free(lnkfilename); 429 } 430 return status; 431 } 432 } 433 else version (Posix) 434 { 435 pid_t childpid; 436 int status; 437 // Build argv[] 438 Strings argv; 439 const(char)* cc = getenv("CC"); 440 if (!cc) 441 cc = "cc"; 442 argv.push(cc); 443 argv.insert(1, global.params.objfiles); 444 version (OSX) 445 { 446 // If we are on Mac OS X and linking a dynamic library, 447 // add the "-dynamiclib" flag 448 if (global.params.dll) 449 argv.push("-dynamiclib"); 450 } 451 else version (Posix) 452 { 453 if (global.params.dll) 454 argv.push("-shared"); 455 } 456 // None of that a.out stuff. Use explicit exe file name, or 457 // generate one from name of first source file. 458 argv.push("-o"); 459 if (global.params.exefile) 460 { 461 argv.push(global.params.exefile); 462 } 463 else if (global.params.run) 464 { 465 version (all) 466 { 467 char[L_tmpnam + 14 + 1] name; 468 strcpy(name.ptr, P_tmpdir); 469 strcat(name.ptr, "/dmd_runXXXXXX"); 470 int fd = mkstemp(name.ptr); 471 if (fd == -1) 472 { 473 error(Loc(), "error creating temporary file"); 474 return 1; 475 } 476 else 477 close(fd); 478 global.params.exefile = mem.xstrdup(name.ptr); 479 argv.push(global.params.exefile); 480 } 481 else 482 { 483 /* The use of tmpnam raises the issue of "is this a security hole"? 484 * The hole is that after tmpnam and before the file is opened, 485 * the attacker modifies the file system to get control of the 486 * file with that name. I do not know if this is an issue in 487 * this context. 488 * We cannot just replace it with mkstemp, because this name is 489 * passed to the linker that actually opens the file and writes to it. 490 */ 491 char[L_tmpnam + 1] s; 492 char* n = tmpnam(s.ptr); 493 global.params.exefile = mem.xstrdup(n); 494 argv.push(global.params.exefile); 495 } 496 } 497 else 498 { 499 // Generate exe file name from first obj name 500 const(char)* n = (*global.params.objfiles)[0]; 501 char* ex; 502 n = FileName.name(n); 503 const(char)* e = FileName.ext(n); 504 if (e) 505 { 506 e--; // back up over '.' 507 ex = cast(char*)mem.xmalloc(e - n + 1); 508 memcpy(ex, n, e - n); 509 ex[e - n] = 0; 510 // If generating dll then force dll extension 511 if (global.params.dll) 512 ex = cast(char*)FileName.forceExt(ex, global.dll_ext); 513 } 514 else 515 ex = cast(char*)"a.out"; // no extension, so give up 516 argv.push(ex); 517 global.params.exefile = ex; 518 } 519 // Make sure path to exe file exists 520 ensurePathToNameExists(Loc(), global.params.exefile); 521 if (global.params.symdebug) 522 argv.push("-g"); 523 if (global.params.is64bit) 524 argv.push("-m64"); 525 else 526 argv.push("-m32"); 527 version (OSX) 528 { 529 /* Without this switch, ld generates messages of the form: 530 * ld: warning: could not create compact unwind for __Dmain: offset of saved registers too far to encode 531 * meaning they are further than 255 bytes from the frame register. 532 * ld reverts to the old method instead. 533 * See: https://ghc.haskell.org/trac/ghc/ticket/5019 534 * which gives this tidbit: 535 * "When a C++ (or x86_64 Objective-C) exception is thrown, the runtime must unwind the 536 * stack looking for some function to catch the exception. Traditionally, the unwind 537 * information is stored in the __TEXT/__eh_frame section of each executable as Dwarf 538 * CFI (call frame information). Beginning in Mac OS X 10.6, the unwind information is 539 * also encoded in the __TEXT/__unwind_info section using a two-level lookup table of 540 * compact unwind encodings. 541 * The unwinddump tool displays the content of the __TEXT/__unwind_info section." 542 * 543 * A better fix would be to save the registers next to the frame pointer. 544 */ 545 argv.push("-Xlinker"); 546 argv.push("-no_compact_unwind"); 547 } 548 if (global.params.map || global.params.mapfile) 549 { 550 argv.push("-Xlinker"); 551 version (OSX) 552 { 553 argv.push("-map"); 554 } 555 else 556 { 557 argv.push("-Map"); 558 } 559 if (!global.params.mapfile) 560 { 561 const(char)* fn = FileName.forceExt(global.params.exefile, "map"); 562 const(char)* path = FileName.path(global.params.exefile); 563 const(char)* p; 564 if (path[0] == '\0') 565 p = FileName.combine(global.params.objdir, fn); 566 else 567 p = fn; 568 global.params.mapfile = cast(char*)p; 569 } 570 argv.push("-Xlinker"); 571 argv.push(global.params.mapfile); 572 } 573 if (0 && global.params.exefile) 574 { 575 /* This switch enables what is known as 'smart linking' 576 * in the Windows world, where unreferenced sections 577 * are removed from the executable. It eliminates unreferenced 578 * functions, essentially making a 'library' out of a module. 579 * Although it is documented to work with ld version 2.13, 580 * in practice it does not, but just seems to be ignored. 581 * Thomas Kuehne has verified that it works with ld 2.16.1. 582 * BUG: disabled because it causes exception handling to fail 583 * because EH sections are "unreferenced" and elided 584 */ 585 argv.push("-Xlinker"); 586 argv.push("--gc-sections"); 587 } 588 for (size_t i = 0; i < global.params.linkswitches.dim; i++) 589 { 590 const(char)* p = (*global.params.linkswitches)[i]; 591 if (!p || !p[0] || !(p[0] == '-' && (p[1] == 'l' || p[1] == 'L'))) 592 { 593 // Don't need -Xlinker if switch starts with -l or -L. 594 // Eliding -Xlinker is significant for -L since it allows our paths 595 // to take precedence over gcc defaults. 596 argv.push("-Xlinker"); 597 } 598 argv.push(p); 599 } 600 /* Add each library, prefixing it with "-l". 601 * The order of libraries passed is: 602 * 1. any libraries passed with -L command line switch 603 * 2. libraries specified on the command line 604 * 3. libraries specified by pragma(lib), which were appended 605 * to global.params.libfiles. 606 * 4. standard libraries. 607 */ 608 for (size_t i = 0; i < global.params.libfiles.dim; i++) 609 { 610 const(char)* p = (*global.params.libfiles)[i]; 611 size_t plen = strlen(p); 612 if (plen > 2 && p[plen - 2] == '.' && p[plen - 1] == 'a') 613 argv.push(p); 614 else 615 { 616 char* s = cast(char*)mem.xmalloc(plen + 3); 617 s[0] = '-'; 618 s[1] = 'l'; 619 memcpy(s + 2, p, plen + 1); 620 argv.push(s); 621 } 622 } 623 for (size_t i = 0; i < global.params.dllfiles.dim; i++) 624 { 625 const(char)* p = (*global.params.dllfiles)[i]; 626 argv.push(p); 627 } 628 /* Standard libraries must go after user specified libraries 629 * passed with -l. 630 */ 631 const(char)* libname = global.params.symdebug ? global.params.debuglibname : global.params.defaultlibname; 632 size_t slen = strlen(libname); 633 if (slen) 634 { 635 char* buf = cast(char*)malloc(3 + slen + 1); 636 strcpy(buf, "-l"); 637 638 if (slen > 3 + 2 && memcmp(libname, "lib".ptr, 3) == 0) 639 { 640 if (memcmp(libname + slen - 2, ".a".ptr, 2) == 0) 641 { 642 argv.push("-Xlinker"); 643 argv.push("-Bstatic"); 644 strncat(buf, libname + 3, slen - 3 - 2); 645 argv.push(buf); 646 argv.push("-Xlinker"); 647 argv.push("-Bdynamic"); 648 } 649 else if (memcmp(libname + slen - 3, ".so".ptr, 3) == 0) 650 { 651 strncat(buf, libname + 3, slen - 3 - 3); 652 argv.push(buf); 653 } 654 else 655 { 656 strcat(buf, libname); 657 argv.push(buf); 658 } 659 } 660 else 661 { 662 strcat(buf, libname); 663 argv.push(buf); 664 } 665 } 666 //argv.push("-ldruntime"); 667 argv.push("-lpthread"); 668 argv.push("-lm"); 669 version (linux) 670 { 671 // Changes in ld for Ubuntu 11.10 require this to appear after phobos2 672 argv.push("-lrt"); 673 // Link against libdl for phobos usage of dlopen 674 argv.push("-ldl"); 675 } 676 if (global.params.verbose) 677 { 678 // Print it 679 for (size_t i = 0; i < argv.dim; i++) 680 fprintf(global.stdmsg, "%s ", argv[i]); 681 fprintf(global.stdmsg, "\n"); 682 } 683 argv.push(null); 684 // set up pipes 685 int[2] fds; 686 if (pipe(fds.ptr) == -1) 687 { 688 perror("unable to create pipe to linker"); 689 return -1; 690 } 691 childpid = fork(); 692 if (childpid == 0) 693 { 694 // pipe linker stderr to fds[0] 695 dup2(fds[1], STDERR_FILENO); 696 close(fds[0]); 697 execvp(argv[0], cast(char**)argv.tdata()); 698 perror(argv[0]); // failed to execute 699 return -1; 700 } 701 else if (childpid == -1) 702 { 703 perror("unable to fork"); 704 return -1; 705 } 706 close(fds[1]); 707 const(int) nme = findNoMainError(fds[0]); 708 waitpid(childpid, &status, 0); 709 if (WIFEXITED(status)) 710 { 711 status = WEXITSTATUS(status); 712 if (status) 713 { 714 if (nme == -1) 715 { 716 perror("error with the linker pipe"); 717 return -1; 718 } 719 else 720 { 721 error(Loc(), "linker exited with status %d", status); 722 if (nme == 1) 723 error(Loc(), "no main function specified"); 724 } 725 } 726 } 727 else if (WIFSIGNALED(status)) 728 { 729 error(Loc(), "linker killed by signal %d", WTERMSIG(status)); 730 status = 1; 731 } 732 return status; 733 } 734 else 735 { 736 error(Loc(), "linking is not yet supported for this version of DMD."); 737 return -1; 738 } 739 } 740 741 742 /****************************** 743 * Execute a rule. Return the status. 744 * cmd program to run 745 * args arguments to cmd, as a string 746 */ 747 version (Windows) 748 { 749 private int executecmd(const(char)* cmd, const(char)* args) 750 { 751 int status; 752 size_t len; 753 if (global.params.verbose) 754 fprintf(global.stdmsg, "%s %s\n", cmd, args); 755 if (!global.params.mscoff) 756 { 757 if ((len = strlen(args)) > 255) 758 { 759 char* q = cast(char*)alloca(8 + len + 1); 760 sprintf(q, "_CMDLINE=%s", args); 761 status = putenv(q); 762 if (status == 0) 763 { 764 args = "@_CMDLINE"; 765 } 766 else 767 { 768 error(Loc(), "command line length of %d is too long", len); 769 } 770 } 771 } 772 // Normalize executable path separators, see Bugzilla 9330 773 cmd = toWinPath(cmd); 774 version (CRuntime_Microsoft) 775 { 776 if (strchr(cmd, ' ')) 777 { 778 // MSVCRT: spawn does not work with spaces in the executable 779 size_t cmdlen = strlen(cmd); 780 char* shortName = (new char[](cmdlen + 1)).ptr; // enough space 781 uint plen = GetShortPathNameA(cmd, shortName, cast(uint)cmdlen + 1); 782 if (plen > 0 && plen <= cmdlen) 783 cmd = shortName; 784 } 785 } 786 status = executearg0(cmd, args); 787 if (status == -1) 788 { 789 // spawnlp returns intptr_t in some systems, not int 790 status = spawnlp(0, cmd, cmd, args, null); 791 } 792 //if (global.params.verbose) 793 // fprintf(global.stdmsg, "\n"); 794 if (status) 795 { 796 if (status == -1) 797 error(Loc(), "can't run '%s', check PATH", cmd); 798 else 799 error(Loc(), "linker exited with status %d", cmd, status); 800 } 801 return status; 802 } 803 } 804 805 /************************************** 806 * Attempt to find command to execute by first looking in the directory 807 * where DMD was run from. 808 * Returns: 809 * -1 did not find command there 810 * !=-1 exit status from command 811 */ 812 version (Windows) 813 { 814 private int executearg0(const(char)* cmd, const(char)* args) 815 { 816 const(char)* file; 817 const(char)* argv0 = global.params.argv0; 818 //printf("argv0='%s', cmd='%s', args='%s'\n",argv0,cmd,args); 819 // If cmd is fully qualified, we don't do this 820 if (FileName.absolute(cmd)) 821 return -1; 822 file = FileName.replaceName(argv0, cmd); 823 //printf("spawning '%s'\n",file); 824 // spawnlp returns intptr_t in some systems, not int 825 return spawnl(0, file, file, args, null); 826 } 827 } 828 829 /*************************************** 830 * Run the compiled program. 831 * Return exit status. 832 */ 833 public int runProgram() 834 { 835 //printf("runProgram()\n"); 836 if (global.params.verbose) 837 { 838 fprintf(global.stdmsg, "%s", global.params.exefile); 839 for (size_t i = 0; i < global.params.runargs.dim; ++i) 840 fprintf(global.stdmsg, " %s", global.params.runargs[i]); 841 fprintf(global.stdmsg, "\n"); 842 } 843 // Build argv[] 844 Strings argv; 845 argv.push(global.params.exefile); 846 for (size_t i = 0; i < global.params.runargs.dim; ++i) 847 { 848 const(char)* a = global.params.runargs[i]; 849 version (Windows) 850 { 851 // BUG: what about " appearing in the string? 852 if (strchr(a, ' ')) 853 { 854 char* b = cast(char*)mem.xmalloc(3 + strlen(a)); 855 sprintf(b, "\"%s\"", a); 856 a = b; 857 } 858 } 859 argv.push(a); 860 } 861 argv.push(null); 862 version (Windows) 863 { 864 const(char)* ex = FileName.name(global.params.exefile); 865 if (ex == global.params.exefile) 866 ex = FileName.combine(".", ex); 867 else 868 ex = global.params.exefile; 869 // spawnlp returns intptr_t in some systems, not int 870 return spawnv(0, ex, argv.tdata()); 871 } 872 else version (Posix) 873 { 874 pid_t childpid; 875 int status; 876 childpid = fork(); 877 if (childpid == 0) 878 { 879 const(char)* fn = argv[0]; 880 if (!FileName.absolute(fn)) 881 { 882 // Make it "./fn" 883 fn = FileName.combine(".", fn); 884 } 885 execv(fn, cast(char**)argv.tdata()); 886 perror(fn); // failed to execute 887 return -1; 888 } 889 waitpid(childpid, &status, 0); 890 if (WIFEXITED(status)) 891 { 892 status = WEXITSTATUS(status); 893 //printf("--- errorlevel %d\n", status); 894 } 895 else if (WIFSIGNALED(status)) 896 { 897 error(Loc(), "program killed by signal %d", WTERMSIG(status)); 898 status = 1; 899 } 900 return status; 901 } 902 else 903 { 904 assert(0); 905 } 906 } 907 908 version (Windows) 909 { 910 /***************************** 911 * Detect whether the link will grab libraries from VS 2015 or later 912 */ 913 private bool detectVS14(const(char)* cmdline) 914 { 915 auto libpaths = new Strings(); 916 // grab library folders passed on the command line 917 for (const(char)* p = cmdline; *p;) 918 { 919 while (isspace(*p)) 920 p++; 921 const(char)* arg = p; 922 const(char)* end = arg; 923 while (*end && !isspace(*end)) 924 { 925 end++; 926 if (end[-1] == '"') 927 { 928 while (*end && *end != '"') 929 { 930 if (*end == '\\' && end[1]) 931 end++; 932 end++; 933 } 934 if (*end) 935 end++; // skip closing quote 936 } 937 } 938 p = end; 939 // remove quotes if spanning complete argument 940 if (end > arg + 1 && arg[0] == '"' && end[-1] == '"') 941 { 942 arg++; 943 end--; 944 } 945 if (arg[0] == '-' || arg[0] == '/') 946 { 947 if (end - arg > 8 && memicmp(arg + 1, "LIBPATH:", 8) == 0) 948 { 949 arg += 9; 950 char* q = cast(char*)memcpy((new char[](end - arg + 1)).ptr, arg, end - arg); 951 q[end - arg] = 0; 952 Strings* paths = FileName.splitPath(q); 953 libpaths.append(paths); 954 } 955 } 956 } 957 // append library paths from environment 958 if (const(char)* lib = getenv("LIB")) 959 libpaths.append(FileName.splitPath(lib)); 960 // if legacy_stdio_definitions.lib can be found in the same folder as 961 // libcmt.lib, libcmt.lib is assumed to be from VS2015 or later 962 const(char)* libcmt = FileName.searchPath(libpaths, "libcmt.lib", true); 963 if (!libcmt) 964 return false; 965 const(char)* liblegacy = FileName.replaceName(libcmt, "legacy_stdio_definitions.lib"); 966 return FileName.exists(liblegacy) == 1; 967 } 968 }