in runtime/elf_file.cc [1073:1315]
bool ElfFileImpl<ElfTypes>::Load(File* file,
bool executable,
bool low_4gb,
std::string* error_msg) {
CHECK(program_header_only_) << file->GetPath();
if (executable) {
InstructionSet elf_ISA = GetInstructionSetFromELF(GetHeader().e_machine, GetHeader().e_flags);
if (elf_ISA != kRuntimeISA) {
std::ostringstream oss;
oss << "Expected ISA " << kRuntimeISA << " but found " << elf_ISA;
*error_msg = oss.str();
return false;
}
}
bool reserved = false;
for (Elf_Word i = 0; i < GetProgramHeaderNum(); i++) {
Elf_Phdr* program_header = GetProgramHeader(i);
if (program_header == nullptr) {
*error_msg = StringPrintf("No program header for entry %d in ELF file %s.",
i, file->GetPath().c_str());
return false;
}
// Record .dynamic header information for later use
if (program_header->p_type == PT_DYNAMIC) {
dynamic_program_header_ = program_header;
continue;
}
// Not something to load, move on.
if (program_header->p_type != PT_LOAD) {
continue;
}
// Found something to load.
// Before load the actual segments, reserve a contiguous chunk
// of required size and address for all segments, but with no
// permissions. We'll then carve that up with the proper
// permissions as we load the actual segments. If p_vaddr is
// non-zero, the segments require the specific address specified,
// which either was specified in the file because we already set
// base_address_ after the first zero segment).
int64_t temp_file_length = file->GetLength();
if (temp_file_length < 0) {
errno = -temp_file_length;
*error_msg = StringPrintf("Failed to get length of file: '%s' fd=%d: %s",
file->GetPath().c_str(), file->Fd(), strerror(errno));
return false;
}
size_t file_length = static_cast<size_t>(temp_file_length);
if (!reserved) {
uint8_t* reserve_base = reinterpret_cast<uint8_t*>(program_header->p_vaddr);
uint8_t* reserve_base_override = reserve_base;
// Override the base (e.g. when compiling with --compile-pic)
if (requested_base_ != nullptr) {
reserve_base_override = requested_base_;
}
std::string reservation_name("ElfFile reservation for ");
reservation_name += file->GetPath();
size_t loaded_size;
if (!GetLoadedSize(&loaded_size, error_msg)) {
DCHECK(!error_msg->empty());
return false;
}
std::unique_ptr<MemMap> reserve(MemMap::MapAnonymous(reservation_name.c_str(),
reserve_base_override,
loaded_size,
PROT_NONE,
low_4gb,
false,
error_msg));
if (reserve.get() == nullptr) {
*error_msg = StringPrintf("Failed to allocate %s: %s",
reservation_name.c_str(), error_msg->c_str());
return false;
}
reserved = true;
// Base address is the difference of actual mapped location and the p_vaddr
base_address_ = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(reserve->Begin())
- reinterpret_cast<uintptr_t>(reserve_base));
// By adding the p_vaddr of a section/symbol to base_address_ we will always get the
// dynamic memory address of where that object is actually mapped
//
// TODO: base_address_ needs to be calculated in ::Open, otherwise
// FindDynamicSymbolAddress returns the wrong values until Load is called.
segments_.push_back(reserve.release());
}
// empty segment, nothing to map
if (program_header->p_memsz == 0) {
continue;
}
uint8_t* p_vaddr = base_address_ + program_header->p_vaddr;
int prot = 0;
if (executable && ((program_header->p_flags & PF_X) != 0)) {
prot |= PROT_EXEC;
}
if ((program_header->p_flags & PF_W) != 0) {
prot |= PROT_WRITE;
}
if ((program_header->p_flags & PF_R) != 0) {
prot |= PROT_READ;
}
int flags = 0;
if (writable_) {
prot |= PROT_WRITE;
flags |= MAP_SHARED;
} else {
flags |= MAP_PRIVATE;
}
if (program_header->p_filesz > program_header->p_memsz) {
*error_msg = StringPrintf("Invalid p_filesz > p_memsz (%" PRIu64 " > %" PRIu64 "): %s",
static_cast<uint64_t>(program_header->p_filesz),
static_cast<uint64_t>(program_header->p_memsz),
file->GetPath().c_str());
return false;
}
if (program_header->p_filesz < program_header->p_memsz &&
!IsAligned<kPageSize>(program_header->p_filesz)) {
*error_msg = StringPrintf("Unsupported unaligned p_filesz < p_memsz (%" PRIu64
" < %" PRIu64 "): %s",
static_cast<uint64_t>(program_header->p_filesz),
static_cast<uint64_t>(program_header->p_memsz),
file->GetPath().c_str());
return false;
}
if (file_length < (program_header->p_offset + program_header->p_filesz)) {
*error_msg = StringPrintf("File size of %zd bytes not large enough to contain ELF segment "
"%d of %" PRIu64 " bytes: '%s'", file_length, i,
static_cast<uint64_t>(program_header->p_offset + program_header->p_filesz),
file->GetPath().c_str());
return false;
}
if (program_header->p_filesz != 0u) {
std::unique_ptr<MemMap> segment(
MemMap::MapFileAtAddress(p_vaddr,
program_header->p_filesz,
prot,
flags,
file->Fd(),
program_header->p_offset,
/*low4_gb*/false,
/*reuse*/true, // implies MAP_FIXED
file->GetPath().c_str(),
error_msg));
if (segment.get() == nullptr) {
*error_msg = StringPrintf("Failed to map ELF file segment %d from %s: %s",
i, file->GetPath().c_str(), error_msg->c_str());
return false;
}
if (segment->Begin() != p_vaddr) {
*error_msg = StringPrintf("Failed to map ELF file segment %d from %s at expected address %p, "
"instead mapped to %p",
i, file->GetPath().c_str(), p_vaddr, segment->Begin());
return false;
}
segments_.push_back(segment.release());
}
if (program_header->p_filesz < program_header->p_memsz) {
std::string name = StringPrintf("Zero-initialized segment %" PRIu64 " of ELF file %s",
static_cast<uint64_t>(i), file->GetPath().c_str());
std::unique_ptr<MemMap> segment(
MemMap::MapAnonymous(name.c_str(),
p_vaddr + program_header->p_filesz,
program_header->p_memsz - program_header->p_filesz,
prot, false, true /* reuse */, error_msg));
if (segment == nullptr) {
*error_msg = StringPrintf("Failed to map zero-initialized ELF file segment %d from %s: %s",
i, file->GetPath().c_str(), error_msg->c_str());
return false;
}
if (segment->Begin() != p_vaddr) {
*error_msg = StringPrintf("Failed to map zero-initialized ELF file segment %d from %s "
"at expected address %p, instead mapped to %p",
i, file->GetPath().c_str(), p_vaddr, segment->Begin());
return false;
}
segments_.push_back(segment.release());
}
}
// Now that we are done loading, .dynamic should be in memory to find .dynstr, .dynsym, .hash
uint8_t* dsptr = base_address_ + GetDynamicProgramHeader().p_vaddr;
if ((dsptr < Begin() || dsptr >= End()) && !ValidPointer(dsptr)) {
*error_msg = StringPrintf("dynamic section address invalid in ELF file %s",
file->GetPath().c_str());
return false;
}
dynamic_section_start_ = reinterpret_cast<Elf_Dyn*>(dsptr);
for (Elf_Word i = 0; i < GetDynamicNum(); i++) {
Elf_Dyn& elf_dyn = GetDynamic(i);
uint8_t* d_ptr = base_address_ + elf_dyn.d_un.d_ptr;
switch (elf_dyn.d_tag) {
case DT_HASH: {
if (!ValidPointer(d_ptr)) {
*error_msg = StringPrintf("DT_HASH value %p does not refer to a loaded ELF segment of %s",
d_ptr, file->GetPath().c_str());
return false;
}
hash_section_start_ = reinterpret_cast<Elf_Word*>(d_ptr);
break;
}
case DT_STRTAB: {
if (!ValidPointer(d_ptr)) {
*error_msg = StringPrintf("DT_HASH value %p does not refer to a loaded ELF segment of %s",
d_ptr, file->GetPath().c_str());
return false;
}
dynstr_section_start_ = reinterpret_cast<char*>(d_ptr);
break;
}
case DT_SYMTAB: {
if (!ValidPointer(d_ptr)) {
*error_msg = StringPrintf("DT_HASH value %p does not refer to a loaded ELF segment of %s",
d_ptr, file->GetPath().c_str());
return false;
}
dynsym_section_start_ = reinterpret_cast<Elf_Sym*>(d_ptr);
break;
}
case DT_NULL: {
if (GetDynamicNum() != i+1) {
*error_msg = StringPrintf("DT_NULL found after %d .dynamic entries, "
"expected %d as implied by size of PT_DYNAMIC segment in %s",
i + 1, GetDynamicNum(), file->GetPath().c_str());
return false;
}
break;
}
}
}
// Check for the existence of some sections.
if (!CheckSectionsExist(file, error_msg)) {
return false;
}
return true;
}