diff options
Diffstat (limited to 'build/unix/elfhack')
-rw-r--r-- | build/unix/elfhack/relrhack.cpp | 71 |
1 files changed, 67 insertions, 4 deletions
diff --git a/build/unix/elfhack/relrhack.cpp b/build/unix/elfhack/relrhack.cpp index 2d78d783c9..c55103ea70 100644 --- a/build/unix/elfhack/relrhack.cpp +++ b/build/unix/elfhack/relrhack.cpp @@ -12,6 +12,7 @@ #include "relrhack.h" #include <algorithm> +#include <cstdio> #include <cstring> #include <filesystem> #include <fstream> @@ -26,6 +27,8 @@ #include <utility> #include <vector> +#include "mozilla/ScopeExit.h" + namespace fs = std::filesystem; class CantSwapSections : public std::runtime_error { @@ -420,10 +423,40 @@ uint16_t get_elf_machine(std::istream& in) { return ehdr.e_machine; } -int run_command(std::vector<const char*>& args) { +int run_command(std::vector<const char*>& args, bool use_response_file) { + std::string at_file; + const char** argv = args.data(); + std::array<const char*, 3> args_with_atfile{}; + if (use_response_file) { + const char* tmpdir = getenv("TMPDIR"); + if (!tmpdir) { + tmpdir = "/tmp"; + } + std::string tmpfile = tmpdir; + tmpfile += "/relrhackXXXXXX"; + int fd = mkstemp(tmpfile.data()); + if (fd < 0) { + std::cerr << "Failed to create temporary file." << std::endl; + return 1; + } + close(fd); + std::ofstream f{tmpfile, f.binary}; + for (auto arg = std::next(args.begin()); arg != args.end(); ++arg) { + f << *arg << "\n"; + } + at_file = "@"; + at_file += tmpfile; + args_with_atfile = {args.front(), at_file.c_str(), nullptr}; + argv = args_with_atfile.data(); + } + auto guard = mozilla::MakeScopeExit([&] { + if (!at_file.empty()) { + unlink(at_file.c_str() + 1); + } + }); pid_t child_pid; if (posix_spawn(&child_pid, args[0], nullptr, nullptr, - const_cast<char* const*>(args.data()), environ) != 0) { + const_cast<char* const*>(argv), environ) != 0) { throw std::runtime_error("posix_spawn failed"); } @@ -442,6 +475,9 @@ int main(int argc, char* argv[]) { std::optional<fs::path> real_linker = std::nullopt; bool shared = false; bool is_android = false; + bool use_response_file = false; + std::vector<char> response_file; + std::vector<const char*> response_file_args; uint16_t elf_machine = EM_NONE; // Scan argv in order to prepare the following: // - get the output file. That's the file we may need to adjust. @@ -458,6 +494,33 @@ int main(int argc, char* argv[]) { // // At the same time, we also construct a new list of arguments, with // --real-linker filtered out. We'll later inject arguments in that list. + if (argc == 2 && argv[1] && argv[1][0] == '@') { + // When GCC is given a response file, it wraps all arguments into a + // new response file with all arguments, even if originally there were + // arguments and a response file. + // In that case, we can't scan for arguments, so we need to read the + // response file. And as we change the arguments, we'll need to write + // a new one. + std::ifstream f{argv[1] + 1, f.binary | f.ate}; + if (!f) { + std::cerr << "Failed to read " << argv[1] + 1 << std::endl; + return 1; + } + size_t len = f.tellg(); + response_file = read_vector_at<char>(f, 0, len); + std::replace(response_file.begin(), response_file.end(), '\n', '\0'); + if (len && response_file[len - 1] != '\0') { + response_file.push_back('\0'); + } + response_file_args.push_back(argv[0]); + for (const char* a = response_file.data(); + a < response_file.data() + response_file.size(); a += strlen(a) + 1) { + response_file_args.push_back(a); + } + argv = const_cast<char**>(response_file_args.data()); + argc = response_file_args.size(); + use_response_file = true; + } for (i = 1, argv++; i < argc && *argv; argv++, i++) { std::string_view arg{*argv}; if (arg == "-shared") { @@ -538,7 +601,7 @@ int main(int argc, char* argv[]) { hacked_args.insert(hacked_args.begin() + crti + 1, inject.c_str()); hacked_args.insert(hacked_args.end() - 1, {"-z", "pack-relative-relocs", "-init=_relrhack_wrap_init"}); - int status = run_command(hacked_args); + int status = run_command(hacked_args, use_response_file); if (status) { return status; } @@ -563,5 +626,5 @@ int main(int argc, char* argv[]) { } } - return run_command(args); + return run_command(args, use_response_file); } |