Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 63 additions & 17 deletions src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@
*
*/

#include <cstdlib>
#include <cstring>
#include <stack>

#include "dwarf.hpp"
#include "salibelf.h"
#include "libproc_impl.h"

DwarfParser::DwarfParser(lib_info *lib) : _lib(lib),
Expand Down Expand Up @@ -75,7 +77,7 @@ uintptr_t DwarfParser::read_leb(bool sign) {
uint64_t DwarfParser::get_entry_length() {
uint64_t length = *(reinterpret_cast<uint32_t *>(_buf));
_buf += 4;
if (length == 0xffffffff) {
if (!_lib->frame.is_debug_frame && length == 0xffffffff) {
length = *(reinterpret_cast<uint64_t *>(_buf));
_buf += 8;
}
Expand All @@ -84,16 +86,20 @@ uint64_t DwarfParser::get_entry_length() {

bool DwarfParser::process_cie(unsigned char *start_of_entry, uint32_t id) {
unsigned char *orig_pos = _buf;
_buf = start_of_entry - id;

// CIE pointer means the offset from FDE in .eh_frame.
// In .debug_frame, CIE pointer means the offset from start of .debug_frame .
_buf = _lib->frame.is_debug_frame ? _lib->frame.data + id
: start_of_entry - id;

uint64_t length = get_entry_length();
if (length == 0L) {
return false;
}
unsigned char *end = _buf + length;

_buf += 4; // Skip ID (This value of CIE would be always 0)
_buf++; // Skip version (assume to be "1")
_buf += 4; // Skip ID (This value of CIE would be always 0 in .eh_frame / 0xffffffff in .debug_frame)
_buf++; // Skip version

char *augmentation_string = reinterpret_cast<char *>(_buf);
bool has_ehdata = (strcmp("eh", augmentation_string) == 0);
Expand Down Expand Up @@ -268,14 +274,14 @@ uint32_t DwarfParser::get_decoded_value() {
// https://gcc.gnu.org/ml/gcc-help/2010-09/msg00166.html
#if defined(_LP64)
if (size == 8) {
result += _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data);
result += _lib->frame.v_addr + static_cast<uintptr_t>(_buf - _lib->frame.data);
size = 4;
} else
#endif
if ((_encoding & 0x70) == 0x10) { // 0x10 = DW_EH_PE_pcrel
result += _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data);
result += _lib->frame.v_addr + static_cast<uintptr_t>(_buf - _lib->frame.data);
} else if (size == 2) {
result = static_cast<int>(result) + _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data);
result = static_cast<int>(result) + _lib->frame.v_addr + static_cast<uintptr_t>(_buf - _lib->frame.data);
size = 4;
}

Expand Down Expand Up @@ -322,30 +328,45 @@ unsigned int DwarfParser::get_pc_range() {

bool DwarfParser::process_dwarf(const uintptr_t pc) {
// https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html
_buf = _lib->eh_frame.data;
unsigned char *end = _lib->eh_frame.data + _lib->eh_frame.size;
_buf = _lib->frame.data;
unsigned char *end = _lib->frame.data + _lib->frame.size;
while (_buf <= end) {
uint64_t length = get_entry_length();
if (length == 0L) {
return false;
break; // it means "terminator" in .eh_frame, so go through to .debug_frame
}
unsigned char *next_entry = _buf + length;
unsigned char *start_of_entry = _buf;
uint32_t id = *(reinterpret_cast<uint32_t *>(_buf));
_buf += 4;
if (id != 0) { // FDE
uintptr_t pc_begin = get_decoded_value() + _lib->eh_frame.library_base_addr;
uintptr_t pc_end = pc_begin + get_pc_range();
// ID for CIE is 0 in .eh_frame, 0xffffffff in .debug_frame
bool is_fde = (_lib->frame.is_debug_frame ? 0xffffffff : 0) != id;
if (is_fde) {
uintptr_t begin_ofs = 0L;
uintptr_t inst_sz = 0L;
if (_lib->frame.is_debug_frame) {
begin_ofs = *(reinterpret_cast<uintptr_t *>(_buf));
_buf += sizeof(void*);
inst_sz = *(reinterpret_cast<uintptr_t *>(_buf));
_buf += sizeof(void*);
} else {
begin_ofs = get_decoded_value();
inst_sz = get_pc_range();
}
uintptr_t pc_begin = begin_ofs + _lib->base;
uintptr_t pc_end = pc_begin + inst_sz;

if ((pc >= pc_begin) && (pc < pc_end)) {
// Process CIE
if (!process_cie(start_of_entry, id)) {
return false;
}

// Skip Augumenation
uintptr_t augmentation_length = read_leb(false);
_buf += augmentation_length; // skip
// Skip Augumenation if .eh_frame
if (!_lib->frame.is_debug_frame) {
uintptr_t augmentation_length = read_leb(false);
_buf += augmentation_length; // skip
}

// Process FDE
parse_dwarf_instructions(pc_begin, pc, next_entry);
Expand All @@ -356,5 +377,30 @@ bool DwarfParser::process_dwarf(const uintptr_t pc) {
_buf = next_entry;
}

return false;
bool result = false;
// try again with .debug_frame section if it hasn't been tried yet.
if (!_lib->frame.tried_debug_frame && _lib->fd != -1) {
// attempts to load .debug_frame from executables.
frame_info frame = {};
if (!read_frame(".debug_frame", _lib->fd, &frame)) {
// attempts again to load .debug_frame from debuginfo if it could not be loaded from executables.
int debug_fd = open_debuginfo(_lib->name, _lib->fd);
if (debug_fd != -1 && read_frame(".debug_frame", debug_fd, &frame)) {
close(debug_fd);
}
}
frame.tried_debug_frame = true;
_lib->frame.tried_debug_frame = true;

// try process_dwarf() again with .debug_frame.
if (frame.data != NULL) {
if (_lib->frame.data != NULL) {
free(_lib->frame.data);
}
memcpy(&_lib->frame, &frame, sizeof(frame_info));
result = process_dwarf(pc);
}
}

return result;
}
4 changes: 2 additions & 2 deletions src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ struct DwarfState {
*/
class DwarfParser {
private:
const lib_info *_lib;
lib_info *_lib;
unsigned char *_buf;
unsigned char _encoding;
unsigned int _code_factor;
Expand Down Expand Up @@ -93,7 +93,7 @@ class DwarfParser {
}

bool is_parseable() {
return _lib->eh_frame.data != NULL;
return _lib->frame.data != NULL;
}
};

Expand Down
30 changes: 15 additions & 15 deletions src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ static void destroy_lib_info(struct ps_prochandle* ph) {
if (lib->symtab) {
destroy_symtab(lib->symtab);
}
free(lib->eh_frame.data);
free(lib->frame.data);
free(lib);
lib = next;
}
Expand Down Expand Up @@ -214,35 +214,35 @@ static bool fill_addr_info(lib_info* lib) {
return (lib->end != -1L) && (lib->exec_start != -1L) && (lib->exec_end != -1L);
}

bool read_eh_frame(struct ps_prochandle* ph, lib_info* lib) {
bool read_frame(const char* section, int fd, frame_info* frame) {
off_t current_pos = -1;
ELF_EHDR ehdr;
ELF_SHDR* shbuf = NULL;
ELF_SHDR* sh = NULL;
char* strtab = NULL;
int cnt;

current_pos = lseek(lib->fd, (off_t)0L, SEEK_CUR);
lseek(lib->fd, (off_t)0L, SEEK_SET);
current_pos = lseek(fd, (off_t)0L, SEEK_CUR);
lseek(fd, (off_t)0L, SEEK_SET);

read_elf_header(lib->fd, &ehdr);
shbuf = read_section_header_table(lib->fd, &ehdr);
strtab = read_section_data(lib->fd, &ehdr, &shbuf[ehdr.e_shstrndx]);
read_elf_header(fd, &ehdr);
shbuf = read_section_header_table(fd, &ehdr);
strtab = read_section_data(fd, &ehdr, &shbuf[ehdr.e_shstrndx]);

for (cnt = 0, sh = shbuf; cnt < ehdr.e_shnum; cnt++, sh++) {
if (strcmp(".eh_frame", sh->sh_name + strtab) == 0) {
lib->eh_frame.library_base_addr = lib->base;
lib->eh_frame.v_addr = sh->sh_addr;
lib->eh_frame.data = read_section_data(lib->fd, &ehdr, sh);
lib->eh_frame.size = sh->sh_size;
if (strcmp(section, sh->sh_name + strtab) == 0) {
frame->v_addr = sh->sh_addr;
frame->data = read_section_data(fd, &ehdr, sh);
frame->size = sh->sh_size;
frame->is_debug_frame = strcmp(section, ".debug_frame") == 0;
break;
}
}

free(strtab);
free(shbuf);
lseek(lib->fd, current_pos, SEEK_SET);
return lib->eh_frame.data != NULL;
lseek(fd, current_pos, SEEK_SET);
return frame->data != NULL;
}

lib_info* add_lib_info_fd(struct ps_prochandle* ph, const char* libname, int fd, uintptr_t base) {
Expand Down Expand Up @@ -286,7 +286,7 @@ lib_info* add_lib_info_fd(struct ps_prochandle* ph, const char* libname, int fd,
}

if (fill_addr_info(newlib)) {
if (!read_eh_frame(ph, newlib)) {
if (!read_frame(".eh_frame", newlib->fd, &newlib->frame)) {
print_debug("Could not find .eh_frame section in %s\n", newlib->name);
}
} else {
Expand Down
18 changes: 13 additions & 5 deletions src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,18 @@

#define BUF_SIZE (PATH_MAX + NAME_MAX + 1)

// .eh_frame data
typedef struct eh_frame_info {
uintptr_t library_base_addr;
// frame data (.eh_frame / .debug_frame)
typedef struct frame_info {
uintptr_t v_addr;
unsigned char* data;
int size;
} eh_frame_info;

// Following fields should be declared as int instead of bool
// because "bool" would be defined as int in C in libproc.h.
// It causes unexpected memory access.
int is_debug_frame; // true if this info comes from .debug_frame
int tried_debug_frame; // true if .debug_frame was tried to load.
} frame_info;

// list of shared objects
typedef struct lib_info {
Expand All @@ -49,7 +54,7 @@ typedef struct lib_info {
uintptr_t end;
uintptr_t exec_start;
uintptr_t exec_end;
eh_frame_info eh_frame;
frame_info frame;
struct symtab* symtab;
int fd; // file descriptor for lib
struct lib_info* next;
Expand Down Expand Up @@ -131,6 +136,9 @@ void print_debug(const char* format,...);
void print_error(const char* format,...);
bool is_debug();

// read frame information for unwinding from specified ELF section.
bool read_frame(const char* section, int fd, frame_info* frame);

// deletes a thread from the thread list
void delete_thread_info(struct ps_prochandle* ph, thread_info* thr);

Expand Down
5 changes: 0 additions & 5 deletions src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -408,11 +408,6 @@ static bool read_lib_info(struct ps_prochandle* ph) {
#endif
if ((lib = add_lib_info(ph, word[5], (uintptr_t)base)) == NULL)
continue; // ignore, add_lib_info prints error

// we don't need to keep the library open, symtab is already
// built. Only for core dump we need to keep the fd open.
close(lib->fd);
lib->fd = -1;
}
}
fclose(fp);
Expand Down
Loading