提交 aa3af031 编写于 作者: S simonis

8019929: PPC64 (part 107): Extend ELF-decoder to support PPC64 function descriptor tables

Summary: Extend ELF-decoder to support PPC64 function descriptor tables
Reviewed-by: kvn, zgu
上级 4892ef3c
......@@ -202,8 +202,8 @@ Src_Files_EXCLUDE/SHARK := $(COMPILER1_SPECIFIC_FILES) $(COMPILER2_SPECIFIC_
Src_Files_EXCLUDE += $(Src_Files_EXCLUDE/$(TYPE))
# Disable 155427 on aix.
Src_Files_EXCLUDE += decoder_elf.cpp elfFile.cpp elfStringTable.cpp elfSymbolTable.cpp
# Disable ELF decoder on AIX (AIX uses XCOFF).
Src_Files_EXCLUDE += decoder_elf.cpp elfFile.cpp elfStringTable.cpp elfSymbolTable.cpp elfFuncDescTable.cpp
# Special handling of arch model.
ifeq ($(Platform_arch_model), x86_32)
......
......@@ -32,6 +32,12 @@ bool ElfDecoder::demangle(const char* symbol, char *buf, int buflen) {
char* result;
size_t size = (size_t)buflen;
#ifdef PPC64
// On PPC64 ElfDecoder::decode() may return a dot (.) prefixed name
// (see elfFuncDescTable.hpp for details)
if (symbol && *symbol == '.') symbol += 1;
#endif
// Don't pass buf to __cxa_demangle. In case of the 'buf' is too small,
// __cxa_demangle will call system "realloc" for additional memory, which
// may use different malloc/realloc mechanism that allocates 'buf'.
......
......@@ -73,4 +73,4 @@ ElfFile* ElfDecoder::get_elf_file(const char* filepath) {
return file;
}
#endif
#endif // !_WINDOWS && !__APPLE__
/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2013, 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
......@@ -55,5 +55,5 @@ private:
ElfFile* _opened_elf_files;
};
#endif
#endif // !_WINDOWS && !__APPLE__
#endif // SHARE_VM_UTILITIES_DECODER_ELF_HPP
/*
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2013, 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
......@@ -34,6 +34,7 @@
#include "memory/allocation.inline.hpp"
#include "utilities/decoder.hpp"
#include "utilities/elfFile.hpp"
#include "utilities/elfFuncDescTable.hpp"
#include "utilities/elfStringTable.hpp"
#include "utilities/elfSymbolTable.hpp"
......@@ -43,6 +44,7 @@ ElfFile::ElfFile(const char* filepath) {
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;
......@@ -119,8 +121,8 @@ bool ElfFile::load_tables() {
m_status = NullDecoder::file_invalid;
return false;
}
// string table
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;
......@@ -128,6 +130,7 @@ bool ElfFile::load_tables() {
}
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;
......@@ -136,6 +139,46 @@ bool ElfFile::load_tables() {
add_symbol_table(table);
}
}
#if defined(PPC64)
// 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;
}
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;
}
}
}
#endif
}
return true;
}
......@@ -151,8 +194,9 @@ bool ElfFile::decode(address addr, char* buf, int buflen, int* offset) {
int off = INT_MAX;
bool found_symbol = false;
while (symbol_table != NULL) {
if (symbol_table->lookup(addr, &string_table_index, &pos_in_string_table, &off)) {
if (symbol_table->lookup(addr, &string_table_index, &pos_in_string_table, &off, m_funcDesc_table)) {
found_symbol = true;
break;
}
symbol_table = symbol_table->m_next;
}
......@@ -221,4 +265,4 @@ bool ElfFile::specifies_noexecstack() {
}
#endif
#endif // _WINDOWS
#endif // !_WINDOWS && !__APPLE__
/*
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2013, 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
......@@ -75,6 +75,7 @@ typedef Elf32_Sym Elf_Sym;
class ElfStringTable;
class ElfSymbolTable;
class ElfFuncDescTable;
// On Solaris/Linux platforms, libjvm.so does contain all private symbols.
......@@ -150,9 +151,12 @@ protected:
// string tables
ElfStringTable* m_string_tables;
// function descriptors table
ElfFuncDescTable* m_funcDesc_table;
NullDecoder::decoder_status m_status;
};
#endif // _WINDOWS
#endif // !_WINDOWS && !__APPLE__
#endif // SHARE_VM_UTILITIES_ELF_FILE_HPP
/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright 2012, 2013 SAP AG. 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "precompiled.hpp"
#if !defined(_WINDOWS) && !defined(__APPLE__)
#include "memory/allocation.inline.hpp"
#include "utilities/elfFuncDescTable.hpp"
ElfFuncDescTable::ElfFuncDescTable(FILE* file, Elf_Shdr shdr, int index) {
assert(file, "null file handle");
// The actual function address (i.e. function entry point) is always the
// first value in the function descriptor (on IA64 and PPC64 they look as follows):
// PPC64: [function entry point, TOC pointer, environment pointer]
// IA64 : [function entry point, GP (global pointer) value]
// Unfortunately 'shdr.sh_entsize' doesn't always seem to contain this size (it's zero on PPC64) so we can't assert
// assert(IA64_ONLY(2) PPC64_ONLY(3) * sizeof(address) == shdr.sh_entsize, "Size mismatch for '.opd' section entries");
m_funcDescs = NULL;
m_file = file;
m_index = index;
m_status = NullDecoder::no_error;
// try to load the function descriptor table
long cur_offset = ftell(file);
if (cur_offset != -1) {
// call malloc so we can back up if memory allocation fails.
m_funcDescs = (address*)os::malloc(shdr.sh_size, mtInternal);
if (m_funcDescs) {
if (fseek(file, shdr.sh_offset, SEEK_SET) ||
fread((void*)m_funcDescs, shdr.sh_size, 1, file) != 1 ||
fseek(file, cur_offset, SEEK_SET)) {
m_status = NullDecoder::file_invalid;
os::free(m_funcDescs);
m_funcDescs = NULL;
}
}
if (!NullDecoder::is_error(m_status)) {
memcpy(&m_shdr, &shdr, sizeof(Elf_Shdr));
}
} else {
m_status = NullDecoder::file_invalid;
}
}
ElfFuncDescTable::~ElfFuncDescTable() {
if (m_funcDescs != NULL) {
os::free(m_funcDescs);
}
}
address ElfFuncDescTable::lookup(Elf_Word index) {
if (NullDecoder::is_error(m_status)) {
return NULL;
}
if (m_funcDescs != NULL) {
if (m_shdr.sh_size > 0 && m_shdr.sh_addr <= index && index <= m_shdr.sh_addr + m_shdr.sh_size) {
// Notice that 'index' is a byte-offset into the function descriptor table.
return m_funcDescs[(index - m_shdr.sh_addr) / sizeof(address)];
}
return NULL;
} else {
long cur_pos;
address addr;
if (!(m_shdr.sh_size > 0 && m_shdr.sh_addr <= index && index <= m_shdr.sh_addr + m_shdr.sh_size)) {
// don't put the whole decoder in error mode if we just tried a wrong index
return NULL;
}
if ((cur_pos = ftell(m_file)) == -1 ||
fseek(m_file, m_shdr.sh_offset + index - m_shdr.sh_addr, SEEK_SET) ||
fread(&addr, sizeof(addr), 1, m_file) != 1 ||
fseek(m_file, cur_pos, SEEK_SET)) {
m_status = NullDecoder::file_invalid;
return NULL;
}
return addr;
}
}
#endif // !_WINDOWS && !__APPLE__
/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright 2012, 2013 SAP AG. 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_VM_UTILITIES_ELF_FUNC_DESC_TABLE_HPP
#define SHARE_VM_UTILITIES_ELF_FUNC_DESC_TABLE_HPP
#if !defined(_WINDOWS) && !defined(__APPLE__)
#include "memory/allocation.hpp"
#include "utilities/decoder.hpp"
#include "utilities/elfFile.hpp"
/*
On PowerPC-64 (and other architectures like for example IA64) a pointer to a
function is not just a plain code address, but instead a pointer to a so called
function descriptor (which is simply a structure containing 3 pointers).
This fact is also reflected in the ELF ABI for PowerPC-64.
On architectures like x86 or SPARC, the ELF symbol table contains the start
address and size of an object. So for example for a function object (i.e. type
'STT_FUNC') the symbol table's 'st_value' and 'st_size' fields directly
represent the starting address and size of that function. On PPC64 however, the
symbol table's 'st_value' field only contains an index into another, PPC64
specific '.opd' (official procedure descriptors) section, while the 'st_size'
field still holds the size of the corresponding function. In order to get the
actual start address of a function, it is necessary to read the corresponding
function descriptor entry in the '.opd' section at the corresponding index and
extract the start address from there.
That's exactly what this 'ElfFuncDescTable' class is used for. If the HotSpot
runs on a PPC64 machine, and the corresponding ELF files contains an '.opd'
section (which is actually mandatory on PPC64) it will be read into an object
of type 'ElfFuncDescTable' just like the string and symbol table sections.
Later on, during symbol lookup in 'ElfSymbolTable::lookup()' this function
descriptor table will be used if available to find the real function address.
All this is how things work today (2013) on contemporary Linux distributions
(i.e. SLES 10) and new version of GCC (i.e. > 4.0). However there is a history,
and it goes like this:
In SLES 9 times (sometimes before GCC 3.4) gcc/ld on PPC64 generated two
entries in the symbol table for every function. The value of the symbol with
the name of the function was the address of the function descriptor while the
dot '.' prefixed name was reserved to hold the actual address of that function
(http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html#FUNC-DES).
For a C-function 'foo' this resulted in two symbol table entries like this
(extracted from the output of 'readelf -a <lib.so>'):
Section Headers:
[ 9] .text PROGBITS 0000000000000a20 00000a20
00000000000005a0 0000000000000000 AX 0 0 16
[21] .opd PROGBITS 00000000000113b8 000013b8
0000000000000138 0000000000000000 WA 0 0 8
Symbol table '.symtab' contains 86 entries:
Num: Value Size Type Bind Vis Ndx Name
76: 00000000000114c0 24 FUNC GLOBAL DEFAULT 21 foo
78: 0000000000000bb0 76 FUNC GLOBAL DEFAULT 9 .foo
You can see now that the '.foo' entry actually points into the '.text' segment
('Ndx'=9) and its value and size fields represent the functions actual address
and size. On the other hand, the entry for plain 'foo' points into the '.opd'
section ('Ndx'=21) and its value and size fields are the index into the '.opd'
section and the size of the corresponding '.opd' section entry (3 pointers on
PPC64).
These so called 'dot symbols' were dropped around gcc 3.4 from GCC and BINUTILS,
see http://gcc.gnu.org/ml/gcc-patches/2004-08/msg00557.html.
But nevertheless it may still be necessary to support both formats because we
either run on an old system or because it is possible at any time that functions
appear in the stack trace which come from old-style libraries.
Therefore we not only have to check for the presence of the function descriptor
table during symbol lookup in 'ElfSymbolTable::lookup()'. We additionally have
to check that the symbol table entry references the '.opd' section. Only in
that case we can resolve the actual function address from there. Otherwise we
use the plain 'st_value' field from the symbol table as function address. This
way we can also lookup the symbols in old-style ELF libraries (although we get
the 'dotted' versions in that case). However, if present, the 'dot' will be
conditionally removed on PPC64 from the symbol in 'ElfDecoder::demangle()' in
decoder_linux.cpp.
Notice that we can not reliably get the function address from old-style
libraries because the 'st_value' field of the symbol table entries which point
into the '.opd' section denote the size of the corresponding '.opd' entry and
not that of the corresponding function. This has changed for the symbol table
entries in new-style libraries as described at the beginning of this
documentation.
*/
class ElfFuncDescTable: public CHeapObj<mtInternal> {
friend class ElfFile;
public:
ElfFuncDescTable(FILE* file, Elf_Shdr shdr, int index);
~ElfFuncDescTable();
// return the function address for the function descriptor at 'index' or NULL on error
address lookup(Elf_Word index);
int get_index() { return m_index; };
NullDecoder::decoder_status get_status() { return m_status; };
protected:
// holds the complete function descriptor section if
// we can allocate enough memory
address* m_funcDescs;
// file contains string table
FILE* m_file;
// section header
Elf_Shdr m_shdr;
// The section index of this function descriptor (i.e. '.opd') section in the ELF file
int m_index;
NullDecoder::decoder_status m_status;
};
#endif // !_WINDOWS && !__APPLE__
#endif // SHARE_VM_UTILITIES_ELF_FUNC_DESC_TABLE_HPP
/*
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2013, 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
......@@ -87,4 +87,4 @@ bool ElfStringTable::string_at(int pos, char* buf, int buflen) {
}
}
#endif // _WINDOWS
#endif // !_WINDOWS && !__APPLE__
......@@ -70,6 +70,6 @@ class ElfStringTable: CHeapObj<mtInternal> {
NullDecoder::decoder_status m_status;
};
#endif // _WINDOWS and _APPLE
#endif // !_WINDOWS && !__APPLE__
#endif // SHARE_VM_UTILITIES_ELF_STRING_TABLE_HPP
/*
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2013, 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
......@@ -27,6 +27,7 @@
#if !defined(_WINDOWS) && !defined(__APPLE__)
#include "memory/allocation.inline.hpp"
#include "utilities/elfFuncDescTable.hpp"
#include "utilities/elfSymbolTable.hpp"
ElfSymbolTable::ElfSymbolTable(FILE* file, Elf_Shdr shdr) {
......@@ -68,7 +69,7 @@ ElfSymbolTable::~ElfSymbolTable() {
}
}
bool ElfSymbolTable::lookup(address addr, int* stringtableIndex, int* posIndex, int* offset) {
bool ElfSymbolTable::lookup(address addr, int* stringtableIndex, int* posIndex, int* offset, ElfFuncDescTable* funcDescTable) {
assert(stringtableIndex, "null string table index pointer");
assert(posIndex, "null string table offset pointer");
assert(offset, "null offset pointer");
......@@ -77,19 +78,25 @@ bool ElfSymbolTable::lookup(address addr, int* stringtableIndex, int* posIndex,
return false;
}
address pc = 0;
size_t sym_size = sizeof(Elf_Sym);
assert((m_shdr.sh_size % sym_size) == 0, "check size");
int count = m_shdr.sh_size / sym_size;
if (m_symbols != NULL) {
for (int index = 0; index < count; index ++) {
if (STT_FUNC == ELF_ST_TYPE(m_symbols[index].st_info)) {
address sym_addr = (address)m_symbols[index].st_value;
if (sym_addr < addr && (addr - sym_addr) < *offset) {
pc = (address)m_symbols[index].st_value;
*offset = (int)(addr - pc);
Elf_Word st_size = m_symbols[index].st_size;
address sym_addr;
if (funcDescTable != NULL && funcDescTable->get_index() == m_symbols[index].st_shndx) {
// We need to go another step trough the function descriptor table (currently PPC64 only)
sym_addr = funcDescTable->lookup(m_symbols[index].st_value);
} else {
sym_addr = (address)m_symbols[index].st_value;
}
if (sym_addr <= addr && (Elf_Word)(addr - sym_addr) < st_size) {
*offset = (int)(addr - sym_addr);
*posIndex = m_symbols[index].st_name;
*stringtableIndex = m_shdr.sh_link;
return true;
}
}
}
......@@ -105,12 +112,19 @@ bool ElfSymbolTable::lookup(address addr, int* stringtableIndex, int* posIndex,
for (int index = 0; index < count; index ++) {
if (fread(&sym, sym_size, 1, m_file) == 1) {
if (STT_FUNC == ELF_ST_TYPE(sym.st_info)) {
address sym_addr = (address)sym.st_value;
if (sym_addr < addr && (addr - sym_addr) < *offset) {
pc = (address)sym.st_value;
*offset = (int)(addr - pc);
Elf_Word st_size = sym.st_size;
address sym_addr;
if (funcDescTable != NULL && funcDescTable->get_index() == sym.st_shndx) {
// We need to go another step trough the function descriptor table (currently PPC64 only)
sym_addr = funcDescTable->lookup(sym.st_value);
} else {
sym_addr = (address)sym.st_value;
}
if (sym_addr <= addr && (Elf_Word)(addr - sym_addr) < st_size) {
*offset = (int)(addr - sym_addr);
*posIndex = sym.st_name;
*stringtableIndex = m_shdr.sh_link;
return true;
}
}
} else {
......@@ -123,4 +137,4 @@ bool ElfSymbolTable::lookup(address addr, int* stringtableIndex, int* posIndex,
return true;
}
#endif // _WINDOWS
#endif // !_WINDOWS && !__APPLE__
/*
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2013, 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
......@@ -45,7 +45,7 @@ class ElfSymbolTable: public CHeapObj<mtInternal> {
~ElfSymbolTable();
// search the symbol that is nearest to the specified address.
bool lookup(address addr, int* stringtableIndex, int* posIndex, int* offset);
bool lookup(address addr, int* stringtableIndex, int* posIndex, int* offset, ElfFuncDescTable* funcDescTable);
NullDecoder::decoder_status get_status() { return m_status; };
......@@ -65,6 +65,6 @@ class ElfSymbolTable: public CHeapObj<mtInternal> {
NullDecoder::decoder_status m_status;
};
#endif // _WINDOWS and _APPLE
#endif // !_WINDOWS and !__APPLE__
#endif // SHARE_VM_UTILITIES_ELF_SYMBOL_TABLE_HPP
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册