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 0
    foreach (const char c; filename)
81
    {
82 0
        if (isalnum(c) || c == '_')
83 0
            continue;
84
        /* Need to quote
85
         */
86 0
        buf.writeByte('"');
87 0
        buf.writestring(filename);
88 0
        buf.writeByte('"');
89 0
        return;
90
    }
91
    /* No quoting necessary
92
     */
93 0
    buf.writestring(filename);
94
}
95

96
private void writeFilename(OutBuffer* buf, const(char)* filename)
97
{
98 0
    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 1
        FILE* stream = fdopen(fd, "r");
123 1
        if (stream is null)
124 0
            return -1;
125 1
        const(size_t) len = 64 * 1024 - 1;
126 1
        char[len + 1] buffer; // + '\0'
127 1
        size_t beg = 0, end = len;
128 1
        bool nmeFound = false;
129
        for (;;)
130
        {
131
            // read linker output
132 1
            const(size_t) n = fread(&buffer[beg], 1, len - beg, stream);
133 1
            if (beg + n < len && ferror(stream))
134 0
                return -1;
135 1
            buffer[(end = beg + n)] = '\0';
136
            // search error message, stop at last complete line
137 1
            const(char)* lastSep = strrchr(buffer.ptr, '\n');
138 1
            if (lastSep)
139 1
                buffer[(end = lastSep - &buffer[0])] = '\0';
140 1
            if (strstr(&buffer[0], nmeErrorMessage))
141 0
                nmeFound = true;
142 1
            if (lastSep)
143 1
                buffer[end++] = '\n';
144 1
            if (fwrite(&buffer[0], 1, end, stderr) < end)
145 0
                return -1;
146 1
            if (beg + n < len && feof(stream))
147 1
                break;
148
            // copy over truncated last line
149 0
            memcpy(&buffer[0], &buffer[end], (beg = len - end));
150
        }
151 1
        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 1
    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 0
        const char[] n = FileName.name(global.params.objfiles[0].toDString);
210 0
        global.params.exefile = FileName.forceExt(n, "exe");
211
    }
212

213
    const(char)[] getMapFilename()
