diff --git a/src/core/util/ElfReader.cc b/src/core/util/ElfReader.cc index 1044f4b5..46a49615 100644 --- a/src/core/util/ElfReader.cc +++ b/src/core/util/ElfReader.cc @@ -5,6 +5,7 @@ #include // memcpy, cstring #include #include "Demangler.hpp" +#include namespace fail { @@ -64,6 +65,16 @@ void ElfReader::setup(const char* path) { << (m_elfclass == ELFCLASS32 ? "32-bit" : "64-bit") << " ELF file: " << path << std::endl; + // Parse ELF segments + Elf64_Phdr seg_hdr; + for(unsigned i = 0; i < ehdr.e_phnum; ++i) { + if(!read_ELF_segment_header(fp, &ehdr, i, &seg_hdr)) { + m_log << "Invalid segment header at index " << i << std::endl; + continue; + } + process_segment(&seg_hdr); + } + // Parse symbol table and generate internal map for (int i = 0; i < num_hdrs; ++i) { if (!read_ELF_section_header(fp, &ehdr, i, &sec_hdr)) { @@ -109,12 +120,20 @@ void ElfReader::setup(const char* path) { int ElfReader::process_section(Elf64_Shdr *sect_hdr, char *sect_name_buff) { // Add section name, start address and size to list int idx=sect_hdr->sh_name; - // m_sections_map.push_back( sect_hdr->sh_addr, sect_hdr->sh_size, sect_name_buff+idx ); + // m_sections_map.push_back( sect_hdr->sh_addr, sect_hdr->sh_size, sect_name_buff+idx ); m_sectiontable.push_back( ElfSymbol(sect_name_buff+idx, sect_hdr->sh_addr, sect_hdr->sh_size, ElfSymbol::SECTION) ); return 0; } +void ElfReader::process_segment(Elf64_Phdr *seg_hdr) { + // FIXME: this does not handle the PT_PHDR segment type where the + // program header is actually located somewhere else. + if(seg_hdr->p_type == PT_LOAD) { + m_segmenttable.emplace_back(seg_hdr); + } +} + static void Elf32to64_Sym(Elf32_Sym const *src, Elf64_Sym *dest) { dest->st_name = src->st_name; @@ -142,7 +161,7 @@ bool ElfReader::process_symboltable(FILE *fp, Elf64_Ehdr const *ehdr, int sect_n link = sect_hdr.sh_link; sym_data_offset = sect_hdr.sh_offset; sym_data_size = sect_hdr.sh_size; - num_sym = sym_data_size / + num_sym = sym_data_size / (m_elfclass == ELFCLASS32 ? sizeof(Elf32_Sym) : sizeof(Elf64_Sym)); // read the corresponding strtab @@ -281,6 +300,37 @@ bool ElfReader::read_ELF_section_header(FILE *fp, Elf64_Ehdr const *filehdr, int return true; } +static void Elf32to64_Phdr(Elf32_Phdr const *src, Elf64_Phdr *dest) { + dest->p_type = src->p_type; + dest->p_offset = src->p_offset; + dest->p_vaddr = src->p_vaddr; + dest->p_paddr = src->p_paddr; + dest->p_filesz = src->p_filesz; + dest->p_memsz = src->p_memsz; + dest->p_flags = src->p_flags; + dest->p_align = src->p_align; +} + +bool ElfReader::read_ELF_segment_header(FILE* fp, Elf64_Ehdr const * filehdr, unsigned segment, Elf64_Phdr * seg_hdr) { + if(filehdr->e_phnum < segment) return false; + off_t phdr_offset = filehdr->e_phoff; + phdr_offset += filehdr->e_phentsize*segment; + fseek(fp, phdr_offset, SEEK_SET); + if(m_elfclass == ELFCLASS32) { + Elf32_Phdr seg_hdr32; + if(!fread(&seg_hdr32, sizeof(seg_hdr32), 1, fp)){ + return false; + } + Elf32to64_Phdr(&seg_hdr32,seg_hdr); + } else { + if(!fread(seg_hdr, sizeof(*seg_hdr), 1, fp)) { + return false; + } + } + + return true; +} + const ElfSymbol& ElfReader::getSymbol(guest_address_t address) { for (container_t::const_iterator it = m_symboltable.begin(); it !=m_symboltable.end(); ++it) { if (it->contains(address)) { @@ -341,7 +391,7 @@ void ElfReader::printDemangled() { if (str == Demangler::DEMANGLE_FAILED) { str = it->getName(); } - m_log << "0x" << std::hex << it->getAddress() << "\t" << str.c_str() << "\t" << it->getSize() << std::endl; + m_log << "0x" << std::hex << it->getAddress() << "\t" << str.c_str() << "\t" << it->getSize() << std::endl; } } @@ -353,8 +403,57 @@ void ElfReader::printMangled() { void ElfReader::printSections() { for (container_t::const_iterator it = m_sectiontable.begin(); it !=m_sectiontable.end(); ++it) { - m_log << "0x" << it->getAddress() << "\t" << it->getName().c_str() << "\t" << it->getSize() << std::endl; + m_log << "0x" << it->getAddress() << "\t" << it->getName().c_str() << "\t" << it->getSize() << std::endl; } } +void ElfReader::printSegments() { + for(auto it = m_segmenttable.begin(); it != m_segmenttable.end(); ++it) { + m_log << *it << std::endl; + } +} + +std::pair +ElfReader::getValidAddressBounds() { + guest_address_t min = std::numeric_limits::max(); + guest_address_t max = std::numeric_limits::min(); + + for(const auto& seg: m_segmenttable) { + min = std::min(min, seg.getStart()); + max = std::max(max, seg.getEnd()); + } + if (min == std::numeric_limits::max()) + throw new std::invalid_argument("invalid minimum ELF address, either the segment header are invalid or the ELF will take no space in memory."); + if (max == std::numeric_limits::min()) + throw new std::invalid_argument("invalid maximum ELF address, either the segment header are invalid or the ELF will take no space in memory."); + + return std::make_pair(min, max); +} + +std::pair +ElfReader::getTextSegmentBounds() { + address_t minimal_ip; + address_t maximal_ip; + bool found_executable_segment = false; + + for (const auto &seg : m_segmenttable) { + if(seg.isExecutable()) { + if(found_executable_segment) { + m_log << "warning: found multiple executable segments in ELF, we will only detect writes to the first executable segment!" << std::endl; + continue; + } + found_executable_segment = true; + minimal_ip = seg.getStart(); + maximal_ip = seg.getEnd(); + } + } + + if (!found_executable_segment) + throw new std::invalid_argument("ELF file has no executable segment. aborting!"); + + return std::make_pair(minimal_ip, maximal_ip); +} + + + } // end-of-namespace fail diff --git a/src/core/util/ElfReader.hpp b/src/core/util/ElfReader.hpp index d6712b93..41f8bd00 100644 --- a/src/core/util/ElfReader.hpp +++ b/src/core/util/ElfReader.hpp @@ -4,11 +4,14 @@ #include #include #include +#include #include #include #include "sal/SALConfig.hpp" // for ADDR_INV #include "Logger.hpp" #include "Demangler.hpp" +#include +#include namespace fail { @@ -16,6 +19,42 @@ struct ELF { static const std::string NOTFOUND; }; +class ElfSegment { + private: + guest_address_t m_paddress; + guest_address_t m_vaddress; + size_t m_size; + unsigned m_flags; + + public: + ElfSegment(Elf64_Phdr const * hdr) + : m_paddress(hdr->p_paddr), + m_vaddress(hdr->p_vaddr), + m_size(hdr->p_memsz), + m_flags(hdr->p_flags) { } + + bool isReadable() const { return m_flags & PF_R; } + bool isWriteable() const { return m_flags & PF_W; } + bool isExecutable() const { return m_flags & PF_X; } + guest_address_t getStart() const { assert(m_paddress == m_vaddress); return m_paddress; } + guest_address_t getEnd() const { assert(m_paddress == m_vaddress); return m_paddress + m_size; } + guest_address_t getSize() const { assert(m_paddress == m_vaddress); return m_size; } + friend std::ostream& operator << (std::ostream&,const ElfSegment&); +}; + +inline std::ostream& operator<< (std::ostream &out, const ElfSegment &s) { + return out << std::hex << std::showbase + << "Elf Segment @ physical: " << s.m_paddress + << " virtual: " << s.m_vaddress + << " size: " << s.m_size + << " (" + << (s.isReadable() ? "R": " ") + << (s.isWriteable() ? "W" : " ") + << (s.isExecutable() ? "X" : " ") + << ")" + << std::dec << std::noshowbase; +} + class ElfSymbol { std::string name; guest_address_t address; @@ -81,6 +120,10 @@ public: typedef container_t::const_iterator symbol_iterator; typedef container_t::const_iterator section_iterator; + typedef ElfSegment seg_entry_t; + typedef std::vector seg_container_t; + typedef seg_container_t::const_iterator segment_iterator; + int m_machine; int m_elfclass; @@ -112,6 +155,11 @@ public: */ void printSections(); + /** + * Print List of all available segments + */ + void printSegments(); + /** * Get symbol by address * @param address Address within range of the symbol @@ -154,6 +202,26 @@ public: container_t::const_iterator sec_begin() { return m_sectiontable.begin(); } container_t::const_iterator sec_end() { return m_sectiontable.end(); } + + + /** + * Get segment iterator. Dereferences to an ElfSymbol + * @return iterator + */ + seg_container_t::const_iterator seg_begin() { return m_segmenttable.begin(); } + seg_container_t::const_iterator seg_end() { return m_segmenttable.end(); } + + /** + * Return minimal and maximal valid address in ELF, this uses the ELF segment headers + * and assumes that there are no holes in the ELFs memory layout. + */ + std::pair getValidAddressBounds(); + + /** + * Return minimal and maximal address in the ELF's text segment + */ + std::pair getTextSegmentBounds(); + const std::string & getFilename() { return m_filename; } private: @@ -163,17 +231,24 @@ private: void setup(const char*); bool process_symboltable(FILE *fp, Elf64_Ehdr const *ehdr, int sect_num); int process_section(Elf64_Shdr *sect_hdr, char *sect_name_buff); + void process_segment(Elf64_Phdr *seg_hdr); - // Returns true if it finds a valid ELF header. Stores ELFCLASS32 or 64 in m_elfclass. + // Returns true if it finds a valid ELF header. Stores ELFCLASS32 or 64 in m_elfclass. bool read_ELF_file_header(FILE *fp, Elf64_Ehdr *ehdr); + // Returns true if it finds a valid ELF section header. bool read_ELF_section_header(FILE *fp, Elf64_Ehdr const *filehdr, int sect_num, Elf64_Shdr *sect_hdr); + // Return true if it finds a valid ELF program header + bool read_ELF_segment_header(FILE* fp, Elf64_Ehdr const *filehdr, unsigned seg_num, Elf64_Phdr* seg_hdr); + container_t m_symboltable; container_t m_sectiontable; + seg_container_t m_segmenttable; guest_address_t getAddress(const std::string& name); std::string getName(guest_address_t address); + }; } // end-of-namespace fail diff --git a/src/experiments/generic-experiment/experiment.cc b/src/experiments/generic-experiment/experiment.cc index b4e227ca..506f0570 100644 --- a/src/experiments/generic-experiment/experiment.cc +++ b/src/experiments/generic-experiment/experiment.cc @@ -17,6 +17,8 @@ #include "sal/bochs/BochsListener.hpp" #include #include +#include +#include #include "campaign.hpp" #include "generic-experiment.pb.h" @@ -117,6 +119,10 @@ bool GenericExperiment::cb_start_experiment() { = cmd.addOption("", "catch-write-outerspace", Arg::None, "--catch-write-outerspace \tCatch writes to the outerspace"); + CommandLine::option_handle WRITE_MEM_LOWERSPACE + = cmd.addOption("", "catch-write-lowerspace", Arg::None, + "--catch-write-lowerspace \tCatch writes to the lowerspace"); + CommandLine::option_handle TIMEOUT = cmd.addOption("", "timeout", Arg::Required, "--timeout TIME \tExperiment timeout in uS"); @@ -166,24 +172,6 @@ bool GenericExperiment::cb_start_experiment() { exit(1); } - address_t minimal_ip = INT_MAX; // Every address is lower - address_t maximal_ip = 0; - address_t minimal_data = 0x100000; // 1 Mbyte - address_t maximal_data = 0; - - for (ElfReader::section_iterator it = m_elf->sec_begin(); - it != m_elf->sec_end(); ++it) { - const ElfSymbol &symbol = *it; - std::string prefix(".text"); - if (symbol.getName().compare(0, prefix.size(), prefix) == 0) { - minimal_ip = std::min(minimal_ip, symbol.getStart()); - maximal_ip = std::max(maximal_ip, symbol.getEnd()); - } else { - minimal_data = std::min(minimal_data, symbol.getStart()); - maximal_data = std::max(maximal_data, symbol.getEnd()); - } - } - if (cmd[SERIAL_PORT]) { option::Option *opt = cmd[SERIAL_PORT].first(); char *endptr; @@ -223,22 +211,41 @@ bool GenericExperiment::cb_start_experiment() { sol.setLimit(serial_goldenrun.size() + 1); } - if (cmd[WRITE_MEM_TEXT]) { - m_log << "Catch writes to text segment from " << hex << minimal_ip << " to " << maximal_ip << std::endl; - enabled_mem_text = true; - l_mem_text.setWatchAddress(minimal_ip); + if (cmd[WRITE_MEM_TEXT]) { + enabled_mem_text = true; + auto bounds = m_elf->getTextSegmentBounds(); + m_log << "Catch writes to text segment from " + << hex << bounds.first << " to " << bounds.second << std::endl; + + l_mem_text.setWatchAddress(bounds.first); + l_mem_text.setWatchWidth(bounds.second - bounds.first); l_mem_text.setTriggerAccessType(MemAccessEvent::MEM_WRITE); - l_mem_text.setWatchWidth(maximal_ip - minimal_ip); } + if (cmd[WRITE_MEM_OUTERSPACE]) { - m_log << "Catch writes to outerspace from " << hex << " from " << maximal_data << std::endl; enabled_mem_outerspace = true; - l_mem_outerspace.setWatchAddress(maximal_data); + auto bounds = m_elf->getValidAddressBounds(); + m_log << "Catch writes to upper outerspace from " << hex << bounds.second << std::endl; + + l_mem_outerspace.setWatchAddress(bounds.second); + l_mem_outerspace.setWatchWidth(numeric_limits::max() - bounds.second); l_mem_outerspace.setTriggerAccessType(MemAccessEvent::MEM_WRITE); - l_mem_outerspace.setWatchWidth(0xfffffff0 - maximal_data); + } + + if (cmd[WRITE_MEM_LOWERSPACE]) { + enabled_mem_lowerspace = true; + + auto bounds = m_elf->getValidAddressBounds(); + m_log << "Catch writes to lower outer-space below " << hex << bounds.first << std::endl; + + // FIXME: this might not work if your benchmark uses any devices mapped below the actual ELF + // however, this is not the case for RISC-V and consequently, it is ignored here. + l_mem_lowerspace.setWatchAddress(numeric_limits::min()); + l_mem_lowerspace.setWatchWidth(bounds.first - numeric_limits::min()); + l_mem_lowerspace.setTriggerAccessType(MemAccessEvent::MEM_WRITE); } if (cmd[TRAP]) { @@ -309,6 +316,10 @@ bool GenericExperiment::cb_before_resume() { std::cout << "enabled mem outerspace " << endl; simulator.addListener(&l_mem_outerspace); } + if (enabled_mem_lowerspace) { + std::cout << "enabled mem lowerspace " << endl; + simulator.addListener(&l_mem_lowerspace); + } if (enabled_timeout) simulator.addListener(&l_timeout); @@ -321,6 +332,8 @@ bool GenericExperiment::cb_before_resume() { return true; // everything OK } + + void GenericExperiment::cb_after_resume(fail::BaseListener *event) { GenericExperimentMessage_Result * result = static_cast(this->get_current_result()); @@ -331,18 +344,43 @@ void GenericExperiment::cb_after_resume(fail::BaseListener *event) { handleEvent(*result, result->TIMEOUT, m_Timeout); } else if (event == &l_trap) { handleEvent(*result, result->TRAP, l_trap.getTriggerNumber()); - } else if (event == &l_mem_text) { + } + //////////////////////////////////////////////////////////////// + // Memory Access Listeners + //////////////////////////////////////////////////////////////// +#define LOG_MEM_LISTENER(l) " @ 0x" \ + << std::hex << (l).getTriggerAddress() \ + << " access type trigger: " \ + << ((l).getTriggerAccessType() == MemAccessEvent::MEM_READ ?"R":"W") + // FIXME: Add after memory types are introduced + // << " memory type watched: " << l_mem_lowerspace.getWatchMemoryType() + // << " memory type trigger: " << l_mem_lowerspace.getMemoryType() + // << " data accessed: " << (l).getAccessedData() + + else if (event == &l_mem_text) { + m_log << "memory text-write triggered" + << LOG_MEM_LISTENER(l_mem_text) + << std::endl; handleEvent(*result, result->WRITE_TEXTSEGMENT, l_mem_text.getTriggerAddress()); - } else if (event == &l_mem_outerspace){ + m_log << "memory upper outerspace triggered" + << LOG_MEM_LISTENER(l_mem_outerspace) + << std::endl; handleEvent(*result, result->WRITE_OUTERSPACE, l_mem_outerspace.getTriggerAddress()); - - ////////////////////////////////////////////////// - // End Marker Groups - ////////////////////////////////////////////////// - } else if (OK_marker.find(event) != OK_marker.end()) { + } else if (event == &l_mem_lowerspace) { + m_log << "memory lower outerspace triggered" + << LOG_MEM_LISTENER(l_mem_lowerspace) + << std::endl; + handleEvent(*result, result->WRITE_LOWERSPACE, + l_mem_lowerspace.getTriggerAddress()); + } +#undef LOG_MEM_LISTENER + ////////////////////////////////////////////////// + // End Marker Groups + ////////////////////////////////////////////////// + else if (OK_marker.find(event) != OK_marker.end()) { const ElfSymbol *symbol = listener_to_symbol[event]; handleEvent(*result, result->OK_MARKER, symbol->getAddress()); diff --git a/src/experiments/generic-experiment/experiment.hpp b/src/experiments/generic-experiment/experiment.hpp index c2883c8d..a53fedb5 100644 --- a/src/experiments/generic-experiment/experiment.hpp +++ b/src/experiments/generic-experiment/experiment.hpp @@ -31,6 +31,9 @@ class GenericExperiment : public fail::DatabaseExperiment { bool enabled_mem_outerspace; fail::MemAccessListener l_mem_outerspace; + bool enabled_mem_lowerspace; + fail::MemAccessListener l_mem_lowerspace; + bool enabled_trap; fail::TrapListener l_trap; @@ -62,6 +65,7 @@ public: l_trap(fail::ANY_TRAP), l_timeout(0) { enabled_mem_text = false; enabled_mem_outerspace = false; + enabled_mem_lowerspace = false; enabled_trap = false; enabled_timeout = false;