1
1
mirror of https://github.com/rui314/mold.git synced 2024-12-28 19:04:27 +03:00
mold/linker_script.cc

303 lines
7.9 KiB
C++
Raw Normal View History

2021-01-05 06:18:12 +03:00
// On Linux, /usr/lib/x86_64-linux-gnu/libc.so is not actually
// a shared object file but an ASCII text file containing a linker
// script to include a "real" libc.so file. Therefore, we need to
// support a (very limited) subset of the linker script language.
2020-11-19 03:24:10 +03:00
#include "mold.h"
2021-03-18 03:19:02 +03:00
#include <cctype>
2021-03-27 04:15:38 +03:00
#include <iomanip>
2021-03-18 03:19:02 +03:00
static thread_local MemoryMappedFile *current_file;
static std::string_view get_line(std::string_view input, const char *pos) {
assert(input.data() <= pos);
assert(pos < input.data() + input.size());
i64 start = input.rfind('\n', pos - input.data());
if (start == input.npos)
start = 0;
else
start++;
i64 end = input.find('\n', pos - input.data());
if (end == input.npos)
end = input.size();
return input.substr(start, end - start);
}
class SyntaxError {
public:
SyntaxError(std::string_view errpos) {
std::string_view contents = current_file->get_contents();
std::string_view line = get_line(contents, errpos.data());
i64 lineno = 1;
for (i64 i = 0; contents.data() + i < line.data(); i++)
if (contents[i] == '\n')
lineno++;
i64 column = errpos.data() - line.data();
std::stringstream ss;
ss << current_file->name << ":" << lineno << ": ";
i64 indent = ss.tellp();
2021-03-27 04:15:38 +03:00
ss << line << "\n" << std::setw(indent + column) << " " << "^ ";
out << ss.str();
}
template <class T> SyntaxError &operator<<(T &&val) {
out << std::forward<T>(val);
return *this;
}
Fatal out;
};
2020-11-19 13:31:13 +03:00
2020-12-10 07:44:58 +03:00
static std::vector<std::string_view> tokenize(std::string_view input) {
std::vector<std::string_view> vec;
2020-11-19 03:24:10 +03:00
while (!input.empty()) {
2021-03-18 03:19:02 +03:00
if (isspace(input[0])) {
2020-11-19 03:24:10 +03:00
input = input.substr(1);
continue;
}
2020-12-10 07:44:58 +03:00
if (input.starts_with("/*")) {
2021-01-24 06:01:43 +03:00
i64 pos = input.find("*/", 2);
2020-12-10 07:44:58 +03:00
if (pos == std::string_view::npos)
SyntaxError(input) << "unclosed comment";
2020-11-19 03:24:10 +03:00
input = input.substr(pos + 2);
continue;
}
2020-12-07 14:24:12 +03:00
if (input[0] == '#') {
2021-01-24 06:01:43 +03:00
i64 pos = input.find("\n", 1);
2020-12-10 07:44:58 +03:00
if (pos == std::string_view::npos)
2020-12-07 14:24:12 +03:00
break;
input = input.substr(pos + 1);
continue;
}
2020-11-19 03:24:10 +03:00
if (input[0] == '"') {
2021-01-24 06:01:43 +03:00
i64 pos = input.find('"', 1);
2020-12-10 07:44:58 +03:00
if (pos == std::string_view::npos)
SyntaxError(input) << "unclosed string literal";
2021-03-04 15:57:18 +03:00
vec.push_back(input.substr(0, pos + 1));
input = input.substr(pos + 1);
2020-11-19 03:24:10 +03:00
continue;
}
2021-01-24 06:01:43 +03:00
i64 pos = input.find_first_not_of(
2020-11-19 03:24:10 +03:00
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789_.$/\\~=+[]*?-!^:");
if (pos == 0)
pos = 1;
2021-03-27 04:15:38 +03:00
else if (pos == input.npos)
pos = input.size();
2020-11-19 03:24:10 +03:00
vec.push_back(input.substr(0, pos));
input = input.substr(pos);
}
return vec;
}
static std::span<std::string_view>
skip(std::span<std::string_view> tok, std::string_view str) {
if (tok.empty())
Fatal() << current_file->name << ": expected '" << str << "', but got EOF";
if (tok[0] != str)
SyntaxError(tok[0]) << "expected '" << str << "'";
return tok.subspan(1);
2020-11-19 03:24:10 +03:00
}
2021-03-04 15:57:18 +03:00
static std::string_view unquote(std::string_view s) {
if (s.size() > 0 && s[0] == '"') {
assert(s[s.size() - 1] == '"');
return s.substr(1, s.size() - 2);
}
return s;
}
static std::span<std::string_view>
read_output_format(std::span<std::string_view> tok) {
tok = skip(tok, "(");
2020-11-19 03:24:10 +03:00
while (!tok.empty() && tok[0] != ")")
2020-12-10 06:54:10 +03:00
tok = tok.subspan(1);
2020-11-19 03:24:10 +03:00
if (tok.empty())
Fatal() << current_file->name << ": expected ')', but got EOF";
return tok.subspan(1);
2020-11-19 03:24:10 +03:00
}
2021-03-29 07:02:32 +03:00
static MemoryMappedFile *resolve_path(std::string_view tok, ReadContext &rctx) {
std::string str(unquote(tok));
2020-12-10 07:44:58 +03:00
if (str.starts_with("/"))
2021-03-29 07:20:51 +03:00
return MemoryMappedFile::must_open(ctx.arg.sysroot + str);
2021-03-08 09:25:23 +03:00
2020-12-10 07:44:58 +03:00
if (str.starts_with("-l"))
2021-03-29 07:20:51 +03:00
return find_library(str.substr(2), ctx.arg.library_paths, rctx);
2021-03-08 09:25:23 +03:00
if (std::string path = path_dirname(current_file->name) + "/";
2021-03-08 09:25:23 +03:00
MemoryMappedFile *mb = MemoryMappedFile::open(path + str))
2020-12-22 11:37:49 +03:00
return mb;
2021-03-08 09:25:23 +03:00
2020-12-22 12:33:16 +03:00
if (MemoryMappedFile *mb = MemoryMappedFile::open(str))
2020-12-22 11:37:49 +03:00
return mb;
2021-03-08 09:25:23 +03:00
2021-03-29 07:20:51 +03:00
for (std::string_view dir : ctx.arg.library_paths) {
std::string root = dir.starts_with("/") ? ctx.arg.sysroot : "";
2021-03-08 09:25:23 +03:00
std::string path = root + std::string(dir) + "/" + str;
if (MemoryMappedFile *mb = MemoryMappedFile::open(path))
2020-12-22 11:37:49 +03:00
return mb;
2020-11-20 13:33:40 +03:00
}
2021-03-08 09:25:23 +03:00
SyntaxError(tok) << "library not found: " << str;
2020-11-19 13:31:13 +03:00
}
2020-12-20 14:14:55 +03:00
static std::span<std::string_view>
2021-03-29 07:02:32 +03:00
read_group(std::span<std::string_view> tok, ReadContext &rctx) {
tok = skip(tok, "(");
2020-11-19 03:24:10 +03:00
while (!tok.empty() && tok[0] != ")") {
if (tok[0] == "AS_NEEDED") {
2021-03-29 07:02:32 +03:00
bool orig = rctx.as_needed;
rctx.as_needed = true;
tok = read_group(tok.subspan(1), rctx);
rctx.as_needed = orig;
2020-11-19 03:24:10 +03:00
continue;
}
2021-03-29 07:02:32 +03:00
MemoryMappedFile *mb = resolve_path(tok[0], rctx);
read_file(mb, rctx);
2020-12-10 06:54:10 +03:00
tok = tok.subspan(1);
2020-11-19 03:24:10 +03:00
}
if (tok.empty())
Fatal() << current_file->name << ": expected ')', but got EOF";
return tok.subspan(1);
2020-11-19 03:24:10 +03:00
}
2021-03-29 07:02:32 +03:00
void parse_linker_script(MemoryMappedFile *mb, ReadContext &rctx) {
current_file = mb;
2020-11-19 13:31:13 +03:00
2021-01-24 06:01:43 +03:00
std::vector<std::string_view> vec = tokenize(mb->get_contents());
std::span<std::string_view> tok = vec;
2020-11-19 03:24:10 +03:00
while (!tok.empty()) {
if (tok[0] == "OUTPUT_FORMAT")
tok = read_output_format(tok.subspan(1));
else if (tok[0] == "INPUT" || tok[0] == "GROUP")
2021-03-29 07:02:32 +03:00
tok = read_group(tok.subspan(1), rctx);
else
SyntaxError(tok[0]) << "unknown token";
2020-11-19 03:24:10 +03:00
}
}
2020-12-07 13:54:07 +03:00
2021-03-08 15:11:54 +03:00
static bool read_label(std::span<std::string_view> &tok,
std::string label) {
if (tok.size() >= 1 && tok[0] == label + ":") {
tok = tok.subspan(1);
return true;
}
if (tok.size() >= 2 && tok[0] == label && tok[1] == ":") {
tok = tok.subspan(2);
return true;
}
return false;
}
static void parse_version_script_commands(std::span<std::string_view> &tok,
i16 &ver, bool is_extern_cpp) {
bool is_global = true;
while (!tok.empty() && tok[0] != "}") {
if (read_label(tok, "global")) {
is_global = true;
continue;
}
if (read_label(tok, "local")) {
is_global = false;
continue;
}
if (tok[0] == "extern") {
tok = tok.subspan(1);
tok = skip(tok, "\"C++\"");
tok = skip(tok, "{");
parse_version_script_commands(tok, ver, true);
tok = skip(tok, "}");
tok = skip(tok, ";");
continue;
}
if (tok[0] == "*")
2021-03-29 07:20:51 +03:00
ctx.arg.default_version = (is_global ? ver : VER_NDX_LOCAL);
else
2021-03-29 07:20:51 +03:00
ctx.arg.version_patterns.push_back({tok[0], ver, is_extern_cpp});
tok = skip(tok.subspan(1), ";");
}
}
2020-12-10 07:44:58 +03:00
void parse_version_script(std::string path) {
current_file = MemoryMappedFile::must_open(path);
std::vector<std::string_view> vec = tokenize(current_file->get_contents());
2020-12-10 07:44:58 +03:00
std::span<std::string_view> tok = vec;
2021-03-07 07:43:58 +03:00
i16 next_ver = VER_NDX_LAST_RESERVED + 1;
2020-12-07 13:54:07 +03:00
2021-03-04 15:57:18 +03:00
while (!tok.empty()) {
i16 ver = VER_NDX_GLOBAL;
if (tok[0] != "{") {
ver = next_ver++;
2021-03-29 07:20:51 +03:00
ctx.arg.version_definitions.push_back(tok[0]);
2020-12-10 06:54:10 +03:00
tok = tok.subspan(1);
2020-12-07 13:54:07 +03:00
}
2021-03-04 15:57:18 +03:00
tok = skip(tok, "{");
parse_version_script_commands(tok, ver, false);
2021-03-04 15:57:18 +03:00
tok = skip(tok, "}");
2021-03-24 07:45:21 +03:00
if (!tok.empty() && tok[0] != ";")
tok = tok.subspan(1);
2021-03-04 15:57:18 +03:00
tok = skip(tok, ";");
2020-12-07 13:54:07 +03:00
}
if (!tok.empty())
SyntaxError(tok[0]) << "trailing garbage token";
2020-12-07 13:54:07 +03:00
}
2021-03-08 15:11:54 +03:00
void parse_dynamic_list(std::string path) {
current_file = MemoryMappedFile::must_open(path);
std::vector<std::string_view> vec = tokenize(current_file->get_contents());
2021-03-08 15:11:54 +03:00
std::span<std::string_view> tok = vec;
tok = skip(tok, "{");
i16 ver = VER_NDX_GLOBAL;
2021-03-08 15:11:54 +03:00
while (!tok.empty() && tok[0] != "}") {
if (read_label(tok, "global")) {
ver = VER_NDX_GLOBAL;
continue;
}
if (read_label(tok, "local")) {
ver = VER_NDX_LOCAL;
continue;
}
if (tok[0] == "*")
2021-03-29 07:20:51 +03:00
ctx.arg.default_version = ver;
2021-03-08 15:11:54 +03:00
else
2021-03-29 07:20:51 +03:00
ctx.arg.version_patterns.push_back({tok[0], ver, false});
2021-03-08 15:11:54 +03:00
tok = skip(tok.subspan(1), ";");
}
tok = skip(tok, "}");
tok = skip(tok, ";");
if (!tok.empty())
SyntaxError(tok[0]) << "trailing garbage token";
2021-03-08 15:11:54 +03:00
}