| // Copyright 2023 Google LLC |
| // |
| // 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 |
| // |
| // https://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 "mpact/sim/util/program_loader/elf_program_loader.h" |
| |
| #include <sys/stat.h> |
| |
| #include <cstdint> |
| #include <cstring> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/str_cat.h" |
| #include "elfio/elf_types.hpp" |
| #include "elfio/elfio_segment.hpp" |
| #include "elfio/elfio_symbols.hpp" |
| #include "mpact/sim/generic/core_debug_interface.h" |
| #include "mpact/sim/generic/data_buffer.h" |
| #include "mpact/sim/util/memory/memory_interface.h" |
| |
| namespace mpact { |
| namespace sim { |
| namespace util { |
| |
| constexpr uint64_t kPtGnuStack = 0x6474e551; |
| |
| ElfProgramLoader::ElfProgramLoader(util::MemoryInterface* code_memory, |
| util::MemoryInterface* data_memory) |
| : code_memory_(code_memory), data_memory_(data_memory) {} |
| |
| ElfProgramLoader::ElfProgramLoader( |
| const std::vector<MemoryDescriptor>& memories) |
| : memories_(&memories) {} |
| |
| ElfProgramLoader::ElfProgramLoader(util::MemoryInterface* memory) |
| : code_memory_(memory), data_memory_(memory) {} |
| |
| ElfProgramLoader::ElfProgramLoader(generic::CoreDebugInterface* dbg_if) |
| : dbg_if_(dbg_if) {} |
| |
| ElfProgramLoader::~ElfProgramLoader() { |
| for (auto* symtab : symbol_accessors_) { |
| delete symtab; |
| } |
| symbol_accessors_.clear(); |
| } |
| |
| // Only load the program into the elf reader so that symbols can be looked up. |
| absl::StatusOr<uint64_t> ElfProgramLoader::LoadSymbols( |
| const std::string& file_name) { |
| struct stat buffer; |
| auto result = stat(file_name.c_str(), &buffer); |
| if (result == -1) { |
| return absl::NotFoundError( |
| absl::StrCat("Unable to open elf file: '", file_name, "'")); |
| } |
| if (!elf_reader_.load(file_name)) { |
| return absl::InternalError( |
| absl::StrCat("Elf loading error for '", file_name, "'")); |
| } |
| std::string msg = elf_reader_.validate(); |
| if (!msg.empty()) { |
| return absl::InternalError( |
| absl::StrCat("Validation error for '", file_name, "': ", msg)); |
| } |
| loaded_ = true; |
| // Now look up any symbol sections. |
| for (auto const& section : elf_reader_.sections) { |
| if (section->get_type() == ELFIO::SHT_SYMTAB) { |
| symbol_accessors_.push_back( |
| new ELFIO::symbol_section_accessor(elf_reader_, section.get())); |
| } |
| } |
| std::string name; |
| ELFIO::Elf_Xword size; |
| unsigned char bind; |
| unsigned char type; |
| ELFIO::Elf_Half section_index; |
| unsigned char other; |
| // Scan symbol table. Place function names in a map for easy lookup. |
| for (auto* symtab : symbol_accessors_) { |
| ELFIO::Elf64_Addr value; |
| for (unsigned i = 0; i < symtab->get_symbols_num(); i++) { |
| symtab->get_symbol(i, name, value, size, bind, type, section_index, |
| other); |
| if (type == ELFIO::STT_FUNC) { |
| fcn_symbol_map_.emplace(value, name); |
| function_range_map_.insert( |
| std::make_pair(AddressRange(value, size / text_size_scale_), name)); |
| } |
| } |
| } |
| return elf_reader_.get_entry(); |
| } |
| |
| // This is the main method of the class. It reads in the elf file, validates it |
| // and iterates over the segments. For each segment it writes it to the |
| // appropriate location in the given memories. |
| absl::StatusOr<uint64_t> ElfProgramLoader::LoadProgram( |
| const std::string& file_name) { |
| auto load_symbols_res = LoadSymbols(file_name); |
| if (!load_symbols_res.ok()) return load_symbols_res.status(); |
| |
| generic::DataBufferFactory db_factory; |
| |
| for (auto const& segment : elf_reader_.segments) { |
| if (segment->get_type() == kPtGnuStack) { |
| stack_size_ = segment->get_memory_size(); |
| has_stack_size_ = (stack_size_ > 0); |
| continue; |
| } |
| // If the section isn 't loadable, continue. |
| if (segment->get_type() != ELFIO::PT_LOAD) continue; |
| if (segment->get_file_size() == 0) continue; |
| // Compute the destination address - use paddr if available, else use |
| // vaddr. |
| uint64_t dest_addr = segment->get_physical_address(); |
| if (dest_addr == 0) { |
| dest_addr = segment->get_virtual_address(); |
| } |
| // Read the data from the elf file. |
| if (dbg_if_ == nullptr) { // Use memory interfaces. |
| auto size = segment->get_file_size(); |
| auto* db = db_factory.Allocate(size); |
| std::memcpy(db->raw_ptr(), segment->get_data(), size); |
| if (memories_ == nullptr) { |
| if (segment->get_flags() & |
| ELFIO::PF_X) { // Executable, so write to code memory. |
| code_memory_->Store(dest_addr, db); |
| } else { // Write to data memory. |
| data_memory_->Store(dest_addr, db); |
| } |
| } else { |
| for (auto& memory : *memories_) { |
| if (memory.predicate_fcn(*segment)) { |
| if (memory.address_fcn) { |
| memory.memory->Store(memory.address_fcn(dest_addr), db); |
| } else { |
| memory.memory->Store(dest_addr, db); |
| } |
| break; |
| } |
| } |
| } |
| db->DecRef(); |
| continue; |
| } |
| // Use debug interface. |
| auto res = dbg_if_->WriteMemory(dest_addr, segment->get_data(), |
| segment->get_file_size()); |
| if (!res.ok() || (res.value() != segment->get_file_size())) { |
| return absl::InternalError("Write error while loading elf segment"); |
| } |
| } |
| |
| return load_symbols_res.value(); |
| } |
| |
| absl::StatusOr<std::pair<uint64_t, uint64_t>> ElfProgramLoader::GetSymbol( |
| const std::string& name) const { |
| if (!loaded_) return absl::InternalError("No program loaded"); |
| if (symbol_accessors_.empty()) |
| return absl::NotFoundError("Symbol table not found"); |
| |
| ELFIO::Elf64_Addr value; |
| ELFIO::Elf_Xword size; |
| unsigned char bind; |
| unsigned char type; |
| ELFIO::Elf_Half section_index; |
| unsigned char other; |
| for (auto* symtab : symbol_accessors_) { |
| if (symtab->get_symbol(name, value, size, bind, type, section_index, |
| other)) { |
| return std::make_pair(static_cast<uint64_t>(value), |
| static_cast<uint64_t>(size)); |
| } |
| } |
| |
| return absl::NotFoundError(absl::StrCat("Symbol '", name, "' not found.")); |
| } |
| |
| absl::StatusOr<std::string> ElfProgramLoader::GetFcnSymbolName( |
| uint64_t address) const { |
| if (!loaded_) return absl::InternalError("No program loaded"); |
| if (fcn_symbol_map_.empty()) |
| return absl::NotFoundError("Symbol information not found"); |
| auto iter = fcn_symbol_map_.find(address); |
| if (iter != fcn_symbol_map_.end()) return iter->second; |
| |
| return absl::NotFoundError("Function symbol not found"); |
| } |
| |
| absl::StatusOr<uint64_t> ElfProgramLoader::GetStackSize() const { |
| if (!has_stack_size_) return absl::NotFoundError("Stack size not found"); |
| return stack_size_; |
| } |
| |
| absl::StatusOr<std::string> ElfProgramLoader::GetFunctionName( |
| uint64_t address) const { |
| if (!loaded_) return absl::InternalError("No program loaded"); |
| if (fcn_symbol_map_.empty()) |
| return absl::NotFoundError("Symbol information not found"); |
| auto iter = function_range_map_.find(AddressRange(address)); |
| if (iter != function_range_map_.end()) return iter->second; |
| return absl::NotFoundError("Function not found"); |
| } |
| |
| } // namespace util |
| } // namespace sim |
| } // namespace mpact |