root/trunk/src/link.c

Revision 676, 15.2 kB (checked in by walter, 2 years ago)

bugzilla 3627 -of with a filename with a double extension confuses linker

  • Property svn:eol-style set to native
Line 
1 // Copyright (c) 1999-2010 by Digital Mars
2 // All Rights Reserved
3 // written by Walter Bright
4 // http://www.digitalmars.com
5 // License for redistribution is by either the Artistic License
6 // in artistic.txt, or the GNU General Public License in gnu.txt.
7 // See the included readme.txt for details.
8
9
10 #include        <stdio.h>
11 #include        <ctype.h>
12 #include        <assert.h>
13 #include        <stdarg.h>
14 #include        <string.h>
15 #include        <stdlib.h>
16
17 #if _WIN32
18 #include        <process.h>
19 #endif
20
21 #if linux || __APPLE__ || __FreeBSD__ || __sun&&__SVR4
22 #include        <sys/types.h>
23 #include        <sys/wait.h>
24 #include        <unistd.h>
25 #endif
26
27 #include        "root.h"
28
29 #include        "mars.h"
30
31 #include        "rmem.h"
32
33 int executecmd(char *cmd, char *args, int useenv);
34 int executearg0(char *cmd, char *args);
35
36 /****************************************
37  * Write filename to cmdbuf, quoting if necessary.
38  */
39
40 void writeFilename(OutBuffer *buf, char *filename, size_t len)
41 {
42     /* Loop and see if we need to quote
43      */
44     for (size_t i = 0; i < len; i++)
45     {   char c = filename[i];
46
47         if (isalnum(c) || c == '_')
48             continue;
49
50         /* Need to quote
51          */
52         buf->writeByte('"');
53         buf->write(filename, len);
54         buf->writeByte('"');
55         return;
56     }
57
58     /* No quoting necessary
59      */
60     buf->write(filename, len);
61 }
62
63 void writeFilename(OutBuffer *buf, char *filename)
64 {
65     writeFilename(buf, filename, strlen(filename));
66 }
67
68 /*****************************
69  * Run the linker.  Return status of execution.
70  */
71
72 int runLINK()
73 {
74 #if _WIN32
75     char *p;
76     int i;
77     int status;
78     OutBuffer cmdbuf;
79
80     global.params.libfiles->push((void *) "user32");
81     global.params.libfiles->push((void *) "kernel32");
82
83     for (i = 0; i < global.params.objfiles->dim; i++)
84     {
85         if (i)
86             cmdbuf.writeByte('+');
87         p = (char *)global.params.objfiles->data[i];
88         char *basename = FileName::removeExt(FileName::name(p));
89         char *ext = FileName::ext(p);
90         if (ext && !strchr(basename, '.'))
91             // Write name sans extension (but not if a double extension)
92             writeFilename(&cmdbuf, p, ext - p - 1);
93         else
94             writeFilename(&cmdbuf, p);
95         mem.free(basename);
96     }
97     cmdbuf.writeByte(',');
98     if (global.params.exefile)
99         writeFilename(&cmdbuf, global.params.exefile);
100     else
101     {   /* Generate exe file name from first obj name.
102          * No need to add it to cmdbuf because the linker will default to it.
103          */
104         char *n = (char *)global.params.objfiles->data[0];
105         n = FileName::name(n);
106         FileName *fn = FileName::forceExt(n, "exe");
107         global.params.exefile = fn->toChars();
108     }
109
110     // Make sure path to exe file exists
111     {   char *p = FileName::path(global.params.exefile);
112         FileName::ensurePathExists(p);
113         mem.free(p);
114     }
115
116     cmdbuf.writeByte(',');
117     if (global.params.mapfile)
118         cmdbuf.writestring(global.params.mapfile);
119     else if (global.params.run)
120         cmdbuf.writestring("nul");
121     cmdbuf.writeByte(',');
122
123     for (i = 0; i < global.params.libfiles->dim; i++)
124     {
125         if (i)
126             cmdbuf.writeByte('+');
127         writeFilename(&cmdbuf, (char *) global.params.libfiles->data[i]);
128     }
129
130     if (global.params.deffile)
131     {
132         cmdbuf.writeByte(',');
133         writeFilename(&cmdbuf, global.params.deffile);
134     }
135
136     /* Eliminate unnecessary trailing commas    */
137     while (1)
138     {   i = cmdbuf.offset;
139         if (!i || cmdbuf.data[i - 1] != ',')
140             break;
141         cmdbuf.offset--;
142     }
143
144     if (global.params.resfile)
145     {
146         cmdbuf.writestring("/RC:");
147         writeFilename(&cmdbuf, global.params.resfile);
148     }
149
150     if (global.params.map || global.params.mapfile)
151         cmdbuf.writestring("/m");
152
153 #if 0
154     if (debuginfo)
155         cmdbuf.writestring("/li");
156     if (codeview)
157     {
158         cmdbuf.writestring("/co");
159         if (codeview3)
160             cmdbuf.writestring(":3");
161     }
162 #else
163     if (global.params.symdebug)
164         cmdbuf.writestring("/co");
165 #endif
166
167     cmdbuf.writestring("/noi");
168     for (i = 0; i < global.params.linkswitches->dim; i++)
169     {
170         cmdbuf.writestring((char *) global.params.linkswitches->data[i]);
171     }
172     cmdbuf.writeByte(';');
173
174     p = cmdbuf.toChars();
175
176     FileName *lnkfilename = NULL;
177     size_t plen = strlen(p);
178     if (plen > 7000)
179     {
180         lnkfilename = FileName::forceExt(global.params.exefile, "lnk");
181         File flnk(lnkfilename);
182         flnk.setbuffer(p, plen);
183         flnk.ref = 1;
184         if (flnk.write())
185             error("error writing file %s", lnkfilename);
186         if (lnkfilename->len() < plen)
187             sprintf(p, "@%s", lnkfilename->toChars());
188     }
189
190     char *linkcmd = getenv("LINKCMD");
191     if (!linkcmd)
192         linkcmd = "link";
193     status = executecmd(linkcmd, p, 1);
194     if (lnkfilename)
195     {
196         remove(lnkfilename->toChars());
197         delete lnkfilename;
198     }
199     return status;
200 #elif linux || __APPLE__ || __FreeBSD__ || __sun&&__SVR4
201     pid_t childpid;
202     int i;
203     int status;
204
205     // Build argv[]
206     Array argv;
207
208     const char *cc = getenv("CC");
209     if (!cc)
210         cc = "gcc";
211     argv.push((void *)cc);
212     argv.insert(1, global.params.objfiles);
213
214 #if __APPLE__
215     // If we are on Mac OS X and linking a dynamic library,
216     // add the "-dynamiclib" flag
217     if (global.params.dll)
218         argv.push((void *) "-dynamiclib");
219 #endif
220
221     // None of that a.out stuff. Use explicit exe file name, or
222     // generate one from name of first source file.
223     argv.push((void *)"-o");
224     if (global.params.exefile)
225     {
226         if (global.params.dll)
227             global.params.exefile = FileName::forceExt(global.params.exefile, global.dll_ext)->toChars();
228         argv.push(global.params.exefile);
229     }
230     else
231     {   // Generate exe file name from first obj name
232         char *n = (char *)global.params.objfiles->data[0];
233         char *e;
234         char *ex;
235
236         n = FileName::name(n);
237         e = FileName::ext(n);
238         if (e)
239         {
240             e--;                        // back up over '.'
241             ex = (char *)mem.malloc(e - n + 1);
242             memcpy(ex, n, e - n);
243             ex[e - n] = 0;
244             // If generating dll then force dll extension
245             if (global.params.dll)
246                 ex = FileName::forceExt(ex, global.dll_ext)->toChars();
247         }
248         else
249             ex = (char *)"a.out";       // no extension, so give up
250         argv.push(ex);
251         global.params.exefile = ex;
252     }
253
254     // Make sure path to exe file exists
255     {   char *p = FileName::path(global.params.exefile);
256         FileName::ensurePathExists(p);
257         mem.free(p);
258     }
259
260     if (global.params.symdebug)
261         argv.push((void *)"-g");
262
263     if (global.params.isX86_64)
264         argv.push((void *)"-m64");
265     else
266         argv.push((void *)"-m32");
267
268     if (global.params.map || global.params.mapfile)
269     {
270         argv.push((void *)"-Xlinker");
271 #if __APPLE__
272         argv.push((void *)"-map");
273 #else
274         argv.push((void *)"-Map");
275 #endif
276         if (!global.params.mapfile)
277         {
278             size_t elen = strlen(global.params.exefile);
279             size_t extlen = strlen(global.map_ext);
280             char *m = (char *)mem.malloc(elen + 1 + extlen + 1);
281             memcpy(m, global.params.exefile, elen);
282             m[elen] = '.';
283             memcpy(m + elen + 1, global.map_ext, extlen);
284             m[elen + 1 + extlen] = 0;
285             global.params.mapfile = m;
286         }
287         argv.push((void *)"-Xlinker");
288         argv.push(global.params.mapfile);
289     }
290
291     if (0 && global.params.exefile)
292     {
293         /* This switch enables what is known as 'smart linking'
294          * in the Windows world, where unreferenced sections
295          * are removed from the executable. It eliminates unreferenced
296          * functions, essentially making a 'library' out of a module.
297          * Although it is documented to work with ld version 2.13,
298          * in practice it does not, but just seems to be ignored.
299          * Thomas Kuehne has verified that it works with ld 2.16.1.
300          * BUG: disabled because it causes exception handling to fail
301          * because EH sections are "unreferenced" and elided
302          */
303         argv.push((void *)"-Xlinker");
304         argv.push((void *)"--gc-sections");
305     }
306
307     for (i = 0; i < global.params.linkswitches->dim; i++)
308     {   char *p = (char *)global.params.linkswitches->data[i];
309         if (!p || !p[0] || !(p[0] == '-' && p[1] == 'l'))
310             // Don't need -Xlinker if switch starts with -l
311             argv.push((void *)"-Xlinker");
312         argv.push((void *) p);
313     }
314
315     /* Add each library, prefixing it with "-l".
316      * The order of libraries passed is:
317      *  1. any libraries passed with -L command line switch
318      *  2. libraries specified on the command line
319      *  3. libraries specified by pragma(lib), which were appended
320      *     to global.params.libfiles.
321      *  4. standard libraries.
322      */
323     for (i = 0; i < global.params.libfiles->dim; i++)
324     {   char *p = (char *)global.params.libfiles->data[i];
325         size_t plen = strlen(p);
326         if (plen > 2 && p[plen - 2] == '.' && p[plen -1] == 'a')
327             argv.push((void *)p);
328         else
329         {
330             char *s = (char *)mem.malloc(plen + 3);
331             s[0] = '-';
332             s[1] = 'l';
333             memcpy(s + 2, p, plen + 1);
334             argv.push((void *)s);
335         }
336     }
337
338     /* Standard libraries must go after user specified libraries
339      * passed with -l.
340      */
341     const char *libname = (global.params.symdebug)
342                                 ? global.params.debuglibname
343                                 : global.params.defaultlibname;
344     char *buf = (char *)malloc(2 + strlen(libname) + 1);
345     strcpy(buf, "-l");
346     strcpy(buf + 2, libname);
347     argv.push((void *)buf);             // turns into /usr/lib/libphobos2.a
348
349 //    argv.push((void *)"-ldruntime");
350     argv.push((void *)"-lpthread");
351     argv.push((void *)"-lm");
352
353     if (!global.params.quiet || global.params.verbose)
354     {
355         // Print it
356         for (i = 0; i < argv.dim; i++)
357             printf("%s ", (char *)argv.data[i]);
358         printf("\n");
359         fflush(stdout);
360     }
361
362     argv.push(NULL);
363     childpid = fork();
364     if (childpid == 0)
365     {
366         execvp((char *)argv.data[0], (char **)argv.data);
367         perror((char *)argv.data[0]);           // failed to execute
368         return -1;
369     }
370
371     waitpid(childpid, &status, 0);
372
373     if (WIFEXITED(status))
374     {
375         status = WEXITSTATUS(status);
376         if (status)
377             printf("--- errorlevel %d\n", status);
378     }
379     else if (WIFSIGNALED(status))
380     {
381         printf("--- killed by signal %d\n", WTERMSIG(status));
382         status = 1;
383     }
384     return status;
385 #else
386     printf ("Linking is not yet supported for this version of DMD.\n");
387     return -1;
388 #endif
389 }
390
391 /**********************************
392  * Delete generated EXE file.
393  */
394
395 void deleteExeFile()
396 {
397     if (global.params.exefile)
398     {
399         //printf("deleteExeFile() %s\n", global.params.exefile);
400         remove(global.params.exefile);
401     }
402 }
403
404 /******************************
405  * Execute a rule.  Return the status.
406  *      cmd     program to run
407  *      args    arguments to cmd, as a string
408  *      useenv  if cmd knows about _CMDLINE environment variable
409  */
410
411 #if _WIN32
412 int executecmd(char *cmd, char *args, int useenv)
413 {
414     int status;
415     char *buff;
416     size_t len;
417
418     if (!global.params.quiet || global.params.verbose)
419     {
420         printf("%s %s\n", cmd, args);
421         fflush(stdout);
422     }
423
424     if ((len = strlen(args)) > 255)
425     {   char *q;
426         static char envname[] = "@_CMDLINE";
427
428         envname[0] = '@';
429         switch (useenv)
430         {   case 0:     goto L1;
431             case 2: envname[0] = '%';   break;
432         }
433         q = (char *) alloca(sizeof(envname) + len + 1);
434         sprintf(q,"%s=%s", envname + 1, args);
435         status = putenv(q);
436         if (status == 0)
437             args = envname;
438         else
439         {
440         L1:
441             error("command line length of %d is too long",len);
442         }
443     }
444
445     status = executearg0(cmd,args);
446 #if _WIN32
447     if (status == -1)
448         status = spawnlp(0,cmd,cmd,args,NULL);
449 #endif
450 //    if (global.params.verbose)
451 //      printf("\n");
452     if (status)
453     {
454         if (status == -1)
455             printf("Can't run '%s', check PATH\n", cmd);
456         else
457             printf("--- errorlevel %d\n", status);
458     }
459     return status;
460 }
461 #endif
462
463 /**************************************
464  * Attempt to find command to execute by first looking in the directory
465  * where DMD was run from.
466  * Returns:
467  *      -1      did not find command there
468  *      !=-1    exit status from command
469  */
470
471 #if _WIN32
472 int executearg0(char *cmd, char *args)
473 {
474     const char *file;
475     char *argv0 = global.params.argv0;
476
477     //printf("argv0='%s', cmd='%s', args='%s'\n",argv0,cmd,args);
478
479     // If cmd is fully qualified, we don't do this
480     if (FileName::absolute(cmd))
481         return -1;
482
483     file = FileName::replaceName(argv0, cmd);
484
485     //printf("spawning '%s'\n",file);
486 #if _WIN32
487     return spawnl(0,file,file,args,NULL);
488 #elif linux || __APPLE__ || __FreeBSD__ || __sun&&__SVR4
489     char *full;
490     int cmdl = strlen(cmd);
491
492     full = (char*) mem.malloc(cmdl + strlen(args) + 2);
493     if (full == NULL)
494         return 1;
495     strcpy(full, cmd);
496     full [cmdl] = ' ';
497     strcpy(full + cmdl + 1, args);
498
499     int result = system(full);
500
501     mem.free(full);
502     return result;
503 #else
504     assert(0);
505 #endif
506 }
507 #endif
508
509 /***************************************
510  * Run the compiled program.
511  * Return exit status.
512  */
513
514 int runProgram()
515 {
516     //printf("runProgram()\n");
517     if (global.params.verbose)
518     {
519         printf("%s", global.params.exefile);
520         for (size_t i = 0; i < global.params.runargs_length; i++)
521             printf(" %s", (char *)global.params.runargs[i]);
522         printf("\n");
523     }
524
525     // Build argv[]
526     Array argv;
527
528     argv.push((void *)global.params.exefile);
529     for (size_t i = 0; i < global.params.runargs_length; i++)
530     {   char *a = global.params.runargs[i];
531
532 #if _WIN32
533         // BUG: what about " appearing in the string?
534         if (strchr(a, ' '))
535         {   char *b = (char *)mem.malloc(3 + strlen(a));
536             sprintf(b, "\"%s\"", a);
537             a = b;
538         }
539 #endif
540         argv.push((void *)a);
541     }
542     argv.push(NULL);
543
544 #if _WIN32
545     char *ex = FileName::name(global.params.exefile);
546     if (ex == global.params.exefile)
547         ex = FileName::combine(".", ex);
548     else
549         ex = global.params.exefile;
550     return spawnv(0,ex,(char **)argv.data);
551 #elif linux || __APPLE__ || __FreeBSD__ || __sun&&__SVR4
552     pid_t childpid;
553     int status;
554
555     childpid = fork();
556     if (childpid == 0)
557     {
558         const char *fn = (const char *)argv.data[0];
559         if (!FileName::absolute(fn))
560         {   // Make it "./fn"
561             fn = FileName::combine(".", fn);
562         }
563         execv(fn, (char **)argv.data);
564         perror(fn);             // failed to execute
565         return -1;
566     }
567
568     waitpid(childpid, &status, 0);
569
570     if (WIFEXITED(status))
571     {
572         status = WEXITSTATUS(status);
573         //printf("--- errorlevel %d\n", status);
574     }
575     else if (WIFSIGNALED(status))
576     {
577         printf("--- killed by signal %d\n", WTERMSIG(status));
578         status = 1;
579     }
580     return status;
581 #else
582     assert(0);
583 #endif
584 }
Note: See TracBrowser for help on using the browser.