[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/certik/lpython/main/src/libasr/stacktrace.cpp [Back]  [Original]

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

// free() and abort() functions
#include 

// For registering SIGSEGV callbacks
#include 


// The following C headers are needed for some specific C functionality (see
// the comments), which is not available in C++:

#ifdef HAVE_LFORTRAN_UNWIND
// For _Unwind_Backtrace() function
#  include 
#endif

#if defined(HAVE_LFORTRAN_DEMANGLE)
// For demangling function names
#  include 
#endif

#ifdef HAVE_LFORTRAN_LINK
// For dl_iterate_phdr() functionality
#  include 
#endif

#ifdef HAVE_LFORTRAN_MACHO
#  include 
#endif

#ifdef HAVE_LFORTRAN_BFD
// For bfd_* family of functions for loading debugging symbols from the binary
// This is the only nonstandard header file and the binary needs to be linked
// with "-lbfd".
// Note: on macOS, one must call `dsymutil` on any binary in order for BFD to
// be able to find line number information. Example:
//
//     $ dsymutil ./test_stacktrace
//
// This is done automatically by our CMake build system for the `lfortran`
// binary.
//
// The bfd.h header file sometimes requires the PACKAGE define to be defined:
#  define PACKAGE 1
#  include 
#endif

namespace LFortran {

std::string binary_executable_path = "/proc/self/exe";

#ifdef HAVE_LFORTRAN_UNWIND

static _Unwind_Reason_Code unwind_callback(struct _Unwind_Context *context,
  void *vdata)
{
  std::vector &d = *(std::vector *)vdata;
  uintptr_t pc;
  pc = _Unwind_GetIP(context);
  if (pc != 0) {
    pc--;
    StacktraceItem i;
    i.pc = pc;
    d.push_back(i);
  }
  return _URC_NO_REASON;
}

#endif // HAVE_LFORTRAN_UNWIND


#ifdef HAVE_LFORTRAN_LINK

/* Tries to find the 'data.addr' in the current shared lib (as passed in
   'info'). If it succeeds, returns (in the 'data') the full path to the shared
   lib and the local address in the file.
*/
int shared_lib_callback(struct dl_phdr_info *info,
  size_t /* size */, void *_data)
{
  StacktraceItem &item = *(StacktraceItem *)_data;
  for (int i=0; i < info->dlpi_phnum; i++) {
    if (info->dlpi_phdr[i].p_type == PT_LOAD) {
      ElfW(Addr) min_addr = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
      ElfW(Addr) max_addr = min_addr + info->dlpi_phdr[i].p_memsz;
      if ((item.pc >= min_addr) && (item.pc < max_addr)) {
        item.binary_filename = info->dlpi_name;
        if (item.binary_filename == "") {
            item.binary_filename = binary_executable_path;
        }
        item.local_pc = item.pc - info->dlpi_addr;
        // We found a match, return a non-zero value
        return 1;
      }
    }
  }
  // We didn't find a match, return a zero value
  return 0;
}

#endif // HAVE_LFORTRAN_LINK


// Fills in `local_pc` and `binary_filename` of `item`
void get_local_address(StacktraceItem &item)
{
#ifdef HAVE_LFORTRAN_LINK
    // Iterate over all loaded shared libraries (see dl_iterate_phdr(3) -
    // Linux man page for more documentation)
    if (dl_iterate_phdr(shared_lib_callback, &item) == 0) {
      // `dl_iterate_phdr` returns the last value returned by our
      // `shared_lib_callback`. It will only be 0 if no shared library
      // (including the main program) contains the address `match.addr`. Given
      // that the addresses are collected from a stacktrace, this should only
      // happen if the stacktrace is somehow corrupted. In that case, we simply
      // abort here.
      // `dl_iterate_phdr` returns the last value returned by our
      // `shared_lib_callback`. It will only be 0 if no shared library
      // `dl_iterate_phdr` returns the last value returned by our
      // `shared_lib_callback`. It will only be 0 if no shared library
      // (including the main program) contains the address `match.addr`. Given
      // that the addresses are collected from a stacktrace, this should only
      // happen if the stacktrace is somehow corrupted. In that case, we simply
      // abort here.
      std::cout ncmds; j++) {
            if (cmd->cmd == LC_SEGMENT) {
                struct segment_command* seg = (struct segment_command*)cmd;
                if (((intptr_t)item.pc >= (seg->vmaddr+offset)) &&
                    ((intptr_t)item.pc < (seg->vmaddr+offset + seg->vmsize))) {
                    item.local_pc = item.pc - offset;
                    item.binary_filename = _dyld_get_image_name(i);
                    // Resolve symlinks to a real path:
                    char buffer[PATH_MAX];
                    char* resolved;
                    resolved = realpath(item.binary_filename.c_str(), buffer);
                    if (resolved) item.binary_filename = resolved;
                    return;
                }
            }
            if (cmd->cmd == LC_SEGMENT_64) {
                struct segment_command_64* seg = (struct segment_command_64*)cmd;
                if ((item.pc >= (seg->vmaddr + offset)) &&
                    (item.pc < (seg->vmaddr + offset + seg->vmsize))) {
                    item.local_pc = item.pc - offset;
                    item.binary_filename = _dyld_get_image_name(i);
                    // Resolve symlinks to a real path:
                    char buffer[PATH_MAX];
                    char* resolved;
                    resolved = realpath(item.binary_filename.c_str(), buffer);
                    if (resolved) item.binary_filename = resolved;
                    return;
                }
            }
            cmd = (struct load_command*)((char*)cmd + cmd->cmdsize);
        }
    }
    std::cout line_found) {
    // If we already found the line, exit
    return;
  }
  if ((section->flags & SEC_ALLOC) == 0) {
    return;
  }

  bfd_vma section_vma = section->vma;
  if (data->addr < section_vma) {
    // If the addr lies above the section, exit
    return;
  }

  bfd_size_type section_size = section->size * 8;
  if (data->addr >= section_vma + section_size) {
    // If the addr lies below the section, exit
    return;
  }

  // Calculate the correct offset of our line in the section
  bfd_vma offset = data->addr - section_vma;

  // Finds the line corresponding to the offset

  const char *filename=NULL, *function_name=NULL;
  data->line_found = bfd_find_nearest_line(abfd, section, data->symbol_table,
    offset, &filename, &function_name, &data->line);

  if (filename == NULL)
    data->filename = "";
  else
    data->filename = filename;

  if (function_name == NULL)
    data->function_name = "";
  else
    data->function_name = function_name;
}