214
    {
215 0
        const(char)[] fn = FileName.forceExt(global.params.exefile, "map");
216 0
        const(char)[] path = FileName.path(global.params.exefile);
217 0
        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 1
        pid_t childpid;
458 1
        int status;
459
        // Build argv[]
460 1
        Strings argv;
461 1
        const(char)* cc = getenv("CC");
462 1
        if (!cc)
463
        {
464 1
            argv.push("cc");
465
        }
466
        else
467
        {
468
            // Split CC command to support link driver arguments such as -fpie or -flto.
469 1
            char* arg = cast(char*)Mem.check(strdup(cc));
470 1
            const(char)* tok = strtok(arg, " ");
471 1
            while (tok)
472
            {
473 1
                argv.push(mem.xstrdup(tok));
474 1
                tok = strtok(null, " ");
475
            }
476 1
            free(arg);
477
        }
478 1
        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 1
            if (global.params.dll)
489 1
                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 1
        argv.push("-o");
494 1
        if (global.params.exefile)
495
        {
496 1
            argv.push(global.params.exefile.xarraydup.ptr);
497
        }
498 1
        else if (global.params.run)
499
        {
500
            version (all)
501
            {
502 1
                char[L_tmpnam + 14 + 1] name;
503 1
                strcpy(name.ptr, P_tmpdir);
504 1
                strcat(name.ptr, "/dmd_runXXXXXX");
505 1
                int fd = mkstemp(name.ptr);
506 1
                if (fd == -1)
507
                {
508 0
                    error(Loc.initial, "error creating temporary file");
509 0
                    return 1;
510
                }
511
                else
512 1
                    close(fd);
513 1
                global.params.exefile = name.arraydup;
514 1
                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 1
            const(char)[] n = global.params.objfiles[0].toDString();
536 1
            const(char)[] ex;
537 1
            n = FileName.name(n);
538 1
            if (const e = FileName.ext(n))
539
            {
540 1
                if (global.params.dll)
541 0
                    ex = FileName.forceExt(ex, global.dll_ext);
542
                else
543 1
                    ex = FileName.removeExt(n);
544
            }
545
            else
546 0
                ex = "a.out"; // no extension, so give up
547 1
            argv.push(ex.ptr);
548 1
            global.params.exefile = ex;
549
        }
550
        // Make sure path to exe file exists
551 1
        ensurePathToNameExists(Loc.initial, global.params.exefile);
552 1
        if (global.params.symdebug)
553 1
            argv.push("-g");
554 1
        if (global.params.is64bit)
555 1
            argv.push("-m64");
556
        else
557 0
            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 1
        if (global.params.map || global.params.mapfile.length)
580
        {
581 0
            argv.push("-Xlinker");
582
            version (OSX)
583
            {
584
                argv.push("-map");
585
            }
586
            else
587
            {
588 0
                argv.push("-Map");
589
            }
590 0
            if (!global.params.mapfile.length)
591
            {
592 0
                const(char)[] fn = FileName.forceExt(global.params.exefile, "map");
593 0
                const(char)[] path = FileName.path(global.params.exefile);
594 0
                global.params.mapfile = path.length ? fn : FileName.combine(global.params.objdir, fn);
595
            }
596 0
            argv.push("-Xlinker");
597 0
            argv.push(global.params.mapfile.xarraydup.ptr);
598
        }
599 1
        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 0
            argv.push("-Xlinker");
612 0
            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 1
            const flag = p.toDString();
619

620 1
            return startsWith(p, "-l") || startsWith(p, "-L")
621 1
                || flag == "-(" || flag == "-)"
622 1
                || flag == "--start-group" || flag == "--end-group"
623 1
                || 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 1
        foreach (pi, p; global.params.linkswitches)
640
        {
641 1
            if (p && p[0] && !flagIsLibraryRelated(p))
642
            {
643 1
                if (!global.params.linkswitchIsForCC[pi])
644 1
                    argv.push("-Xlinker");
645 1
                argv.push(p);
646
            }
647
        }
648

649
        // STEP 2
650 1
        foreach (p; global.params.libfiles)
651
        {
652 1
            if (FileName.equalsExt(p, "a"))
653 1
                argv.push(p);
654
        }
655

656
        // STEP 3
657 1
        foreach (pi, p; global.params.linkswitches)
658
        {
659 1
            if (p && p[0] && flagIsLibraryRelated(p))
660
            {
661 1
                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 1
                    argv.push("-Xlinker");
668
                }
669 1
                argv.push(p);
670
            }
671
        }
672

673
        // STEP 4
674 1
        foreach (p; global.params.libfiles)
675
        {
676 1
            if (!FileName.equalsExt(p, "a"))
677
            {
678 1
                const plen = strlen(p);
679 1
                char* s = cast(char*)mem.xmalloc(plen + 3);
680 1
                s[0] = '-';
681 1
                s[1] = 'l';
682 1
                memcpy(s + 2, p, plen + 1);
683 1
                argv.push(s);
684
            }
685
        }
686

687
        // STEP 5
688 1
        foreach (p; global.params.dllfiles)
689
        {
690 0
            argv.push(p);
691
        }
692

693
        // STEP 6
694
        /* D runtime libraries must go after user specified libraries
695
         * passed with -l.
696
         */
697 1
        const libname = phobosLibname;
698 1
        if (libname.length)
699
        {
700 1
            const bufsize = 2 + libname.length + 1;
701 1
            auto buf = (cast(char*) malloc(bufsize))[0 .. bufsize];
702 1
            if (!buf)
703 0
                Mem.error();
704 1
            buf[0 .. 2] = "-l";
705

706
            char* getbuf(const(char)[] suffix)
707
            {
708 1
                buf[2 .. 2 + suffix.length] = suffix[];
709 1
                buf[2 + suffix.length] = 0;
710 1
                return buf.ptr;
711
            }
712

713 1
            if (libname.length > 3 + 2 && libname[0 .. 3] == "lib")
714
            {
715 1
                if (libname[$-2 .. $] == ".a")
716
                {
717 1
                    argv.push("-Xlinker");
718 1
                    argv.push("-Bstatic");
719 1
                    argv.push(getbuf(libname[3 .. $-2]));
720 1
                    argv.push("-Xlinker");
721 1
                    argv.push("-Bdynamic");
722
                }
723 1
                else if (libname[$-3 .. $] == ".so")
724 1
                    argv.push(getbuf(libname[3 .. $-3]));
725
                else
726 0
                    argv.push(getbuf(libname));
727
            }
728
            else
729
            {
730 0
                argv.push(getbuf(libname));
731
            }
732
        }
733
        //argv.push("-ldruntime");
734 1
        argv.push("-lpthread");
735 1
        argv.push("-lm");
736
        version (linux)
737
        {
738
            // Changes in ld for Ubuntu 11.10 require this to appear after phobos2
739 1
            argv.push("-lrt");
740
            // Link against libdl for phobos usage of dlopen
741 1
            argv.push("-ldl");
742
        }
743 1
        if (global.params.verbose)
744
        {
745
            // Print it
746 1
            OutBuffer buf;
747 1
            for (size_t i = 0; i < argv.dim; i++)
748
            {
749 1
                buf.writestring(argv[i]);
750 1
                buf.writeByte(' ');
751
            }
752 1
            message(buf.peekChars());
753
        }
754 1
        argv.push(null);
755
        // set up pipes
756 1
        int[2] fds;
757 1
        if (pipe(fds.ptr) == -1)
758
        {
759 0
            perror("unable to create pipe to linker");
760 0
            return -1;
761
        }
762
        // vfork instead of fork to avoid https://issues.dlang.org/show_bug.cgi?id=21089
763 1
        childpid = vfork();
764 1
        if (childpid == 0)
765
        {
766
            // pipe linker stderr to fds[0]
767 1
            dup2(fds[1], STDERR_FILENO);
768 1
            close(fds[0]);
769 1
            execvp(argv[0], argv.tdata());
770 0
            perror(argv[0]); // failed to execute
771 0
            _exit(-1);
772
        }
773 1
        else if (childpid == -1)
774
        {
775 0
            perror("unable to fork");
776 0
            return -1;
777
        }
778 1
        close(fds[1]);
779 1
        const(int) nme = findNoMainError(fds[0]);
780 1
        waitpid(childpid, &status, 0);
781 1
        if (WIFEXITED(status))
782
        {
783 1
            status = WEXITSTATUS(status);
784 1
            if (status)
785
            {
786 1
                if (nme == -1)
787
                {
788 0
                    perror("error with the linker pipe");
789 0
                    return -1;
790
                }
791
                else
792
                {
793 1
                    error(Loc.initial, "linker exited with status %d", status);
794 1
                    if (nme == 1)
795 0
                        error(Loc.initial, "no main function specified");
796
                }
797
            }
798
        }
799 0
        else if (WIFSIGNALED(status))
800
        {
801 0
            error(Loc.initial, "linker killed by signal %d", WTERMSIG(status));
802 0
            status = 1;
803
        }
804 1
        return status;
805
    }
