--- /dev/null
+#include <link.h>
+#include <sys/mman.h>
+#include "injector.h"
+#include "internal_libc.h"
+
+
+#if __WORDSIZE == 64
+# define ELF_R_SYM(i) ELF64_R_SYM(i)
+#elif __WORDSIZE == 32
+# define ELF_R_SYM(i) ELF32_R_SYM(i)
+#else
+# error "Unsupported word size"
+#endif
+
+
+namespace __swap {
+namespace injector {
+
+namespace elf {
+using Addr = ElfW(Addr);
+using Dyn = ElfW(Dyn);
+using Rel = ElfW(Rel);
+using Rela = ElfW(Rela);
+using Sym = ElfW(Sym);
+using Sxword = ElfW(Sxword);
+using Xword = ElfW(Xword);
+
+// TODO: Add prelinked libraries support
+#if defined(__arm__) || defined(__i386__)
+// ARM and x86 never uses Elf32_Rela relocations for the dynamic linker.
+// Prelinked libraries may use Elf32_Rela though.
+using PltRel = Rel;
+#else
+using PltRel = Rela;
+#endif
+
+
+template <typename T, Sxword TAG_ADDR, Sxword TAG_SIZE>
+struct Table
+{
+ using Type = T;
+ T *table = nullptr;
+ Xword size = {};
+
+ bool consume(const Dyn *dyn) noexcept
+ {
+ if (dyn->d_tag == TAG_ADDR) {
+ table = reinterpret_cast<T*>(dyn->d_un.d_ptr);
+ return true;
+ } else if (dyn->d_tag == TAG_SIZE) {
+ size = dyn->d_un.d_val;
+ return true;
+ }
+ return false;
+ }
+};
+
+
+using RelTable = Table<Rel, DT_REL, DT_RELSZ>;
+using RelaTable = Table<Rela, DT_RELA, DT_RELASZ>;
+using JmprelTable = Table<PltRel, DT_JMPREL, DT_PLTRELSZ>;
+using SymbolTable = Table<Sym, DT_SYMTAB, DT_SYMENT>;
+using StringTable = Table<const char, DT_STRTAB, DT_STRSZ>;
+
+} // namespace elf
+
+
+static void rewrite_addr(unsigned long *ptr, unsigned long value) noexcept
+{
+ if (*ptr == value)
+ return;
+
+ auto addr = reinterpret_cast<unsigned long>(ptr);
+
+ // TODO: Replace it.
+ // To check protect status, read it from "/proc/self/maps"
+
+ // Try to make the page read/write accessible, which is hackish.
+ // Required for some shared libraries.
+ auto page = reinterpret_cast<void *>(addr & ~(0x1000 - 1));
+ internal::mprotect(page, 0x1000, PROT_READ | PROT_WRITE);
+ *ptr = value;
+}
+
+
+struct injection_data {
+ struct injection_info *info;
+ unsigned long size;
+};
+
+template <typename Table>
+static void rewrite_elftable(const Table &jumps,
+ const elf::StringTable &strings,
+ const elf::SymbolTable &symbols,
+ const elf::Addr base,
+ const injection_data *data) noexcept
+{
+ const auto jumps_table_end = reinterpret_cast<const char *>(jumps.table) + jumps.size;
+ const auto rela_end = reinterpret_cast<const typename Table::Type *>(jumps_table_end);
+ for (auto rela = jumps.table; rela < rela_end; ++rela) {
+ const auto index = ELF_R_SYM(rela->r_info);
+ const char* symname = strings.table + symbols.table[index].st_name;
+ auto addr = rela->r_offset + base;
+
+ // optimization
+ if (symname[0] == '\0')
+ continue;
+
+ for (auto info = data->info, end = data->info + data->size; info != end; ++info) {
+ if (!internal::strcmp(symname, info->name)) {
+ auto *ptr = reinterpret_cast<unsigned long *>(addr);
+ rewrite_addr(ptr, info->addr);
+ }
+ }
+ }
+}
+
+static void rewrite_symbols(const elf::Dyn *dyn,
+ const elf::Addr base,
+ const injection_data *data) noexcept
+{
+ elf::SymbolTable symbols;
+ elf::RelTable rels;
+ elf::RelaTable relas;
+ elf::JmprelTable jmprels;
+ elf::StringTable strings;
+
+ // initialize the elf tables
+ for (; dyn->d_tag != DT_NULL; ++dyn) {
+ symbols.consume(dyn)
+ || strings.consume(dyn)
+ || rels.consume(dyn)
+ || relas.consume(dyn)
+ || jmprels.consume(dyn);
+ }
+
+ // find symbols to rewrite
+ rewrite_elftable(rels, strings, symbols, base, data);
+ rewrite_elftable(relas, strings, symbols, base, data);
+ rewrite_elftable(jmprels, strings, symbols, base, data);
+}
+
+static int phdr_callback(dl_phdr_info *info, size_t /*size*/, void *data) noexcept
+{
+ auto *inject_data = reinterpret_cast<const injection_data *>(data);
+
+ for (auto phdr = info->dlpi_phdr, phdr_end = phdr + info->dlpi_phnum; phdr != phdr_end; ++phdr) {
+ if (phdr->p_type == PT_DYNAMIC) {
+ auto *dyn = reinterpret_cast<const elf::Dyn *>(phdr->p_vaddr + info->dlpi_addr);
+ rewrite_symbols(dyn, info->dlpi_addr, inject_data);
+ }
+ }
+
+ return 0;
+}
+
+void inject(injection_info info[], unsigned long size) noexcept
+{
+ const injection_data inject_data = {
+ .info = info,
+ .size = size,
+ };
+
+ dl_iterate_phdr(phdr_callback, const_cast<injection_data *>(&inject_data));
+}
+
+} // namespace injector
+} // namespace __swap