diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /tools/fix_elf/patch.cpp | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'tools/fix_elf/patch.cpp')
-rw-r--r-- | tools/fix_elf/patch.cpp | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/tools/fix_elf/patch.cpp b/tools/fix_elf/patch.cpp new file mode 100644 index 0000000000..d49ebab307 --- /dev/null +++ b/tools/fix_elf/patch.cpp @@ -0,0 +1,269 @@ +#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; +} |