mirror of
https://github.com/rui314/mold.git
synced 2024-09-11 21:17:28 +03:00
[ELF][LTO] Restart mold if a LTO plugin does not support get_symbols v3 API
This commit is contained in:
parent
8f0acbf413
commit
b57da5413c
@ -490,6 +490,10 @@ void parse_nonpositional_args(Context<E> &ctx,
|
||||
if (pos == arg.npos || pos == arg.size() - 1)
|
||||
Fatal(ctx) << "-defsym: syntax error: " << arg;
|
||||
ctx.arg.defsyms.push_back({arg.substr(0, pos), arg.substr(pos + 1)});
|
||||
} else if (read_flag(args, ":lto-pass2")) {
|
||||
ctx.arg.lto_pass2 = true;
|
||||
} else if (read_arg(ctx, args, arg, ":ignore-ir-file")) {
|
||||
ctx.arg.ignore_ir_file.insert(arg);
|
||||
} else if (read_flag(args, "demangle")) {
|
||||
ctx.arg.demangle = true;
|
||||
} else if (read_flag(args, "no-demangle")) {
|
||||
|
61
elf/lto.cc
61
elf/lto.cc
@ -82,9 +82,11 @@
|
||||
#include "../lto.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <sstream>
|
||||
#include <unistd.h>
|
||||
|
||||
#if 0
|
||||
# define LOG std::cerr
|
||||
@ -113,6 +115,7 @@ static PluginStatus message(int level, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
return LDPS_OK;
|
||||
}
|
||||
|
||||
@ -265,9 +268,10 @@ get_symbols(const void *handle, int nsyms, PluginSymbol *psyms, bool is_v2) {
|
||||
// to the final result, we need to make the plugin to ignore all
|
||||
// symbols.
|
||||
if (!file.is_alive) {
|
||||
assert(!is_v2);
|
||||
for (int i = 0; i < nsyms; i++)
|
||||
psyms[i].resolution = LDPR_PREEMPTED_REG;
|
||||
return is_v2 ? LDPS_OK : LDPS_NO_SYMS;
|
||||
return LDPS_NO_SYMS;
|
||||
}
|
||||
|
||||
auto get_resolution = [&](ElfSym<E> &esym, Symbol<E> &sym) {
|
||||
@ -485,6 +489,8 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
|
||||
|
||||
// Create mold's object instance
|
||||
ObjectFile<E> *obj = new ObjectFile<E>;
|
||||
ctx.obj_pool.push_back(std::unique_ptr<ObjectFile<E>>(obj));
|
||||
|
||||
obj->filename = mf->name;
|
||||
obj->symbols.push_back(new Symbol<E>);
|
||||
obj->first_global = 1;
|
||||
@ -502,6 +508,9 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
|
||||
if (file->fd == -1)
|
||||
Fatal(ctx) << "cannot open " << file->name << ": " << errno_string();
|
||||
|
||||
if (mf->parent)
|
||||
obj->archive_name = mf->parent->name;
|
||||
|
||||
file->offset = mf->get_offset();
|
||||
file->filesize = mf->size;
|
||||
file->handle = (void *)obj;
|
||||
@ -529,11 +538,61 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Returns true if a given linker plugin supports the get_symbols_v3 API.
|
||||
// Currently, we simply assume that LLVM supports it and GCC does not.
|
||||
template <typename E>
|
||||
static bool suppots_v3_api(Context<E> &ctx) {
|
||||
return ctx.arg.plugin.ends_with("LLVMgold.so");
|
||||
}
|
||||
|
||||
// This function restarts mold itself with `--:lto-pass2` and
|
||||
// `--:ignore-ir-file` flags. We do this as a workaround for the old
|
||||
// linker plugins that do not support the get_symbols_v3 API.
|
||||
//
|
||||
// get_symbols_v1 and get_symbols_v2 don't provide a way to ignore an
|
||||
// object file we previously passed to the linker plugin. So we can't
|
||||
// "unload" object files in archives that we ended up not choosing to
|
||||
// include into the final output.
|
||||
//
|
||||
// As a around, we restart the linker with a list of object files the
|
||||
// linker has to ignore, so that it won't read object files from
|
||||
// archives next time.
|
||||
//
|
||||
// This is an ugly hack and should be removed once GCC adopts the v3 API.
|
||||
template <typename E>
|
||||
static void restart_process(Context<E> &ctx) {
|
||||
std::vector<const char *> args;
|
||||
|
||||
for (std::string_view arg : ctx.cmdline_args)
|
||||
args.push_back(strdup(std::string(arg).c_str()));
|
||||
|
||||
for (std::unique_ptr<ObjectFile<E>> &file : ctx.obj_pool) {
|
||||
if (file->is_lto_obj && !file->is_alive) {
|
||||
args.push_back("--:ignore-ir-file");
|
||||
args.push_back(strdup(file->mf->get_identifier().c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
args.push_back("--:lto-pass2");
|
||||
args.push_back(nullptr);
|
||||
|
||||
std::string self = std::filesystem::read_symlink("/proc/self/exe");
|
||||
|
||||
std::cout << std::flush;
|
||||
std::cerr << std::flush;
|
||||
execv(self.c_str(), (char * const *)args.data());
|
||||
std::cerr << "execv failed: " << errno_string() << "\n";
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
// Entry point
|
||||
template <typename E>
|
||||
void do_lto(Context<E> &ctx) {
|
||||
Timer t(ctx, "do_lto");
|
||||
|
||||
if (!ctx.arg.lto_pass2 && !suppots_v3_api(ctx))
|
||||
restart_process(ctx);
|
||||
|
||||
assert(phase == 1);
|
||||
phase = 2;
|
||||
|
||||
|
@ -40,6 +40,9 @@ static ObjectFile<E> *new_lto_obj(Context<E> &ctx, MappedFile<Context<E>> *mf,
|
||||
static Counter count("parsed_lto_objs");
|
||||
count++;
|
||||
|
||||
if (ctx.arg.ignore_ir_file.count(mf->get_identifier()))
|
||||
return new ObjectFile<E>;
|
||||
|
||||
ObjectFile<E> *file = read_lto_object(ctx, mf);
|
||||
file->priority = ctx.file_priority++;
|
||||
file->is_in_lib = ctx.in_lib || (!archive_name.empty() && !ctx.whole_archive);
|
||||
|
@ -1441,6 +1441,7 @@ struct Context {
|
||||
bool hash_style_sysv = true;
|
||||
bool icf = false;
|
||||
bool is_static = false;
|
||||
bool lto_pass2 = false;
|
||||
bool omagic = false;
|
||||
bool pack_dyn_relocs_relr = false;
|
||||
bool perf = false;
|
||||
@ -1499,6 +1500,7 @@ struct Context {
|
||||
std::string soname;
|
||||
std::string sysroot;
|
||||
std::unique_ptr<std::unordered_set<std::string_view>> retain_symbols_file;
|
||||
std::unordered_set<std::string_view> ignore_ir_file;
|
||||
std::unordered_set<std::string_view> wrap;
|
||||
std::vector<std::pair<std::string_view, std::string_view>> defsyms;
|
||||
std::vector<std::string> library_paths;
|
||||
|
@ -88,60 +88,78 @@ static void mark_live_objects(Context<E> &ctx) {
|
||||
for (std::string_view name : ctx.arg.require_defined)
|
||||
mark_symbol(name);
|
||||
|
||||
auto mark_file = [&](InputFile<E> *file, tbb::feeder<InputFile<E> *> &feeder) {
|
||||
std::vector<InputFile<E> *> roots;
|
||||
|
||||
for (InputFile<E> *file : ctx.objs)
|
||||
if (file->is_alive)
|
||||
roots.push_back(file);
|
||||
|
||||
for (InputFile<E> *file : ctx.dsos)
|
||||
if (file->is_alive)
|
||||
roots.push_back(file);
|
||||
|
||||
tbb::parallel_for_each(roots, [&](InputFile<E> *file,
|
||||
tbb::feeder<InputFile<E> *> &feeder) {
|
||||
if (file->is_alive)
|
||||
file->mark_live_objects(ctx, [&](InputFile<E> *obj) { feeder.add(obj); });
|
||||
};
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, mark_file);
|
||||
tbb::parallel_for_each(ctx.dsos, mark_file);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void resolve_symbols(Context<E> &ctx) {
|
||||
Timer t(ctx, "resolve_symbols");
|
||||
|
||||
// Register symbols
|
||||
tbb::parallel_for_each(ctx.objs, [&](InputFile<E> *file) {
|
||||
auto resolve = [&](InputFile<E> *file) {
|
||||
file->resolve_symbols(ctx);
|
||||
});
|
||||
};
|
||||
|
||||
tbb::parallel_for_each(ctx.dsos, [&](InputFile<E> *file) {
|
||||
file->resolve_symbols(ctx);
|
||||
});
|
||||
auto clear = [&](InputFile<E> *file) {
|
||||
if (!file->is_alive)
|
||||
file->clear_symbols(ctx);
|
||||
};
|
||||
|
||||
// Register symbols
|
||||
tbb::parallel_for_each(ctx.objs, resolve);
|
||||
tbb::parallel_for_each(ctx.dsos, resolve);
|
||||
|
||||
// Do link-time optimization. We pass all IR object files to the
|
||||
// compiler backend to compile them into a few ELF object files.
|
||||
if (ctx.has_lto_object) {
|
||||
Timer t(ctx, "do_lto");
|
||||
|
||||
// The compiler backend needs to know how symbols are resolved,
|
||||
// so compute symbolvisibility, import/export bits, etc early.
|
||||
mark_live_objects(ctx);
|
||||
apply_version_script(ctx);
|
||||
parse_symbol_version(ctx);
|
||||
compute_import_export(ctx);
|
||||
|
||||
do_lto(ctx);
|
||||
|
||||
// Redo name resolution from scratch. We probably don't have to clear
|
||||
// all symbols, but we do this to keep it simple.
|
||||
tbb::parallel_for_each(ctx.objs, clear);
|
||||
tbb::parallel_for_each(ctx.dsos, clear);
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, resolve);
|
||||
tbb::parallel_for_each(ctx.dsos, resolve);
|
||||
}
|
||||
|
||||
// Mark reachable objects to decide which files to include into an output.
|
||||
// This also merges symbol visibility.
|
||||
mark_live_objects(ctx);
|
||||
|
||||
std::vector<InputFile<E> *> files;
|
||||
append(files, ctx.objs);
|
||||
append(files, ctx.dsos);
|
||||
|
||||
// Remove symbols of eliminated files.
|
||||
tbb::parallel_for_each(files, [&](InputFile<E> *file) {
|
||||
if (!file->is_alive)
|
||||
file->clear_symbols(ctx);
|
||||
});
|
||||
tbb::parallel_for_each(ctx.objs, clear);
|
||||
tbb::parallel_for_each(ctx.dsos, clear);
|
||||
|
||||
// Since we have turned on object files live bits, their symbols
|
||||
// may now have higher priority than before. So run the symbol
|
||||
// resolution pass again to get the final resolution result.
|
||||
tbb::parallel_for_each(files, [&](InputFile<E> *file) {
|
||||
tbb::parallel_for_each(ctx.objs, [&](InputFile<E> *file) {
|
||||
if (file->is_alive)
|
||||
file->resolve_symbols(ctx);
|
||||
});
|
||||
|
||||
tbb::parallel_for_each(ctx.dsos, [&](InputFile<E> *file) {
|
||||
if (file->is_alive)
|
||||
file->resolve_symbols(ctx);
|
||||
});
|
||||
|
11
mold.h
11
mold.h
@ -626,6 +626,17 @@ public:
|
||||
return parent ? (data - parent->data + parent->get_offset()) : 0;
|
||||
}
|
||||
|
||||
// Returns a string that uniquely identify a file that is possibly
|
||||
// in an archive.
|
||||
std::string get_identifier() const {
|
||||
if (parent) {
|
||||
// We use the file offset within an archive as an identifier
|
||||
// because archive members may have the same name.
|
||||
return parent->name + ":" + std::to_string(get_offset());
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
u8 *data = nullptr;
|
||||
i64 size = 0;
|
||||
|
Loading…
Reference in New Issue
Block a user