blob: b1562d1f515840d3cc97f74723c4d98600b986bf [file] [log] [blame]
Nick Lewycky3e62b2d2009-02-03 07:13:24 +00001//===-- gold-plugin.cpp - Plugin to gold for Link Time Optimization ------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This is a gold plugin for LLVM. It provides an LLVM implementation of the
11// interface described in http://gcc.gnu.org/wiki/whopr/driver .
12//
13//===----------------------------------------------------------------------===//
14
Duncan Sands09b5d902009-10-22 16:03:32 +000015#include "llvm/Config/config.h"
Nick Lewycky3e62b2d2009-02-03 07:13:24 +000016#include "plugin-api.h"
17
18#include "llvm-c/lto.h"
19
20#include "llvm/Support/raw_ostream.h"
Jeffrey Yasskined1c0ff2009-07-01 18:11:20 +000021#include "llvm/System/Errno.h"
Nick Lewycky3e62b2d2009-02-03 07:13:24 +000022#include "llvm/System/Path.h"
Rafael Espindola42de34f2009-06-15 10:14:18 +000023#include "llvm/System/Program.h"
Nick Lewycky3e62b2d2009-02-03 07:13:24 +000024
Torok Edwin6cbbdfd2009-02-04 21:00:02 +000025#include <cerrno>
Nick Lewycky3e62b2d2009-02-03 07:13:24 +000026#include <cstdlib>
27#include <cstring>
Nick Lewyckyca428622009-02-22 22:15:44 +000028#include <fstream>
Nick Lewycky3e62b2d2009-02-03 07:13:24 +000029#include <list>
30#include <vector>
31
32using namespace llvm;
33
34namespace {
35 ld_plugin_status discard_message(int level, const char *format, ...) {
36 // Die loudly. Recent versions of Gold pass ld_plugin_message as the first
37 // callback in the transfer vector. This should never be called.
38 abort();
39 }
40
41 ld_plugin_add_symbols add_symbols = NULL;
42 ld_plugin_get_symbols get_symbols = NULL;
43 ld_plugin_add_input_file add_input_file = NULL;
44 ld_plugin_message message = discard_message;
45
46 int api_version = 0;
47 int gold_version = 0;
48
Nick Lewyckyca428622009-02-22 22:15:44 +000049 bool generate_api_file = false;
Nick Lewyckya9b90322009-06-07 00:50:45 +000050 const char *as_path = NULL;
Nick Lewyckyca428622009-02-22 22:15:44 +000051
Nick Lewycky3e62b2d2009-02-03 07:13:24 +000052 struct claimed_file {
53 lto_module_t M;
54 void *handle;
55 std::vector<ld_plugin_symbol> syms;
56 };
57
58 lto_codegen_model output_type = LTO_CODEGEN_PIC_MODEL_STATIC;
59 std::list<claimed_file> Modules;
60 std::vector<sys::Path> Cleanup;
61}
62
63ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
64 int *claimed);
65ld_plugin_status all_symbols_read_hook(void);
66ld_plugin_status cleanup_hook(void);
67
68extern "C" ld_plugin_status onload(ld_plugin_tv *tv);
69ld_plugin_status onload(ld_plugin_tv *tv) {
70 // We're given a pointer to the first transfer vector. We read through them
71 // until we find one where tv_tag == LDPT_NULL. The REGISTER_* tagged values
72 // contain pointers to functions that we need to call to register our own
73 // hooks. The others are addresses of functions we can use to call into gold
74 // for services.
75
76 bool registeredClaimFile = false;
77 bool registeredAllSymbolsRead = false;
78 bool registeredCleanup = false;
79
80 for (; tv->tv_tag != LDPT_NULL; ++tv) {
81 switch (tv->tv_tag) {
82 case LDPT_API_VERSION:
83 api_version = tv->tv_u.tv_val;
84 break;
85 case LDPT_GOLD_VERSION: // major * 100 + minor
86 gold_version = tv->tv_u.tv_val;
87 break;
88 case LDPT_LINKER_OUTPUT:
89 switch (tv->tv_u.tv_val) {
90 case LDPO_REL: // .o
91 case LDPO_DYN: // .so
92 output_type = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
93 break;
94 case LDPO_EXEC: // .exe
95 output_type = LTO_CODEGEN_PIC_MODEL_STATIC;
96 break;
97 default:
98 (*message)(LDPL_ERROR, "Unknown output file type %d",
99 tv->tv_u.tv_val);
100 return LDPS_ERR;
101 }
102 // TODO: add an option to disable PIC.
103 //output_type = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC;
104 break;
105 case LDPT_OPTION:
Nick Lewyckyca428622009-02-22 22:15:44 +0000106 if (strcmp("generate-api-file", tv->tv_u.tv_string) == 0) {
107 generate_api_file = true;
Nick Lewyckya9b90322009-06-07 00:50:45 +0000108 } else if (strncmp("as=", tv->tv_u.tv_string, 3) == 0) {
109 if (as_path) {
110 (*message)(LDPL_WARNING, "Path to as specified twice. "
111 "Discarding %s", tv->tv_u.tv_string);
112 } else {
113 as_path = strdup(tv->tv_u.tv_string + 3);
114 }
Nick Lewyckyca428622009-02-22 22:15:44 +0000115 } else {
116 (*message)(LDPL_WARNING, "Ignoring flag %s", tv->tv_u.tv_string);
117 }
Nick Lewycky3e62b2d2009-02-03 07:13:24 +0000118 break;
119 case LDPT_REGISTER_CLAIM_FILE_HOOK: {
120 ld_plugin_register_claim_file callback;
121 callback = tv->tv_u.tv_register_claim_file;
122
123 if ((*callback)(claim_file_hook) != LDPS_OK)
124 return LDPS_ERR;
125
126 registeredClaimFile = true;
127 } break;
128 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK: {
129 ld_plugin_register_all_symbols_read callback;
130 callback = tv->tv_u.tv_register_all_symbols_read;
131
132 if ((*callback)(all_symbols_read_hook) != LDPS_OK)
133 return LDPS_ERR;
134
135 registeredAllSymbolsRead = true;
136 } break;
137 case LDPT_REGISTER_CLEANUP_HOOK: {
138 ld_plugin_register_cleanup callback;
139 callback = tv->tv_u.tv_register_cleanup;
140
141 if ((*callback)(cleanup_hook) != LDPS_OK)
142 return LDPS_ERR;
143
144 registeredCleanup = true;
145 } break;
146 case LDPT_ADD_SYMBOLS:
147 add_symbols = tv->tv_u.tv_add_symbols;
148 break;
149 case LDPT_GET_SYMBOLS:
150 get_symbols = tv->tv_u.tv_get_symbols;
151 break;
152 case LDPT_ADD_INPUT_FILE:
153 add_input_file = tv->tv_u.tv_add_input_file;
154 break;
155 case LDPT_MESSAGE:
156 message = tv->tv_u.tv_message;
157 break;
158 default:
159 break;
160 }
161 }
162
Rafael Espindola98c507e2009-02-18 08:30:15 +0000163 if (!registeredClaimFile) {
Rafael Espindola6210a942009-02-18 17:49:06 +0000164 (*message)(LDPL_ERROR, "register_claim_file not passed to LLVMgold.");
165 return LDPS_ERR;
166 }
Rafael Espindola98c507e2009-02-18 08:30:15 +0000167 if (!add_symbols) {
Rafael Espindola6210a942009-02-18 17:49:06 +0000168 (*message)(LDPL_ERROR, "add_symbols not passed to LLVMgold.");
169 return LDPS_ERR;
170 }
Nick Lewycky3e62b2d2009-02-03 07:13:24 +0000171
172 return LDPS_OK;
173}
174
175/// claim_file_hook - called by gold to see whether this file is one that
176/// our plugin can handle. We'll try to open it and register all the symbols
177/// with add_symbol if possible.
178ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
179 int *claimed) {
Torok Edwin3e5a0d82009-02-04 17:39:30 +0000180 void *buf = NULL;
Torok Edwin3e5a0d82009-02-04 17:39:30 +0000181 if (file->offset) {
Nick Lewyckyc1da8862009-02-05 04:14:23 +0000182 // Gold has found what might be IR part-way inside of a file, such as
183 // an .a archive.
Torok Edwin3e5a0d82009-02-04 17:39:30 +0000184 if (lseek(file->fd, file->offset, SEEK_SET) == -1) {
Nick Lewycky0df91b22009-02-07 03:15:01 +0000185 (*message)(LDPL_ERROR,
Torok Edwin3e5a0d82009-02-04 17:39:30 +0000186 "Failed to seek to archive member of %s at offset %d: %s\n",
187 file->name,
Jeffrey Yasskined1c0ff2009-07-01 18:11:20 +0000188 file->offset, sys::StrError(errno).c_str());
Torok Edwin3e5a0d82009-02-04 17:39:30 +0000189 return LDPS_ERR;
190 }
191 buf = malloc(file->filesize);
192 if (!buf) {
Nick Lewycky0df91b22009-02-07 03:15:01 +0000193 (*message)(LDPL_ERROR,
Torok Edwin3e5a0d82009-02-04 17:39:30 +0000194 "Failed to allocate buffer for archive member of size: %d\n",
195 file->filesize);
196 return LDPS_ERR;
197 }
198 if (read(file->fd, buf, file->filesize) != file->filesize) {
Nick Lewycky0df91b22009-02-07 03:15:01 +0000199 (*message)(LDPL_ERROR,
200 "Failed to read archive member of %s at offset %d: %s\n",
Torok Edwin3e5a0d82009-02-04 17:39:30 +0000201 file->name,
Nick Lewycky0df91b22009-02-07 03:15:01 +0000202 file->offset,
Jeffrey Yasskined1c0ff2009-07-01 18:11:20 +0000203 sys::StrError(errno).c_str());
Nick Lewycky0df91b22009-02-07 03:15:01 +0000204 free(buf);
Torok Edwin3e5a0d82009-02-04 17:39:30 +0000205 return LDPS_ERR;
206 }
Nick Lewycky0df91b22009-02-07 03:15:01 +0000207 if (!lto_module_is_object_file_in_memory(buf, file->filesize)) {
208 free(buf);
Torok Edwin3e5a0d82009-02-04 17:39:30 +0000209 return LDPS_OK;
Nick Lewycky0df91b22009-02-07 03:15:01 +0000210 }
Torok Edwin3e5a0d82009-02-04 17:39:30 +0000211 } else if (!lto_module_is_object_file(file->name))
Nick Lewycky3e62b2d2009-02-03 07:13:24 +0000212 return LDPS_OK;
213
214 *claimed = 1;
215 Modules.resize(Modules.size() + 1);
216 claimed_file &cf = Modules.back();
217
Nick Lewyckyc1da8862009-02-05 04:14:23 +0000218 cf.M = buf ? lto_module_create_from_memory(buf, file->filesize) :
219 lto_module_create(file->name);
Nick Lewyckyea97aa62009-02-06 01:58:34 +0000220 free(buf);
Nick Lewycky3e62b2d2009-02-03 07:13:24 +0000221 if (!cf.M) {
222 (*message)(LDPL_ERROR, "Failed to create LLVM module: %s",
223 lto_get_error_message());
224 return LDPS_ERR;
225 }
226 cf.handle = file->handle;
227 unsigned sym_count = lto_module_get_num_symbols(cf.M);
228 cf.syms.reserve(sym_count);
229
230 for (unsigned i = 0; i != sym_count; ++i) {
231 lto_symbol_attributes attrs = lto_module_get_symbol_attribute(cf.M, i);
232 if ((attrs & LTO_SYMBOL_SCOPE_MASK) == LTO_SYMBOL_SCOPE_INTERNAL)
233 continue;
234
235 cf.syms.push_back(ld_plugin_symbol());
236 ld_plugin_symbol &sym = cf.syms.back();
237 sym.name = const_cast<char *>(lto_module_get_symbol_name(cf.M, i));
238 sym.version = NULL;
239
240 int scope = attrs & LTO_SYMBOL_SCOPE_MASK;
241 switch (scope) {
242 case LTO_SYMBOL_SCOPE_HIDDEN:
243 sym.visibility = LDPV_HIDDEN;
244 break;
245 case LTO_SYMBOL_SCOPE_PROTECTED:
246 sym.visibility = LDPV_PROTECTED;
247 break;
248 case 0: // extern
249 case LTO_SYMBOL_SCOPE_DEFAULT:
250 sym.visibility = LDPV_DEFAULT;
251 break;
252 default:
253 (*message)(LDPL_ERROR, "Unknown scope attribute: %d", scope);
254 return LDPS_ERR;
255 }
256
257 int definition = attrs & LTO_SYMBOL_DEFINITION_MASK;
258 switch (definition) {
259 case LTO_SYMBOL_DEFINITION_REGULAR:
260 sym.def = LDPK_DEF;
261 break;
262 case LTO_SYMBOL_DEFINITION_UNDEFINED:
263 sym.def = LDPK_UNDEF;
264 break;
265 case LTO_SYMBOL_DEFINITION_TENTATIVE:
266 sym.def = LDPK_COMMON;
267 break;
268 case LTO_SYMBOL_DEFINITION_WEAK:
269 sym.def = LDPK_WEAKDEF;
270 break;
Rafael Espindola7431af02009-04-24 16:55:21 +0000271 case LTO_SYMBOL_DEFINITION_WEAKUNDEF:
272 sym.def = LDPK_WEAKUNDEF;
273 break;
Nick Lewycky3e62b2d2009-02-03 07:13:24 +0000274 default:
275 (*message)(LDPL_ERROR, "Unknown definition attribute: %d", definition);
276 return LDPS_ERR;
277 }
278
279 // LLVM never emits COMDAT.
280 sym.size = 0;
281 sym.comdat_key = NULL;
282
283 sym.resolution = LDPR_UNKNOWN;
284 }
285
286 cf.syms.reserve(cf.syms.size());
287
288 if (!cf.syms.empty()) {
289 if ((*add_symbols)(cf.handle, cf.syms.size(), &cf.syms[0]) != LDPS_OK) {
290 (*message)(LDPL_ERROR, "Unable to add symbols!");
291 return LDPS_ERR;
292 }
293 }
294
295 return LDPS_OK;
296}
297
298/// all_symbols_read_hook - gold informs us that all symbols have been read.
299/// At this point, we use get_symbols to see if any of our definitions have
300/// been overridden by a native object file. Then, perform optimization and
301/// codegen.
302ld_plugin_status all_symbols_read_hook(void) {
303 lto_code_gen_t cg = lto_codegen_create();
304
305 for (std::list<claimed_file>::iterator I = Modules.begin(),
306 E = Modules.end(); I != E; ++I)
307 lto_codegen_add_module(cg, I->M);
308
Nick Lewyckyca428622009-02-22 22:15:44 +0000309 std::ofstream api_file;
310 if (generate_api_file) {
311 api_file.open("apifile.txt", std::ofstream::out | std::ofstream::trunc);
312 if (!api_file.is_open()) {
313 (*message)(LDPL_FATAL, "Unable to open apifile.txt for writing.");
314 abort();
315 }
316 }
317
Nick Lewycky3e62b2d2009-02-03 07:13:24 +0000318 // If we don't preserve any symbols, libLTO will assume that all symbols are
319 // needed. Keep all symbols unless we're producing a final executable.
320 if (output_type == LTO_CODEGEN_PIC_MODEL_STATIC) {
321 bool anySymbolsPreserved = false;
322 for (std::list<claimed_file>::iterator I = Modules.begin(),
323 E = Modules.end(); I != E; ++I) {
324 (*get_symbols)(I->handle, I->syms.size(), &I->syms[0]);
325 for (unsigned i = 0, e = I->syms.size(); i != e; i++) {
Nick Lewyckye0afaa32009-02-15 22:49:17 +0000326 if (I->syms[i].resolution == LDPR_PREVAILING_DEF ||
327 (I->syms[i].def == LDPK_COMMON &&
328 I->syms[i].resolution == LDPR_RESOLVED_IR)) {
Nick Lewycky3e62b2d2009-02-03 07:13:24 +0000329 lto_codegen_add_must_preserve_symbol(cg, I->syms[i].name);
330 anySymbolsPreserved = true;
Nick Lewyckyca428622009-02-22 22:15:44 +0000331
332 if (generate_api_file)
333 api_file << I->syms[i].name << "\n";
Nick Lewycky3e62b2d2009-02-03 07:13:24 +0000334 }
335 }
336 }
337
Nick Lewyckyca428622009-02-22 22:15:44 +0000338 if (generate_api_file)
339 api_file.close();
340
Nick Lewycky3e62b2d2009-02-03 07:13:24 +0000341 if (!anySymbolsPreserved) {
342 // This entire file is unnecessary!
343 lto_codegen_dispose(cg);
344 return LDPS_OK;
345 }
346 }
347
348 lto_codegen_set_pic_model(cg, output_type);
349 lto_codegen_set_debug_model(cg, LTO_DEBUG_MODEL_DWARF);
Rafael Espindola42de34f2009-06-15 10:14:18 +0000350 if (as_path) {
351 sys::Path p = sys::Program::FindProgramByName(as_path);
352 lto_codegen_set_assembler_path(cg, p.c_str());
353 }
Nick Lewycky3e62b2d2009-02-03 07:13:24 +0000354
355 size_t bufsize = 0;
356 const char *buffer = static_cast<const char *>(lto_codegen_compile(cg,
357 &bufsize));
358
359 std::string ErrMsg;
360
361 sys::Path uniqueObjPath("/tmp/llvmgold.o");
362 if (uniqueObjPath.createTemporaryFileOnDisk(true, &ErrMsg)) {
363 (*message)(LDPL_ERROR, "%s", ErrMsg.c_str());
364 return LDPS_ERR;
365 }
Chris Lattner17e9edc2009-08-23 02:51:22 +0000366 raw_fd_ostream *objFile =
367 new raw_fd_ostream(uniqueObjPath.c_str(), ErrMsg,
Dan Gohmanbaa26392009-08-25 15:34:52 +0000368 raw_fd_ostream::F_Binary);
Nick Lewycky3e62b2d2009-02-03 07:13:24 +0000369 if (!ErrMsg.empty()) {
370 delete objFile;
371 (*message)(LDPL_ERROR, "%s", ErrMsg.c_str());
372 return LDPS_ERR;
373 }
374
375 objFile->write(buffer, bufsize);
376 objFile->close();
377
378 lto_codegen_dispose(cg);
Nick Lewycky3e62b2d2009-02-03 07:13:24 +0000379
380 if ((*add_input_file)(const_cast<char*>(uniqueObjPath.c_str())) != LDPS_OK) {
381 (*message)(LDPL_ERROR, "Unable to add .o file to the link.");
382 (*message)(LDPL_ERROR, "File left behind in: %s", uniqueObjPath.c_str());
383 return LDPS_ERR;
384 }
385
386 Cleanup.push_back(uniqueObjPath);
387
388 return LDPS_OK;
389}
390
391ld_plugin_status cleanup_hook(void) {
392 std::string ErrMsg;
393
394 for (int i = 0, e = Cleanup.size(); i != e; ++i)
395 if (Cleanup[i].eraseFromDisk(false, &ErrMsg))
396 (*message)(LDPL_ERROR, "Failed to delete '%s': %s", Cleanup[i].c_str(),
397 ErrMsg.c_str());
398
399 return LDPS_OK;
400}