123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- #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;
- }
|