diff -r 66173ef5fbbf -r 2c35fd3c5789 src/hotspot/share/utilities/elfFile.cpp --- a/src/hotspot/share/utilities/elfFile.cpp Wed Feb 14 16:42:00 2018 +0100 +++ b/src/hotspot/share/utilities/elfFile.cpp Wed Feb 14 17:20:59 2018 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,60 +31,150 @@ #include #include +#include "logging/log.hpp" #include "memory/allocation.inline.hpp" +#include "memory/resourceArea.hpp" #include "utilities/decoder.hpp" #include "utilities/elfFile.hpp" #include "utilities/elfFuncDescTable.hpp" #include "utilities/elfStringTable.hpp" #include "utilities/elfSymbolTable.hpp" +#include "utilities/ostream.hpp" +// For test only, disable elf section cache and force to read from file directly. +bool ElfFile::_do_not_cache_elf_section = false; + +ElfSection::ElfSection(FILE* fd, const Elf_Shdr& hdr) : _section_data(NULL) { + _stat = load_section(fd, hdr); +} + +ElfSection::~ElfSection() { + if (_section_data != NULL) { + os::free(_section_data); + } +} + +NullDecoder::decoder_status ElfSection::load_section(FILE* const fd, const Elf_Shdr& shdr) { + memcpy((void*)&_section_hdr, (const void*)&shdr, sizeof(shdr)); + + if (ElfFile::_do_not_cache_elf_section) { + log_debug(decoder)("Elf section cache is disabled"); + return NullDecoder::no_error; + } + + _section_data = os::malloc(shdr.sh_size, mtInternal); + // No enough memory for caching. It is okay, we can try to read from + // file instead. + if (_section_data == NULL) return NullDecoder::no_error; -ElfFile::ElfFile(const char* filepath) { - assert(filepath, "null file path"); - memset(&m_elfHdr, 0, sizeof(m_elfHdr)); - m_string_tables = NULL; - m_symbol_tables = NULL; - m_funcDesc_table = NULL; - m_next = NULL; - m_status = NullDecoder::no_error; + MarkedFileReader mfd(fd); + if (mfd.has_mark() && + mfd.set_position(shdr.sh_offset) && + mfd.read(_section_data, shdr.sh_size)) { + return NullDecoder::no_error; + } else { + os::free(_section_data); + _section_data = NULL; + return NullDecoder::file_invalid; + } +} + +bool FileReader::read(void* buf, size_t size) { + assert(buf != NULL, "no buffer"); + assert(size > 0, "no space"); + return fread(buf, size, 1, _fd) == 1; +} + +int FileReader::read_buffer(void* buf, size_t size) { + assert(buf != NULL, "no buffer"); + assert(size > 0, "no space"); + return fread(buf, 1, size, _fd); +} + +bool FileReader::set_position(long offset) { + return fseek(_fd, offset, SEEK_SET) == 0; +} + +MarkedFileReader::MarkedFileReader(FILE* fd) : FileReader(fd) { + _marked_pos = ftell(fd); +} + +MarkedFileReader::~MarkedFileReader() { + if (_marked_pos != -1) { + set_position(_marked_pos); + } +} + +ElfFile::ElfFile(const char* filepath) : + _string_tables(NULL), _symbol_tables(NULL), _funcDesc_table(NULL), + _next(NULL), _status(NullDecoder::no_error), + _shdr_string_table(NULL), _file(NULL), _filepath(NULL) { + memset(&_elfHdr, 0, sizeof(_elfHdr)); int len = strlen(filepath) + 1; - m_filepath = (const char*)os::malloc(len * sizeof(char), mtInternal); - if (m_filepath != NULL) { - strcpy((char*)m_filepath, filepath); - m_file = fopen(filepath, "r"); - if (m_file != NULL) { - load_tables(); - } else { - m_status = NullDecoder::file_not_found; - } - } else { - m_status = NullDecoder::out_of_memory; + _filepath = (char*)os::malloc(len * sizeof(char), mtInternal); + if (_filepath == NULL) { + _status = NullDecoder::out_of_memory; + return; + } + strcpy(_filepath, filepath); + + _status = parse_elf(filepath); + + // we no longer need section header string table + if (_shdr_string_table != NULL) { + delete _shdr_string_table; + _shdr_string_table = NULL; } } ElfFile::~ElfFile() { - if (m_string_tables != NULL) { - delete m_string_tables; + if (_shdr_string_table != NULL) { + delete _shdr_string_table; } - if (m_symbol_tables != NULL) { - delete m_symbol_tables; + cleanup_tables(); + + if (_file != NULL) { + fclose(_file); + } + + if (_filepath != NULL) { + os::free((void*)_filepath); } - if (m_file != NULL) { - fclose(m_file); + if (_next != NULL) { + delete _next; + } +} + +void ElfFile::cleanup_tables() { + if (_string_tables != NULL) { + delete _string_tables; + _string_tables = NULL; + } + + if (_symbol_tables != NULL) { + delete _symbol_tables; + _symbol_tables = NULL; } - if (m_filepath != NULL) { - os::free((void*)m_filepath); + if (_funcDesc_table != NULL) { + delete _funcDesc_table; + _funcDesc_table = NULL; } +} - if (m_next != NULL) { - delete m_next; +NullDecoder::decoder_status ElfFile::parse_elf(const char* filepath) { + assert(filepath, "null file path"); + + _file = fopen(filepath, "r"); + if (_file != NULL) { + return load_tables(); + } else { + return NullDecoder::file_not_found; } -}; - +} //Check elf header to ensure the file is valid. bool ElfFile::is_elf_file(Elf_Ehdr& hdr) { @@ -96,116 +186,134 @@ ELFDATANONE != hdr.e_ident[EI_DATA]); } -bool ElfFile::load_tables() { - assert(m_file, "file not open"); - assert(!NullDecoder::is_error(m_status), "already in error"); +NullDecoder::decoder_status ElfFile::load_tables() { + assert(_file, "file not open"); + assert(!NullDecoder::is_error(_status), "already in error"); + FileReader freader(fd()); // read elf file header - if (fread(&m_elfHdr, sizeof(m_elfHdr), 1, m_file) != 1) { - m_status = NullDecoder::file_invalid; - return false; + if (!freader.read(&_elfHdr, sizeof(_elfHdr))) { + return NullDecoder::file_invalid; } - if (!is_elf_file(m_elfHdr)) { - m_status = NullDecoder::file_invalid; - return false; + // Check signature + if (!is_elf_file(_elfHdr)) { + return NullDecoder::file_invalid; } // walk elf file's section headers, and load string tables Elf_Shdr shdr; - if (!fseek(m_file, m_elfHdr.e_shoff, SEEK_SET)) { - if (NullDecoder::is_error(m_status)) return false; + if (!freader.set_position(_elfHdr.e_shoff)) { + return NullDecoder::file_invalid; + } - for (int index = 0; index < m_elfHdr.e_shnum; index ++) { - if (fread((void*)&shdr, sizeof(Elf_Shdr), 1, m_file) != 1) { - m_status = NullDecoder::file_invalid; - return false; - } - if (shdr.sh_type == SHT_STRTAB) { - // string tables - ElfStringTable* table = new (std::nothrow) ElfStringTable(m_file, shdr, index); - if (table == NULL) { - m_status = NullDecoder::out_of_memory; - return false; - } - add_string_table(table); - } else if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) { - // symbol tables - ElfSymbolTable* table = new (std::nothrow) ElfSymbolTable(m_file, shdr); - if (table == NULL) { - m_status = NullDecoder::out_of_memory; - return false; - } - add_symbol_table(table); - } + for (int index = 0; index < _elfHdr.e_shnum; index ++) { + if (!freader.read(&shdr, sizeof(shdr))) { + return NullDecoder::file_invalid; } -#if defined(PPC64) && !defined(ABI_ELFv2) - // Now read the .opd section wich contains the PPC64 function descriptor table. - // The .opd section is only available on PPC64 (see for example: - // http://refspecs.linuxfoundation.org/LSB_3.1.1/LSB-Core-PPC64/LSB-Core-PPC64/specialsections.html) - // so this code should do no harm on other platforms but because of performance reasons we only - // execute it on PPC64 platforms. - // Notice that we can only find the .opd section after we have successfully read in the string - // tables in the previous loop, because we need to query the name of each section which is - // contained in one of the string tables (i.e. the one with the index m_elfHdr.e_shstrndx). - - // Reset the file pointer - if (fseek(m_file, m_elfHdr.e_shoff, SEEK_SET)) { - m_status = NullDecoder::file_invalid; - return false; + if (shdr.sh_type == SHT_STRTAB) { + // string tables + ElfStringTable* table = new (std::nothrow) ElfStringTable(fd(), shdr, index); + if (table == NULL) { + return NullDecoder::out_of_memory; + } + if (index == _elfHdr.e_shstrndx) { + assert(_shdr_string_table == NULL, "Only set once"); + _shdr_string_table = table; + } else { + add_string_table(table); + } + } else if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) { + // symbol tables + ElfSymbolTable* table = new (std::nothrow) ElfSymbolTable(fd(), shdr); + if (table == NULL) { + return NullDecoder::out_of_memory; + } + add_symbol_table(table); } - for (int index = 0; index < m_elfHdr.e_shnum; index ++) { - if (fread((void*)&shdr, sizeof(Elf_Shdr), 1, m_file) != 1) { - m_status = NullDecoder::file_invalid; - return false; - } - if (m_elfHdr.e_shstrndx != SHN_UNDEF && shdr.sh_type == SHT_PROGBITS) { - ElfStringTable* string_table = get_string_table(m_elfHdr.e_shstrndx); - if (string_table == NULL) { - m_status = NullDecoder::file_invalid; - return false; - } - char buf[8]; // '8' is enough because we only want to read ".opd" - if (string_table->string_at(shdr.sh_name, buf, sizeof(buf)) && !strncmp(".opd", buf, 4)) { - m_funcDesc_table = new (std::nothrow) ElfFuncDescTable(m_file, shdr, index); - if (m_funcDesc_table == NULL) { - m_status = NullDecoder::out_of_memory; - return false; - } - break; - } + } +#if defined(PPC64) && !defined(ABI_ELFv2) + // Now read the .opd section wich contains the PPC64 function descriptor table. + // The .opd section is only available on PPC64 (see for example: + // http://refspecs.linuxfoundation.org/LSB_3.1.1/LSB-Core-PPC64/LSB-Core-PPC64/specialsections.html) + // so this code should do no harm on other platforms but because of performance reasons we only + // execute it on PPC64 platforms. + // Notice that we can only find the .opd section after we have successfully read in the string + // tables in the previous loop, because we need to query the name of each section which is + // contained in one of the string tables (i.e. the one with the index m_elfHdr.e_shstrndx). + + // Reset the file pointer + int sect_index = section_by_name(".opd", shdr); + + if (sect_index == -1) { + return NullDecoder::file_invalid; + } + + _funcDesc_table = new (std::nothrow) ElfFuncDescTable(_file, shdr, sect_index); + if (_funcDesc_table == NULL) { + return NullDecoder::out_of_memory; + } +#endif + return NullDecoder::no_error; +} + +int ElfFile::section_by_name(const char* name, Elf_Shdr& hdr) { + assert(name != NULL, "No section name"); + size_t len = strlen(name) + 1; + ResourceMark rm; + char* buf = NEW_RESOURCE_ARRAY(char, len); + if (buf == NULL) { + return -1; + } + + assert(_shdr_string_table != NULL, "Section header string table should be loaded"); + ElfStringTable* const table = _shdr_string_table; + MarkedFileReader mfd(fd()); + if (!mfd.has_mark() || !mfd.set_position(_elfHdr.e_shoff)) return -1; + + int sect_index = -1; + for (int index = 0; index < _elfHdr.e_shnum; index ++) { + if (!mfd.read((void*)&hdr, sizeof(hdr))) { + break; + } + if (table->string_at(hdr.sh_name, buf, len)) { + if (strncmp(buf, name, len) == 0) { + sect_index = index; + break; } } -#endif - } - return true; + return sect_index; } bool ElfFile::decode(address addr, char* buf, int buflen, int* offset) { // something already went wrong, just give up - if (NullDecoder::is_error(m_status)) { + if (NullDecoder::is_error(_status)) { return false; } - ElfSymbolTable* symbol_table = m_symbol_tables; + int string_table_index; int pos_in_string_table; int off = INT_MAX; bool found_symbol = false; + ElfSymbolTable* symbol_table = _symbol_tables; + while (symbol_table != NULL) { - if (symbol_table->lookup(addr, &string_table_index, &pos_in_string_table, &off, m_funcDesc_table)) { + if (symbol_table->lookup(addr, &string_table_index, &pos_in_string_table, &off, _funcDesc_table)) { found_symbol = true; break; } - symbol_table = symbol_table->m_next; + symbol_table = symbol_table->next(); } - if (!found_symbol) return false; + if (!found_symbol) { + return false; + } ElfStringTable* string_table = get_string_table(string_table_index); if (string_table == NULL) { - m_status = NullDecoder::file_invalid; + _status = NullDecoder::file_invalid; return false; } if (offset) *offset = off; @@ -213,74 +321,31 @@ return string_table->string_at(pos_in_string_table, buf, buflen); } - void ElfFile::add_symbol_table(ElfSymbolTable* table) { - if (m_symbol_tables == NULL) { - m_symbol_tables = table; + if (_symbol_tables == NULL) { + _symbol_tables = table; } else { - table->m_next = m_symbol_tables; - m_symbol_tables = table; + table->set_next(_symbol_tables); + _symbol_tables = table; } } void ElfFile::add_string_table(ElfStringTable* table) { - if (m_string_tables == NULL) { - m_string_tables = table; + if (_string_tables == NULL) { + _string_tables = table; } else { - table->m_next = m_string_tables; - m_string_tables = table; + table->set_next(_string_tables); + _string_tables = table; } } ElfStringTable* ElfFile::get_string_table(int index) { - ElfStringTable* p = m_string_tables; + ElfStringTable* p = _string_tables; while (p != NULL) { if (p->index() == index) return p; - p = p->m_next; + p = p->next(); } return NULL; } -#ifdef LINUX -bool ElfFile::specifies_noexecstack(const char* filepath) { - // Returns true if the elf file is marked NOT to require an executable stack, - // or if the file could not be opened. - // Returns false if the elf file requires an executable stack, the stack flag - // is not set at all, or if the file can not be read. - if (filepath == NULL) return true; - - FILE* file = fopen(filepath, "r"); - if (file == NULL) return true; - - // AARCH64 defaults to noexecstack. All others default to execstack. -#ifdef AARCH64 - bool result = true; -#else - bool result = false; -#endif - - // Read file header - Elf_Ehdr head; - if (fread(&head, sizeof(Elf_Ehdr), 1, file) == 1 && - is_elf_file(head) && - fseek(file, head.e_phoff, SEEK_SET) == 0) { - - // Read program header table - Elf_Phdr phdr; - for (int index = 0; index < head.e_phnum; index ++) { - if (fread((void*)&phdr, sizeof(Elf_Phdr), 1, file) != 1) { - result = false; - break; - } - if (phdr.p_type == PT_GNU_STACK) { - result = (phdr.p_flags == (PF_R | PF_W)); - break; - } - } - } - fclose(file); - return result; -} -#endif // LINUX - #endif // !_WINDOWS && !__APPLE__