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