/*
    Copyright (C) 1995-2026, The AROS Development Team. All rights reserved.
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

#include "env.h"
#include "misc.h"
#include "docommand.h"
#include "backend.h"
#include "ldscript.h"
#include "gensets.h"

#define EXTRA_ARG_CNT 2

#define EI_OSABI        7
#define EI_ABIVERSION   8

#define ELFOSABI_AROS   15


static char *ldscriptname, *tempoutput, *ld_name, *strip_name;
static FILE *ldscriptfile;

int have_gnunm = 0;

static void exitfunc(void)
{
    if (ldscriptfile != NULL)
        fclose(ldscriptfile);

    if (getenv("COLLECT_AROS_DEBUG") != NULL)
    {
        fprintf(stderr, "[collect-aros] keeping intermediates: tempoutput=%s ldscript=%s\n",
            tempoutput ? tempoutput : "(none)", ldscriptname ? ldscriptname : "(none)");
        return;
    }

    if (ldscriptname != NULL)
        remove(ldscriptname);

    if (tempoutput != NULL)
        remove(tempoutput);
}

static int set_os_and_abi(const char *file)
{
    int f;
    const unsigned char osabi = ELFOSABI_AROS;
    const unsigned char abiversion = 1;

    /* Modify OS and ABI fields */

    f = open(file, O_RDWR);
    if (f >= 0) {
        lseek(f, EI_OSABI, SEEK_SET);
        if (write(f, &osabi, 1) == 1) {
            lseek(f, EI_ABIVERSION, SEEK_SET);
            if (write(f, &abiversion, 1) == 1) {
                close(f);
                return 1;
            }
        }
    }

    perror(file);
    if (f >= 0)
            close(f);
    return 0;
}

struct libentry
{
    int idx;
    char *str;
};

#define ISWS(c) ((c)==' '||(c)=='\t'||(c)=='\n'||(c)=='\r'||(c)=='\f'||(c)=='\v')

/* Expand @file response-file arguments into the argument list. clang switches to
   a response file when the linker command line is long (e.g. stdc.library's
   ~1700 objects); it then invokes us as `collect-aros @/tmp/response-XXX`. Without
   expanding it here we never parse -o and write to the default "a.out", so the
   real module is never produced. Tokens are whitespace-separated with '...'/"..."
   grouping and \-escapes (the GNU/clang response-file syntax); we parse in place
   (parsed length never exceeds the raw length) and keep the file buffer alive
   since the resulting argv entries point into it (this tool is short-lived). */
static char **expand_response_files(int *pargc, char **argv)
{
    int argc = *pargc, i, n = 0;
    long extra = 0;
    struct stat st;
    char **out;

    for (i = 0; i < argc; i++)
        if (argv[i] && argv[i][0] == '@' && stat(&argv[i][1], &st) == 0)
            extra += st.st_size + 1;          /* upper bound on token count */

    if (extra == 0)
        return argv;                          /* nothing to expand */

    out = xmalloc(sizeof(char *) * (argc + extra + 1));
    for (i = 0; i < argc; i++)
    {
        FILE *f;
        if (argv[i] && argv[i][0] == '@' && (f = fopen(&argv[i][1], "rb")) != NULL)
        {
            long len;
            char *buf, *p;

            fseek(f, 0, SEEK_END); len = ftell(f); fseek(f, 0, SEEK_SET);
            buf = xmalloc(len + 1);
            len = fread(buf, 1, len, f);
            buf[len] = '\0';
            fclose(f);

            for (p = buf; *p; )
            {
                char *tok, *w;
                while (ISWS(*p)) p++;
                if (!*p) break;
                tok = w = p;
                while (*p && !ISWS(*p))
                {
                    if (*p == '"' || *p == '\'')
                    {
                        char q = *p++;
                        while (*p && *p != q) { if (*p == '\\' && p[1]) p++; *w++ = *p++; }
                        if (*p == q) p++;
                    }
                    else if (*p == '\\' && p[1]) { p++; *w++ = *p++; }
                    else *w++ = *p++;
                }
                if (*p) p++;                   /* step past the separator */
                *w = '\0';                     /* terminate token (w <= old p) */
                out[n++] = tok;
            }
        }
        else
            out[n++] = argv[i];
    }
    out[n] = NULL;
    *pargc = n;
    return out;
}

