Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AIX support #593

Merged
merged 31 commits into from
Oct 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ff19c8e
Initial AIX (and PASE) support in Sentry library
NattyNarwhal Sep 16, 2021
af2b201
On PASE, flock is provided by libutil
NattyNarwhal Sep 16, 2021
389c626
AIX doesn't have thread names, nop out as fallback
NattyNarwhal Sep 16, 2021
2ffa12f
Similar hack as musl wrt recursive mutex init
NattyNarwhal Sep 16, 2021
e9f031f
AIX doesn't have timegm, hack around w/ setting TZ wrapper around mktime
NattyNarwhal Sep 16, 2021
a5dedad
AIX doesn't have locale version of funcs
NattyNarwhal Sep 16, 2021
2d666d6
AIX needs libm explicitly specified
NattyNarwhal Sep 16, 2021
ab33c7b
Add CMake variable for AIX
NattyNarwhal Sep 16, 2021
124f398
AIX support for module finder
NattyNarwhal Sep 16, 2021
ab1a9a0
Reimplement dladdr on AIX
NattyNarwhal Sep 16, 2021
5ad2d6a
Disable crashpad and breakpad tests on AIX
NattyNarwhal Sep 17, 2021
767d7ff
Properly initialize a static recursive mutex
NattyNarwhal Sep 17, 2021
f232af1
AIX requires a read-write file descriptor for the lock
NattyNarwhal Sep 20, 2021
68a7d54
Allow IBM i to use backtrace
NattyNarwhal Sep 20, 2021
d373443
AIX uses function descriptors, work around
NattyNarwhal Sep 20, 2021
77b6b46
Implement partial get current executable for AIX
NattyNarwhal Sep 20, 2021
f345ebf
Word align searching for traceback on AIX
NattyNarwhal Sep 20, 2021
106bc3a
More AIX problems w/ function descriptors
NattyNarwhal Sep 21, 2021
57bf111
Both sym and lib name need to be allocated in AIX dladdr shim (XXX)
NattyNarwhal Sep 21, 2021
b09a9fc
Default to curl on AIX (fixes some tests)
NattyNarwhal Sep 24, 2021
2501fc1
Ersatz debug_id from timestamp in XCOFF header
NattyNarwhal Sep 24, 2021
70b0d03
On AIX, the null page means null derefs don't fault
NattyNarwhal Sep 28, 2021
6b9a6dd
Run symbolizer on client side on AIX
NattyNarwhal Sep 28, 2021
8ca133e
Clean up memory handling in AIX dladdr shim
NattyNarwhal Sep 28, 2021
cf24439
Explicitly check if we're deleting the current run path when cleaning
NattyNarwhal Oct 14, 2021
dd53eac
Handle PASE returning EINVAL on mkdir
NattyNarwhal Oct 14, 2021
47c4cfd
Correct comment explaining platform symbolification
NattyNarwhal Oct 14, 2021
ecddca9
use arpad's comment for symbolification
NattyNarwhal Oct 15, 2021
d42998d
Clean up AIX modulefinder and remove alloca
NattyNarwhal Oct 15, 2021
597ac06
Make clang-format happy
NattyNarwhal Oct 19, 2021
b32eeaa
This needs to be a wide comparison on Windows
NattyNarwhal Oct 20, 2021
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
13 changes: 12 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ set(CMAKE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/sentry")

if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(LINUX TRUE)
elseif(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "OS400")
set(AIX TRUE)
endif()

#setup sentry library type
Expand Down Expand Up @@ -84,7 +86,7 @@ endif()

if(WIN32)
set(SENTRY_DEFAULT_TRANSPORT "winhttp")
elseif((APPLE AND NOT IOS) OR LINUX)
elseif((APPLE AND NOT IOS) OR LINUX OR AIX)
set(SENTRY_DEFAULT_TRANSPORT "curl")
else()
set(SENTRY_DEFAULT_TRANSPORT "none")
Expand Down Expand Up @@ -248,6 +250,15 @@ else()
endif()
target_compile_definitions(sentry PRIVATE SIZEOF_LONG=${CMAKE_SIZEOF_LONG})

# AIX needs libm for isnan used in test suite
if(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "OS400")
target_link_libraries(sentry PRIVATE m)
endif()
# On IBM i PASE, flock is in libutil. Here because "sentry" exists now.
if(CMAKE_SYSTEM_NAME STREQUAL "OS400")
target_link_libraries(sentry PRIVATE util)
endif()

if(SENTRY_TRANSPORT_CURL)
find_package(CURL REQUIRED)
if(TARGET CURL::libcurl) # Only available in cmake 3.12+
Expand Down
7 changes: 7 additions & 0 deletions examples/example.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,14 @@ has_arg(int argc, char **argv, const char *arg)
return false;
}

