Exemple #1
0
int runLINK()
{
#if _WIN32
    char *p;
    int i;
    int status;
    OutBuffer cmdbuf;

    global.params.libfiles->push("user32");
    global.params.libfiles->push("kernel32");

    for (i = 0; i < global.params.objfiles->dim; i++)
    {
        if (i)
            cmdbuf.writeByte('+');
        p = global.params.objfiles->tdata()[i];
        char *basename = FileName::removeExt(FileName::name(p));
        char *ext = FileName::ext(p);
        if (ext && !strchr(basename, '.'))
            // Write name sans extension (but not if a double extension)
            writeFilename(&cmdbuf, p, ext - p - 1);
        else
            writeFilename(&cmdbuf, p);
        mem.free(basename);
    }
    cmdbuf.writeByte(',');
    if (global.params.exefile)
        writeFilename(&cmdbuf, global.params.exefile);
    else
    {   /* Generate exe file name from first obj name.
         * No need to add it to cmdbuf because the linker will default to it.
         */
        char *n = global.params.objfiles->tdata()[0];
        n = FileName::name(n);
        FileName *fn = FileName::forceExt(n, "exe");
        global.params.exefile = fn->toChars();
    }

    // Make sure path to exe file exists
    {   char *p = FileName::path(global.params.exefile);
        FileName::ensurePathExists(p);
        mem.free(p);
    }

    cmdbuf.writeByte(',');
    if (global.params.mapfile)
        writeFilename(&cmdbuf, global.params.mapfile);
    else if (global.params.map)
    {
        FileName *fn = FileName::forceExt(global.params.exefile, "map");

        char *path = FileName::path(global.params.exefile);
        char *p;
        if (path[0] == '\0')
            p = FileName::combine(global.params.objdir, fn->toChars());
        else
            p = fn->toChars();

        writeFilename(&cmdbuf, p);
    }
    else
        cmdbuf.writestring("nul");
    cmdbuf.writeByte(',');

    for (i = 0; i < global.params.libfiles->dim; i++)
    {
        if (i)
            cmdbuf.writeByte('+');
        writeFilename(&cmdbuf, global.params.libfiles->tdata()[i]);
    }

    if (global.params.deffile)
    {
        cmdbuf.writeByte(',');
        writeFilename(&cmdbuf, global.params.deffile);
    }

    /* Eliminate unnecessary trailing commas    */
    while (1)
    {   i = cmdbuf.offset;
        if (!i || cmdbuf.data[i - 1] != ',')
            break;
        cmdbuf.offset--;
    }

    if (global.params.resfile)
    {
        cmdbuf.writestring("/RC:");
        writeFilename(&cmdbuf, global.params.resfile);
    }

    if (global.params.map || global.params.mapfile)
        cmdbuf.writestring("/m");

#if 0
    if (debuginfo)
        cmdbuf.writestring("/li");
    if (codeview)
    {
        cmdbuf.writestring("/co");
        if (codeview3)
            cmdbuf.writestring(":3");
    }
#else
    if (global.params.symdebug)
        cmdbuf.writestring("/co");
#endif

    cmdbuf.writestring("/noi");
    for (i = 0; i < global.params.linkswitches->dim; i++)
    {
        cmdbuf.writestring(global.params.linkswitches->tdata()[i]);
    }
    cmdbuf.writeByte(';');

    p = cmdbuf.toChars();

    FileName *lnkfilename = NULL;
    size_t plen = strlen(p);
    if (plen > 7000)
    {
        lnkfilename = FileName::forceExt(global.params.exefile, "lnk");
        File flnk(lnkfilename);
        flnk.setbuffer(p, plen);
        flnk.ref = 1;
        if (flnk.write())
            error("error writing file %s", lnkfilename);
        if (lnkfilename->len() < plen)
            sprintf(p, "@%s", lnkfilename->toChars());
    }

    char *linkcmd = getenv("LINKCMD");
    if (!linkcmd)
        linkcmd = "link";
    status = executecmd(linkcmd, p, 1);
    if (lnkfilename)
    {
        remove(lnkfilename->toChars());
        delete lnkfilename;
    }
    return status;
#elif linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4
    pid_t childpid;
    int i;
    int status;

    // Build argv[]
    Strings argv;

    const char *cc = getenv("CC");
    if (!cc)
        cc = "gcc";
    argv.push((char *)cc);
    argv.insert(1, global.params.objfiles);

#if __APPLE__
    // If we are on Mac OS X and linking a dynamic library,
    // add the "-dynamiclib" flag
    if (global.params.dll)
        argv.push((char *) "-dynamiclib");
#endif

    // None of that a.out stuff. Use explicit exe file name, or
    // generate one from name of first source file.
    argv.push((char *)"-o");
    if (global.params.exefile)
    {
        if (global.params.dll)
            global.params.exefile = FileName::forceExt(global.params.exefile, global.dll_ext)->toChars();
        argv.push(global.params.exefile);
    }
    else
    {   // Generate exe file name from first obj name
        char *n = global.params.objfiles->tdata()[0];
        char *e;
        char *ex;

        n = FileName::name(n);
        e = FileName::ext(n);
        if (e)
        {
            e--;                        // back up over '.'
            ex = (char *)mem.malloc(e - n + 1);
            memcpy(ex, n, e - n);
            ex[e - n] = 0;
            // If generating dll then force dll extension
            if (global.params.dll)
                ex = FileName::forceExt(ex, global.dll_ext)->toChars();
        }
        else
            ex = (char *)"a.out";       // no extension, so give up
        argv.push(ex);
        global.params.exefile = ex;
    }

    // Make sure path to exe file exists
    {   char *p = FileName::path(global.params.exefile);
        FileName::ensurePathExists(p);
        mem.free(p);
    }

    if (global.params.symdebug)
        argv.push((char *)"-g");

    if (global.params.isX86_64)
        argv.push((char *)"-m64");
    else
        argv.push((char *)"-m32");

    if (global.params.map || global.params.mapfile)
    {
        argv.push((char *)"-Xlinker");
#if __APPLE__
        argv.push((char *)"-map");
#else
        argv.push((char *)"-Map");
#endif
        if (!global.params.mapfile)
        {
            FileName *fn = FileName::forceExt(global.params.exefile, "map");

            char *path = FileName::path(global.params.exefile);
            char *p;
            if (path[0] == '\0')
                p = FileName::combine(global.params.objdir, fn->toChars());
            else
                p = fn->toChars();

            global.params.mapfile = p;
        }
        argv.push((char *)"-Xlinker");
        argv.push(global.params.mapfile);
    }

    if (0 && global.params.exefile)
    {
        /* This switch enables what is known as 'smart linking'
         * in the Windows world, where unreferenced sections
         * are removed from the executable. It eliminates unreferenced
         * functions, essentially making a 'library' out of a module.
         * Although it is documented to work with ld version 2.13,
         * in practice it does not, but just seems to be ignored.
         * Thomas Kuehne has verified that it works with ld 2.16.1.
         * BUG: disabled because it causes exception handling to fail
         * because EH sections are "unreferenced" and elided
         */
        argv.push((char *)"-Xlinker");
        argv.push((char *)"--gc-sections");
    }

    for (i = 0; i < global.params.linkswitches->dim; i++)
    {   char *p = global.params.linkswitches->tdata()[i];
        if (!p || !p[0] || !(p[0] == '-' && p[1] == 'l'))
            // Don't need -Xlinker if switch starts with -l
            argv.push((char *)"-Xlinker");
        argv.push(p);
    }

    /* Add each library, prefixing it with "-l".
     * The order of libraries passed is:
     *  1. any libraries passed with -L command line switch
     *  2. libraries specified on the command line
     *  3. libraries specified by pragma(lib), which were appended
     *     to global.params.libfiles.
     *  4. standard libraries.
     */
    for (i = 0; i < global.params.libfiles->dim; i++)
    {   char *p = global.params.libfiles->tdata()[i];
        size_t plen = strlen(p);
        if (plen > 2 && p[plen - 2] == '.' && p[plen -1] == 'a')
            argv.push(p);
        else
        {
            char *s = (char *)mem.malloc(plen + 3);
            s[0] = '-';
            s[1] = 'l';
            memcpy(s + 2, p, plen + 1);
            argv.push(s);
        }
    }

    /* Standard libraries must go after user specified libraries
     * passed with -l.
     */
    const char *libname = (global.params.symdebug)
                                ? global.params.debuglibname
                                : global.params.defaultlibname;
    char *buf = (char *)malloc(2 + strlen(libname) + 1);
    strcpy(buf, "-l");
    strcpy(buf + 2, libname);
    argv.push(buf);             // turns into /usr/lib/libphobos2.a

//    argv.push((void *)"-ldruntime");
    argv.push((char *)"-lpthread");
    argv.push((char *)"-lm");

    if (!global.params.quiet || global.params.verbose)
    {
        // Print it
        for (i = 0; i < argv.dim; i++)
            printf("%s ", argv.tdata()[i]);
        printf("\n");
        fflush(stdout);
    }

    argv.push(NULL);
    childpid = fork();
    if (childpid == 0)
    {
        execvp(argv.tdata()[0], argv.tdata());
        perror(argv.tdata()[0]);           // failed to execute
        return -1;
    }

    waitpid(childpid, &status, 0);

    if (WIFEXITED(status))
    {
        status = WEXITSTATUS(status);
        if (status)
            printf("--- errorlevel %d\n", status);
    }
    else if (WIFSIGNALED(status))
    {
        printf("--- killed by signal %d\n", WTERMSIG(status));
        status = 1;
    }
    return status;
#else
    printf ("Linking is not yet supported for this version of DMD.\n");
    return -1;
#endif
}
Exemple #2
0
int runProgram()
{
    //printf("runProgram()\n");
    if (global.params.verbose)
    {
        printf("%s", global.params.exefile);
        for (size_t i = 0; i < global.params.runargs_length; i++)
            printf(" %s", (char *)global.params.runargs[i]);
        printf("\n");
    }

    // Build argv[]
    Strings argv;

    argv.push(global.params.exefile);
    for (size_t i = 0; i < global.params.runargs_length; i++)
    {   char *a = global.params.runargs[i];

#if _WIN32
        // BUG: what about " appearing in the string?
        if (strchr(a, ' '))
        {   char *b = (char *)mem.malloc(3 + strlen(a));
            sprintf(b, "\"%s\"", a);
            a = b;
        }
#endif
        argv.push(a);
    }
    argv.push(NULL);

#if _WIN32
    char *ex = FileName::name(global.params.exefile);
    if (ex == global.params.exefile)
        ex = FileName::combine(".", ex);
    else
        ex = global.params.exefile;
    return spawnv(0,ex,argv.tdata());
#elif linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4
    pid_t childpid;
    int status;

    childpid = fork();
    if (childpid == 0)
    {
        char *fn = argv.tdata()[0];
        if (!FileName::absolute(fn))
        {   // Make it "./fn"
            fn = FileName::combine(".", fn);
        }
        execv(fn, argv.tdata());
        perror(fn);             // failed to execute
        return -1;
    }

    waitpid(childpid, &status, 0);

    if (WIFEXITED(status))
    {
        status = WEXITSTATUS(status);
        //printf("--- errorlevel %d\n", status);
    }
    else if (WIFSIGNALED(status))
    {
        printf("--- killed by signal %d\n", WTERMSIG(status));
        status = 1;
    }
    return status;
#else
    assert(0);
#endif
}
Exemple #3
0
void Library::scanObjModule(ObjModule *om)
{   int easyomf;
    unsigned u;
    unsigned char result = 0;
    char name[LIBIDMAX + 1];

    Strings names;
    names.push(NULL);           // don't use index 0

    assert(om);
    easyomf = 0;                                // assume not EASY-OMF
    unsigned char *pend = om->base + om->length;

    unsigned char *pnext;
    for (unsigned char *p = om->base; 1; p = pnext)
    {
        assert(p < pend);
        unsigned char recTyp = *p++;
        unsigned short recLen = *(unsigned short *)p;
        p += 2;
        pnext = p + recLen;
        recLen--;                               // forget the checksum

        switch (recTyp)
        {
            case LNAMES:
            case LLNAMES:
                while (p + 1 < pnext)
                {
                    parseName(&p, name);
                    names.push(strdup(name));
                }
                break;

            case PUBDEF:
                if (easyomf)
                    recTyp = PUB386;            // convert to MS format
            case PUB386:
                if (!(parseIdx(&p) | parseIdx(&p)))
                    p += 2;                     // skip seg, grp, frame
                while (p + 1 < pnext)
                {
                    parseName(&p, name);
                    p += (recTyp == PUBDEF) ? 2 : 4;    // skip offset
                    parseIdx(&p);                               // skip type index
                    addSymbol(om, name);
                }
                break;

            case COMDAT:
                if (easyomf)
                    recTyp = COMDAT+1;          // convert to MS format
            case COMDAT+1:
                int pickAny = 0;

                if (*p++ & 5)           // if continuation or local comdat
                    break;

                unsigned char attr = *p++;
                if (attr & 0xF0)        // attr: if multiple instances allowed
                    pickAny = 1;
                p++;                    // align

                p += 2;                 // enum data offset
                if (recTyp == COMDAT+1)
                    p += 2;                     // enum data offset

                parseIdx(&p);                   // type index

                if ((attr & 0x0F) == 0) // if explicit allocation
                {   parseIdx(&p);               // base group
                    parseIdx(&p);               // base segment
                }

                unsigned idx = parseIdx(&p);    // public name index
                if( idx == 0 || idx >= names.dim)
                {
                    //debug(printf("[s] name idx=%d, uCntNames=%d\n", idx, uCntNames));
                    error("corrupt COMDAT");
                    return;
                }

                //printf("[s] name='%s'\n",name);
                addSymbol(om, names.tdata()[idx],pickAny);
                break;

            case ALIAS:
                while (p + 1 < pnext)
                {
                    parseName(&p, name);
                    addSymbol(om, name);
                    parseName(&p, name);
                }
                break;

            case MODEND:
            case M386END:
                result = 1;
                goto Ret;

            case COMENT:
                // Recognize Phar Lap EASY-OMF format
                {   static unsigned char omfstr[7] =
                        {0x80,0xAA,'8','0','3','8','6'};

                    if (recLen == sizeof(omfstr))
                    {
                        for (unsigned i = 0; i < sizeof(omfstr); i++)
                            if (*p++ != omfstr[i])
                                goto L1;
                        easyomf = 1;
                        break;
                    L1: ;
                    }
                }
                // Recognize .IMPDEF Import Definition Records
                {   static unsigned char omfstr[] =
                        {0,0xA0,1};

                    if (recLen >= 7)
                    {
                        p++;
                        for (unsigned i = 1; i < sizeof(omfstr); i++)
                            if (*p++ != omfstr[i])
                                goto L2;
                        p++;            // skip OrdFlag field
                        parseName(&p, name);
                        addSymbol(om, name);
                        break;
                    L2: ;
                    }
                }
                break;

            default:
                // ignore
                ;
        }
    }
Ret:
    for (u = 1; u < names.dim; u++)
        free(names.tdata()[u]);
}
Exemple #4
0
void getenv_setargv(const char *envvar, int *pargc, char** *pargv)
{
    char *p;

    int instring;
    int slash;
    char c;

    char *env = getenv(envvar);
    if (!env)
        return;

    env = mem.strdup(env);      // create our own writable copy

    int argc = *pargc;
    Strings *argv = new Strings();
    argv->setDim(argc);

    int argc_left = 0;
    for (int i = 0; i < argc; i++) {
        if (!strcmp((*pargv)[i], "-run") || !strcmp((*pargv)[i], "--run")) {
            // HACK: set flag to indicate we saw '-run' here
            global.params.run = true;
            // Don't eat -run yet so the program arguments don't get changed
            argc_left = argc - i;
            argc = i;
            *pargv = &(*pargv)[i];
            argv->setDim(i);
            break;
        } else {
        }
    }
    // HACK to stop required values from command line being drawn from DFLAGS
    argv->push((char*)"");
    argc++;

    size_t j = 1;               // leave argv[0] alone
    while (1)
    {
        int wildcard = 1;       // do wildcard expansion
        switch (*env)
        {
            case ' ':
            case '\t':
                env++;
                break;

            case 0:
                goto Ldone;

            case '"':
                wildcard = 0;
            default:
                argv->push(env);                // append
                //argv->insert(j, env);         // insert at position j
                j++;
                argc++;
                p = env;
                slash = 0;
                instring = 0;
                c = 0;

                while (1)
                {
                    c = *env++;
                    switch (c)
                    {
                        case '"':
                            p -= (slash >> 1);
                            if (slash & 1)
                            {   p--;
                                goto Laddc;
                            }
                            instring ^= 1;
                            slash = 0;
                            continue;

                        case ' ':
                        case '\t':
                            if (instring)
                                goto Laddc;
                            *p = 0;
                            //if (wildcard)
                                //wildcardexpand();     // not implemented
                            break;

                        case '\\':
                            slash++;
                            *p++ = c;
                            continue;

                        case 0:
                            *p = 0;
                            //if (wildcard)
                                //wildcardexpand();     // not implemented
                            goto Ldone;

                        default:
                        Laddc:
                            slash = 0;
                            *p++ = c;
                            continue;
                    }
                    break;
                }
        }
    }

Ldone:
    assert(argc == argv->dim);
    argv->reserve(argc_left);
    for (int i = 0; i < argc_left; i++)
        argv->data[argc++] = (void *)(*pargv)[i];

    *pargc = argc;
    *pargv = argv->tdata();
}
Exemple #5
0
int runLINK()
{
#if _WIN32
    if (global.params.is64bit)
    {
        OutBuffer cmdbuf;

        cmdbuf.writestring("/NOLOGO ");

        for (size_t i = 0; i < global.params.objfiles->dim; i++)
        {
            if (i)
                cmdbuf.writeByte(' ');
            const char *p = (*global.params.objfiles)[i];
            const char *basename = FileName::removeExt(FileName::name(p));
            const char *ext = FileName::ext(p);
            if (ext && !strchr(basename, '.'))
            {
                // Write name sans extension (but not if a double extension)
                writeFilename(&cmdbuf, p, ext - p - 1);
            }
            else
                writeFilename(&cmdbuf, p);
            FileName::free(basename);
        }

        if (global.params.resfile)
        {
            cmdbuf.writeByte(' ');
            writeFilename(&cmdbuf, global.params.resfile);
        }

        cmdbuf.writeByte(' ');
        if (global.params.exefile)
        {   cmdbuf.writestring("/OUT:");
            writeFilename(&cmdbuf, global.params.exefile);
        }
        else
        {   /* Generate exe file name from first obj name.
             * No need to add it to cmdbuf because the linker will default to it.
             */
            const char *n = (*global.params.objfiles)[0];
            n = FileName::name(n);
            global.params.exefile = (char *)FileName::forceExt(n, "exe");
        }

        // Make sure path to exe file exists
        ensurePathToNameExists(Loc(), global.params.exefile);

        cmdbuf.writeByte(' ');
        if (global.params.mapfile)
        {   cmdbuf.writestring("/MAP:");
            writeFilename(&cmdbuf, global.params.mapfile);
        }
        else if (global.params.map)
        {
            const char *fn = FileName::forceExt(global.params.exefile, "map");

            const char *path = FileName::path(global.params.exefile);
            const char *p;
            if (path[0] == '\0')
                p = FileName::combine(global.params.objdir, fn);
            else
                p = fn;

            cmdbuf.writestring("/MAP:");
            writeFilename(&cmdbuf, p);
        }

        for (size_t i = 0; i < global.params.libfiles->dim; i++)
        {
            cmdbuf.writeByte(' ');
            cmdbuf.writestring("/DEFAULTLIB:");
            writeFilename(&cmdbuf, (*global.params.libfiles)[i]);
        }

        if (global.params.deffile)
        {
            cmdbuf.writeByte(' ');
            cmdbuf.writestring("/DEF:");
            writeFilename(&cmdbuf, global.params.deffile);
        }

        if (global.params.symdebug)
        {
            cmdbuf.writeByte(' ');
            cmdbuf.writestring("/DEBUG");

            // in release mode we need to reactivate /OPT:REF after /DEBUG
            if (global.params.release)
                cmdbuf.writestring(" /OPT:REF");
        }

        if (global.params.dll)
        {
            cmdbuf.writeByte(' ');
            cmdbuf.writestring("/DLL");
        }

        for (size_t i = 0; i < global.params.linkswitches->dim; i++)
        {
            cmdbuf.writeByte(' ');
            cmdbuf.writestring((*global.params.linkswitches)[i]);
        }

        /* Append the path to the VC lib files, and then the SDK lib files
         */
        const char *vcinstalldir = getenv("VCINSTALLDIR");
        if (vcinstalldir)
        {   cmdbuf.writestring(" \"/LIBPATH:");
            cmdbuf.writestring(vcinstalldir);
            cmdbuf.writestring("lib\\amd64\"");
        }

        const char *windowssdkdir = getenv("WindowsSdkDir");
        if (windowssdkdir)
        {   cmdbuf.writestring(" \"/LIBPATH:");
            cmdbuf.writestring(windowssdkdir);
            cmdbuf.writestring("lib\\x64\"");
        }

        char *p = cmdbuf.peekString();

        const char *lnkfilename = NULL;
        size_t plen = strlen(p);
        if (plen > 7000)
        {
            lnkfilename = FileName::forceExt(global.params.exefile, "lnk");
            File flnk(lnkfilename);
            flnk.setbuffer(p, plen);
            flnk.ref = 1;
            if (flnk.write())
                error(Loc(), "error writing file %s", lnkfilename);
            if (strlen(lnkfilename) < plen)
                sprintf(p, "@%s", lnkfilename);
        }

        const char *linkcmd = getenv("LINKCMD64");
        if (!linkcmd)
            linkcmd = getenv("LINKCMD"); // backward compatible
        if (!linkcmd)
        {
            if (vcinstalldir)
            {
                OutBuffer linkcmdbuf;
                linkcmdbuf.writestring(vcinstalldir);
                linkcmdbuf.writestring("bin\\amd64\\link");
                linkcmd = linkcmdbuf.extractString();
            }
            else
                linkcmd = "link";
        }
        int status = executecmd(linkcmd, p);
        if (lnkfilename)
        {
            remove(lnkfilename);
            FileName::free(lnkfilename);
        }
        return status;
    }
    else
    {
        OutBuffer cmdbuf;

        global.params.libfiles->push("user32");
        global.params.libfiles->push("kernel32");

        for (size_t i = 0; i < global.params.objfiles->dim; i++)
        {
            if (i)
                cmdbuf.writeByte('+');
            const char *p = (*global.params.objfiles)[i];
            const char *basename = FileName::removeExt(FileName::name(p));
            const char *ext = FileName::ext(p);
            if (ext && !strchr(basename, '.'))
            {
                // Write name sans extension (but not if a double extension)
                writeFilename(&cmdbuf, p, ext - p - 1);
            }
            else
                writeFilename(&cmdbuf, p);
            FileName::free(basename);
        }
        cmdbuf.writeByte(',');
        if (global.params.exefile)
            writeFilename(&cmdbuf, global.params.exefile);
        else
        {   /* Generate exe file name from first obj name.
             * No need to add it to cmdbuf because the linker will default to it.
             */
            const char *n = (*global.params.objfiles)[0];
            n = FileName::name(n);
            global.params.exefile = (char *)FileName::forceExt(n, "exe");
        }

        // Make sure path to exe file exists
        ensurePathToNameExists(Loc(), global.params.exefile);

        cmdbuf.writeByte(',');
        if (global.params.mapfile)
            writeFilename(&cmdbuf, global.params.mapfile);
        else if (global.params.map)
        {
            const char *fn = FileName::forceExt(global.params.exefile, "map");

            const char *path = FileName::path(global.params.exefile);
            const char *p;
            if (path[0] == '\0')
                p = FileName::combine(global.params.objdir, fn);
            else
                p = fn;

            writeFilename(&cmdbuf, p);
        }
        else
            cmdbuf.writestring("nul");
        cmdbuf.writeByte(',');

        for (size_t i = 0; i < global.params.libfiles->dim; i++)
        {
            if (i)
                cmdbuf.writeByte('+');
            writeFilename(&cmdbuf, (*global.params.libfiles)[i]);
        }

        if (global.params.deffile)
        {
            cmdbuf.writeByte(',');
            writeFilename(&cmdbuf, global.params.deffile);
        }

        /* Eliminate unnecessary trailing commas    */
        while (1)
        {   size_t i = cmdbuf.offset;
            if (!i || cmdbuf.data[i - 1] != ',')
                break;
            cmdbuf.offset--;
        }

        if (global.params.resfile)
        {
            cmdbuf.writestring("/RC:");
            writeFilename(&cmdbuf, global.params.resfile);
        }

        if (global.params.map || global.params.mapfile)
            cmdbuf.writestring("/m");

#if 0
        if (debuginfo)
            cmdbuf.writestring("/li");
        if (codeview)
        {
            cmdbuf.writestring("/co");
            if (codeview3)
                cmdbuf.writestring(":3");
        }
#else
        if (global.params.symdebug)
            cmdbuf.writestring("/co");
#endif

        cmdbuf.writestring("/noi");
        for (size_t i = 0; i < global.params.linkswitches->dim; i++)
        {
            cmdbuf.writestring((*global.params.linkswitches)[i]);
        }
        cmdbuf.writeByte(';');

        char *p = cmdbuf.peekString();

        const char *lnkfilename = NULL;
        size_t plen = strlen(p);
        if (plen > 7000)
        {
            lnkfilename = FileName::forceExt(global.params.exefile, "lnk");
            File flnk(lnkfilename);
            flnk.setbuffer(p, plen);
            flnk.ref = 1;
            if (flnk.write())
                error(Loc(), "error writing file %s", lnkfilename);
            if (strlen(lnkfilename) < plen)
                sprintf(p, "@%s", lnkfilename);
        }

        const char *linkcmd = getenv("LINKCMD");
        if (!linkcmd)
            linkcmd = "link";
        int status = executecmd(linkcmd, p);
        if (lnkfilename)
        {
            remove(lnkfilename);
            FileName::free(lnkfilename);
        }
        return status;
    }
#elif __linux__ || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun
    pid_t childpid;
    int status;

    // Build argv[]
    Strings argv;

    const char *cc = getenv("CC");
    if (!cc)
        cc = "gcc";
    argv.push(cc);
    argv.insert(1, global.params.objfiles);

#if __APPLE__
    // If we are on Mac OS X and linking a dynamic library,
    // add the "-dynamiclib" flag
    if (global.params.dll)
        argv.push("-dynamiclib");
#elif __linux__ || __FreeBSD__ || __OpenBSD__ || __sun
    if (global.params.dll)
        argv.push("-shared");
#endif

    // None of that a.out stuff. Use explicit exe file name, or
    // generate one from name of first source file.
    argv.push("-o");
    if (global.params.exefile)
    {
        argv.push(global.params.exefile);
    }
    else if (global.params.run)
    {
#if 1
        char name[L_tmpnam + 14 + 1];
        strcpy(name, P_tmpdir);
        strcat(name, "/dmd_runXXXXXX");
        int fd = mkstemp(name);
        if (fd == -1)
        {   error(Loc(), "error creating temporary file");
            return 1;
        }
        else
            close(fd);
        global.params.exefile = mem.strdup(name);
        argv.push(global.params.exefile);
#else
        /* The use of tmpnam raises the issue of "is this a security hole"?
         * The hole is that after tmpnam and before the file is opened,
         * the attacker modifies the file system to get control of the
         * file with that name. I do not know if this is an issue in
         * this context.
         * We cannot just replace it with mkstemp, because this name is
         * passed to the linker that actually opens the file and writes to it.
         */
        char s[L_tmpnam + 1];
        char *n = tmpnam(s);
        global.params.exefile = mem.strdup(n);
        argv.push(global.params.exefile);
#endif
    }
    else
    {   // Generate exe file name from first obj name
        const char *n = (*global.params.objfiles)[0];
        char *ex;

        n = FileName::name(n);
        const char *e = FileName::ext(n);
        if (e)
        {
            e--;                        // back up over '.'
            ex = (char *)mem.malloc(e - n + 1);
            memcpy(ex, n, e - n);
            ex[e - n] = 0;
            // If generating dll then force dll extension
            if (global.params.dll)
                ex = (char *)FileName::forceExt(ex, global.dll_ext);
        }
        else
            ex = (char *)"a.out";       // no extension, so give up
        argv.push(ex);
        global.params.exefile = ex;
    }

    // Make sure path to exe file exists
    ensurePathToNameExists(Loc(), global.params.exefile);

    if (global.params.symdebug)
        argv.push("-g");

    if (global.params.is64bit)
        argv.push("-m64");
    else
        argv.push("-m32");

    if (global.params.map || global.params.mapfile)
    {
        argv.push("-Xlinker");
#if __APPLE__
        argv.push("-map");
#else
        argv.push("-Map");
#endif
        if (!global.params.mapfile)
        {
            const char *fn = FileName::forceExt(global.params.exefile, "map");

            const char *path = FileName::path(global.params.exefile);
            const char *p;
            if (path[0] == '\0')
                p = FileName::combine(global.params.objdir, fn);
            else
                p = fn;

            global.params.mapfile = (char *)p;
        }
        argv.push("-Xlinker");
        argv.push(global.params.mapfile);
    }

    /* This switch enables what is known as 'smart linking'
     * in the Windows world, where unreferenced sections
     * are removed from the executable. It eliminates unreferenced
     * functions, essentially making a 'library' out of a module.
     * Although it is documented to work with ld version 2.13,
     * in practice it does not, but just seems to be ignored.
     * Thomas Kuehne has verified that it works with ld 2.16.1.
     */
#if __linux__
    /* Only works on linux because the linkers shipped with other
     * OSes don't yet support this flag.
     */
    argv.push("-Xlinker");
    argv.push("--gc-sections");
#endif

    for (size_t i = 0; i < global.params.linkswitches->dim; i++)
    {   const char *p = (*global.params.linkswitches)[i];
        if (!p || !p[0] || !(p[0] == '-' && (p[1] == 'l' || p[1] == 'L')))
            // Don't need -Xlinker if switch starts with -l or -L.
            // Eliding -Xlinker is significant for -L since it allows our paths
            // to take precedence over gcc defaults.
            argv.push("-Xlinker");
        argv.push(p);
    }

    /* Add each library, prefixing it with "-l".
     * The order of libraries passed is:
     *  1. any libraries passed with -L command line switch
     *  2. libraries specified on the command line
     *  3. libraries specified by pragma(lib), which were appended
     *     to global.params.libfiles.
     *  4. standard libraries.
     */
    for (size_t i = 0; i < global.params.libfiles->dim; i++)
    {   const char *p = (*global.params.libfiles)[i];
        size_t plen = strlen(p);
        if (plen > 2 && p[plen - 2] == '.' && p[plen -1] == 'a')
            argv.push(p);
        else
        {
            char *s = (char *)mem.malloc(plen + 3);
            s[0] = '-';
            s[1] = 'l';
            memcpy(s + 2, p, plen + 1);
            argv.push(s);
        }
    }

    for (size_t i = 0; i < global.params.dllfiles->dim; i++)
    {
        const char *p = (*global.params.dllfiles)[i];
        argv.push(p);
    }

    /* Standard libraries must go after user specified libraries
     * passed with -l.
     */
    const char *libname = (global.params.symdebug)
                                ? global.params.debuglibname
                                : global.params.defaultlibname;
    size_t slen = strlen(libname);
    if (slen)
    {
        char *buf = (char *)malloc(3 + slen + 1);
        strcpy(buf, "-l");
        /* Use "-l:libname.a" if the library name is complete
         */
        if (slen > 3 + 2 &&
            memcmp(libname, "lib", 3) == 0 &&
            (memcmp(libname + slen - 2, ".a", 2) == 0 ||
             memcmp(libname + slen - 3, ".so", 3) == 0)
           )
        {
            strcat(buf, ":");
        }
        strcat(buf, libname);
        argv.push(buf);             // turns into /usr/lib/libphobos2.a
    }

#ifdef __sun
    argv.push("-mt");
#endif

//    argv.push("-ldruntime");
    argv.push("-lpthread");
    argv.push("-lm");
#if __linux__
    // Changes in ld for Ubuntu 11.10 require this to appear after phobos2
    argv.push("-lrt");
#endif

    if (global.params.verbose)
    {
        // Print it
        for (size_t i = 0; i < argv.dim; i++)
            fprintf(global.stdmsg, "%s ", argv[i]);
        fprintf(global.stdmsg, "\n");
    }

    argv.push(NULL);

    // set up pipes
    int fds[2];

    if (pipe(fds) == -1)
    {
        perror("Unable to create pipe to linker");
        return -1;
    }

    childpid = fork();
    if (childpid == 0)
    {
        // pipe linker stderr to fds[0]
        dup2(fds[1], STDERR_FILENO);
        close(fds[0]);

        execvp(argv[0], (char **)argv.tdata());
        perror(argv[0]);           // failed to execute
        return -1;
    }
    else if (childpid == -1)
    {
        perror("Unable to fork");
        return -1;
    }
    close(fds[1]);
    const int nme = findNoMainError(fds[0]);
    waitpid(childpid, &status, 0);

    if (WIFEXITED(status))
    {
        status = WEXITSTATUS(status);
        if (status)
        {
            if (nme == -1)
            {
                perror("Error with the linker pipe");
                return -1;
            }
            else
            {
                printf("--- errorlevel %d\n", status);
                if (nme == 1) error(Loc(), "no main function specified");
            }
        }
    }
    else if (WIFSIGNALED(status))
    {
        printf("--- killed by signal %d\n", WTERMSIG(status));
        status = 1;
    }
    return status;
#else
    printf ("Linking is not yet supported for this version of DMD.\n");
    return -1;
#endif
}