1
1
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:
Rui Ueyama 2022-02-17 16:02:45 +09:00
parent 8f0acbf413
commit b57da5413c
6 changed files with 120 additions and 23 deletions

View File

@ -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")) {

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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
View File

@ -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;