#ifdef SENTRY_PLATFORM_AIX
// AIX has a null page mapped to the bottom of memory, which means null derefs
// don't segfault. try dereferencing the top of memory instead; the top nibble
// seems to be unusable.
static void *invalid_mem = (void *)0xFFFFFFFFFFFFFF9B; // -100 for memset
#else
static void *invalid_mem = (void *)1;
#endif

static void
trigger_crash()
Expand Down
4 changes: 4 additions & 0 deletions include/sentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ extern "C" {
#elif defined(__linux) || defined(__linux__)
# define SENTRY_PLATFORM_LINUX
# define SENTRY_PLATFORM_UNIX
#elif defined(_AIX)
/* IBM i PASE is also counted as AIX */
# define SENTRY_PLATFORM_AIX
# define SENTRY_PLATFORM_UNIX
#else
# error unsupported platform
#endif
Expand Down
4 changes: 4 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ elseif(LINUX OR ANDROID)
sentry_target_sources_cwd(sentry
modulefinder/sentry_modulefinder_linux.c
)
elseif(AIX)
sentry_target_sources_cwd(sentry
modulefinder/sentry_modulefinder_aix.c
)
endif()

# transport
Expand Down
109 changes: 109 additions & 0 deletions src/modulefinder/sentry_modulefinder_aix.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include "sentry_boot.h"

#include "sentry_core.h"
#include "sentry_string.h"
#include "sentry_sync.h"
#include "sentry_value.h"

#include <limits.h>
#include <stdio.h>

#define __XCOFF64__
#include <sys/ldr.h>
#include <xcoff.h>

/* library filename + ( + member file name + ) + NUL */
#define AIX_PRINTED_LIB_LEN ((PATH_MAX * 2) + 3)

static bool g_initialized = false;
static sentry_mutex_t g_mutex = SENTRY__MUTEX_INIT;
static sentry_value_t g_modules = { 0 };

static void
load_modules(void)
{
char buf[10000];
int r = loadquery(L_GETINFO, buf, 10000);
if (r == -1) {
return;
}
/* The loader info structures are also a linked list. */
struct ld_info *cur = (struct ld_info *)buf;
do {
sentry_value_t module = sentry_value_new_object();
sentry_value_set_by_key(
module, "type", sentry_value_new_string("xcoff"));

char *tb = (char *)cur->ldinfo_textorg; // text includes XCOFF image
sentry_value_set_by_key(
module, "image_addr", sentry__value_new_addr((uint64_t)tb));
// actually a 64-bit value on 64-bit AIX
uint64_t ts = (uint64_t)cur->ldinfo_textsize;
sentry_value_set_by_key(
module, "image_size", sentry_value_new_int32((uint32_t)ts));

/*
* Under AIX, there are no UUIDs for executables, but we can try to
* use some other fields as an ersatz substitute.
*/
FILHDR *xcoff_header = (FILHDR *)tb;
char timestamp[128];
snprintf(timestamp, 128, "%x", xcoff_header->f_timdat);
sentry_value_set_by_key(
module, "debug_id", sentry_value_new_string(timestamp));

/* library filename + ( + member + ) + NUL */
char libname[AIX_PRINTED_LIB_LEN];
char *file_part = cur->ldinfo_filename;
char *member_part = file_part + strlen(file_part) + 1;
/*
* This can't be a const char*, because it exists from
* a stack allocated buffer. Also append the member.
*
* XXX: See if we can't frob usla's memory ranges for
* const strings; but is quite difficult.
*/
if (member_part[0] == '\0') {
/* Not an archive, just copy the file name. */
snprintf(libname, AIX_PRINTED_LIB_LEN, "%s", file_part);
} else {
/* It's an archive with member. */
snprintf(
libname, AIX_PRINTED_LIB_LEN, "%s(%s)", file_part, member_part);
}
// XXX: This is not an absolute path because AIX doesn't provide
// it. It will have the member name for library archives.
sentry_value_set_by_key(
module, "code_file", sentry_value_new_string(libname));

sentry_value_append(g_modules, module);

cur = (struct ld_info *)((char *)cur + cur->ldinfo_next);
} while (cur->ldinfo_next != 0);
}

sentry_value_t
sentry_get_modules_list(void)
{
sentry__mutex_lock(&g_mutex);
if (!g_initialized) {
g_modules = sentry_value_new_list();
g_initialized = true;
load_modules();
sentry_value_freeze(g_modules);
}
sentry_value_t modules = g_modules;
sentry_value_incref(modules);
sentry__mutex_unlock(&g_mutex);
return modules;
}

void
sentry_clear_modulecache(void)
{
sentry__mutex_lock(&g_mutex);
sentry_value_decref(g_modules);
g_modules = sentry_value_new_null();
g_initialized = false;
sentry__mutex_unlock(&g_mutex);
}
32 changes: 29 additions & 3 deletions src/path/sentry_path_unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
# include <mach-o/dyld.h>
#endif

#ifdef SENTRY_PLATFORM_AIX
# include <procinfo.h>
#endif

// only read this many bytes to memory ever
static const size_t MAX_READ_TO_BUFFER = 134217728;

Expand Down Expand Up @@ -50,7 +54,15 @@ sentry__filelock_try_lock(sentry_filelock_t *lock)
{
lock->is_locked = false;

int fd = open(lock->path->path, O_RDONLY | O_CREAT | O_TRUNC,
const int oflags =
#ifdef SENTRY_PLATFORM_AIX
// Under AIX, O_TRUNC can only be set if it can be written to, and
// flock (well, fcntl) will return EBADF if the fd is not read-write.
O_RDWR | O_CREAT | O_TRUNC;
#else
O_RDONLY | O_CREAT | O_TRUNC;
#endif
int fd = open(lock->path->path, oflags,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (fd < 0) {
return false;
Expand Down Expand Up @@ -130,6 +142,18 @@ sentry__path_current_exe(void)
}
buf[len] = 0;
return sentry__path_from_str(buf);
#elif defined(SENTRY_PLATFORM_AIX)
// You can't get the full path to the current executable; the best is
// either argv[0], or getting the name of the current executable, which
// doesn't even include a path. Let's go with that for now.
// (Actually, AIX may be able to under procfs, but it's System V style,
// not like Linux. And it's not available under PASE anyways.)
struct procentry64 proc;
pid_t pid = getpid();
if (getprocs64(&proc, sizeof(proc), NULL, 0, &pid, 1) < 1) {
return NULL;
}
return sentry__path_from_str(proc.pi_comm);
#endif
return NULL;
}
Expand Down Expand Up @@ -307,7 +331,7 @@ sentry__path_create_dir_all(const sentry_path_t *path)
#define _TRY_MAKE_DIR \
do { \
int mrv = mkdir(p, 0700); \
if (mrv != 0 && errno != EEXIST) { \
if (mrv != 0 && errno != EEXIST && errno != EINVAL) { \
rv = 1; \
goto done; \
} \
Expand Down Expand Up @@ -452,7 +476,9 @@ write_buffer_with_flags(
int fd = open(
path->path, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
if (fd < 0) {
SENTRY_TRACEF("failed to open file \"%s\" for writing", path->path);
SENTRY_TRACEF(
"failed to open file \"%s\" for writing (errno %d, flags %x)",
path->path, errno, flags);
return 1;
}

Expand Down
8 changes: 8 additions & 0 deletions src/sentry_database.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,14 @@ sentry__process_old_runs(const sentry_options_t *options, uint64_t last_crash)
sentry__filelock_free(lock);
continue;
}
// make sure we don't delete ourselves if the lock check fails
#ifdef SENTRY_PLATFORM_WINDOWS
if (wcscmp(options->run->run_path->path, run_dir->path) == 0) {
#else
if (strcmp(options->run->run_path->path, run_dir->path) == 0) {
#endif
continue;
}
sentry_pathiter_t *run_iter = sentry__path_iter_directory(run_dir);
const sentry_path_t *file;
while ((file = sentry__pathiter_next(run_iter)) != NULL) {
Expand Down
5 changes: 4 additions & 1 deletion src/sentry_options.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ sentry_options_new(void)
opts->auto_session_tracking = true;
opts->system_crash_reporter_enabled = false;
opts->symbolize_stacktraces =
#ifdef SENTRY_PLATFORM_ANDROID
// AIX doesn't have reliable debug IDs for server-side symbolication,
// and the diversity of Android makes it infeasible to have access to debug
// files.
#if defined(SENTRY_PLATFORM_ANDROID) || defined(SENTRY_PLATFORM_AIX)
true;
#else
false;
Expand Down
5 changes: 4 additions & 1 deletion src/sentry_sync.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,11 @@ sentry__thread_setname(sentry_threadid_t thread_id, const char *thread_name)
return 1;
}
return pthread_setname_np(thread_name);
# else
# elif defined(SENTRY_PLATFORM_LINUX) /* and possibly others (like BSDs) */
return pthread_setname_np(thread_id, thread_name);
# else
/* XXX: AIX doesn't have it, but PASE does via ILE APIs. */
return 0;
# endif
}
#endif
Expand Down
16 changes: 16 additions & 0 deletions src/sentry_sync.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,22 @@ typedef pthread_cond_t sentry_cond_t;
}
# endif
# define SENTRY__MUTEX_INIT PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
# elif defined(SENTRY_PLATFORM_AIX)
// AIX lacks PTHREAD_RECURSIVE_MUTEX_INITIALIZER, though it does have at least
// PTHREAD_MUTEX_INITIALIZER. Unfortunately, it means you must call the mutex
// init function with an initialized mutexattrs set to recursive. This isn't
// workable due to all the mutexes just sitting around not initialized but
// immediately used. The fields are basically guesswork from what happens when
// you initialize the mutex "properly" but changing the bare minimum from a
// static initialization. (Don't ask me what these fields mean, the struct is
// opaquely defined as long[n] fields.)
# define SENTRY__MUTEX_INIT \
{ \
{ \
0, 0, 0, 0, /*_PTH_FLAGS_INIT64*/ 1, \
PTHREAD_MUTEX_RECURSIVE \
} \
}
# else
# define SENTRY__MUTEX_INIT PTHREAD_RECURSIVE_MUTEX_INITIALIZER
# endif
Expand Down
29 changes: 27 additions & 2 deletions src/sentry_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,29 @@ sentry__iso8601_to_msec(const char *iso)
tm.tm_sec = s;
#ifdef SENTRY_PLATFORM_WINDOWS
time_t time = _mkgmtime(&tm);
#elif defined(SENTRY_PLATFORM_AIX)
/*
* timegm is a GNU extension that AIX doesn't support. We'll have to fake
* it by setting TZ instead w/ mktime, then unsets it. Changes global env.
*/
time_t time;
char *tz_env;
tz_env = getenv("TZ");
if (tz_env) {
/* make a copy of it, since it'll change when we set it to UTC */
tz_env = strdup(tz_env);
}
setenv("TZ", "UTC", 1);
tzset();
time = mktime(&tm);
/* revert */
if (tz_env) {
setenv("TZ", tz_env, 1);
free(tz_env);
} else {
unsetenv("TZ");
}
tzset();
#else
time_t time = timegm(&tm);
#endif
Expand Down Expand Up @@ -476,7 +499,8 @@ sentry__strtod_c(const char *ptr, char **endptr)
{
#ifdef SENTRY_PLATFORM_WINDOWS
return _strtod_l(ptr, endptr, c_locale());
#elif defined(SENTRY_PLATFORM_ANDROID) || defined(SENTRY_PLATFORM_IOS)
#elif defined(SENTRY_PLATFORM_ANDROID) || defined(SENTRY_PLATFORM_IOS) \
|| defined(SENTRY_PLATFORM_AIX)
return strtod(ptr, endptr);
#else
return strtod_l(ptr, endptr, c_locale());
Expand All @@ -492,7 +516,8 @@ sentry__snprintf_c(char *buf, size_t buf_size, const char *fmt, ...)
int rv;
#ifdef SENTRY_PLATFORM_WINDOWS
rv = _vsnprintf_l(buf, buf_size, fmt, c_locale(), args);
#elif defined(SENTRY_PLATFORM_ANDROID) || defined(SENTRY_PLATFORM_IOS)
#elif defined(SENTRY_PLATFORM_ANDROID) || defined(SENTRY_PLATFORM_IOS) \
|| defined(SENTRY_PLATFORM_AIX)
rv = vsnprintf(buf, buf_size, fmt, args);
#elif defined(SENTRY_PLATFORM_DARWIN)
rv = vsnprintf_l(buf, buf_size, c_locale(), fmt, args);
Expand Down
Loading