int main(int argc, char *argv[])
{
    argv = expand_response_files(&argc, argv);
    struct libentry llibs[argc];
    struct libentry rellibs[argc];
    char *output, **ldargs;
    char *do_verbose = NULL;
    /* incremental = 1 -> don't do final linking.
       incremental = 2 -> don't do final linking AND STILL produce symbol sets.  */
    int incremental = 0, ignore_undefined_symbols = 0;
    int strip_all   = 0;
    int cnt, i, libcnt = 0, relcnt = 0;
    char *linkAddress = NULL;

    setnode *setlist = NULL, *liblist = NULL, *extralist = NULL;

    program_name = argv[0];
    ld_name = LD_NAME;
    strip_name = STRIP_NAME;

    /* Do some stuff with the arguments */
    output = "a.out";
    for (cnt = 1; argv[cnt]; cnt++)
    {
        /* We've encountered an option */
        if (argv[cnt][0]=='-')
        {
            /* Get the output file name */
            if (argv[cnt][1]=='o')
                output = argv[cnt][2]?&argv[cnt][2]:argv[++cnt];
            else
            /* Incremental linking is requested */
            if ((argv[cnt][1]=='r' || argv[cnt][1]=='i') && argv[cnt][2]=='\0')
                incremental  = 1;
            else
            /* Incremental, but produce the symbol sets */
            if (strncmp(&argv[cnt][1], "Ur", 3) == 0)
            {
                incremental  = 2;
                
                argv[cnt][1] = 'r';  /* Just some non-harming option... */
                argv[cnt][2] = '\0';
            }
            else
            /* linklib */
            if (strncmp(&argv[cnt][1], "l", 1) == 0)
            {
                int lnlen = strlen(&argv[cnt][2]);
                if ((lnlen > 4) && (strcmp("_rel", &argv[cnt][lnlen - 2]) == 0))
                {
                    rellibs[relcnt].idx = cnt;
                    rellibs[relcnt++].str = &argv[cnt][2];
                }
                else
                {
                    llibs[libcnt].idx = cnt;
                    llibs[libcnt++].str = &argv[cnt][2];
                }
            }
            else
            /* Complete stripping is requested, but we do it our own way */
            /* Ignoring of missing symbols is requested */
            if (strncmp(&argv[cnt][1], "ius", 4) == 0)
            {
                ignore_undefined_symbols = 1;
                argv[cnt][1] = 'r';  /* Just some non-harming option... */
                argv[cnt][2] = '\0';
            }
            else
            /* Complete stripping is requested, but we do it our own way */
            if (argv[cnt][1]=='s' && argv[cnt][2]=='\0')
            {
                strip_all = 1;
                argv[cnt][1] = 'r'; /* Just some non-harming option... */
            }
            else
            /* Start address specified .. */
            if ((strncmp(&argv[cnt][1], "Ttext", 6) == 0) || ((strncmp(&argv[cnt][1], "Wl,-Ttext", 10) == 0)))
            {
                if (argv[cnt][1] =='T')
                    linkAddress = &argv[cnt][7];
                else
                    linkAddress = &argv[cnt][11];
            }
            else
            /* Use the specified linker.. */
            if ((strncmp(&argv[cnt][1], "-ld-path", 8) == 0) || ((strncmp(&argv[cnt][1], "Wl,--ld-path", 12) == 0)))
            {
                if (argv[cnt][1] =='-')
                    ld_name = &argv[cnt][10];
                else
                    ld_name = &argv[cnt][14];
                argv[cnt][1] = 'r';  /* Just some non-harming option... */
                argv[cnt][2] = '\0';
                break;
            }
            else
            /* The user just requested help info, don't do anything else */
            if (strncmp(&argv[cnt][1], "-help", 6) == 0)
            {
                /* I know, it's not incremental linking we're after, but the end result
                   is the same */
                incremental = 1;
                break;
            }
            else
            /* verbose output */
            if (strncmp(&argv[cnt][1], "-verbose", 9) == 0)
            {
                do_verbose = argv[cnt];
                break;
            }
        }
    }

    ldargs = xmalloc(sizeof(char *) * (argc + EXTRA_ARG_CNT
        + ((incremental == 1) ? 0 : 2) + 1));

    ldargs[0] = ld_name;
    ldargs[1] = OBJECT_FORMAT;
    ldargs[2] = "-r";

    for (i = 1; i < argc; i++)
        ldargs[i + EXTRA_ARG_CNT] = argv[i];
    cnt = argc + EXTRA_ARG_CNT;

    if (incremental != 1)
    {
        atexit(exitfunc);
        if
        (
            !(tempoutput   = make_temp_file(NULL))     ||
            !(ldscriptname = make_temp_file(NULL))     ||
            !(ldscriptfile = fopen(ldscriptname, "w"))
        )
        {
            fatal(ldscriptname ? ldscriptname : "make_temp_file()", strerror(errno));
        }

        ldargs[cnt++] = "-o";
        ldargs[cnt++] = tempoutput;
    }

    // Fixup relbase lib linking 
    if (relcnt > 0)
    {
        int relid, libid;
        for (relid = 0; relid < relcnt; relid++)
            for (libid = 0; libid < libcnt; libid++)
            {
                if (!strncmp(llibs[libid].str, rellibs[relid].str, strlen(llibs[libid].str)))
                {
                    ldargs[llibs[libid].idx + EXTRA_ARG_CNT] = ldargs[rellibs[relid].idx + EXTRA_ARG_CNT];
                }
            }
    }
      
    ldargs[cnt] = NULL;

    if (getenv("COLLECT_AROS_DEBUG") != NULL)
    {
        int dbgi;
        fprintf(stderr, "[collect-aros] relocatable link command:\n[collect-aros]");
        for (dbgi = 0; ldargs[dbgi] != NULL; dbgi++)
            fprintf(stderr, " %s", ldargs[dbgi]);
        fprintf(stderr, "\n");
    }

    docommandvp(ld_name, ldargs);

    if (incremental == 1)
        return set_os_and_abi(output) ? EXIT_SUCCESS : EXIT_FAILURE;

    /*
       * initalize the backend and process the 
       * remaining work
       */
    backend_init(ldargs[0]);
    collect_sets(tempoutput, &setlist);
    collect_extra(tempoutput, &extralist);
    collect_libs(tempoutput, &liblist);

    if (linkAddress != NULL)
        fprintf(ldscriptfile, "ENTRY(_start)\n");

    if (setlist) {
        struct setnode *n;
        for (n = setlist; n; n = n->next) {
            if (strncmp(n->secname,".aros.set.",10) == 0) {
               fprintf(ldscriptfile, "EXTERN(__%s__symbol_set_handler_missing)\n", &n->secname[10]);
            }
        }
    }

    fwrite(LDSCRIPT_PART1, sizeof(LDSCRIPT_PART1) - 1, 1, ldscriptfile);
    if (linkAddress != NULL)
        fprintf(ldscriptfile, "  . = %s;\n", linkAddress);
#ifdef TARGET_FORMAT_EXE
    fprintf(ldscriptfile, "  .tag  :\n");
#else
    if (linkAddress != NULL)
        fprintf(ldscriptfile, "  .tag   :\n");
    else
        fprintf(ldscriptfile, "  .tag 0 :\n");
#endif
    fwrite(LDSCRIPT_PART2A, sizeof(LDSCRIPT_PART2A) - 1, 1, ldscriptfile);
    if (linkAddress != NULL)
        fprintf(ldscriptfile, "\n");
    else
        fprintf(ldscriptfile, "=0x90909090\n");
#ifdef TARGET_FORMAT_EXE
    fprintf(ldscriptfile, "  . = SEGMENT_START(\"text-segment\", 0x80000000);\n");
    fprintf(ldscriptfile, "  .text  :\n");
#else
    if (linkAddress != NULL)
        fprintf(ldscriptfile, "  .text   :\n");
    else
        fprintf(ldscriptfile, "  .text 0 :\n");
#endif
    fwrite(LDSCRIPT_PART2B, sizeof(LDSCRIPT_PART2B) - 1, 1, ldscriptfile);
    if (linkAddress != NULL)
        fprintf(ldscriptfile, "    KEEP(*(.text._start))\n");
    fwrite(LDSCRIPT_PART3A, sizeof(LDSCRIPT_PART3A) - 1, 1, ldscriptfile);
    if (linkAddress != NULL)
        fprintf(ldscriptfile, "\n\n");
    else
        fprintf(ldscriptfile, "=0x90909090\n\n");
#ifdef TARGET_FORMAT_EXE
    fprintf(ldscriptfile, "  . = SEGMENT_START(\"rodata-segment\", . );\n");
    fprintf(ldscriptfile, "  .rodata   :\n");
#else
    if (linkAddress != NULL)
        fprintf(ldscriptfile, "  .rodata    :\n");
    else
        fprintf(ldscriptfile, "  .rodata  0 :\n");
#endif
    fwrite(LDSCRIPT_PART3B, sizeof(LDSCRIPT_PART3B) - 1, 1, ldscriptfile);
    emit_sets(setlist, ldscriptfile);
    emit_libs(liblist, ldscriptfile);
    fprintf(ldscriptfile, "  }\n");
#ifdef TARGET_FORMAT_EXE
    fprintf(ldscriptfile, "  .rodata1  : { *(.rodata1) }\n");
#else
    if (linkAddress != NULL)
        fprintf(ldscriptfile, "  .rodata1   : { *(.rodata1) }\n");
    else
        fprintf(ldscriptfile, "  .rodata1 0 : { *(.rodata1) }\n");
#endif
    fwrite(LDSCRIPT_PART4A, sizeof(LDSCRIPT_PART4A) - 1, 1, ldscriptfile);
#ifdef TARGET_FORMAT_EXE
    fprintf(ldscriptfile, "  .sdata2   : { *(.sdata2) *(.sdata2.*) *(.gnu.linkonce.s2.*) }\n");
    fprintf(ldscriptfile, "  .sbss2    : { *(.sbss2)  *(.sbss2.*)  *(.gnu.linkonce.sb2.*) }\n\n");
    fprintf(ldscriptfile, "  .data   :\n");
#else
    if (linkAddress != NULL)
    {
        fprintf(ldscriptfile, "  .sdata2    : { *(.sdata2) *(.sdata2.*) *(.gnu.linkonce.s2.*) }\n");
        fprintf(ldscriptfile, "  .sbss2     : { *(.sbss2)  *(.sbss2.*)  *(.gnu.linkonce.sb2.*) }\n\n");
        fprintf(ldscriptfile, "  .data      :\n");
    }
    else
    {
        fprintf(ldscriptfile, "  .sdata2  0 : { *(.sdata2) *(.sdata2.*) *(.gnu.linkonce.s2.*) }\n");
        fprintf(ldscriptfile, "  .sbss2   0 : { *(.sbss2)  *(.sbss2.*)  *(.gnu.linkonce.sb2.*) }\n\n");
        fprintf(ldscriptfile, "  .data    0 :\n");
    }
#endif
    fwrite(LDSCRIPT_PART4B, sizeof(LDSCRIPT_PART4B) - 1, 1, ldscriptfile);
#ifdef TARGET_FORMAT_EXE
#else
    if (linkAddress != NULL)
        fprintf(ldscriptfile, "  .data1              : { *(.data1) }\n");
    else
        fprintf(ldscriptfile, "  .data1            0 : { *(.data1) }\n");
#endif
#if defined(TARGET_CPU_i386) || defined(TARGET_CPU_x86_64)
    fwrite(LDSCRIPT_PART4C, sizeof(LDSCRIPT_PART4C) - 1, 1, ldscriptfile);
#endif
    fwrite(LDSCRIPT_PART4D, sizeof(LDSCRIPT_PART4D) - 1, 1, ldscriptfile);
#ifdef TARGET_FORMAT_EXE
    fprintf(ldscriptfile, "  .eh_frame          :\n");
#else
    if (linkAddress != NULL)
        fprintf(ldscriptfile, "  .eh_frame           :\n");
    else
        fprintf(ldscriptfile, "  .eh_frame         0 :\n");
#endif
    fwrite(LDSCRIPT_PART4E, sizeof(LDSCRIPT_PART4E) - 1, 1, ldscriptfile);
    /* Append .eh_frame terminator only on final stage */
    if (incremental == 0)
        fputs("LONG(0)\n", ldscriptfile);
    fwrite(LDSCRIPT_PART5A, sizeof(LDSCRIPT_PART5A) - 1, 1, ldscriptfile);
#ifdef TARGET_FORMAT_EXE
    fprintf(ldscriptfile, "  .gcc_except_table  : { *(.gcc_except_table) }\n");
#else
    if (linkAddress != NULL)
        fprintf(ldscriptfile, "  .gcc_except_table   : { *(.gcc_except_table) }\n");
    else
        fprintf(ldscriptfile, "  .gcc_except_table 0 : { *(.gcc_except_table) }\n");
#endif
    fwrite(LDSCRIPT_PART5B, sizeof(LDSCRIPT_PART5B) - 1, 1, ldscriptfile);
#ifdef TARGET_FORMAT_EXE
    fprintf(ldscriptfile, "  .sdata    :\n");
#else
    if (linkAddress != NULL)
        fprintf(ldscriptfile, "  .sdata     :\n");
    else
        fprintf(ldscriptfile, "  .sdata   0 :\n");
#endif
    fwrite(LDSCRIPT_PART5C, sizeof(LDSCRIPT_PART5C) - 1, 1, ldscriptfile);
#ifdef TARGET_FORMAT_EXE
    fprintf(ldscriptfile, "  .sbss  :\n");
#else
    if (linkAddress != NULL)
        fprintf(ldscriptfile, "  .sbss   :\n");
    else
        fprintf(ldscriptfile, "  .sbss 0 :\n");
#endif
    fwrite(LDSCRIPT_PART5D, sizeof(LDSCRIPT_PART5D) - 1, 1, ldscriptfile);
#ifdef TARGET_FORMAT_EXE
    fprintf(ldscriptfile, "  .bss  :\n");
#else
    if (linkAddress != NULL)
        fprintf(ldscriptfile, "  .bss   :\n");
    else
        fprintf(ldscriptfile, "  .bss 0 :\n");
#endif
    fwrite(LDSCRIPT_PART5E, sizeof(LDSCRIPT_PART5E) - 1, 1, ldscriptfile);
    fwrite(LDSCRIPT_PART6, sizeof(LDSCRIPT_PART6) - 1, 1, ldscriptfile);

    fclose(ldscriptfile);
    ldscriptfile = NULL;

    /*
     * The GCC LTO linker plugin only resolves "pass-through" libraries (the
     * standard AROS link sequence) during the relocatable stage above. Symbols
     * that are provided by direct, non pass-through static archives passed on
     * the command line (e.g. -lpthread) can therefore be left undefined in the
     * intermediate object, even though the archive was present. Re-supply the
     * libraries (and all -L search paths) to the final link so ld can pull the
     * required members and resolve those symbols (including any further symbols
     * those members pull from the standard libraries). The compiler's own
     * support libraries (-lgcc*) are skipped: they live on search paths that
     * are private to the gcc driver and are already linked in.
     */
    char **libargv = xmalloc(sizeof(char *) * (argc + 2));
    int libargc = 0;
    /*
     * Only perform the re-supply when the relocatable object actually has
     * undefined symbols left over from the plugin stage. Clean links - which
     * is every core AROS module and by far the common case - then take exactly
     * the original final-link path and are completely unaffected by this fixup
     * (no extra libraries, no --allow-multiple-definition).
     */
    if (has_undefined_symbols(tempoutput))
    {
        if (getenv("COLLECT_AROS_DEBUG") != NULL)
            fprintf(stderr, "[collect-aros] relocatable object has undefined symbols;"
                            " re-supplying libraries to the final link\n");
        /*
         * Members of the re-supplied archives may already have been pulled into the
         * relocatable object during the plugin stage (e.g. when only some of a
         * library's symbols were left undefined). Allow the resulting identical
         * duplicate definitions rather than erroring on them.
         */
        libargv[libargc++] = "--allow-multiple-definition";
        for (i = 1; i < argc; i++)
        {
            if (argv[i][0] != '-')
            {
                /* re-supply explicit static archives (e.g. libtool convenience
                   libraries) - a member defining a symbol that is only referenced
                   by a later -l library may not have been pulled during the plugin
                   stage */
                size_t l = strlen(argv[i]);
                if (l > 2 && strcmp(&argv[i][l - 2], ".a") == 0)
                    libargv[libargc++] = argv[i];
                continue;
            }

            if (argv[i][1] == 'L')
            {
                /* keep all library search paths, including the separate "-L" "path"
                   form */
                libargv[libargc++] = argv[i];
                if (argv[i][2] == '\0' && (i + 1) < argc)
                    libargv[libargc++] = argv[++i];
            }
            else if (argv[i][1] == 'l')
            {
                const char *name;
                int separate = (argv[i][2] == '\0' && (i + 1) < argc);

                name = separate ? argv[i + 1] : &argv[i][2];

                /* skip the gcc support libraries (private driver search paths) */
                if (strncmp(name, "gcc", 3) == 0)
                {
                    if (separate)
                        i++;
                    continue;
                }

                libargv[libargc++] = argv[i];
                if (separate)
                    libargv[libargc++] = argv[++i];
            }
        }
    }

    char **cmdargv;
    int argallocs = libargc;
    if (extralist) {
        struct setnode *n;
        for (n = extralist; n; n = n->next)
            argallocs += 1;
    }

#ifdef TARGET_FORMAT_EXE
    if (incremental == 0)
    {
#ifdef OBJECT_FORMAT_EXTRA_FINAL
        argallocs += 10;
        cmdargv = xmalloc(sizeof(char *) * (argallocs + 1));
        cmdargv[0] = ld_name;
        cmdargv[1] = OBJECT_FORMAT;
        cmdargv[2] = OBJECT_FORMAT_EXTRA_FINAL;
        cmdargv[3] = "-o";
        cmdargv[4] = output;
        cmdargv[5] = tempoutput;

        cnt = 0;
        if (extralist) {
        struct setnode *n;
        for (n = extralist; n; n = n->next)
            cmdargv[6 + cnt++] = n->secname;
        }
    
        cmdargv[6 + cnt] = "-T";
        cmdargv[7 + cnt] = ldscriptname;
        cmdargv[8 + cnt] = do_verbose;
        cmdargv[9 + cnt] = NULL;

        docommandvp(ld_name, cmdargv);
#else
        argallocs += 9;
        cmdargv = xmalloc(sizeof(char *) * (argallocs + 1));
        cmdargv[0] = ld_name;
        cmdargv[1] = OBJECT_FORMAT;
        cmdargv[2] = "-o";
        cmdargv[3] = output;
        cmdargv[4] = tempoutput;
        
        cnt = 0;
        if (extralist) {
        struct setnode *n;
        for (n = extralist; n; n = n->next)
            cmdargv[6 + cnt++] = n->secname;
        }
    
        cmdargv[5 + cnt] = "-T";
        cmdargv[6 + cnt] = ldscriptname;
        cmdargv[7 + cnt] = do_verbose;
        cmdargv[8 + cnt] = NULL;

        docommandvp(ld_name, cmdargv);
#endif
    }
    else
    {
        argallocs += 10;
        cmdargv = xmalloc(sizeof(char *) * (argallocs + 1));
        cmdargv[0] = ld_name;
        cmdargv[1] = OBJECT_FORMAT;
        cmdargv[2] = "-r";
        cmdargv[3] = "-o";
        cmdargv[4] = output;
        cmdargv[5] = tempoutput;

        cnt = 0;
        if (extralist) {
        struct setnode *n;
        for (n = extralist; n; n = n->next)
            cmdargv[6 + cnt++] = n->secname;
        }
    
        cmdargv[6 + cnt] = "-T";
        cmdargv[7 + cnt] = ldscriptname;
        cmdargv[8 + cnt] = do_verbose;
        cmdargv[9 + cnt] = NULL;

        docommandvp(ld_name, cmdargv);
    }
#else
#ifdef OBJECT_FORMAT_EXTRA_FINAL
    if (incremental == 0)
    {
        argallocs += 11;
        cmdargv = xmalloc(sizeof(char *) * (argallocs + 1));
        cmdargv[0] = ld_name;
        cmdargv[1] = OBJECT_FORMAT;
        cmdargv[2] = OBJECT_FORMAT_EXTRA_FINAL;
        cmdargv[3] = "-r";
        cmdargv[4] = "-o";
        cmdargv[5] = output;
        cmdargv[6] = tempoutput;
        
        cnt = 0;
        if (extralist) {
        struct setnode *n;
        for (n = extralist; n; n = n->next)
            cmdargv[6 + cnt++] = n->secname;
        }
    
        cmdargv[7 + cnt] = "-T";
        cmdargv[8 + cnt] = ldscriptname;
        cmdargv[9 + cnt] = do_verbose;
        cmdargv[10 + cnt] = NULL;

        docommandvp(ld_name, cmdargv);
    }
    else
#endif
    {
        argallocs += 10;
        cmdargv = xmalloc(sizeof(char *) * (argallocs + 1));
        cmdargv[0] = ld_name;
        cmdargv[1] = OBJECT_FORMAT;
        cmdargv[2] = "-r";
        cmdargv[3] = "-o";
        cmdargv[4] = output;
        cmdargv[5] = tempoutput;

        cnt = 0;
        if (extralist) {
        struct setnode *n;
        for (n = extralist; n; n = n->next)
            cmdargv[6 + cnt++] = n->secname;
        }

        {
        int li;
        for (li = 0; li < libargc; li++)
            cmdargv[6 + cnt++] = libargv[li];
        }

        cmdargv[6 + cnt] = "-T";
        cmdargv[7 + cnt] = ldscriptname;
        cmdargv[8 + cnt] = do_verbose;
        cmdargv[9 + cnt] = NULL;

        docommandvp(ld_name, cmdargv);
    }
#endif

    if (incremental != 0)
        return set_os_and_abi(output) ? EXIT_SUCCESS : EXIT_FAILURE;
        
    if (!ignore_undefined_symbols && check_and_print_undefined_symbols(output))
    {
        remove(output);
        return EXIT_FAILURE;
    }

    chmod(output, 0766);

    if (strip_all)
    {
        docommandlp(strip_name, strip_name, "--strip-unneeded", output, NULL);
    }

    if (!set_os_and_abi(output))
    {
        remove(output);
        return EXIT_FAILURE;
    }

    return 0;
}
