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