text: #include "patch.h"
  
  #include <library/cpp/getopt/last_getopt.h>
  
  #include <util/generic/algorithm.h>
  #include <util/generic/hash.h>
  #include <util/stream/null.h>
  #include <util/string/cast.h>
  #include <util/system/defaults.h>
  
  namespace NElf {
  
  bool IsElf(const TString& path) {
      TUnbufferedFileInput in(path);
      char buffer[EI_NIDENT];
      size_t nread = in.Load(buffer, sizeof(buffer));
  
      if (nread != sizeof(buffer) || TStringBuf(buffer, SELFMAG) != ELFMAG) {
          Cerr << "fix_elf skip " << path << " (not an ELF file)";
          return false;
      }
  
      if (buffer[EI_CLASS] != ELFCLASS64) {
          Cerr << "fix_elf skip " << path << " (ELF class is not ELF64)";
          return false;
      }
  
  #ifdef _little_endian_
      if (buffer[EI_DATA] != ELFDATA2LSB) {
          Cerr << "fix_elf skip " << path << " (ELF byte order is not native LSB)";
          return false;
      }
  #else
      if (buffer[EI_DATA] != ELFDATA2MSB) {
          Cerr << "fix_elf skip " << path << " (ELF byte order is not native MSB)";
          return false;
      }
  #endif
  
      if (buffer[EI_VERSION] != 1) {
          Cerr << "fix_elf skip " << path << " (ELF version is not 1)";
          return false;
      }
  
      return true;
  }
  
  } // namespace NElf
  
  using namespace NElf;
  
  void ReadNum(TStringBuf& src, TStringBuf& dst) {
      const char* c = src.data();
      while (isdigit(*c)) {
          ++c;
      }
      size_t len = c - src.data();
  
      dst = TStringBuf(src.data(), len);
      src.Skip(len);
  }
  
  int NumericStrCmp(TStringBuf s1, TStringBuf s2) {
      while (!s1.empty() || !s2.empty()) {
          char c1 = *s1.data();
          char c2 = *s2.data();
  
          if (isdigit(c1) && isdigit(c2)) {
              TStringBuf num1, num2;
              ReadNum(s1, num1);
              ReadNum(s2, num2);
  
              int c = FromString<int>(num1) - FromString<int>(num2);
              if (c) {
                  return c;
              }
  
          } else {
              int c = int(c1) - int(c2);
              if (c) {
                  return c;
              }
          }
  
          s1.Skip(1);
          s2.Skip(1);
      }
  
      return 0;
  }
  
  class TVernauxCmp {
  public:
      TVernauxCmp(TSection strSect)
          : StrSect(strSect)
      {
      }
  
      bool operator()(Elf64_Vernaux* v1, Elf64_Vernaux* v2) {
          TStringBuf s1 = StrSect.GetStr(v1->vna_name);
          TStringBuf s2 = StrSect.GetStr(v2->vna_name);
  
          return NumericStrCmp(s1, s2) < 0;
      }
  
  private:
      TSection StrSect;
  };
  
  void Patch(const TString& path, const TString& library, IOutputStream& verboseOut) {
      TElf elf(path);
  
      TVerneedSection verneedSect(&elf);
      if (verneedSect.IsNull()) {
          verboseOut << "No symbol versions section" << Endl;
          return;
      }
  
      TSection verStrings(&elf, elf.GetSection(verneedSect.GetLink()));
  
      TStringBuf skipFrom("GLIBC_2.14");
      TStringBuf patchFrom("GLIBC_2.2.5");
  
      TVector<Elf64_Vernaux*> patchAux;
  
      Elf64_Vernaux* patchFromAux = nullptr;
  
      Elf64_Verneed* verneed = verneedSect.GetFirstVerneed();
      while (verneed) {
  
          TStringBuf file = verStrings.GetStr(verneed->vn_file);
          verboseOut << file;
  
          if (file != library) {
              verboseOut << " skipped" << Endl;
  
          } else {
              verboseOut << Endl;
  
              Elf64_Vernaux* vernaux = verneedSect.GetFirstVernaux(verneed);
              while (vernaux) {
  
                  TStringBuf name = verStrings.GetStr(vernaux->vna_name);
                  verboseOut << "\t" << name;
  
                  if (!patchFromAux && name == patchFrom) {
                      verboseOut << " taken as patch source" << Endl;
                      patchFromAux = vernaux;
  
                  } else {
  
                      if (NumericStrCmp(name, skipFrom) < 0) {
                          verboseOut << " skipped" << Endl;
  
                      } else {
                          verboseOut << " will be patched" << Endl;
                          patchAux.push_back(vernaux);
                      }
                  }
                  vernaux = verneedSect.GetNextVernaux(vernaux);
              }
          }
          verneed = verneedSect.GetNextVerneed(verneed);
      }
  
      if (patchAux.empty()) {
          verboseOut << "Nothing to patch" << Endl;
          return;
      }
  
      if (!patchFromAux) {
          ythrow yexception() << path << ": no ELF64_Vernaux source to patch from";
      }
  
      TSection dynsymSect(&elf, elf.GetSectionByType(SHT_DYNSYM));
      TSection symstrSect(&elf, elf.GetSection(dynsymSect.GetLink()));
      TSection dynverSect(&elf, elf.GetSectionByType(SHT_GNU_versym));
  
      for (size_t i = 0, c = dynsymSect.GetEntryCount(); i < c; ++i) {
          Elf64_Sym* sym = dynsymSect.GetEntry<Elf64_Sym>(i);
          Elf64_Half* ver = dynverSect.GetEntry<Elf64_Half>(i);
          for (auto aux : patchAux) {
              if (*ver == aux->vna_other) {
                  *ver = 0;
                  verboseOut << "Symbol " << i << ": " << symstrSect.GetStr(sym->st_name)
                      << "@" << verStrings.GetStr(aux->vna_name) << " version removed" << Endl;
              }
          }
      }
  
      for (auto aux : patchAux) {
          TStringBuf name = verStrings.GetStr(aux->vna_name);
          aux->vna_name = patchFromAux->vna_name;
          aux->vna_hash = patchFromAux->vna_hash;
          verboseOut << "Version dependence " << name << " [" << aux->vna_other
              << "] patched from " << patchFrom << " [" << patchFromAux->vna_other << "]" << Endl;
      }
  }
  
  void PatchGnuUnique(const TString& path, IOutputStream& verboseOut) {
      TElf elf(path);
  
      for (Elf64_Shdr* it = elf.GetSectionBegin(), *end = elf.GetSectionEnd(); it != end; ++it) {
          if (it->sh_type == SHT_SYMTAB) {
  
              TSection section{&elf, it};
              verboseOut << "Found symbol section [" << section.GetName() << ']' << Endl;
  
              for (size_t i = 0, count = section.GetEntryCount(); i < count; ++i) {
                  Elf64_Sym* symbol = section.GetEntry<Elf64_Sym>(i);
                  auto& info = symbol->st_info;
  
                  if (ELF64_ST_BIND(info) == STB_GNU_UNIQUE) {
                      verboseOut << "Found GNU unique symbol #" << i << Endl;
                      info = ELF64_ST_INFO(STB_GLOBAL, ELF64_ST_TYPE(info));
                  }
              }
          }
      }
  }
  
  int main(int argc, char* argv[]) {
      bool verbose = false;
      bool rewrite_unique = false;
  
      using namespace NLastGetopt;
  
      TOpts opts = NLastGetopt::TOpts::Default();
      opts.AddHelpOption();
  
      opts.AddLongOption('v', "verbose").NoArgument().StoreValue(&verbose, true);
      opts.AddLongOption('u', "rewrite-gnu-unique", "Change STB_GNU_UNIQUE to STB_GLOBAL").NoArgument().StoreValue(&rewrite_unique, true);
  
      opts.SetFreeArgsMin(1);
      opts.SetFreeArgTitle(0, "<file>", "File");
  
      TOptsParseResult res(&opts, argc, argv);
      TVector<TString> files = res.GetFreeArgs();
  
      IOutputStream& verboseOut = verbose ? Cout : Cnull;
  
      bool first = true;
      for (auto path : files) {
  
          if (!IsElf(path)) {
              continue;
          }
  
          if (!first) {
              verboseOut << Endl;
          }
          first = false;
  
          verboseOut << "Patching " << path << Endl;
  
          try {
              if (rewrite_unique) {
                  PatchGnuUnique(path, verboseOut);
              } else {
                  Patch(path, "libc.so.6", verboseOut);
                  Patch(path, "libm.so.6", verboseOut);
              }
          } catch (const yexception& e) {
              Cerr << "Patching failed: " << e.what() << Endl;
          }
      }
  
      return 0;
  }
  