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