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