806
    else
807
    {
808
        error(Loc.initial, "linking is not yet supported for this version of DMD.");
809
        return -1;
810
    }
811
}
812

813

814
/******************************
815
 * Execute a rule.  Return the status.
816
 *      cmd     program to run
817
 *      args    arguments to cmd, as a string
818
 */
819
version (Windows)
820
{
821
    private int executecmd(const(char)* cmd, const(char)* args)
822
    {
823
        int status;
824
        size_t len;
825
        if (global.params.verbose)
826
            message("%s %s", cmd, args);
827
        if (!global.params.mscoff)
828
        {
829
            if ((len = strlen(args)) > 255)
830
            {
831
                status = putenvRestorable("_CMDLINE", args[0 .. len]);
832
                if (status == 0)
833
                    args = "@_CMDLINE";
834
                else
835
                    error(Loc.initial, "command line length of %llu is too long", cast(ulong) len);
836
            }
837
        }
838
        // Normalize executable path separators
839
        // https://issues.dlang.org/show_bug.cgi?id=9330
840
        cmd = toWinPath(cmd);
841
        version (CRuntime_Microsoft)
842
        {
843
            // Open scope so dmd doesn't complain about alloca + exception handling
844
            {
845
                // Use process spawning through the WinAPI to avoid issues with executearg0 and spawnlp
846
                OutBuffer cmdbuf;
847
                cmdbuf.writestring("\"");
848
                cmdbuf.writestring(cmd);
849
                cmdbuf.writestring("\" ");
850
                cmdbuf.writestring(args);
851

852
                STARTUPINFOA startInf;
853
                startInf.dwFlags = STARTF_USESTDHANDLES;
854
                startInf.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
855
                startInf.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
856
                startInf.hStdError = GetStdHandle(STD_ERROR_HANDLE);
857
                PROCESS_INFORMATION procInf;
858

859
                BOOL b = CreateProcessA(null, cmdbuf.peekChars(), null, null, 1, NORMAL_PRIORITY_CLASS, null, null, &startInf, &procInf);
860
                if (b)
861
                {
862
                    WaitForSingleObject(procInf.hProcess, INFINITE);
863
                    DWORD returnCode;
864
                    GetExitCodeProcess(procInf.hProcess, &returnCode);
865
                    status = returnCode;
866
                    CloseHandle(procInf.hProcess);
867
                }
868
                else
869
                {
870
                    status = -1;
871
                }
872
            }
873
        }
874
        else
875
        {
876
            status = executearg0(cmd, args);
877
            if (status == -1)
878
            {
879
                status = spawnlp(0, cmd, cmd, args, null);
880
            }
881
        }
882
        if (status)
883
        {
884
            if (status == -1)
885
                error(Loc.initial, "can't run '%s', check PATH", cmd);
886
            else
887
                error(Loc.initial, "linker exited with status %d", status);
888
        }
889
        return status;
890
    }
891
}
892