/* Loads the symbol table into 'data->symbol_table'.  */
int load_symbol_table(bfd *abfd, line_data *data)
{
  if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0)
    // If we don't have any symbols, return
    return 0;

  void **symbol_table_ptr = reinterpret_cast(&data->symbol_table);
  long n_symbols;
  unsigned int symbol_size;
  n_symbols = bfd_read_minisymbols(abfd, false, symbol_table_ptr, &symbol_size);
  if (n_symbols == 0) {
    // If the bfd_read_minisymbols() already allocated the table, we need
    // to free it first:
    if (data->symbol_table != NULL)
      free(data->symbol_table);
    // dynamic
    n_symbols = bfd_read_minisymbols(abfd, true, symbol_table_ptr, &symbol_size);
  }

  if (n_symbols < 0) {
    // bfd_read_minisymbols() failed
    return 1;
  }

  return 0;
}

void get_symbol_info_bfd(std::string binary_filename, uintptr_t addr,
  std::string &source_filename, std::string &function_name,
  int &line_number)
{
  line_data data;
  data.addr = addr;
  data.line_found = 0;
  // Initialize 'abfd' and do some sanity checks
  bfd *abfd;
  abfd = bfd_openr(binary_filename.c_str(), NULL);
  if (abfd == NULL) {
    std::cout 

Web Proxy Viewer  |  New URL  |  Original Page