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