893
/**************************************
894
 * Attempt to find command to execute by first looking in the directory
895
 * where DMD was run from.
896
 * Returns:
897
 *      -1      did not find command there
898
 *      !=-1    exit status from command
899
 */
900
version (Windows)
901
{
902
    private int executearg0(const(char)* cmd, const(char)* args)
903
    {
904
        const argv0 = global.params.argv0;
905
        //printf("argv0='%s', cmd='%s', args='%s'\n",argv0,cmd,args);
906
        // If cmd is fully qualified, we don't do this
907
        if (FileName.absolute(cmd.toDString()))
908
            return -1;
909
        const file = FileName.replaceName(argv0, cmd.toDString);
910
        //printf("spawning '%s'\n",file);
911
        // spawnlp returns intptr_t in some systems, not int
912
        return spawnl(0, file.ptr, file.ptr, args, null);
913
    }
914
}
915

916
/***************************************
917
 * Run the compiled program.
918
 * Return exit status.
919
 */
920
public int runProgram()
921
{
922
    //printf("runProgram()\n");
923 1
    if (global.params.verbose)
924
    {
925 0
        OutBuffer buf;
926 0
        buf.writestring(global.params.exefile);
927 0
        for (size_t i = 0; i < global.params.runargs.dim; ++i)
928
        {
929 0
            buf.writeByte(' ');
930 0
            buf.writestring(global.params.runargs[i]);
931
        }
932 0
        message(buf.peekChars());
933
    }
934
    // Build argv[]
935 1
    Strings argv;
936 1
    argv.push(global.params.exefile.xarraydup.ptr);
937 1
    for (size_t i = 0; i < global.params.runargs.dim; ++i)
938
    {
939 1
        const(char)* a = global.params.runargs[i];
940
        version (Windows)
941
        {
942
            // BUG: what about " appearing in the string?
943
            if (strchr(a, ' '))
944
            {
945
                char* b = cast(char*)mem.xmalloc(3 + strlen(a));
946
                sprintf(b, "\"%s\"", a);
947
                a = b;
948
            }
949
        }
950 1
        argv.push(a);
951
    }
952 1
    argv.push(null);
953 1
    restoreEnvVars();
954
    version (Windows)
955
    {
956
        const(char)[] ex = FileName.name(global.params.exefile);
957
        if (ex == global.params.exefile)
958
            ex = FileName.combine(".", ex);
959
        else
960
            ex = global.params.exefile;
961
        // spawnlp returns intptr_t in some systems, not int
962
        return spawnv(0, ex.xarraydup.ptr, argv.tdata());
963
    }
964
    else version (Posix)
965
    {
966 1
        pid_t childpid;
967 1
        int status;
968 1
        childpid = fork();
969 1
        if (childpid == 0)
970
        {
971 0
            const(char)[] fn = argv[0].toDString();
972
            // Make it "./fn" if needed
973 0
            if (!FileName.absolute(fn))
974 0
                fn = FileName.combine(".", fn);
975 0
            fn.toCStringThen!((fnp) {
976 0
                    execv(fnp.ptr, argv.tdata());
977
                    // If execv returns, it failed to execute
978 0
                    perror(fnp.ptr);
979
                });
980 0
            return -1;
981
        }
982 1
        waitpid(childpid, &status, 0);
983 1
        if (WIFEXITED(status))
984
        {
985 1
            status = WEXITSTATUS(status);
986
            //printf("--- errorlevel %d\n", status);
987
        }
988 0
        else if (WIFSIGNALED(status))
989
        {
990 0
            error(Loc.initial, "program killed by signal %d", WTERMSIG(status));
991 0
            status = 1;
992
        }
993 1
        return status;
994
    }
995
    else
996
    {
997
        assert(0);
998
    }
999
}

Read our documentation on viewing source code .

Loading