| // 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 |