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