[svn r297] Fixed: rewrote linker code to use LLVM's Program facilities instead of DMD's oldschool broken "native" approach.

This commit is contained in:
Tomas Lindquist Olsen
2008-06-20 17:45:13 +02:00
parent 928f7d4de5
commit c743549032
6 changed files with 184 additions and 297 deletions

View File

@@ -37,299 +37,6 @@
int executecmd(char *cmd, char *args, int useenv);
int executearg0(char *cmd, char *args);
/*****************************
* Run the linker. Return status of execution.
*/
int runLINK()
{
Logger::println("*** Linking executable ***");
#if _WIN32
assert(0 && "linking not done for win32");
char *p;
int i;
int status;
OutBuffer cmdbuf;
global.params.libfiles->push((void *) "user32");
global.params.libfiles->push((void *) "kernel32");
for (i = 0; i < global.params.objfiles->dim; i++)
{
if (i)
cmdbuf.writeByte('+');
p = (char *)global.params.objfiles->data[i];
char *ext = FileName::ext(p);
if (ext)
cmdbuf.write(p, ext - p - 1);
else
cmdbuf.writestring(p);
}
cmdbuf.writeByte(',');
if (global.params.exefile)
cmdbuf.writestring(global.params.exefile);
else
{ // Generate exe file name from first obj name
char *n = (char *)global.params.objfiles->data[0];
char *ex;
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.run)
cmdbuf.writestring("nul");
// if (mapfile)
// cmdbuf.writestring(output);
cmdbuf.writeByte(',');
for (i = 0; i < global.params.libfiles->dim; i++)
{
if (i)
cmdbuf.writeByte('+');
cmdbuf.writestring((char *) global.params.libfiles->data[i]);
}
if (global.params.deffile)
{
cmdbuf.writeByte(',');
cmdbuf.writestring(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:");
cmdbuf.writestring(global.params.resfile);
}
#if 0
if (mapfile)
cmdbuf.writestring("/m");
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((char *) global.params.linkswitches->data[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
pid_t childpid;
int i;
int status;
// Build argv[]
Array argv;
//char *cc = getenv("CC");
//if (!cc)
//cc = "gcc";
char *cc = "llvm-ld";
argv.push((void *)cc);
// None of that a.out stuff. Use explicit exe file name, or
// generate one from name of first source file.
OutBuffer* exestr = new OutBuffer;
if (global.params.exefile)
{
exestr->printf("-o=%s", global.params.exefile);
argv.push(exestr->toChars());
}
else
{ // Generate exe file name from first obj name
char *n = (char *)global.params.objfiles->data[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;
}
else
ex = (char *)"a.out"; // no extension, so give up
exestr->printf("-o=%s", ex);
ex = exestr->toChars();
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);
}
argv.insert(argv.dim, global.params.libfiles);
if (!global.params.symdebug)
argv.push((void *)"-strip-debug");
//argv.push((void *)"-m32");
if (!global.params.optimize)
argv.push((void *)"-disable-opt");
else {
const char* s = 0;
switch(global.params.optimizeLevel) {
case 0:
s = "-O0"; break;
case 1:
s = "-O1"; break;
case 2:
s = "-O2"; break;
case 3:
s = "-O3"; break;
case 4:
s = "-O4"; break;
case 5:
s = "-O5"; break;
default:
assert(0);
}
argv.push((void*)s);
}
if (!(global.params.useInline || global.params.llvmInline)) {
argv.push((void *)"-disable-inlining");
}
#if 0
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
*/
argv.push((void *)"-Xlinker");
argv.push((void *)"--gc-sections");
}
#endif
for (i = 0; i < global.params.linkswitches->dim; i++)
{ char *p = (char *)global.params.linkswitches->data[i];
//if (!p || !p[0] || !(p[0] == '-' && p[1] == 'l'))
//{ // Don't need -Xlinker if switch starts with -l
// argv.push((void *)"-Xlinker");
//}
argv.push((void *) p);
}
argv.push((void*)"-native");
/* Standard libraries must go after user specified libraries
* passed with -l.
*/
argv.push((void*)"-ltango-base-c-llvmdc");
argv.push((void*)"-lpthread");
argv.push((void*)"-ldl");
argv.push((void*)"-lm");
argv.append(global.params.objfiles);
std::string runtime_path(global.params.runtimePath);
if (*runtime_path.rbegin() != '/')
runtime_path.append("/");
runtime_path.append("libtango-base-llvmdc.a");
argv.push((void*)runtime_path.c_str());
if (!global.params.quiet || global.params.verbose)
{
// Print it
for (i = 0; i < argv.dim; i++)
printf("%s ", (char *)argv.data[i]);
printf("\n");
fflush(stdout);
}
argv.push(NULL);
childpid = fork();
if (childpid == 0)
{
execvp((char *)argv.data[0], (char **)argv.data);
perror((char *)argv.data[0]); // failed to execute
return -1;
}
waitpid(childpid, &status, 0);
status=WEXITSTATUS(status);
if (status)
printf("--- errorlevel %d\n", status);
return status;
#else
printf ("Linking is not yet supported for this version of LLVMDC.\n");
return -1;
#endif
}
/**********************************
* Delete generated EXE file.
*/

View File

@@ -1103,7 +1103,8 @@ int main(int argc, char *argv[])
else
{
if (global.params.link)
status = runLINK();
//status = runLINK();
linkExecutable();
if (global.params.run)
{

View File

@@ -304,12 +304,15 @@ void error(Loc loc, const char *format, ...);
void verror(Loc loc, const char *format, va_list);
void fatal();
void err_nomem();
int runLINK();
//int runLINK(); // no longer used
void deleteExeFile();
int runProgram();
void inifile(char *argv0, char *inifile);
void halt();
// LLVMDC
int linkExecutable();
/*** Where to send error messages ***/
#if IN_GCC
#define stdmsg stderr

View File

@@ -1,8 +1,15 @@
#include "gen/llvm.h"
#include "llvm/Linker.h"
#include "llvm/System/Program.h"
#include "root.h"
#include "mars.h"
#include "module.h"
#define NO_COUT_LOGGER
#include "gen/logger.h"
//////////////////////////////////////////////////////////////////////////////
typedef std::vector<llvm::Module*> Module_vector;
@@ -23,3 +30,154 @@ void linkModules(llvm::Module* dst, const Module_vector& MV)
}
}
}
//////////////////////////////////////////////////////////////////////////////
int linkExecutable()
{
Logger::println("*** Linking executable ***");
// error string
std::string errstr;
// find the llvm-ld program
llvm::sys::Path ldpath = llvm::sys::Program::FindProgramByName("llvm-ld");
if (ldpath.isEmpty())
{
error("linker program not found");
fatal();
}
// build arguments
std::vector<const char*> args;
// first the program name ??
args.push_back("llvm-ld");
// output filename
std::string exestr;
if (global.params.exefile)
{ // explicit
exestr = global.params.exefile;
}
else
{ // inferred
// try root module name
if (Module::rootModule)
exestr = Module::rootModule->toChars();
else
exestr = "a.out";
}
if (global.params.isWindows)
exestr.append(".exe");
std::string outopt = "-o=" + exestr;
args.push_back(outopt.c_str());
// create path to exe
llvm::sys::Path exepath(exestr);
exepath.set(exepath.getDirname());
exepath.createDirectoryOnDisk(true, &errstr);
if (!errstr.empty())
{
error("failed to create path to linking output\n%s", errstr.c_str());
fatal();
}
// strip debug info
if (!global.params.symdebug)
args.push_back("-strip-debug");
// optimization level
if (!global.params.optimize)
args.push_back("-disable-opt");
else
{
const char* s = 0;
switch(global.params.optimizeLevel)
{
case 0:
s = "-O0"; break;
case 1:
s = "-O1"; break;
case 2:
s = "-O2"; break;
case 3:
s = "-O3"; break;
case 4:
s = "-O4"; break;
case 5:
s = "-O5"; break;
default:
assert(0);
}
args.push_back(s);
}
// inlining
if (!(global.params.useInline || global.params.llvmInline))
{
args.push_back("-disable-inlining");
}
// additional linker switches
for (int i = 0; i < global.params.linkswitches->dim; i++)
{
char *p = (char *)global.params.linkswitches->data[i];
args.push_back(p);
}
// native please
args.push_back("-native");
// user libs
for (int i = 0; i < global.params.libfiles->dim; i++)
{
char *p = (char *)global.params.libfiles->data[i];
args.push_back(p);
}
// default libs
args.push_back("-ltango-base-c-llvmdc");
args.push_back("-lpthread");
args.push_back("-ldl");
args.push_back("-lm");
// object files
for (int i = 0; i < global.params.objfiles->dim; i++)
{
char *p = (char *)global.params.objfiles->data[i];
args.push_back(p);
}
// runtime library
// must be linked in last to null terminate the moduleinfo appending list
std::string runtime_path(global.params.runtimePath);
if (*runtime_path.rbegin() != '/')
runtime_path.append("/");
runtime_path.append("libtango-base-llvmdc.a");
args.push_back(runtime_path.c_str());
// print link command?
if (!global.params.quiet || global.params.verbose)
{
// Print it
for (int i = 0; i < args.size(); i++)
printf("%s ", args[i]);
printf("\n");
fflush(stdout);
}
// terminate args list
args.push_back(NULL);
// try to call linker!!!
if (int status = llvm::sys::Program::ExecuteAndWait(ldpath, &args[0], NULL, NULL, 0,0, &errstr))
{
error("linking failed:\nstatus: %d", status);
if (!errstr.empty())
error("message: %s", errstr.c_str());
fatal();
}
}

View File

@@ -8,4 +8,10 @@
*/
void linkModules(llvm::Module* dst, const std::vector<llvm::Module*>& MV);
/**
* Link an executable.
* @return 0 on success.
*/
int linkExecutable();
#endif // LLVMDC_GEN_LINKER_H

View File

@@ -1,8 +1,20 @@
module asm1;
extern(C) int printf(char*, ...);
void main()
{
version(LLVM_InlineAsm_X86_64)
version(D_InlineAsm_X86)
{
int x;
asm
{
mov EAX, 42;
mov x, EAX;
}
printf("x = %d\n", x);
}
else version(D_InlineAsm_X86_64)
{
long x;
asm
@@ -14,6 +26,6 @@ void main()
}
else
{
static assert(0, "no llvm inline asm for this platform yet");
static assert(0, "no inline asm for this platform yet");
}
}