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 }