Skip to content

Commit

Permalink
Add initial macOS support for stacktraces
Browse files Browse the repository at this point in the history
So far a binary file name and a local address works.
  • Loading branch information
certik committed Aug 27, 2020
1 parent bd0cf4a commit c488f7b
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 3 deletions.
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ set(WITH_BFD no
CACHE BOOL "Build with BFD support")
set(WITH_LINKH no
CACHE BOOL "Build with link.h support")
set(WITH_MACHO no
CACHE BOOL "Build with mach-o support")
set(WITH_STACKTRACE no
CACHE BOOL "Build with stacktrace support (requires binutils-dev)")
if (WITH_STACKTRACE)
Expand All @@ -160,6 +162,10 @@ if (WITH_LINKH)
find_package(LINKH REQUIRED)
set(HAVE_LFORTRAN_LINK yes)
endif()
if (WITH_MACHO)
find_package(MACHO REQUIRED)
set(HAVE_LFORTRAN_MACHO yes)
endif()
if (WITH_UNWIND)
set(HAVE_LFORTRAN_UNWIND yes)
endif()
Expand Down Expand Up @@ -188,6 +194,7 @@ message("WITH_STACKTRACE: ${WITH_STACKTRACE}")
message("WITH_UNWIND: ${WITH_UNWIND}")
message("WITH_BFD: ${WITH_BFD}")
message("WITH_LINKH: ${WITH_LINKH}")
message("WITH_MACHO: ${WITH_MACHO}")
message("HAVE_LFORTRAN_DEMANGLE: ${HAVE_LFORTRAN_DEMANGLE}")
message("WITH_LLVM: ${WITH_LLVM}")
message("WITH_XEUS: ${WITH_XEUS}")
Expand Down
8 changes: 8 additions & 0 deletions cmake/FindMACHO.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
find_path(MACHO_INCLUDE_DIR mach-o/dyld.h)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MACHO DEFAULT_MSG MACHO_INCLUDE_DIR)

add_library(p::macho INTERFACE IMPORTED)
set_property(TARGET p::macho PROPERTY INTERFACE_INCLUDE_DIRECTORIES
${MACHO_INCLUDE_DIR})
1 change: 1 addition & 0 deletions src/lfortran/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#cmakedefine HAVE_LFORTRAN_STACKTRACE
#cmakedefine HAVE_LFORTRAN_BFD
#cmakedefine HAVE_LFORTRAN_LINK
#cmakedefine HAVE_LFORTRAN_MACHO
#cmakedefine HAVE_LFORTRAN_UNWIND

/* Define if cxxabi.h is present */
Expand Down
54 changes: 51 additions & 3 deletions src/lfortran/stacktrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
# include <link.h>
#endif

#ifdef HAVE_LFORTRAN_MACHO
# include <mach-o/dyld.h>
#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
Expand Down Expand Up @@ -94,12 +98,24 @@ int shared_lib_callback(struct dl_phdr_info *info,
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
Expand All @@ -109,10 +125,44 @@ void get_local_address(StacktraceItem &item)
std::cout << "The stack address was not found in any shared library or the main program, the stack is probably corrupted. Aborting." << std::endl;
abort();
}
#else
#ifdef HAVE_LFORTRAN_MACHO
for (uint32_t i = 0; i < _dyld_image_count(); i++) {
const struct mach_header *header = _dyld_get_image_header(i);
intptr_t offset = _dyld_get_image_vmaddr_slide(i);
struct load_command* cmd = (struct load_command*)((char *)header + sizeof(struct mach_header));
if(header->magic == MH_MAGIC_64) {
cmd = (struct load_command*)((char *)header + sizeof(struct mach_header_64));
}
for (uint32_t j = 0; j < header->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);
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);
return;
}
}
cmd = (struct load_command*)((char*)cmd + cmd->cmdsize);
}
}
std::cout << "The stack address was not found in any shared library or the main program, the stack is probably corrupted. Aborting." << std::endl;
abort();
#endif // HAVE_LFORTRAN_MACHO
#endif // HAVE_LFORTRAN_LINK
}


#endif // HAVE_LFORTRAN_LINK


/* Demangles the function name if needed (if the 'name' is coming from C, it
Expand Down Expand Up @@ -457,9 +507,7 @@ std::vector<StacktraceItem> get_stacktrace_addresses()
void get_local_addresses(std::vector<StacktraceItem> &d)
{
for (size_t i=0; i < d.size(); i++) {
#ifdef HAVE_LFORTRAN_LINK
get_local_address(d[i]);
#endif
}
}

Expand Down

0 comments on commit c488f7b

Please sign in to comment.