test_elf_image_builder.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/debug/test_elf_image_builder.h"
  5. #include <cstring>
  6. #include <type_traits>
  7. #include <utility>
  8. #include "base/bits.h"
  9. #include "base/check.h"
  10. #include "base/notreached.h"
  11. #include "build/build_config.h"
  12. #if __SIZEOF_POINTER__ == 4
  13. using Dyn = Elf32_Dyn;
  14. using Nhdr = Elf32_Nhdr;
  15. using Shdr = Elf32_Shdr;
  16. #else
  17. using Dyn = Elf64_Dyn;
  18. using Nhdr = Elf64_Nhdr;
  19. using Shdr = Elf64_Shdr;
  20. #endif
  21. namespace base {
  22. namespace {
  23. // Sizes/alignments to use in the ELF image.
  24. static constexpr size_t kPageSize = 4096;
  25. static constexpr size_t kPhdrAlign = 0x4;
  26. static constexpr size_t kNoteAlign = 0x4;
  27. static constexpr size_t kLoadAlign = 0x1000;
  28. static constexpr size_t kDynamicAlign = 0x4;
  29. } // namespace
  30. struct TestElfImageBuilder::LoadSegment {
  31. Word flags;
  32. Word size;
  33. };
  34. TestElfImage::TestElfImage(std::vector<uint8_t> buffer, const void* elf_start)
  35. : buffer_(std::move(buffer)), elf_start_(elf_start) {}
  36. TestElfImage::~TestElfImage() = default;
  37. TestElfImage::TestElfImage(TestElfImage&&) = default;
  38. TestElfImage& TestElfImage::operator=(TestElfImage&&) = default;
  39. TestElfImageBuilder::TestElfImageBuilder(MappingType mapping_type)
  40. : mapping_type_(mapping_type) {}
  41. TestElfImageBuilder::~TestElfImageBuilder() = default;
  42. TestElfImageBuilder& TestElfImageBuilder::AddLoadSegment(Word flags,
  43. size_t size) {
  44. load_segments_.push_back({flags, static_cast<Word>(size)});
  45. return *this;
  46. }
  47. TestElfImageBuilder& TestElfImageBuilder::AddNoteSegment(
  48. Word type,
  49. StringPiece name,
  50. span<const uint8_t> desc) {
  51. const size_t name_with_null_size = name.size() + 1;
  52. std::vector<uint8_t> buffer(
  53. sizeof(Nhdr) + bits::AlignUp(name_with_null_size, size_t{4}) +
  54. bits::AlignUp(desc.size(), size_t{4}),
  55. '\0');
  56. uint8_t* loc = &buffer.front();
  57. Nhdr* nhdr = reinterpret_cast<Nhdr*>(loc);
  58. nhdr->n_namesz = name_with_null_size;
  59. nhdr->n_descsz = desc.size();
  60. nhdr->n_type = type;
  61. loc += sizeof(Nhdr);
  62. memcpy(loc, name.data(), name.size());
  63. *(loc + name.size()) = '\0';
  64. loc += bits::AlignUp(name_with_null_size, size_t{4});
  65. memcpy(loc, &desc.front(), desc.size());
  66. loc += bits::AlignUp(desc.size(), size_t{4});
  67. DCHECK_EQ(&buffer.front() + buffer.size(), loc);
  68. note_contents_.push_back(std::move(buffer));
  69. return *this;
  70. }
  71. TestElfImageBuilder& TestElfImageBuilder::AddSoName(StringPiece soname) {
  72. DCHECK(!soname_.has_value());
  73. soname_.emplace(soname);
  74. return *this;
  75. }
  76. struct TestElfImageBuilder::ImageMeasures {
  77. size_t phdrs_required;
  78. size_t note_start;
  79. size_t note_size;
  80. std::vector<size_t> load_segment_start;
  81. size_t dynamic_start;
  82. size_t strtab_start;
  83. size_t total_size;
  84. };
  85. Addr TestElfImageBuilder::GetVirtualAddressForOffset(
  86. Off offset,
  87. const uint8_t* elf_start) const {
  88. switch (mapping_type_) {
  89. case RELOCATABLE:
  90. return static_cast<Addr>(offset);
  91. case RELOCATABLE_WITH_BIAS:
  92. return static_cast<Addr>(offset + kLoadBias);
  93. case NON_RELOCATABLE:
  94. return reinterpret_cast<Addr>(elf_start + offset);
  95. }
  96. }
  97. TestElfImageBuilder::ImageMeasures TestElfImageBuilder::MeasureSizesAndOffsets()
  98. const {
  99. ImageMeasures measures;
  100. measures.phdrs_required = 1 + load_segments_.size();
  101. if (!note_contents_.empty())
  102. ++measures.phdrs_required;
  103. if (soname_.has_value())
  104. ++measures.phdrs_required;
  105. // The current offset into the image, where the next bytes are to be written.
  106. // Starts after the ELF header.
  107. size_t offset = sizeof(Ehdr);
  108. // Add space for the program header table.
  109. offset = bits::AlignUp(offset, kPhdrAlign);
  110. offset += sizeof(Phdr) * measures.phdrs_required;
  111. // Add space for the notes.
  112. measures.note_start = offset;
  113. if (!note_contents_.empty())
  114. offset = bits::AlignUp(offset, kNoteAlign);
  115. for (const std::vector<uint8_t>& contents : note_contents_)
  116. offset += contents.size();
  117. measures.note_size = offset - measures.note_start;
  118. // Add space for the load segments.
  119. for (auto it = load_segments_.begin(); it != load_segments_.end(); ++it) {
  120. // The first non PT_PHDR program header is expected to be a PT_LOAD and
  121. // start at the already-aligned start of the ELF header.
  122. if (it == load_segments_.begin()) {
  123. measures.load_segment_start.push_back(0);
  124. } else {
  125. offset = bits::AlignUp(offset, kLoadAlign);
  126. measures.load_segment_start.push_back(offset);
  127. }
  128. offset += it->size;
  129. }
  130. // Add space for the dynamic segment.
  131. measures.dynamic_start = bits::AlignUp(offset, kDynamicAlign);
  132. offset += sizeof(Dyn) * (soname_ ? 2 : 1);
  133. measures.strtab_start = offset;
  134. // Add space for the string table.
  135. ++offset; // The first string table byte holds a null character.
  136. if (soname_)
  137. offset += soname_->size() + 1;
  138. measures.total_size = offset;
  139. return measures;
  140. }
  141. TestElfImage TestElfImageBuilder::Build() {
  142. ImageMeasures measures = MeasureSizesAndOffsets();
  143. // Write the ELF contents into |buffer|. Extends the buffer back to the 0
  144. // address in the case of load bias, so that the memory between the 0 address
  145. // and the image start is zero-initialized.
  146. const size_t load_bias =
  147. mapping_type_ == RELOCATABLE_WITH_BIAS ? kLoadBias : 0;
  148. std::vector<uint8_t> buffer(load_bias + (kPageSize - 1) + measures.total_size,
  149. '\0');
  150. uint8_t* const elf_start =
  151. bits::AlignUp(&buffer.front() + load_bias, kPageSize);
  152. uint8_t* loc = elf_start;
  153. // Add the ELF header.
  154. loc = AppendHdr(CreateEhdr(measures.phdrs_required), loc);
  155. // Add the program header table.
  156. loc = bits::AlignUp(loc, kPhdrAlign);
  157. loc = AppendHdr(
  158. CreatePhdr(PT_PHDR, PF_R, kPhdrAlign, loc - elf_start,
  159. GetVirtualAddressForOffset(loc - elf_start, elf_start),
  160. sizeof(Phdr) * measures.phdrs_required),
  161. loc);
  162. for (size_t i = 0; i < load_segments_.size(); ++i) {
  163. const LoadSegment& load_segment = load_segments_[i];
  164. size_t size = load_segment.size;
  165. // The first non PT_PHDR program header is expected to be a PT_LOAD and
  166. // encompass all the preceding headers.
  167. if (i == 0)
  168. size += loc - elf_start;
  169. loc = AppendHdr(CreatePhdr(PT_LOAD, load_segment.flags, kLoadAlign,
  170. measures.load_segment_start[i],
  171. GetVirtualAddressForOffset(
  172. measures.load_segment_start[i], elf_start),
  173. size),
  174. loc);
  175. }
  176. if (measures.note_size != 0) {
  177. loc = AppendHdr(
  178. CreatePhdr(PT_NOTE, PF_R, kNoteAlign, measures.note_start,
  179. GetVirtualAddressForOffset(measures.note_start, elf_start),
  180. measures.note_size),
  181. loc);
  182. }
  183. if (soname_) {
  184. loc = AppendHdr(
  185. CreatePhdr(
  186. PT_DYNAMIC, PF_R | PF_W, kDynamicAlign, measures.dynamic_start,
  187. GetVirtualAddressForOffset(measures.dynamic_start, elf_start),
  188. sizeof(Dyn) * 2),
  189. loc);
  190. }
  191. // Add the notes.
  192. loc = bits::AlignUp(loc, kNoteAlign);
  193. for (const std::vector<uint8_t>& contents : note_contents_) {
  194. memcpy(loc, &contents.front(), contents.size());
  195. loc += contents.size();
  196. }
  197. // Add the load segments.
  198. for (auto it = load_segments_.begin(); it != load_segments_.end(); ++it) {
  199. if (it != load_segments_.begin())
  200. loc = bits::AlignUp(loc, kLoadAlign);
  201. memset(loc, 0, it->size);
  202. loc += it->size;
  203. }
  204. loc = bits::AlignUp(loc, kDynamicAlign);
  205. // Add the soname state.
  206. if (soname_) {
  207. // Add a DYNAMIC section for the soname.
  208. Dyn* soname_dyn = reinterpret_cast<Dyn*>(loc);
  209. soname_dyn->d_tag = DT_SONAME;
  210. soname_dyn->d_un.d_val = 1; // One char into the string table.
  211. loc += sizeof(Dyn);
  212. }
  213. Dyn* strtab_dyn = reinterpret_cast<Dyn*>(loc);
  214. strtab_dyn->d_tag = DT_STRTAB;
  215. #if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_ANDROID)
  216. // Fuchsia and Android do not alter the symtab pointer on ELF load -- it's
  217. // expected to remain a 'virutal address'.
  218. strtab_dyn->d_un.d_ptr =
  219. GetVirtualAddressForOffset(measures.strtab_start, elf_start);
  220. #else
  221. // Linux relocates this value on ELF load, so produce the pointer value after
  222. // relocation. That value will always be equal to the actual memory address.
  223. strtab_dyn->d_un.d_ptr =
  224. reinterpret_cast<uintptr_t>(elf_start + measures.strtab_start);
  225. #endif
  226. loc += sizeof(Dyn);
  227. // Add a string table with one entry for the soname, if necessary.
  228. *loc++ = '\0'; // The first byte holds a null character.
  229. if (soname_) {
  230. memcpy(loc, soname_->data(), soname_->size());
  231. *(loc + soname_->size()) = '\0';
  232. loc += soname_->size() + 1;
  233. }
  234. // The offset past the end of the contents should be consistent with the size
  235. // mmeasurement above.
  236. DCHECK_EQ(loc, elf_start + measures.total_size);
  237. return TestElfImage(std::move(buffer), elf_start);
  238. }
  239. // static
  240. template <typename T>
  241. uint8_t* TestElfImageBuilder::AppendHdr(const T& hdr, uint8_t* loc) {
  242. static_assert(std::is_trivially_copyable<T>::value,
  243. "T should be a plain struct");
  244. memcpy(loc, &hdr, sizeof(T));
  245. return loc + sizeof(T);
  246. }
  247. Ehdr TestElfImageBuilder::CreateEhdr(Half phnum) {
  248. Ehdr ehdr;
  249. ehdr.e_ident[EI_MAG0] = ELFMAG0;
  250. ehdr.e_ident[EI_MAG1] = ELFMAG1;
  251. ehdr.e_ident[EI_MAG2] = ELFMAG2;
  252. ehdr.e_ident[EI_MAG3] = ELFMAG3;
  253. ehdr.e_ident[EI_CLASS] = __SIZEOF_POINTER__ == 4 ? 1 : 2;
  254. ehdr.e_ident[EI_DATA] = 1; // Little endian.
  255. ehdr.e_ident[EI_VERSION] = 1;
  256. ehdr.e_ident[EI_OSABI] = 0x00;
  257. ehdr.e_ident[EI_ABIVERSION] = 0;
  258. ehdr.e_ident[EI_PAD] = 0;
  259. ehdr.e_type = ET_DYN;
  260. ehdr.e_machine = 0x28; // ARM.
  261. ehdr.e_version = 1;
  262. ehdr.e_entry = 0;
  263. ehdr.e_phoff = sizeof(Ehdr);
  264. ehdr.e_shoff = 0;
  265. ehdr.e_flags = 0;
  266. ehdr.e_ehsize = sizeof(Ehdr);
  267. ehdr.e_phentsize = sizeof(Phdr);
  268. ehdr.e_phnum = phnum;
  269. ehdr.e_shentsize = sizeof(Shdr);
  270. ehdr.e_shnum = 0;
  271. ehdr.e_shstrndx = 0;
  272. return ehdr;
  273. }
  274. Phdr TestElfImageBuilder::CreatePhdr(Word type,
  275. Word flags,
  276. size_t align,
  277. Off offset,
  278. Addr vaddr,
  279. size_t size) {
  280. Phdr phdr;
  281. phdr.p_type = type;
  282. phdr.p_flags = flags;
  283. phdr.p_offset = offset;
  284. phdr.p_filesz = size;
  285. phdr.p_vaddr = vaddr;
  286. phdr.p_paddr = 0;
  287. phdr.p_memsz = phdr.p_filesz;
  288. phdr.p_align = align;
  289. return phdr;
  290. }
  291. } // namespace base