patch.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. #include "patch.h"
  2. #include <library/cpp/getopt/last_getopt.h>
  3. #include <util/generic/algorithm.h>
  4. #include <util/generic/hash.h>
  5. #include <util/stream/null.h>
  6. #include <util/string/cast.h>
  7. #include <util/system/defaults.h>
  8. namespace NElf {
  9. bool IsElf(const TString& path) {
  10. TUnbufferedFileInput in(path);
  11. char buffer[EI_NIDENT];
  12. size_t nread = in.Load(buffer, sizeof(buffer));
  13. if (nread != sizeof(buffer) || TStringBuf(buffer, SELFMAG) != ELFMAG) {
  14. Cerr << "fix_elf skip " << path << " (not an ELF file)";
  15. return false;
  16. }
  17. if (buffer[EI_CLASS] != ELFCLASS64) {
  18. Cerr << "fix_elf skip " << path << " (ELF class is not ELF64)";
  19. return false;
  20. }
  21. #ifdef _little_endian_
  22. if (buffer[EI_DATA] != ELFDATA2LSB) {
  23. Cerr << "fix_elf skip " << path << " (ELF byte order is not native LSB)";
  24. return false;
  25. }
  26. #else
  27. if (buffer[EI_DATA] != ELFDATA2MSB) {
  28. Cerr << "fix_elf skip " << path << " (ELF byte order is not native MSB)";
  29. return false;
  30. }
  31. #endif
  32. if (buffer[EI_VERSION] != 1) {
  33. Cerr << "fix_elf skip " << path << " (ELF version is not 1)";
  34. return false;
  35. }
  36. return true;
  37. }
  38. } // namespace NElf
  39. using namespace NElf;
  40. void ReadNum(TStringBuf& src, TStringBuf& dst) {
  41. const char* c = src.data();
  42. while (isdigit(*c)) {
  43. ++c;
  44. }
  45. size_t len = c - src.data();
  46. dst = TStringBuf(src.data(), len);
  47. src.Skip(len);
  48. }
  49. int NumericStrCmp(TStringBuf s1, TStringBuf s2) {
  50. while (!s1.empty() || !s2.empty()) {
  51. char c1 = *s1.data();
  52. char c2 = *s2.data();
  53. if (isdigit(c1) && isdigit(c2)) {
  54. TStringBuf num1, num2;
  55. ReadNum(s1, num1);
  56. ReadNum(s2, num2);
  57. int c = FromString<int>(num1) - FromString<int>(num2);
  58. if (c) {
  59. return c;
  60. }
  61. } else {
  62. int c = int(c1) - int(c2);
  63. if (c) {
  64. return c;
  65. }
  66. }
  67. s1.Skip(1);
  68. s2.Skip(1);
  69. }
  70. return 0;
  71. }
  72. class TVernauxCmp {
  73. public:
  74. TVernauxCmp(TSection strSect)
  75. : StrSect(strSect)
  76. {
  77. }
  78. bool operator()(Elf64_Vernaux* v1, Elf64_Vernaux* v2) {
  79. TStringBuf s1 = StrSect.GetStr(v1->vna_name);
  80. TStringBuf s2 = StrSect.GetStr(v2->vna_name);
  81. return NumericStrCmp(s1, s2) < 0;
  82. }
  83. private:
  84. TSection StrSect;
  85. };
  86. void Patch(const TString& path, const TString& library, IOutputStream& verboseOut) {
  87. TElf elf(path);
  88. TVerneedSection verneedSect(&elf);
  89. if (verneedSect.IsNull()) {
  90. verboseOut << "No symbol versions section" << Endl;
  91. return;
  92. }
  93. TSection verStrings(&elf, elf.GetSection(verneedSect.GetLink()));
  94. TStringBuf skipFrom("GLIBC_2.14");
  95. TStringBuf patchFrom("GLIBC_2.2.5");
  96. TVector<Elf64_Vernaux*> patchAux;
  97. Elf64_Vernaux* patchFromAux = nullptr;
  98. Elf64_Verneed* verneed = verneedSect.GetFirstVerneed();
  99. while (verneed) {
  100. TStringBuf file = verStrings.GetStr(verneed->vn_file);
  101. verboseOut << file;
  102. if (file != library) {
  103. verboseOut << " skipped" << Endl;
  104. } else {
  105. verboseOut << Endl;
  106. Elf64_Vernaux* vernaux = verneedSect.GetFirstVernaux(verneed);
  107. while (vernaux) {
  108. TStringBuf name = verStrings.GetStr(vernaux->vna_name);
  109. verboseOut << "\t" << name;
  110. if (!patchFromAux && name == patchFrom) {
  111. verboseOut << " taken as patch source" << Endl;
  112. patchFromAux = vernaux;
  113. } else {
  114. if (NumericStrCmp(name, skipFrom) < 0) {
  115. verboseOut << " skipped" << Endl;
  116. } else {
  117. verboseOut << " will be patched" << Endl;
  118. patchAux.push_back(vernaux);
  119. }
  120. }
  121. vernaux = verneedSect.GetNextVernaux(vernaux);
  122. }
  123. }
  124. verneed = verneedSect.GetNextVerneed(verneed);
  125. }
  126. if (patchAux.empty()) {
  127. verboseOut << "Nothing to patch" << Endl;
  128. return;
  129. }
  130. if (!patchFromAux) {
  131. ythrow yexception() << path << ": no ELF64_Vernaux source to patch from";
  132. }
  133. TSection dynsymSect(&elf, elf.GetSectionByType(SHT_DYNSYM));
  134. TSection symstrSect(&elf, elf.GetSection(dynsymSect.GetLink()));
  135. TSection dynverSect(&elf, elf.GetSectionByType(SHT_GNU_versym));
  136. for (size_t i = 0, c = dynsymSect.GetEntryCount(); i < c; ++i) {
  137. Elf64_Sym* sym = dynsymSect.GetEntry<Elf64_Sym>(i);
  138. Elf64_Half* ver = dynverSect.GetEntry<Elf64_Half>(i);
  139. for (auto aux : patchAux) {
  140. if (*ver == aux->vna_other) {
  141. *ver = 0;
  142. verboseOut << "Symbol " << i << ": " << symstrSect.GetStr(sym->st_name)
  143. << "@" << verStrings.GetStr(aux->vna_name) << " version removed" << Endl;
  144. }
  145. }
  146. }
  147. for (auto aux : patchAux) {
  148. TStringBuf name = verStrings.GetStr(aux->vna_name);
  149. aux->vna_name = patchFromAux->vna_name;
  150. aux->vna_hash = patchFromAux->vna_hash;
  151. verboseOut << "Version dependence " << name << " [" << aux->vna_other
  152. << "] patched from " << patchFrom << " [" << patchFromAux->vna_other << "]" << Endl;
  153. }
  154. }
  155. void PatchGnuUnique(const TString& path, IOutputStream& verboseOut) {
  156. TElf elf(path);
  157. for (Elf64_Shdr* it = elf.GetSectionBegin(), *end = elf.GetSectionEnd(); it != end; ++it) {
  158. if (it->sh_type == SHT_SYMTAB) {
  159. TSection section{&elf, it};
  160. verboseOut << "Found symbol section [" << section.GetName() << ']' << Endl;
  161. for (size_t i = 0, count = section.GetEntryCount(); i < count; ++i) {
  162. Elf64_Sym* symbol = section.GetEntry<Elf64_Sym>(i);
  163. auto& info = symbol->st_info;
  164. if (ELF64_ST_BIND(info) == STB_GNU_UNIQUE) {
  165. verboseOut << "Found GNU unique symbol #" << i << Endl;
  166. info = ELF64_ST_INFO(STB_GLOBAL, ELF64_ST_TYPE(info));
  167. }
  168. }
  169. }
  170. }
  171. }
  172. int main(int argc, char* argv[]) {
  173. bool verbose = false;
  174. bool rewrite_unique = false;
  175. using namespace NLastGetopt;
  176. TOpts opts = NLastGetopt::TOpts::Default();
  177. opts.AddHelpOption();
  178. opts.AddLongOption('v', "verbose").NoArgument().StoreValue(&verbose, true);
  179. opts.AddLongOption('u', "rewrite-gnu-unique", "Change STB_GNU_UNIQUE to STB_GLOBAL").NoArgument().StoreValue(&rewrite_unique, true);
  180. opts.SetFreeArgsMin(1);
  181. opts.SetFreeArgTitle(0, "<file>", "File");
  182. TOptsParseResult res(&opts, argc, argv);
  183. TVector<TString> files = res.GetFreeArgs();
  184. IOutputStream& verboseOut = verbose ? Cout : Cnull;
  185. bool first = true;
  186. for (auto path : files) {
  187. if (!IsElf(path)) {
  188. continue;
  189. }
  190. if (!first) {
  191. verboseOut << Endl;
  192. }
  193. first = false;
  194. verboseOut << "Patching " << path << Endl;
  195. try {
  196. if (rewrite_unique) {
  197. PatchGnuUnique(path, verboseOut);
  198. } else {
  199. Patch(path, "libc.so.6", verboseOut);
  200. Patch(path, "libm.so.6", verboseOut);
  201. }
  202. } catch (const yexception& e) {
  203. Cerr << "Patching failed: " << e.what() << Endl;
  204. }
  205. }
  206. return 0;
  207. }