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 }