[base] Add ElfReader tests for relocatable ELF files with load bias
Updates the ELF image builder to support building relocatable ELF images
with virtual address mapping subject to load bias. Parameterizes tests
based on mapping state. Fixes ReadElfLibraryName() for the load bias case.
Bug: 1105170
Change-Id: I74c37342e50636fb875c2e0a463a987d7312adc9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2305569
Reviewed-by: Wez <[email protected]>
Reviewed-by: Kevin Marshall <[email protected]>
Commit-Queue: Mike Wittman <[email protected]>
Cr-Commit-Position: refs/heads/master@{#794616}
diff --git a/base/debug/test_elf_image_builder.cc b/base/debug/test_elf_image_builder.cc
index 5c1d612..a12ed70e 100644
--- a/base/debug/test_elf_image_builder.cc
+++ b/base/debug/test_elf_image_builder.cc
@@ -10,6 +10,7 @@
#include "base/bits.h"
#include "base/check.h"
+#include "base/notreached.h"
#include "build/build_config.h"
#if __SIZEOF_POINTER__ == 4
@@ -47,7 +48,8 @@
TestElfImage& TestElfImage::operator=(TestElfImage&&) = default;
-TestElfImageBuilder::TestElfImageBuilder() = default;
+TestElfImageBuilder::TestElfImageBuilder(MappingType mapping_type)
+ : mapping_type_(mapping_type) {}
TestElfImageBuilder::~TestElfImageBuilder() = default;
@@ -103,6 +105,16 @@
size_t total_size;
};
+Addr TestElfImageBuilder::GetVirtualAddressForOffset(Off offset) const {
+ switch (mapping_type_) {
+ case RELOCATABLE:
+ return static_cast<Addr>(offset);
+
+ case RELOCATABLE_WITH_BIAS:
+ return static_cast<Addr>(offset + kLoadBias);
+ }
+}
+
TestElfImageBuilder::ImageMeasures TestElfImageBuilder::MeasureSizesAndOffsets()
const {
ImageMeasures measures;
@@ -163,9 +175,15 @@
TestElfImage TestElfImageBuilder::Build() {
ImageMeasures measures = MeasureSizesAndOffsets();
- // Write the ELF contents into |buffer|.
- std::vector<uint8_t> buffer(measures.total_size + kPageSize - 1, '\0');
- uint8_t* const elf_start = bits::Align(&buffer.front(), kPageSize);
+ // Write the ELF contents into |buffer|. Extends the buffer back to the 0
+ // address in the case of load bias, so that the memory between the 0 address
+ // and the image start is zero-initialized.
+ const size_t load_bias =
+ mapping_type_ == RELOCATABLE_WITH_BIAS ? kLoadBias : 0;
+ std::vector<uint8_t> buffer(load_bias + (kPageSize - 1) + measures.total_size,
+ '\0');
+ uint8_t* const elf_start =
+ bits::Align(&buffer.front() + load_bias, kPageSize);
uint8_t* loc = elf_start;
// Add the ELF header.
@@ -173,7 +191,8 @@
// Add the program header table.
loc = bits::Align(loc, kPhdrAlign);
- loc = AppendHdr(CreatePhdr(PT_PHDR, PF_R, kPhdrAlign, loc - elf_start,
+ loc = AppendHdr(CreatePhdr(PT_PHDR, PF_R, kPhdrAlign,
+ GetVirtualAddressForOffset(loc - elf_start),
sizeof(Phdr) * measures.phdrs_required),
loc);
for (size_t i = 0; i < load_segments_.size(); ++i) {
@@ -183,19 +202,24 @@
// encompass all the preceding headers.
if (i == 0)
size += loc - elf_start;
- loc = AppendHdr(CreatePhdr(PT_LOAD, load_segment.flags, kLoadAlign,
- measures.load_segment_start[i], size),
- loc);
+ loc = AppendHdr(
+ CreatePhdr(PT_LOAD, load_segment.flags, kLoadAlign,
+ GetVirtualAddressForOffset(measures.load_segment_start[i]),
+ size),
+ loc);
}
if (measures.note_size != 0) {
- loc = AppendHdr(CreatePhdr(PT_NOTE, PF_R, kNoteAlign, measures.note_start,
+ loc = AppendHdr(CreatePhdr(PT_NOTE, PF_R, kNoteAlign,
+ GetVirtualAddressForOffset(measures.note_start),
measures.note_size),
loc);
}
if (soname_) {
- loc = AppendHdr(CreatePhdr(PT_DYNAMIC, PF_R | PF_W, kDynamicAlign,
- measures.dynamic_start, sizeof(Dyn) * 2),
- loc);
+ loc =
+ AppendHdr(CreatePhdr(PT_DYNAMIC, PF_R | PF_W, kDynamicAlign,
+ GetVirtualAddressForOffset(measures.dynamic_start),
+ sizeof(Dyn) * 2),
+ loc);
}
// Add the notes.
@@ -227,9 +251,12 @@
Dyn* strtab_dyn = reinterpret_cast<Dyn*>(loc);
strtab_dyn->d_tag = DT_STRTAB;
#if defined(OS_FUCHSIA) || defined(OS_ANDROID)
- // Fuchsia and Android do not relocate the symtab pointer on ELF load.
- strtab_dyn->d_un.d_ptr = measures.strtab_start;
+ // Fuchsia and Android do not alter the symtab pointer on ELF load -- it's
+ // expected to remain a 'virutal address'.
+ strtab_dyn->d_un.d_ptr = GetVirtualAddressForOffset(measures.strtab_start);
#else
+ // Linux relocates this value on ELF load, so produce the pointer value after
+ // relocation. That value will always be equal to the actual memory address.
strtab_dyn->d_un.d_ptr =
reinterpret_cast<uintptr_t>(elf_start + measures.strtab_start);
#endif