blob: 332d59ad6d329076c321e03b78a3017b80da67d0 [file] [log] [blame]
// Copyright 2019 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/elf_loader/elf_loader_impl.h"
#include <string>
#include "starboard/common/log.h"
#include "starboard/common/scoped_ptr.h"
#include "starboard/elf_loader/elf.h"
#include "starboard/elf_loader/elf_loader_constants.h"
#include "starboard/elf_loader/file.h"
#include "starboard/elf_loader/file_impl.h"
#include "starboard/elf_loader/log.h"
#include "starboard/elf_loader/lz4_file_impl.h"
#include "starboard/extension/loader_app_metrics.h"
#include "starboard/memory.h"
#include "starboard/string.h"
#include "starboard/system.h"
namespace starboard {
namespace elf_loader {
namespace {
bool EndsWith(const std::string& s, const std::string& suffix) {
if (s.size() < suffix.size()) {
return false;
}
return strcmp(s.c_str() + (s.size() - suffix.size()), suffix.c_str()) == 0;
}
} // namespace
ElfLoaderImpl::ElfLoaderImpl() {
#if SB_API_VERSION >= 16
SB_CHECK(kSbCanMapExecutableMemory)
<< "Elf_loader requires executable memory support!";
#else
#if !SB_CAN(MAP_EXECUTABLE_MEMORY)
SB_CHECK(false) << "The elf_loader requires "
"executable memory map support!";
#endif
#endif
}
bool ElfLoaderImpl::Load(const char* name,
bool use_compression,
bool use_memory_mapped_files) {
if (use_compression && use_memory_mapped_files) {
SB_LOG(ERROR) << "Loading " << name
<< " Compression is not supported with memory mapped files.";
return false;
}
scoped_ptr<File> elf_file;
if (use_compression && EndsWith(name, kCompressionSuffix)) {
elf_file.reset(new LZ4FileImpl());
SB_LOG(INFO) << "Loading " << name << " using compression";
} else {
SB_LOG(INFO) << "Loading " << name;
elf_file.reset(new FileImpl());
}
elf_file->Open(name);
elf_header_loader_.reset(new ElfHeader());
if (!elf_header_loader_->LoadElfHeader(elf_file.get())) {
SB_LOG(ERROR) << "Failed to load ELF header";
return false;
}
SB_DLOG(INFO) << "Loaded ELF header";
if (use_memory_mapped_files) {
const auto* memory_mapped_file_extension =
reinterpret_cast<const CobaltExtensionMemoryMappedFileApi*>(
SbSystemGetExtension(kCobaltExtensionMemoryMappedFileName));
if (!memory_mapped_file_extension ||
strcmp(memory_mapped_file_extension->name,
kCobaltExtensionMemoryMappedFileName) != 0 ||
memory_mapped_file_extension->version < 1) {
SB_LOG(ERROR) << "CobaltExtensionMemoryMappedFileApi not implemented";
return false;
}
program_table_.reset(new ProgramTable(memory_mapped_file_extension));
} else {
program_table_.reset(new ProgramTable(nullptr));
}
program_table_->LoadProgramHeader(elf_header_loader_->GetHeader(),
elf_file.get());
SB_DLOG(INFO) << "Loaded Program header";
if (!program_table_->ReserveLoadMemory()) {
SB_LOG(ERROR) << "Failed to reserve memory space";
return false;
}
SB_DLOG(INFO) << "Reserved address space";
if (!program_table_->LoadSegments(elf_file.get())) {
SB_LOG(ERROR) << "Failed to load segments";
return false;
}
SB_DLOG(INFO) << "Loaded segments";
Dyn* dynamic = NULL;
size_t dynamic_count = 0;
Word dynamic_flags = 0;
program_table_->GetDynamicSection(&dynamic, &dynamic_count, &dynamic_flags);
if (!dynamic) {
SB_LOG(ERROR) << "No PT_DYNAMIC section!";
return false;
}
dynamic_section_.reset(
new DynamicSection(program_table_->GetBaseMemoryAddress(), dynamic,
dynamic_count, dynamic_flags));
if (!dynamic_section_->InitDynamicSection()) {
SB_LOG(ERROR) << "Failed to initialize dynamic section";
return false;
}
SB_DLOG(INFO) << "Initialized dynamic section";
exported_symbols_.reset(new ExportedSymbols());
relocations_.reset(new Relocations(program_table_->GetBaseMemoryAddress(),
dynamic_section_.get(),
exported_symbols_.get()));
if (!relocations_->InitRelocations()) {
SB_LOG(ERROR) << "Failed to initialize relocations";
return false;
}
if (relocations_->HasTextRelocations()) {
SB_DLOG(INFO) << "HasTextRelocations";
// Adjust the memory protection to its to allow modifications.
if (program_table_->AdjustMemoryProtectionOfReadOnlySegments(
kSbMemoryMapProtectWrite) < 0) {
SB_LOG(ERROR) << "Unable to make segments writable";
return false;
}
}
SB_DLOG(INFO) << "Loaded relocations";
if (!relocations_->ApplyAllRelocations()) {
SB_LOG(ERROR) << "Failed to apply relocations";
return false;
}
if (relocations_->HasTextRelocations()) {
// Restores the memory protection to its original state.
if (program_table_->AdjustMemoryProtectionOfReadOnlySegments(
kSbMemoryMapProtectReserved) < 0) {
SB_LOG(ERROR) << "Unable to restore segment protection";
return false;
}
}
SB_DLOG(INFO) << "Applied relocations";
auto metrics_extension =
static_cast<const StarboardExtensionLoaderAppMetricsApi*>(
SbSystemGetExtension(kStarboardExtensionLoaderAppMetricsName));
if (metrics_extension &&
strcmp(metrics_extension->name,
kStarboardExtensionLoaderAppMetricsName) == 0 &&
metrics_extension->version >= 2) {
// CPU memory use is likely the highest at this point of the ELF loading
// process, and observations support this. This value is therefore recorded
// here in an attempt to efficiently approximate the maximum CPU memory used
// at any point during loading of the ELF dynamic shared library.
metrics_extension->RecordUsedCpuBytesDuringElfLoad(
SbSystemGetUsedCPUMemory());
}
program_table_->PublishEvergreenInfo(name);
SB_DLOG(INFO) << "Published Evergreen Info";
SB_DLOG(INFO) << "Call constructors";
dynamic_section_->CallConstructors();
SB_DLOG(INFO) << "Finished loading";
return true;
}
void* ElfLoaderImpl::LookupSymbol(const char* symbol) {
const Sym* sym = dynamic_section_->LookupByName(symbol);
void* address = NULL;
if (sym) {
address = reinterpret_cast<void*>(program_table_->GetBaseMemoryAddress() +
sym->st_value);
}
return address;
}
ElfLoaderImpl::~ElfLoaderImpl() {
if (dynamic_section_) {
dynamic_section_->CallDestructors();
}
}
} // namespace elf_loader
} // namespace starboard