relrhack.cpp (22930B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 // This program acts as a linker wrapper. Its executable name is meant 6 // to be that of a linker, and it will find the next linker with the same 7 // name in $PATH. However, if for some reason the next linker cannot be 8 // found this way, the caller may pass its path via the --real-linker 9 // option. 10 // 11 // More in-depth background on https://glandium.org/blog/?p=4297 12 13 #include "relrhack.h" 14 #include <algorithm> 15 #include <array> 16 #include <cstdio> 17 #include <cstring> 18 #include <filesystem> 19 #include <fstream> 20 #include <iostream> 21 #include <optional> 22 #include <spawn.h> 23 #include <sstream> 24 #include <stdexcept> 25 #include <sys/wait.h> 26 #include <unistd.h> 27 #include <unordered_map> 28 #include <utility> 29 #include <vector> 30 31 #include "mozilla/ScopeExit.h" 32 33 namespace fs = std::filesystem; 34 35 class CantSwapSections : public std::runtime_error { 36 public: 37 CantSwapSections(const char* what) : std::runtime_error(what) {} 38 }; 39 40 template <int bits> 41 struct Elf {}; 42 43 #define ELF(bits) \ 44 template <> \ 45 struct Elf<bits> { \ 46 using Ehdr = Elf##bits##_Ehdr; \ 47 using Phdr = Elf##bits##_Phdr; \ 48 using Shdr = Elf##bits##_Shdr; \ 49 using Dyn = Elf##bits##_Dyn; \ 50 using Addr = Elf##bits##_Addr; \ 51 using Word = Elf##bits##_Word; \ 52 using Off = Elf##bits##_Off; \ 53 using Verneed = Elf##bits##_Verneed; \ 54 using Vernaux = Elf##bits##_Vernaux; \ 55 } 56 57 ELF(32); 58 ELF(64); 59 60 template <int bits> 61 struct RelR : public Elf<bits> { 62 using Elf_Ehdr = typename Elf<bits>::Ehdr; 63 using Elf_Phdr = typename Elf<bits>::Phdr; 64 using Elf_Shdr = typename Elf<bits>::Shdr; 65 using Elf_Dyn = typename Elf<bits>::Dyn; 66 using Elf_Addr = typename Elf<bits>::Addr; 67 using Elf_Word = typename Elf<bits>::Word; 68 using Elf_Off = typename Elf<bits>::Off; 69 using Elf_Verneed = typename Elf<bits>::Verneed; 70 using Elf_Vernaux = typename Elf<bits>::Vernaux; 71 72 #define TAG_NAME(t) {t, #t} 73 class DynInfo { 74 public: 75 using Tag = decltype(Elf_Dyn::d_tag); 76 using Value = decltype(Elf_Dyn::d_un.d_val); 77 bool is_wanted(Tag tag) const { return tag_names.count(tag); } 78 void insert(off_t offset, Tag tag, Value val) { 79 data[tag] = std::make_pair(offset, val); 80 } 81 off_t offset(Tag tag) const { return data.at(tag).first; } 82 bool contains(Tag tag) const { return data.count(tag); } 83 Value& operator[](Tag tag) { 84 if (!is_wanted(tag)) { 85 std::stringstream msg; 86 msg << "Tag 0x" << std::hex << tag << " is not in DynInfo::tag_names"; 87 throw std::runtime_error(msg.str()); 88 } 89 return data[tag].second; 90 } 91 const char* name(Tag tag) const { return tag_names.at(tag); } 92 93 private: 94 std::unordered_map<Tag, std::pair<off_t, Value>> data; 95 96 const std::unordered_map<Tag, const char*> tag_names = { 97 TAG_NAME(DT_JMPREL), TAG_NAME(DT_PLTRELSZ), TAG_NAME(DT_RELR), 98 TAG_NAME(DT_RELRENT), TAG_NAME(DT_RELRSZ), TAG_NAME(DT_RELA), 99 TAG_NAME(DT_RELASZ), TAG_NAME(DT_RELAENT), TAG_NAME(DT_REL), 100 TAG_NAME(DT_RELSZ), TAG_NAME(DT_RELENT), TAG_NAME(DT_STRTAB), 101 TAG_NAME(DT_STRSZ), TAG_NAME(DT_VERNEED), TAG_NAME(DT_VERNEEDNUM), 102 }; 103 }; 104 105 // Translate a virtual address into an offset in the file based on the program 106 // headers' PT_LOAD. 107 static Elf_Addr get_offset(const std::vector<Elf_Phdr>& phdr, Elf_Addr addr) { 108 for (const auto& p : phdr) { 109 if (p.p_type == PT_LOAD && addr >= p.p_vaddr && 110 addr < p.p_vaddr + p.p_filesz) { 111 return addr - (p.p_vaddr - p.p_paddr); 112 } 113 } 114 return 0; 115 } 116 117 static bool hack(std::fstream& f, bool set_relrhack_bit); 118 }; 119 120 template <typename T> 121 T read_one_at(std::istream& in, off_t pos) { 122 T result; 123 in.seekg(pos, std::ios::beg); 124 in.read(reinterpret_cast<char*>(&result), sizeof(T)); 125 return result; 126 } 127 128 template <typename T> 129 std::vector<T> read_vector_at(std::istream& in, off_t pos, size_t num) { 130 std::vector<T> result(num); 131 in.seekg(pos, std::ios::beg); 132 in.read(reinterpret_cast<char*>(result.data()), num * sizeof(T)); 133 return result; 134 } 135 136 void write_at(std::ostream& out, off_t pos, const char* buf, size_t len) { 137 out.seekp(pos, std::ios::beg); 138 out.write(buf, len); 139 } 140 141 template <typename T> 142 void write_one_at(std::ostream& out, off_t pos, const T& data) { 143 write_at(out, pos, reinterpret_cast<const char*>(&data), sizeof(T)); 144 } 145 146 template <typename T> 147 void write_vector_at(std::ostream& out, off_t pos, const std::vector<T>& vec) { 148 write_at(out, pos, reinterpret_cast<const char*>(&vec.front()), 149 vec.size() * sizeof(T)); 150 } 151 152 template <int bits> 153 bool RelR<bits>::hack(std::fstream& f, bool set_relrhack_bit) { 154 auto ehdr = read_one_at<Elf_Ehdr>(f, 0); 155 if (ehdr.e_phentsize != sizeof(Elf_Phdr)) { 156 throw std::runtime_error("Invalid ELF?"); 157 } 158 auto phdr = read_vector_at<Elf_Phdr>(f, ehdr.e_phoff, ehdr.e_phnum); 159 const auto& dyn_phdr = 160 std::find_if(phdr.begin(), phdr.end(), 161 [](const auto& p) { return p.p_type == PT_DYNAMIC; }); 162 if (dyn_phdr == phdr.end()) { 163 return false; 164 } 165 if (dyn_phdr->p_filesz % sizeof(Elf_Dyn)) { 166 throw std::runtime_error("Invalid ELF?"); 167 } 168 auto dyn = read_vector_at<Elf_Dyn>(f, dyn_phdr->p_offset, 169 dyn_phdr->p_filesz / sizeof(Elf_Dyn)); 170 off_t dyn_offset = dyn_phdr->p_offset; 171 DynInfo dyn_info; 172 for (const auto& d : dyn) { 173 if (d.d_tag == DT_NULL) { 174 break; 175 } 176 177 if (dyn_info.is_wanted(d.d_tag)) { 178 if (dyn_info.contains(d.d_tag)) { 179 std::stringstream msg; 180 msg << dyn_info.name(d.d_tag) << " appears twice?"; 181 throw std::runtime_error(msg.str()); 182 } 183 dyn_info.insert(dyn_offset, d.d_tag, d.d_un.d_val); 184 } 185 dyn_offset += sizeof(Elf_Dyn); 186 } 187 188 // Find the location and size of the SHT_RELR section, which contains the 189 // packed-relative-relocs. 190 Elf_Addr relr_off = 191 dyn_info.contains(DT_RELR) ? get_offset(phdr, dyn_info[DT_RELR]) : 0; 192 Elf_Off relrsz = dyn_info[DT_RELRSZ]; 193 const decltype(Elf_Dyn::d_tag) rel_tags[3][2] = { 194 {DT_REL, DT_RELA}, {DT_RELSZ, DT_RELASZ}, {DT_RELENT, DT_RELAENT}}; 195 for (const auto& [rel_tag, rela_tag] : rel_tags) { 196 if (dyn_info.contains(rel_tag) && dyn_info.contains(rela_tag)) { 197 std::stringstream msg; 198 msg << "Both " << dyn_info.name(rel_tag) << " and " 199 << dyn_info.name(rela_tag) << " appear?"; 200 throw std::runtime_error(msg.str()); 201 } 202 } 203 Elf_Off relent = 204 dyn_info.contains(DT_RELENT) ? dyn_info[DT_RELENT] : dyn_info[DT_RELAENT]; 205 206 // Estimate the size of the unpacked relative relocations corresponding 207 // to the SHT_RELR section. 208 auto relr = read_vector_at<Elf_Addr>(f, relr_off, relrsz / sizeof(Elf_Addr)); 209 size_t relocs = 0; 210 for (const auto& entry : relr) { 211 if ((entry & 1) == 0) { 212 // LSB is 0, this is a pointer for a single relocation. 213 relocs++; 214 } else { 215 // LSB is 1, remaining bits are a bitmap. Each bit represents a 216 // relocation. 217 relocs += __builtin_popcount(entry) - 1; 218 } 219 } 220 // If the packed relocations + some overhead (we pick 4K arbitrarily, the 221 // real size would require digging into the section sizes of the injected 222 // .o file, which is not worth the error) is larger than the estimated 223 // unpacked relocations, we'll just relink without packed relocations. 224 if (relocs * relent < relrsz + 4096) { 225 return false; 226 } 227 228 if (set_relrhack_bit) { 229 // Change DT_RELR* tags to add DT_RELRHACK_BIT. 230 for (const auto tag : {DT_RELR, DT_RELRSZ, DT_RELRENT}) { 231 write_one_at(f, dyn_info.offset(tag), tag | DT_RELRHACK_BIT); 232 } 233 } 234 235 bool is_glibc = false; 236 237 if (dyn_info.contains(DT_VERNEEDNUM) && dyn_info.contains(DT_VERNEED) && 238 dyn_info.contains(DT_STRSZ) && dyn_info.contains(DT_STRTAB)) { 239 // Scan SHT_VERNEED for the GLIBC_ABI_DT_RELR version on the libc 240 // library. 241 Elf_Addr verneed_off = get_offset(phdr, dyn_info[DT_VERNEED]); 242 Elf_Off verneednum = dyn_info[DT_VERNEEDNUM]; 243 // SHT_STRTAB section, which contains the string table for, among other 244 // things, the symbol versions in the SHT_VERNEED section. 245 auto strtab = read_vector_at<char>(f, get_offset(phdr, dyn_info[DT_STRTAB]), 246 dyn_info[DT_STRSZ]); 247 // Guarantee a nul character at the end of the string table. 248 strtab.push_back(0); 249 while (verneednum--) { 250 auto verneed = read_one_at<Elf_Verneed>(f, verneed_off); 251 if (std::string_view{"libc.so.6"} == &strtab.at(verneed.vn_file)) { 252 is_glibc = true; 253 Elf_Addr vernaux_off = verneed_off + verneed.vn_aux; 254 Elf_Addr relr = 0; 255 Elf_Vernaux reuse; 256 for (auto n = 0; n < verneed.vn_cnt; n++) { 257 auto vernaux = read_one_at<Elf_Vernaux>(f, vernaux_off); 258 if (std::string_view{"GLIBC_ABI_DT_RELR"} == 259 &strtab.at(vernaux.vna_name)) { 260 relr = vernaux_off; 261 } else { 262 reuse = vernaux; 263 } 264 vernaux_off += vernaux.vna_next; 265 } 266 // In the case where we do have the GLIBC_ABI_DT_RELR version, we 267 // need to edit the binary to make the following changes: 268 // - Remove the GLIBC_ABI_DT_RELR version, we replace it with an 269 // arbitrary other version entry, which is simpler than completely 270 // removing it. We need to remove it because older versions of glibc 271 // don't have the version (after all, that's why the symbol version 272 // is there in the first place, to avoid running against older versions 273 // of glibc that don't support packed relocations). 274 // - Alter the DT_RELR* tags in the dynamic section, so that they 275 // are not recognized by ld.so, because, while all versions of ld.so 276 // ignore tags they don't know, glibc's ld.so versions that support 277 // packed relocations don't want to load a binary that has DT_RELR* 278 // tags but *not* a dependency on the GLIBC_ABI_DT_RELR version. 279 if (relr) { 280 // Don't overwrite vn_aux. 281 write_at(f, relr, reinterpret_cast<char*>(&reuse), 282 sizeof(reuse) - sizeof(Elf_Word)); 283 } 284 } 285 verneed_off += verneed.vn_next; 286 } 287 } 288 289 // Location of the .rel.plt section. 290 Elf_Addr jmprel = dyn_info.contains(DT_JMPREL) ? dyn_info[DT_JMPREL] : 0; 291 if (is_glibc) { 292 #ifndef MOZ_STDCXX_COMPAT 293 try { 294 #endif 295 // ld.so in glibc 2.16 to 2.23 expects .rel.plt to strictly follow 296 // .rel.dyn. (https://sourceware.org/bugzilla/show_bug.cgi?id=14341) 297 // BFD ld places .relr.dyn after .rel.plt, so this works fine, but lld 298 // places it between both sections, which doesn't work out for us. In that 299 // case, we want to swap .relr.dyn and .rel.plt. 300 Elf_Addr rel_end = dyn_info.contains(DT_REL) 301 ? (dyn_info[DT_REL] + dyn_info[DT_RELSZ]) 302 : (dyn_info[DT_RELA] + dyn_info[DT_RELASZ]); 303 if (dyn_info.contains(DT_JMPREL) && dyn_info[DT_PLTRELSZ] && 304 dyn_info[DT_JMPREL] != rel_end) { 305 if (dyn_info[DT_RELR] != rel_end) { 306 throw CantSwapSections("RELR section doesn't follow REL/RELA?"); 307 } 308 if (dyn_info[DT_JMPREL] != dyn_info[DT_RELR] + dyn_info[DT_RELRSZ]) { 309 throw CantSwapSections("PLT REL/RELA doesn't follow RELR?"); 310 } 311 auto plt_rel = read_vector_at<char>( 312 f, get_offset(phdr, dyn_info[DT_JMPREL]), dyn_info[DT_PLTRELSZ]); 313 // Write the content of both sections swapped, and adjust the 314 // corresponding PT_DYNAMIC entries. 315 write_vector_at(f, relr_off, plt_rel); 316 write_vector_at(f, relr_off + plt_rel.size(), relr); 317 dyn_info[DT_JMPREL] = rel_end; 318 dyn_info[DT_RELR] = rel_end + plt_rel.size(); 319 for (const auto tag : {DT_JMPREL, DT_RELR}) { 320 write_one_at(f, dyn_info.offset(tag) + sizeof(typename DynInfo::Tag), 321 dyn_info[tag]); 322 } 323 } 324 #ifndef MOZ_STDCXX_COMPAT 325 } catch (const CantSwapSections& err) { 326 // When binary compatibility with older libstdc++/glibc is not enabled, we 327 // only emit a warning about why swapping the sections is not happening. 328 std::cerr << "WARNING: " << err.what() << std::endl; 329 } 330 #endif 331 } 332 333 off_t shdr_offset = ehdr.e_shoff; 334 auto shdr = read_vector_at<Elf_Shdr>(f, ehdr.e_shoff, ehdr.e_shnum); 335 for (auto& s : shdr) { 336 // Some tools don't like sections of types they don't know, so change 337 // SHT_RELR, which might be unknown on older systems, to SHT_PROGBITS. 338 if (s.sh_type == SHT_RELR) { 339 s.sh_type = SHT_PROGBITS; 340 // If DT_RELR has been adjusted to swap with DT_JMPREL, also adjust 341 // the corresponding SHT_RELR section header. 342 if (s.sh_addr != dyn_info[DT_RELR]) { 343 s.sh_offset += dyn_info[DT_RELR] - s.sh_addr; 344 s.sh_addr = dyn_info[DT_RELR]; 345 } 346 write_one_at(f, shdr_offset, s); 347 } else if (jmprel && (s.sh_addr == jmprel) && 348 (s.sh_addr != dyn_info[DT_JMPREL])) { 349 // If DT_JMPREL has been adjusted to swap with DT_RELR, also adjust 350 // the corresponding section header. 351 s.sh_offset -= s.sh_addr - dyn_info[DT_JMPREL]; 352 s.sh_addr = dyn_info[DT_JMPREL]; 353 write_one_at(f, shdr_offset, s); 354 } 355 shdr_offset += sizeof(Elf_Shdr); 356 } 357 return true; 358 } 359 360 std::vector<std::string> get_path() { 361 std::vector<std::string> result; 362 std::stringstream stream{std::getenv("PATH")}; 363 std::string item; 364 365 while (std::getline(stream, item, ':')) { 366 result.push_back(std::move(item)); 367 } 368 369 return result; 370 } 371 372 std::optional<fs::path> next_program(fs::path& this_program, 373 std::optional<fs::path>& program) { 374 auto program_name = program ? *program : this_program.filename(); 375 for (const auto& dir : get_path()) { 376 auto path = fs::path(dir) / program_name; 377 auto status = fs::status(path); 378 if ((status.type() == fs::file_type::regular) && 379 ((status.permissions() & fs::perms::owner_exec) == 380 fs::perms::owner_exec) && 381 !fs::equivalent(path, this_program)) 382 return path; 383 } 384 return std::nullopt; 385 } 386 387 unsigned char get_elf_class(unsigned char (&e_ident)[EI_NIDENT]) { 388 if (std::string_view{reinterpret_cast<char*>(e_ident), SELFMAG} != 389 std::string_view{ELFMAG, SELFMAG}) { 390 throw std::runtime_error("Not ELF?"); 391 } 392 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 393 if (e_ident[EI_DATA] != ELFDATA2LSB) { 394 throw std::runtime_error("Not Little Endian ELF?"); 395 } 396 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 397 if (e_ident[EI_DATA] != ELFDATA2MSB) { 398 throw std::runtime_error("Not Big Endian ELF?"); 399 } 400 #else 401 # error Unknown byte order. 402 #endif 403 if (e_ident[EI_VERSION] != 1) { 404 throw std::runtime_error("Not ELF version 1?"); 405 } 406 auto elf_class = e_ident[EI_CLASS]; 407 if (elf_class != ELFCLASS32 && elf_class != ELFCLASS64) { 408 throw std::runtime_error("Not 32 or 64-bits ELF?"); 409 } 410 return elf_class; 411 } 412 413 unsigned char get_elf_class(std::istream& in) { 414 unsigned char e_ident[EI_NIDENT]; 415 in.read(reinterpret_cast<char*>(e_ident), sizeof(e_ident)); 416 return get_elf_class(e_ident); 417 } 418 419 uint16_t get_elf_machine(std::istream& in) { 420 // As far as e_machine is concerned, both Elf32_Ehdr and Elf64_Ehdr are equal. 421 Elf32_Ehdr ehdr; 422 in.read(reinterpret_cast<char*>(&ehdr), sizeof(ehdr)); 423 // get_elf_class will throw exceptions for the cases we don't handle. 424 get_elf_class(ehdr.e_ident); 425 return ehdr.e_machine; 426 } 427 428 int run_command(std::vector<const char*>& args, bool use_response_file) { 429 std::string at_file; 430 const char** argv = args.data(); 431 std::array<const char*, 3> args_with_atfile{}; 432 if (use_response_file) { 433 const char* tmpdir = getenv("TMPDIR"); 434 if (!tmpdir) { 435 tmpdir = "/tmp"; 436 } 437 std::string tmpfile = tmpdir; 438 tmpfile += "/relrhackXXXXXX"; 439 int fd = mkstemp(tmpfile.data()); 440 if (fd < 0) { 441 std::cerr << "Failed to create temporary file." << std::endl; 442 return 1; 443 } 444 close(fd); 445 std::ofstream f{tmpfile, f.binary}; 446 for (auto arg = std::next(args.begin()); arg != args.end(); ++arg) { 447 f << *arg << "\n"; 448 } 449 at_file = "@"; 450 at_file += tmpfile; 451 args_with_atfile = {args.front(), at_file.c_str(), nullptr}; 452 argv = args_with_atfile.data(); 453 } 454 auto guard = mozilla::MakeScopeExit([&] { 455 if (!at_file.empty()) { 456 unlink(at_file.c_str() + 1); 457 } 458 }); 459 pid_t child_pid; 460 if (posix_spawn(&child_pid, args[0], nullptr, nullptr, 461 const_cast<char* const*>(argv), environ) != 0) { 462 throw std::runtime_error("posix_spawn failed"); 463 } 464 465 int status; 466 waitpid(child_pid, &status, 0); 467 return WEXITSTATUS(status); 468 } 469 470 int main(int argc, char* argv[]) { 471 auto this_program = fs::absolute(argv[0]); 472 473 std::vector<const char*> args; 474 475 int i, crti = 0; 476 std::optional<fs::path> output = std::nullopt; 477 std::optional<fs::path> real_linker = std::nullopt; 478 bool shared = false; 479 bool is_android = false; 480 bool use_response_file = false; 481 std::vector<char> response_file; 482 std::vector<const char*> response_file_args; 483 uint16_t elf_machine = EM_NONE; 484 // Scan argv in order to prepare the following: 485 // - get the output file. That's the file we may need to adjust. 486 // - get the --real-linker if one was passed. 487 // - detect whether we're linking a shared library or something else. As of 488 // now, only shared libraries are handled. Technically speaking, programs 489 // could be handled as well, but for the purpose of Firefox, that actually 490 // doesn't work because programs contain a memory allocator that ends up 491 // being called before the injected code has any chance to apply relocations, 492 // and the allocator itself needs the relocations to have been applied. 493 // - detect the position of crti.o so that we can inject our own object 494 // right after it, and also to detect the machine type to pick the right 495 // object to inject. 496 // 497 // At the same time, we also construct a new list of arguments, with 498 // --real-linker filtered out. We'll later inject arguments in that list. 499 if (argc == 2 && argv[1] && argv[1][0] == '@') { 500 // When GCC is given a response file, it wraps all arguments into a 501 // new response file with all arguments, even if originally there were 502 // arguments and a response file. 503 // In that case, we can't scan for arguments, so we need to read the 504 // response file. And as we change the arguments, we'll need to write 505 // a new one. 506 std::ifstream f{argv[1] + 1, f.binary | f.ate}; 507 if (!f) { 508 std::cerr << "Failed to read " << argv[1] + 1 << std::endl; 509 return 1; 510 } 511 size_t len = f.tellg(); 512 response_file = read_vector_at<char>(f, 0, len); 513 std::replace(response_file.begin(), response_file.end(), '\n', '\0'); 514 if (len && response_file[len - 1] != '\0') { 515 response_file.push_back('\0'); 516 } 517 response_file_args.push_back(argv[0]); 518 for (const char* a = response_file.data(); 519 a < response_file.data() + response_file.size(); a += strlen(a) + 1) { 520 response_file_args.push_back(a); 521 } 522 argv = const_cast<char**>(response_file_args.data()); 523 argc = response_file_args.size(); 524 use_response_file = true; 525 } 526 for (i = 1, argv++; i < argc && *argv; argv++, i++) { 527 std::string_view arg{*argv}; 528 if (arg == "-shared") { 529 shared = true; 530 } else if (arg == "-o") { 531 args.push_back(*(argv++)); 532 ++i; 533 output = *argv; 534 } else if (arg == "--real-linker") { 535 ++i; 536 real_linker = *(++argv); 537 continue; 538 } else if (elf_machine == EM_NONE) { 539 auto filename = fs::path(arg).filename(); 540 if (filename == "crti.o" || filename == "crtbegin_so.o") { 541 is_android = (filename == "crtbegin_so.o"); 542 crti = i; 543 std::fstream f{std::string(arg), f.binary | f.in}; 544 f.exceptions(f.failbit); 545 elf_machine = get_elf_machine(f); 546 } 547 } 548 args.push_back(*argv); 549 } 550 551 if (!output) { 552 std::cerr << "Could not determine output file." << std::endl; 553 return 1; 554 } 555 556 if (!crti) { 557 std::cerr << "Could not find CRT object on the command line." << std::endl; 558 return 1; 559 } 560 561 if (!real_linker || !real_linker->has_parent_path()) { 562 auto linker = next_program(this_program, real_linker); 563 if (!linker) { 564 std::cerr << "Could not find next " 565 << (real_linker ? real_linker->filename() 566 : this_program.filename()) 567 << std::endl; 568 return 1; 569 } 570 real_linker = linker; 571 } 572 args.insert(args.begin(), real_linker->c_str()); 573 args.push_back(nullptr); 574 575 std::string stem; 576 switch (elf_machine) { 577 case EM_NONE: 578 std::cerr << "Could not determine target machine type." << std::endl; 579 return 1; 580 case EM_386: 581 stem = "x86"; 582 break; 583 case EM_X86_64: 584 stem = "x86_64"; 585 break; 586 case EM_ARM: 587 stem = "arm"; 588 break; 589 case EM_AARCH64: 590 stem = "aarch64"; 591 break; 592 default: 593 std::cerr << "Unsupported target machine type." << std::endl; 594 return 1; 595 } 596 if (is_android) { 597 stem += "-android"; 598 } 599 600 if (shared) { 601 std::vector<const char*> hacked_args(args); 602 auto inject = this_program.parent_path() / "inject" / (stem + ".o"); 603 hacked_args.insert(hacked_args.begin() + crti + 1, inject.c_str()); 604 hacked_args.insert(hacked_args.end() - 1, {"-z", "pack-relative-relocs", 605 "-init=_relrhack_wrap_init"}); 606 int status = run_command(hacked_args, use_response_file); 607 if (status) { 608 return status; 609 } 610 bool hacked = false; 611 try { 612 std::fstream f{*output, f.binary | f.in | f.out}; 613 f.exceptions(f.failbit); 614 auto elf_class = get_elf_class(f); 615 f.seekg(0, std::ios::beg); 616 // On Android, we don't set the relrhack bit so that the system linker 617 // can handle the RELR relocations when supported. On desktop, the 618 // glibc unfortunately rejects the binaries without the bit set. 619 // (see comment about GLIBC_ABI_DT_RELR) 620 if (elf_class == ELFCLASS32) { 621 hacked = RelR<32>::hack(f, /* set_relrhack_bit = */ !is_android); 622 } else if (elf_class == ELFCLASS64) { 623 hacked = RelR<64>::hack(f, /* set_relrhack_bit = */ !is_android); 624 } 625 } catch (const std::runtime_error& err) { 626 std::cerr << "Failed to hack " << output->string() << ": " << err.what() 627 << std::endl; 628 return 1; 629 } 630 if (hacked) { 631 return 0; 632 } 633 } 634 635 return run_command(args, use_response_file); 636 }