blob: f9b8a93bdccbeec2caf78b04e01a5166ccb787b7 [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
danakj51d26a42024-04-25 14:23:565#ifdef UNSAFE_BUFFERS_BUILD
6// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7#pragma allow_unsafe_buffers
8#endif
9
Mike Wittmanf1ff2df2020-07-28 19:58:0710#include "base/debug/test_elf_image_builder.h"
11
12#include <cstring>
Helmut Januschkae1fe1aa2024-03-13 02:21:2813#include <string_view>
Mike Wittmanf1ff2df2020-07-28 19:58:0714#include <type_traits>
15#include <utility>
16
17#include "base/bits.h"
18#include "base/check.h"
Mike Wittman6a7b5172020-08-04 18:35:4419#include "base/notreached.h"
Mike Wittmanf1ff2df2020-07-28 19:58:0720#include "build/build_config.h"
21
22#if __SIZEOF_POINTER__ == 4
23using Dyn = Elf32_Dyn;
24using Nhdr = Elf32_Nhdr;
25using Shdr = Elf32_Shdr;
26#else
27using Dyn = Elf64_Dyn;
28using Nhdr = Elf64_Nhdr;
29using Shdr = Elf64_Shdr;
30#endif
31
32namespace base {
33
34namespace {
35// Sizes/alignments to use in the ELF image.
36static constexpr size_t kPageSize = 4096;
37static constexpr size_t kPhdrAlign = 0x4;
38static constexpr size_t kNoteAlign = 0x4;
39static constexpr size_t kLoadAlign = 0x1000;
40static constexpr size_t kDynamicAlign = 0x4;
41} // namespace
42
43struct TestElfImageBuilder::LoadSegment {
44 Word flags;
45 Word size;
46};
47
48TestElfImage::TestElfImage(std::vector<uint8_t> buffer, const void* elf_start)
49 : buffer_(std::move(buffer)), elf_start_(elf_start) {}
50
51TestElfImage::~TestElfImage() = default;
52
53TestElfImage::TestElfImage(TestElfImage&&) = default;
54
55TestElfImage& TestElfImage::operator=(TestElfImage&&) = default;
56
Mike Wittman6a7b5172020-08-04 18:35:4457TestElfImageBuilder::TestElfImageBuilder(MappingType mapping_type)
58 : mapping_type_(mapping_type) {}
Mike Wittmanf1ff2df2020-07-28 19:58:0759
60TestElfImageBuilder::~TestElfImageBuilder() = default;
61
62TestElfImageBuilder& TestElfImageBuilder::AddLoadSegment(Word flags,
63 size_t size) {
Peter Kastingd5543152021-06-21 14:26:4764 load_segments_.push_back({flags, static_cast<Word>(size)});
Mike Wittmanf1ff2df2020-07-28 19:58:0765 return *this;
66}
67
68TestElfImageBuilder& TestElfImageBuilder::AddNoteSegment(
69 Word type,
Helmut Januschkae1fe1aa2024-03-13 02:21:2870 std::string_view name,
Mike Wittmanf1ff2df2020-07-28 19:58:0771 span<const uint8_t> desc) {
72 const size_t name_with_null_size = name.size() + 1;
Peter Kasting6a4bf14c2022-07-13 14:53:3373 std::vector<uint8_t> buffer(
74 sizeof(Nhdr) + bits::AlignUp(name_with_null_size, size_t{4}) +
75 bits::AlignUp(desc.size(), size_t{4}),
76 '\0');
Mike Wittmanf1ff2df2020-07-28 19:58:0777 uint8_t* loc = &buffer.front();
David Benjamind3e2629c32023-10-23 22:24:2078 Nhdr nhdr;
79 nhdr.n_namesz = name_with_null_size;
80 nhdr.n_descsz = desc.size();
81 nhdr.n_type = type;
82 loc = AppendHdr(nhdr, loc);
Mike Wittmanf1ff2df2020-07-28 19:58:0783
84 memcpy(loc, name.data(), name.size());
85 *(loc + name.size()) = '\0';
Peter Kasting6a4bf14c2022-07-13 14:53:3386 loc += bits::AlignUp(name_with_null_size, size_t{4});
Mike Wittmanf1ff2df2020-07-28 19:58:0787
88 memcpy(loc, &desc.front(), desc.size());
Peter Kasting6a4bf14c2022-07-13 14:53:3389 loc += bits::AlignUp(desc.size(), size_t{4});
Mike Wittmanf1ff2df2020-07-28 19:58:0790
91 DCHECK_EQ(&buffer.front() + buffer.size(), loc);
92
93 note_contents_.push_back(std::move(buffer));
94
95 return *this;
96}
97
Helmut Januschkae1fe1aa2024-03-13 02:21:2898TestElfImageBuilder& TestElfImageBuilder::AddSoName(std::string_view soname) {
Mike Wittmanf1ff2df2020-07-28 19:58:0799 DCHECK(!soname_.has_value());
100 soname_.emplace(soname);
101 return *this;
102}
103
104struct TestElfImageBuilder::ImageMeasures {
105 size_t phdrs_required;
106 size_t note_start;
107 size_t note_size;
108 std::vector<size_t> load_segment_start;
109 size_t dynamic_start;
110 size_t strtab_start;
111 size_t total_size;
112};
113
Mike Wittman87363cc12020-08-04 19:10:15114Addr TestElfImageBuilder::GetVirtualAddressForOffset(
115 Off offset,
116 const uint8_t* elf_start) const {
Mike Wittman6a7b5172020-08-04 18:35:44117 switch (mapping_type_) {
118 case RELOCATABLE:
119 return static_cast<Addr>(offset);
120
121 case RELOCATABLE_WITH_BIAS:
122 return static_cast<Addr>(offset + kLoadBias);
Mike Wittman87363cc12020-08-04 19:10:15123
124 case NON_RELOCATABLE:
125 return reinterpret_cast<Addr>(elf_start + offset);
Mike Wittman6a7b5172020-08-04 18:35:44126 }
127}
128
Mike Wittmanf1ff2df2020-07-28 19:58:07129TestElfImageBuilder::ImageMeasures TestElfImageBuilder::MeasureSizesAndOffsets()
130 const {
131 ImageMeasures measures;
132
133 measures.phdrs_required = 1 + load_segments_.size();
Peter Kasting134ef9af2024-12-28 02:30:09134 if (!note_contents_.empty()) {
Mike Wittmanf1ff2df2020-07-28 19:58:07135 ++measures.phdrs_required;
Peter Kasting134ef9af2024-12-28 02:30:09136 }
137 if (soname_.has_value()) {
Mike Wittmanf1ff2df2020-07-28 19:58:07138 ++measures.phdrs_required;
Peter Kasting134ef9af2024-12-28 02:30:09139 }
Mike Wittmanf1ff2df2020-07-28 19:58:07140
141 // The current offset into the image, where the next bytes are to be written.
142 // Starts after the ELF header.
143 size_t offset = sizeof(Ehdr);
144
145 // Add space for the program header table.
Benoit Lize7b302c42021-02-04 11:20:38146 offset = bits::AlignUp(offset, kPhdrAlign);
Mike Wittmanf1ff2df2020-07-28 19:58:07147 offset += sizeof(Phdr) * measures.phdrs_required;
148
149 // Add space for the notes.
150 measures.note_start = offset;
Peter Kasting134ef9af2024-12-28 02:30:09151 if (!note_contents_.empty()) {
Benoit Lize7b302c42021-02-04 11:20:38152 offset = bits::AlignUp(offset, kNoteAlign);
Peter Kasting134ef9af2024-12-28 02:30:09153 }
154 for (const std::vector<uint8_t>& contents : note_contents_) {
Mike Wittmanf1ff2df2020-07-28 19:58:07155 offset += contents.size();
Peter Kasting134ef9af2024-12-28 02:30:09156 }
Mike Wittmanf1ff2df2020-07-28 19:58:07157 measures.note_size = offset - measures.note_start;
158
159 // Add space for the load segments.
160 for (auto it = load_segments_.begin(); it != load_segments_.end(); ++it) {
Mike Wittmanf1ff2df2020-07-28 19:58:07161 // The first non PT_PHDR program header is expected to be a PT_LOAD and
162 // start at the already-aligned start of the ELF header.
163 if (it == load_segments_.begin()) {
Mike Wittmanf1ff2df2020-07-28 19:58:07164 measures.load_segment_start.push_back(0);
165 } else {
Benoit Lize7b302c42021-02-04 11:20:38166 offset = bits::AlignUp(offset, kLoadAlign);
Mike Wittmanf1ff2df2020-07-28 19:58:07167 measures.load_segment_start.push_back(offset);
168 }
169 offset += it->size;
170 }
171
172 // Add space for the dynamic segment.
Benoit Lize7b302c42021-02-04 11:20:38173 measures.dynamic_start = bits::AlignUp(offset, kDynamicAlign);
Mike Wittmanf1ff2df2020-07-28 19:58:07174 offset += sizeof(Dyn) * (soname_ ? 2 : 1);
175 measures.strtab_start = offset;
176
177 // Add space for the string table.
178 ++offset; // The first string table byte holds a null character.
Peter Kasting134ef9af2024-12-28 02:30:09179 if (soname_) {
Mike Wittmanf1ff2df2020-07-28 19:58:07180 offset += soname_->size() + 1;
Peter Kasting134ef9af2024-12-28 02:30:09181 }
Mike Wittmanf1ff2df2020-07-28 19:58:07182
183 measures.total_size = offset;
184
185 return measures;
186}
187
188TestElfImage TestElfImageBuilder::Build() {
189 ImageMeasures measures = MeasureSizesAndOffsets();
190
Mike Wittman6a7b5172020-08-04 18:35:44191 // Write the ELF contents into |buffer|. Extends the buffer back to the 0
192 // address in the case of load bias, so that the memory between the 0 address
193 // and the image start is zero-initialized.
194 const size_t load_bias =
195 mapping_type_ == RELOCATABLE_WITH_BIAS ? kLoadBias : 0;
196 std::vector<uint8_t> buffer(load_bias + (kPageSize - 1) + measures.total_size,
197 '\0');
198 uint8_t* const elf_start =
Benoit Lize7b302c42021-02-04 11:20:38199 bits::AlignUp(&buffer.front() + load_bias, kPageSize);
Mike Wittmanf1ff2df2020-07-28 19:58:07200 uint8_t* loc = elf_start;
201
202 // Add the ELF header.
203 loc = AppendHdr(CreateEhdr(measures.phdrs_required), loc);
204
205 // Add the program header table.
Benoit Lize7b302c42021-02-04 11:20:38206 loc = bits::AlignUp(loc, kPhdrAlign);
Mike Wittman87363cc12020-08-04 19:10:15207 loc = AppendHdr(
208 CreatePhdr(PT_PHDR, PF_R, kPhdrAlign, loc - elf_start,
209 GetVirtualAddressForOffset(loc - elf_start, elf_start),
210 sizeof(Phdr) * measures.phdrs_required),
211 loc);
Mike Wittmanf1ff2df2020-07-28 19:58:07212 for (size_t i = 0; i < load_segments_.size(); ++i) {
213 const LoadSegment& load_segment = load_segments_[i];
214 size_t size = load_segment.size;
215 // The first non PT_PHDR program header is expected to be a PT_LOAD and
216 // encompass all the preceding headers.
Peter Kasting134ef9af2024-12-28 02:30:09217 if (i == 0) {
Mike Wittmanf1ff2df2020-07-28 19:58:07218 size += loc - elf_start;
Peter Kasting134ef9af2024-12-28 02:30:09219 }
Mike Wittman87363cc12020-08-04 19:10:15