Skip to content

Commit

Permalink
cppify (#3)
Browse files Browse the repository at this point in the history
* c++ify bunch of files
* fix tracer env issue (std::string was not available in fork due to
`init()` not being called in fork)
* some refactoring
* making parts optional
* add a bit of debug
  • Loading branch information
partouf authored Sep 5, 2024
1 parent 8551045 commit e3fa80a
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 34 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:

jobs:
build:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04

steps:
- uses: actions/checkout@v4
Expand All @@ -30,7 +30,7 @@ jobs:
export LIBSEGFAULT_TRACER="$PWD/build/tracer"
export LIBSEGFAULT_DEBUG=1
export LD_PRELOAD="$PWD/build/libSegFault.so"
build/faulty-example 2>&1
build/faulty-example 2>&1 | grep "faulty-example.cpp"
- name: Release
uses: softprops/action-gh-release@v2
Expand Down
5 changes: 3 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ FetchContent_MakeAvailable(cpptrace)
add_library(
SegFault
SHARED
libSegFault/segfault.c
libSegFault/_itoa.c
libSegFault/config.cpp
libSegFault/segfault.cpp
libSegFault/_itoa.cpp
libSegFault/signal-safe-trace.cpp
)
target_include_directories(SegFault PRIVATE libSegFault/${ARCH} libSegFault/) # Note: Order is important
Expand Down
34 changes: 31 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,46 @@
This is a compiler-explorer fork of the glibc-tools repository, which contains tools and library provided by glibc that
either have been deprectated of moved out from the project. At the time of this fork, it contained only libSegFault.

Changes to libSegFault:
## Changes to libSegFault
- CMake for building
- Improved stack tracing with [cpptrace](https://github.com/jeremy-rifkin/cpptrace)

Dependencies:
## Dependencies

- GCC 11 or higher (or equivalent)
- Cpptrace depends on libdwarf, zlib, zstd, and is configured to use libunwind.

Contributing:
## Contributing

To build locally run `make build`. Example usage:

```
LD_PRELOAD=$LD_PRELOAD:build/libSegFault.so LIBSEGFAULT_TRACER=build/tracer ./some_program
```

## Environment variables

### Required

* LIBSEGFAULT_TRACER
- Full path to the `tracer` program

### Optional

* SEGFAULT_SIGNALS="..."
- Space separated list of signals to listen to
- Supported values are: segv, ill, abrt, fpe, bus, stkflt, all

* LIBSEGFAULT_REGISTERS=1
- Include the state of all the registers

* LIBSEGFAULT_MEMORY=1
- Include a memory map

* LIBSEGFAULT_DEBUG=1
- For situations when libSegFault doesn't work as expected

### Deprecated / Should not use

* SEGFAULT_USE_ALTSTACK
* SEGFAULT_OUTPUT_NAME
4 changes: 2 additions & 2 deletions libSegFault/_itoa.c → libSegFault/_itoa.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
#include <_itoa.h>

/* Upper-case digits. */
static const char _itoa_upper_digits[36] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static const char _itoa_upper_digits[37] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/* Lower-case digits. */
static const char _itoa_lower_digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz";
static const char _itoa_lower_digits[37] = "0123456789abcdefghijklmnopqrstuvwxyz";

char *_itoa_word(_ITOA_WORD_TYPE value, char *buflim, unsigned int base, int upper_case)
{
Expand Down
51 changes: 51 additions & 0 deletions libSegFault/config.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <cstdlib>
#include <cstring>

const char *getTracerProgram()
{
static const char *emptystr = "";
if (const char *value = getenv("LIBSEGFAULT_TRACER"))
{
return value;
}

return emptystr;
}

bool isDebugMode()
{
if (const char *value = getenv("LIBSEGFAULT_DEBUG"))
{
if (strcmp(value, "1") == 0)
return true;
}

return false;
}

bool useAlternativeStack()
{
return getenv("SEGFAULT_USE_ALTSTACK") != 0;
}

bool dumpRegisters()
{
if (const char *value = getenv("LIBSEGFAULT_REGISTERS"))
{
if (strcmp(value, "1") == 0)
return true;
}

return false;
}

bool dumpMemory()
{
if (const char *value = getenv("LIBSEGFAULT_MEMORY"))
{
if (strcmp(value, "1") == 0)
return true;
}

return false;
}
7 changes: 7 additions & 0 deletions libSegFault/config.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once

bool isDebugMode();
const char *getTracerProgram();
bool useAlternativeStack();
bool dumpRegisters();
bool dumpMemory();
30 changes: 18 additions & 12 deletions libSegFault/segfault.c → libSegFault/segfault.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <sigcontextinfo.h>

#include "signal-safe-trace.hpp"
#include "config.hpp"

#ifdef SA_SIGINFO
#define SIGCONTEXT siginfo_t *info, void *
Expand Down Expand Up @@ -84,27 +85,33 @@ static void catch_segfault(int signal, SIGCONTEXT ctx)
WRITE_STRING("\n");

#ifdef REGISTER_DUMP
REGISTER_DUMP;
if (dumpRegisters())
{
REGISTER_DUMP;
}
#endif

WRITE_STRING("\n");

do_signal_safe_trace();

#ifdef HAVE_PROC_SELF
/* Now the link map. */
int mapfd = open("/proc/self/maps", O_RDONLY);
if (mapfd != -1)
if (dumpMemory())
{
WRITE_STRING("\nMemory map:\n\n");
/* Now the link map. */
int mapfd = open("/proc/self/maps", O_RDONLY);
if (mapfd != -1)
{
WRITE_STRING("\nMemory map:\n\n");

char buf[256];
ssize_t n;
char buf[256];
ssize_t n;

while ((n = TEMP_FAILURE_RETRY(read(mapfd, buf, sizeof(buf)))) > 0)
TEMP_FAILURE_RETRY(write(fd, buf, n));
while ((n = TEMP_FAILURE_RETRY(read(mapfd, buf, sizeof(buf)))) > 0)
TEMP_FAILURE_RETRY(write(fd, buf, n));

close(mapfd);
close(mapfd);
}
}
#endif

Expand Down Expand Up @@ -133,8 +140,7 @@ static void __attribute__((constructor)) install_handler(void)
sigemptyset(&sa.sa_mask);
sa.sa_flags |= SA_RESTART;

/* Maybe we are expected to use an alternative stack. */
if (getenv("SEGFAULT_USE_ALTSTACK") != 0)
if (useAlternativeStack())
{
void *stack_mem = malloc(2 * SIGSTKSZ);
stack_t ss;
Expand Down
36 changes: 24 additions & 12 deletions libSegFault/signal-safe-trace.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "signal-safe-trace.hpp"
#include "config.hpp"

#include <cpptrace/cpptrace.hpp>

Expand All @@ -14,6 +15,8 @@
#include <sys/wait.h>
#include <unistd.h>

#include <errno.h>

using namespace std::literals;

struct pipe_t
Expand All @@ -30,8 +33,6 @@ struct pipe_t
};
static_assert(sizeof(pipe_t) == 2 * sizeof(int), "Unexpected struct packing");

std::string tracer_program = "tracer";

void warmup_cpptrace()
{
cpptrace::frame_ptr buffer[10];
Expand All @@ -40,19 +41,17 @@ void warmup_cpptrace()
cpptrace::get_safe_object_frame(buffer[0], &frame);
}


[[gnu::constructor]] void init()
{
warmup_cpptrace();
if (const char *value = getenv("LIBSEGFAULT_TRACER"))
{
tracer_program = value;
}
}

constexpr auto fork_failure_message = "fork() failed, unable to collect trace\n"sv;
auto no_tracer_message = "exec(signal_tracer) failed: Please supply the environment variable LIBSEGFAULT_TRACER.\n"sv;
const auto fork_failure_message = "fork() failed, unable to collect trace\n"sv;
const auto no_tracer_message = "exec(signal_tracer) failed: Please supply the environment variable LIBSEGFAULT_TRACER.\n"sv;

auto exec_failure_message = "exec(signal_tracer) failed: Make sure the signal_tracer exists and the executable permissions are correct.\n"sv;
const auto exec_failure_message =
"exec(signal_tracer) failed: Make sure the signal_tracer exists and the executable permissions are correct.\n"sv;

void do_signal_safe_trace()
{
Expand All @@ -74,10 +73,23 @@ void do_signal_safe_trace()
dup2(input_pipe.read_end, STDIN_FILENO);
close(input_pipe.read_end);
close(input_pipe.write_end);
if (tracer_program.length() == 0) {

bool is_debug = isDebugMode();
const char *tracer_program = getTracerProgram();
if (strlen(tracer_program) == 0)
{
std::ignore = write(STDERR_FILENO, no_tracer_message.data(), no_tracer_message.size());
} else {
execl(tracer_program.c_str(), tracer_program.c_str(), nullptr);
}
else
{
execl(tracer_program, tracer_program, nullptr);

if (is_debug)
{
auto errcode = errno;
fprintf(stderr, "errno: %d\n", errcode);
fprintf(stderr, "tried to execute: %s\n", tracer_program);
}

// https://linux.die.net/man/3/execl - execl() only returns when an error has occured
// otherwise this basically exits out of this code
Expand Down
2 changes: 1 addition & 1 deletion libSegFault/x86_64/register-dump.h
Original file line number Diff line number Diff line change
Expand Up @@ -319,4 +319,4 @@ static void register_dump(int fd, ucontext_t *ctx)
}


#define REGISTER_DUMP register_dump(fd, ctx)
#define REGISTER_DUMP register_dump(fd, static_cast<ucontext_t *>(ctx))

0 comments on commit e3fa80a

Please sign in to comment.