blob: 309dd071ac96bd40aca9b28b349b789b1d896c66 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2018 The Chromium Authors
Mike Wittmanf1ff2df2020-07-28 19:58:072// 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"
Mike Wittman6a7b5172020-08-04 18:35:4413#include "base/notreached.h"
Mike Wittmanf1ff2df2020-07-28 19:58:0714#include "build/build_config.h"
15
16#if __SIZEOF_POINTER__ == 4
17using Dyn = Elf32_Dyn;
18using Nhdr = Elf32_Nhdr;
19using Shdr = Elf32_Shdr;
20#else
21using Dyn = Elf64_Dyn;
22using Nhdr = Elf64_Nhdr;
23using Shdr = Elf64_Shdr;
24#endif
25
26namespace base {
27
28namespace {
29// Sizes/alignments to use in the ELF image.
30static constexpr size_t kPageSize = 4096;
31static constexpr size_t kPhdrAlign = 0x4;
32static constexpr size_t kNoteAlign = 0x4;
33static constexpr size_t kLoadAlign = 0x1000;
34static constexpr size_t kDynamicAlign = 0x4;
35} // namespace
36
37struct TestElfImageBuilder::LoadSegment {
38 Word flags;
39 Word size;
40};
41
42TestElfImage::TestElfImage(std::vector<uint8_t> buffer, const void* elf_start)
43 : buffer_(std::move(buffer)), elf_start_(elf_start) {}
44
45TestElfImage::~TestElfImage() = default;
46
47TestElfImage::TestElfImage(TestElfImage&&) = default;
48
49TestElfImage& TestElfImage::operator=(TestElfImage&&) = default;
50
Mike Wittman6a7b5172020-08-04 18:35:4451TestElfImageBuilder::TestElfImageBuilder(MappingType mapping_type)
52 : mapping_type_(mapping_type) {}
Mike Wittmanf1ff2df2020-07-28 19:58:0753
54TestElfImageBuilder::~TestElfImageBuilder() = default;
55
56TestElfImageBuilder& TestElfImageBuilder::AddLoadSegment(Word flags,
57 size_t size) {
Peter Kastingd5543152021-06-21 14:26:4758 load_segments_.push_back({flags, static_cast<Word>(size)});
Mike Wittmanf1ff2df2020-07-28 19:58:0759 return *this;
60}
61
62TestElfImageBuilder& 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;
Peter Kasting6a4bf14c2022-07-13 14:53:3367 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');
Mike Wittmanf1ff2df2020-07-28 19:58:0771 uint8_t* loc = &buffer.front();
David Benjamind3e2629c32023-10-23 22:24:2072 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);
Mike Wittmanf1ff2df2020-07-28 19:58:0777
78 memcpy(loc, name.data(), name.size());
79 *(loc + name.size()) = '\0';
Peter Kasting6a4bf14c2022-07-13 14:53:3380 loc += bits::AlignUp(name_with_null_size, size_t{4});
Mike Wittmanf1ff2df2020-07-28 19:58:0781
82 memcpy(loc, &desc.front(), desc.size());
Peter Kasting6a4bf14c2022-07-13 14:53:3383 loc += bits::AlignUp(desc.size(), size_t{4});
Mike Wittmanf1ff2df2020-07-28 19:58:0784
85 DCHECK_EQ(&buffer.front() + buffer.size(), loc);
86
87 note_contents_.push_back(std::move(buffer));
88
89 return *this;
90}
91
92TestElfImageBuilder& TestElfImageBuilder::AddSoName(StringPiece soname) {
93 DCHECK(!soname_.has_value());
94 soname_.emplace(soname);
95 return *this;
96}
97
98struct 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
Mike Wittman87363cc12020-08-04 19:10:15108Addr TestElfImageBuilder::GetVirtualAddressForOffset(
109 Off offset,
110 const uint8_t* elf_start) const {
Mike Wittman6a7b5172020-08-04 18:35:44111 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);
Mike Wittman87363cc12020-08-04 19:10:15117
118 case NON_RELOCATABLE:
119 return reinterpret_cast<Addr>(elf_start + offset);
Mike Wittman6a7b5172020-08-04 18:35:44120 }
121}
122
Mike Wittmanf1ff2df2020-07-28 19:58:07123TestElfImageBuilder::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.
Benoit Lize7b302c42021-02-04 11:20:38138 offset = bits::AlignUp(offset, kPhdrAlign);
Mike Wittmanf1ff2df2020-07-28 19:58:07139 offset += sizeof(Phdr) * measures.phdrs_required;
140
141 // Add space for the notes.
142 measures.note_start = offset;
143 if (!note_contents_.empty())
Benoit Lize7b302c42021-02-04 11:20:38144 offset = bits::AlignUp(offset, kNoteAlign);
Mike Wittmanf1ff2df2020-07-28 19:58:07145 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) {
Mike Wittmanf1ff2df2020-07-28 19:58:07151 // 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()) {
Mike Wittmanf1ff2df2020-07-28 19:58:07154 measures.load_segment_start.push_back(0);
155 } else {
Benoit Lize7b302c42021-02-04 11:20:38156 offset = bits::AlignUp(offset, kLoadAlign);
Mike Wittmanf1ff2df2020-07-28 19:58:07157 measures.load_segment_start.push_back(offset);
158 }
159 offset += it->size;
160 }
161
162 // Add space for the dynamic segment.
Benoit Lize7b302c42021-02-04 11:20:38163 measures.dynamic_start = bits::AlignUp(offset, kDynamicAlign);
Mike Wittmanf1ff2df2020-07-28 19:58:07164 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
177TestElfImage TestElfImageBuilder::Build() {
178 ImageMeasures measures = MeasureSizesAndOffsets();
179
Mike Wittman6a7b5172020-08-04 18:35:44180 // 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 =
Benoit Lize7b302c42021-02-04 11:20:38188 bits::AlignUp(&buffer.front() + load_bias, kPageSize);
Mike Wittmanf1ff2df2020-07-28 19:58:07189 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.
Benoit Lize7b302c42021-02-04 11:20:38195 loc = bits::AlignUp(loc, kPhdrAlign);
Mike Wittman87363cc12020-08-04 19:10:15196 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);
Mike Wittmanf1ff2df2020-07-28 19:58:07201 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;
Mike Wittman87363cc12020-08-04 19:10:15208 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),
Mike Wittmanf1ff2df2020-07-28 19:58:07213 loc);
214 }
Mike Wittman87363cc12020-08-04 19:10:15215 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 }
Mike Wittmanf1ff2df2020-07-28 19:58:07222 if (soname_) {
Mike Wittman87363cc12020-08-04 19:10:15223 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);
Mike Wittmanf1ff2df2020-07-28 19:58:07229 }
230
231 // Add the notes.
Benoit Lize7b302c42021-02-04 11:20:38232 loc = bits::AlignUp(loc, kNoteAlign);
Mike Wittmanf1ff2df2020-07-28 19:58:07233 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())
Benoit Lize7b302c42021-02-04 11:20:38241 loc = bits::AlignUp(loc, kLoadAlign);
Mike Wittmanf1ff2df2020-07-28 19:58:07242 memset(loc, 0, it->size);
243 loc += it->size;
244 }
245
Benoit Lize7b302c42021-02-04 11:20:38246 loc = bits::AlignUp(loc, kDynamicAlign);
Mike Wittmanf1ff2df2020-07-28 19:58:07247
248 // Add the soname state.
249 if (soname_) {
250 // Add a DYNAMIC section for the soname.
David Benjamind3e2629c32023-10-23 22:24:20251 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);
Mike Wittmanf1ff2df2020-07-28 19:58:07255 }
256
David Benjamind3e2629c32023-10-23 22:24:20257 Dyn strtab_dyn;
258 strtab_dyn.d_tag = DT_STRTAB;
Xiaohan Wang131aa4d2022-01-15 19:39:41259#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_ANDROID)
Mike Wittman6a7b5172020-08-04 18:35:44260 // Fuchsia and Android do not alter the symtab pointer on ELF load -- it's
261 // expected to remain a 'virutal address'.
David Benjamind3e2629c32023-10-23 22:24:20262 strtab_dyn.d_un.d_ptr =
Mike Wittman87363cc12020-08-04 19:10:15263 GetVirtualAddressForOffset(measures.strtab_start, elf_start);
Mike Wittmanf1ff2df2020-07-28 19:58:07264#else
Mike Wittman6a7b5172020-08-04 18:35:44265 // 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.
David Benjamind3e2629c32023-10-23 22:24:20267 strtab_dyn.d_un.d_ptr =
Mike Wittmanf1ff2df2020-07-28 19:58:07268 reinterpret_cast<uintptr_t>(elf_start + measures.strtab_start);
269#endif
David Benjamind3e2629c32023-10-23 22:24:20270 loc = AppendHdr(strtab_dyn, loc);
Mike Wittmanf1ff2df2020-07-28 19:58:07271
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
288template <typename T>
289uint8_t* TestElfImageBuilder::AppendHdr(const T& hdr, uint8_t* loc) {
Andrew Rayskiyb42fe7522023-10-17 13:16:19290 static_assert(std::is_trivially_copyable_v<T>, "T should be a plain struct");
Mike Wittmanf1ff2df2020-07-28 19:58:07291 memcpy(loc, &hdr, sizeof(T));
292 return loc + sizeof(T);
293}
294
295Ehdr 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
324Phdr TestElfImageBuilder::CreatePhdr(Word type,
325 Word flags,
326 size_t align,
327 Off offset,
Mike Wittman87363cc12020-08-04 19:10:15328 Addr vaddr,
Mike Wittmanf1ff2df2020-07-28 19:58:07329 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;
Mike Wittman87363cc12020-08-04 19:10:15335 phdr.p_vaddr = vaddr;
Mike Wittmanf1ff2df2020-07-28 19:58:07336 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