Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[sanitizer_common] Implement address sanitizer on AIX: add/update functions (4/n) #131868

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

Open
wants to merge 21 commits into
base: main
Choose a base branch
from

Conversation

jakeegan
Copy link
Member

@jakeegan jakeegan commented Mar 18, 2025

Add/update functions for AIX support. We call libc functions via function pointer to avoid timing issues.

Previous PR: #131866 next PR: #131870

Copy link

github-actions bot commented Mar 18, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@llvmbot
Copy link
Member

llvmbot commented Mar 24, 2025

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: Jake Egan (jakeegan)

Changes

Implements AIX specific process memory mapping, stack unwinding, and symbolization. Some AIX differences that influenced these changes:

  • procmap returns incomplete paths to libraries, so the full path has to be constructed.
  • The environment variable LIBPATH is used for shared library paths.
  • Instructions in shared libraries start at non-zero addresses.
  • Internal libc functions may be called before interceptor initialization, so need to call the real libc function via function pointer

Builds on the effort to support AIX in sanitizer_common/asan.


Patch is 47.92 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/131868.diff

21 Files Affected:

  • (modified) compiler-rt/lib/sanitizer_common/CMakeLists.txt (+4)
  • (added) compiler-rt/lib/sanitizer_common/sanitizer_aix.cpp (+500)
  • (added) compiler-rt/lib/sanitizer_common/sanitizer_aix.h (+47)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_common.cpp (+6)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_common.h (+6-1)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_file.cpp (+2-2)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_file.h (+2-1)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_posix.h (+5)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp (+26-3)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_procmaps.h (+9-9)
  • (added) compiler-rt/lib/sanitizer_common/sanitizer_procmaps_aix.cpp (+212)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_procmaps_common.cpp (+10-5)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cpp (+5-4)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.cpp (+4-1)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cpp (+3)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp (+33-6)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.cpp (+1-1)
  • (added) compiler-rt/lib/sanitizer_common/sanitizer_unwind_aix.cpp (+66)
  • (modified) compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cpp (+6-6)
  • (added) compiler-rt/test/sanitizer_common/TestCases/Inputs/sanitizer_default_arch/llvm-symbolizer (+7)
  • (added) compiler-rt/test/sanitizer_common/TestCases/symbolizer_default_arch_ppc64.cpp (+18)
diff --git a/compiler-rt/lib/sanitizer_common/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/CMakeLists.txt
index 09391e4f5f370..8789e02780050 100644
--- a/compiler-rt/lib/sanitizer_common/CMakeLists.txt
+++ b/compiler-rt/lib/sanitizer_common/CMakeLists.txt
@@ -2,6 +2,7 @@
 # These components are shared between AddressSanitizer and ThreadSanitizer.
 
 set(SANITIZER_SOURCES_NOTERMINATION
+  sanitizer_aix.cpp
   sanitizer_allocator.cpp
   sanitizer_common.cpp
   sanitizer_deadlock_detector1.cpp
@@ -25,6 +26,7 @@ set(SANITIZER_SOURCES_NOTERMINATION
   sanitizer_platform_limits_solaris.cpp
   sanitizer_posix.cpp
   sanitizer_printf.cpp
+  sanitizer_procmaps_aix.cpp
   sanitizer_procmaps_common.cpp
   sanitizer_procmaps_bsd.cpp
   sanitizer_procmaps_fuchsia.cpp
@@ -95,6 +97,7 @@ set(SANITIZER_SYMBOLIZER_SOURCES
   sanitizer_symbolizer_report_fuchsia.cpp
   sanitizer_symbolizer_win.cpp
   sanitizer_thread_history.cpp
+  sanitizer_unwind_aix.cpp
   sanitizer_unwind_linux_libcdep.cpp
   sanitizer_unwind_fuchsia.cpp
   sanitizer_unwind_win.cpp
@@ -107,6 +110,7 @@ set(SANITIZER_IMPL_HEADERS
   sancov_flags.h
   sancov_flags.inc
   sanitizer_addrhashmap.h
+  sanitizer_aix.h
   sanitizer_allocator.h
   sanitizer_allocator_checks.h
   sanitizer_allocator_combined.h
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_aix.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_aix.cpp
new file mode 100644
index 0000000000000..b45ce619d7098
--- /dev/null
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_aix.cpp
@@ -0,0 +1,500 @@
+//===-- sanitizer_aix.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries and
+// implements AIX-specific functions.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_AIX
+#  include <dlfcn.h>
+#  include <errno.h>
+#  include <fcntl.h>
+#  include <pthread.h>
+#  include <sched.h>
+#  include <signal.h>
+#  include <stdio.h>
+#  include <stdlib.h>
+#  include <string.h>
+#  include <sys/errno.h>
+#  include <sys/mman.h>
+#  include <sys/procfs.h>
+#  include <sys/stat.h>
+#  include <sys/thread.h>
+#  include <sys/time.h>
+#  include <sys/types.h>
+#  include <sys/ucontext.h>
+#  include <unistd.h>
+
+#  include "interception/interception.h"
+#  include "sanitizer_aix.h"
+#  include "sanitizer_common.h"
+#  include "sanitizer_file.h"
+#  include "sanitizer_libc.h"
+#  include "sanitizer_procmaps.h"
+
+extern char **environ;
+extern char **p_xargv;
+
+namespace __sanitizer {
+
+#  include "sanitizer_syscall_generic.inc"
+
+static void *GetFuncAddr(const char *name) {
+  // FIXME: if we are going to ship dynamic asan library, we may need to search
+  // all the loaded modules with RTLD_DEFAULT if RTLD_NEXT failed.
+  void *addr = dlsym(RTLD_NEXT, name);
+  return addr;
+}
+
+// Internal implementation for the libc functions are also calling to the same
+// name function in libc. However because the same name function to libc may be
+// intercepted, and in the interceptor function, it may call REAL(func). But the
+// REAL(func) may be not assigned at this time, because internal_func may be
+// called before interceptor init functions are called. So we need to call to
+// libc function via function pointer.
+
+#  define _REAL(func, ...) real##_##func(__VA_ARGS__)
+
+#  define DEFINE__REAL(ret_type, func, ...)                       \
+    static ret_type (*real_##func)(__VA_ARGS__) = NULL;           \
+    if (!real_##func) {                                           \
+      real_##func = (ret_type(*)(__VA_ARGS__))GetFuncAddr(#func); \
+    }                                                             \
+    CHECK(real_##func);
+
+uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
+                   u64 offset) {
+  DEFINE__REAL(uptr, mmap, void *addr, uptr length, int prot, int flags, int fd,
+               u64 offset);
+  return _REAL(mmap, addr, length, prot, flags, fd, offset);
+}
+
+uptr internal_munmap(void *addr, uptr length) {
+  DEFINE__REAL(uptr, munmap, void *addr, uptr length);
+  return _REAL(munmap, addr, length);
+}
+
+int internal_mprotect(void *addr, uptr length, int prot) {
+  DEFINE__REAL(int, mprotect, void *addr, uptr length, int prot);
+  return _REAL(mprotect, addr, length, prot);
+}
+
+int internal_madvise(uptr addr, uptr length, int advice) {
+  char *raddr = reinterpret_cast<char *>(addr);
+  DEFINE__REAL(int, madvise, char *raddr, uptr length, int advice)
+  return _REAL(madvise, raddr, length, advice);
+}
+
+uptr internal_close(fd_t fd) {
+  DEFINE__REAL(uptr, close, fd_t fd);
+  return _REAL(close, fd);
+}
+
+uptr internal_open(const char *filename, int flags) {
+  DEFINE__REAL(uptr, open, const char *filename, int flags);
+  return _REAL(open, filename, flags);
+}
+
+uptr internal_open(const char *filename, int flags, u32 mode) {
+  DEFINE__REAL(uptr, open, const char *filename, int flags, u32 mode);
+  return _REAL(open, filename, flags, mode);
+}
+
+__sanitizer_FILE *internal_popen(const char *command, const char *type) {
+  DEFINE__REAL(__sanitizer_FILE *, popen, const char *command,
+               const char *type);
+  return _REAL(popen, command, type);
+}
+
+int internal_pclose(__sanitizer_FILE *file) {
+  FILE *rfile = reinterpret_cast<FILE *>(file);
+  DEFINE__REAL(int, pclose, FILE *file);
+  return _REAL(pclose, rfile);
+}
+
+uptr internal_read(fd_t fd, void *buf, uptr count) {
+  DEFINE__REAL(uptr, read, fd_t fd, void *buf, uptr count);
+  return _REAL(read, fd, buf, count);
+}
+
+uptr internal_write(fd_t fd, const void *buf, uptr count) {
+  DEFINE__REAL(uptr, write, fd_t fd, const void *buf, uptr count);
+  return _REAL(write, fd, buf, count);
+}
+
+uptr internal_stat(const char *path, void *buf) {
+  DEFINE__REAL(uptr, stat, const char *path, void *buf);
+  return _REAL(stat, path, buf);
+}
+
+uptr internal_lstat(const char *path, void *buf) {
+  DEFINE__REAL(uptr, lstat, const char *path, void *buf);
+  return _REAL(lstat, path, buf);
+}
+
+uptr internal_fstat(fd_t fd, void *buf) {
+  DEFINE__REAL(uptr, fstat, fd_t fd, void *buf);
+  return _REAL(fstat, fd, buf);
+}
+
+uptr internal_filesize(fd_t fd) {
+  struct stat st;
+  if (internal_fstat(fd, &st))
+    return -1;
+  return (uptr)st.st_size;
+}
+
+uptr internal_dup(int oldfd) {
+  DEFINE__REAL(uptr, dup, int oldfd);
+  return _REAL(dup, oldfd);
+}
+
+uptr internal_dup2(int oldfd, int newfd) {
+  DEFINE__REAL(uptr, dup2, int oldfd, int newfd);
+  return _REAL(dup2, oldfd, newfd);
+}
+
+uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
+  DEFINE__REAL(uptr, readlink, const char *path, char *buf, uptr bufsize);
+  return _REAL(readlink, path, buf, bufsize);
+}
+
+uptr internal_unlink(const char *path) {
+  DEFINE__REAL(uptr, unlink, const char *path);
+  return _REAL(unlink, path);
+}
+
+uptr internal_sched_yield() {
+  DEFINE__REAL(uptr, sched_yield);
+  return _REAL(sched_yield);
+}
+
+void FutexWait(atomic_uint32_t *p, u32 cmp) { internal_sched_yield(); }
+
+void FutexWake(atomic_uint32_t *p, u32 count) {}
+
+void internal__exit(int exitcode) {
+  DEFINE__REAL(void, _exit, int exitcode);
+  _REAL(_exit, exitcode);
+  Die();  // Unreachable.
+}
+
+void internal_usleep(u64 useconds) {
+  DEFINE__REAL(void, usleep, u64 useconds);
+  _REAL(usleep, useconds);
+}
+
+uptr internal_getpid() {
+  DEFINE__REAL(uptr, getpid);
+  return _REAL(getpid);
+}
+
+int internal_dlinfo(void *handle, int request, void *p) { return 0; }
+
+int internal_sigaction(int signum, const void *act, void *oldact) {
+  DEFINE__REAL(int, sigaction, int signum, const void *act, void *oldact);
+  return _REAL(sigaction, signum, act, oldact);
+}
+
+void internal_sigfillset(__sanitizer_sigset_t *set) {
+  sigset_t *rset = reinterpret_cast<sigset_t *>(set);
+  DEFINE__REAL(void, sigfillset, sigset_t *rset);
+  _REAL(sigfillset, rset);
+}
+
+uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
+                          __sanitizer_sigset_t *oldset) {
+  sigset_t *rset = reinterpret_cast<sigset_t *>(set);
+  sigset_t *roldset = reinterpret_cast<sigset_t *>(oldset);
+  DEFINE__REAL(uptr, sigprocmask, int how, sigset_t *rset, sigset_t *roldset);
+  return _REAL(sigprocmask, how, rset, roldset);
+}
+
+char *internal_getcwd(char *buf, uptr size) {
+  DEFINE__REAL(char *, getcwd, char *buf, uptr size);
+  return _REAL(getcwd, buf, size);
+}
+
+int internal_fork() {
+  DEFINE__REAL(int, fork);
+  return _REAL(fork);
+}
+
+uptr internal_execve(const char *filename, char *const argv[],
+                     char *const envp[]) {
+  DEFINE__REAL(uptr, execve, const char *filename, char *const argv[],
+               char *const envp[]);
+  return _REAL(execve, filename, argv, envp);
+}
+
+uptr internal_waitpid(int pid, int *status, int options) {
+  DEFINE__REAL(uptr, waitpid, int pid, int *status, int options);
+  return _REAL(waitpid, pid, status, options);
+}
+
+int internal_pthread_join(pthread_t thread, void **status) {
+  DEFINE__REAL(int, pthread_join, pthread_t thread, void **status);
+  return _REAL(pthread_join, thread, status);
+}
+
+int internal_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
+                            void *(*start_routine)(void *), void *arg) {
+  DEFINE__REAL(int, pthread_create, pthread_t *thread,
+               const pthread_attr_t *attr, void *(*start_routine)(void *),
+               void *arg);
+  return _REAL(pthread_create, thread, attr, start_routine, arg);
+}
+
+void *internal_start_thread(void *(*func)(void *arg), void *arg) {
+  // Start the thread with signals blocked, otherwise it can steal user signals.
+  __sanitizer_sigset_t set, old;
+  internal_sigfillset(&set);
+  internal_sigprocmask(SIG_SETMASK, &set, &old);
+  pthread_t th;
+  internal_pthread_create(&th, 0, func, arg);
+  internal_sigprocmask(SIG_SETMASK, &old, 0);
+  // pthread_t is unsinged int on AIX
+  return reinterpret_cast<void *>(th);
+}
+
+void internal_join_thread(void *th) {
+  internal_pthread_join((pthread_t)(reinterpret_cast<uptr>(th)), nullptr);
+}
+
+uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp) {
+  clock_t rclk_id = reinterpret_cast<clock_t>(clk_id);
+  struct timespec *rtp = reinterpret_cast<struct timespec *>(tp);
+  DEFINE__REAL(uptr, clock_gettime, clock_t rclk_id, struct timespec *rtp);
+  return _REAL(clock_gettime, rclk_id, rtp);
+}
+
+void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
+                                uptr *stack_bottom) {
+  CHECK(stack_top);
+  CHECK(stack_bottom);
+  if (at_initialization) {
+    // This is the main thread. Libpthread may not be initialized yet.
+    struct rlimit rl;
+    CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
+
+    // Find the mapping that contains a stack variable.
+    MemoryMappingLayout proc_maps(/*cache_enabled*/ true);
+    if (proc_maps.Error()) {
+      *stack_top = *stack_bottom = 0;
+      return;
+    }
+    MemoryMappedSegment segment;
+    uptr prev_end = 0;
+    while (proc_maps.Next(&segment)) {
+      if ((uptr)&rl < segment.end)
+        break;
+      prev_end = segment.end;
+    }
+
+    CHECK((uptr)&rl >= segment.start && (uptr)&rl < segment.end);
+
+    // Get stacksize from rlimit, but clip it so that it does not overlap
+    // with other mappings.
+    uptr stacksize = rl.rlim_cur;
+    if (stacksize > segment.end - prev_end)
+      stacksize = segment.end - prev_end;
+    // When running with unlimited stack size, we still want to set some limit.
+    // The unlimited stack size is caused by 'ulimit -s unlimited'.
+    // Also, for some reason, GNU make spawns subprocesses with unlimited stack.
+    if (stacksize > kMaxThreadStackSize)
+      stacksize = kMaxThreadStackSize;
+    *stack_top = segment.end;
+    *stack_bottom = segment.end - stacksize;
+    return;
+  }
+  uptr stacksize = 0;
+  void *stackaddr = nullptr;
+  pthread_attr_t attr;
+  pthread_attr_init(&attr);
+  CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);
+  internal_pthread_attr_getstack(&attr, &stackaddr, &stacksize);
+  pthread_attr_destroy(&attr);
+
+  *stack_top = (uptr)stackaddr;
+  *stack_bottom = (uptr)stackaddr - stacksize;
+}
+
+void GetThreadStackAndTls(bool main, uptr *stk_begin, uptr *stk_end,
+                          uptr *tls_begin, uptr *tls_end) {
+  // FIXME: handle TLS related flag
+  *tls_begin = 0;
+  *tls_end = 0;
+
+  uptr stack_top, stack_bottom;
+  GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
+  *stk_begin = stack_bottom;
+  *stk_end = stack_top;
+}
+
+const char *GetEnv(const char *name) { return getenv(name); }
+
+tid_t GetTid() { return thread_self(); }
+
+uptr ReadBinaryName(char *buf, uptr buf_len) {
+  struct stat statData;
+  struct psinfo psinfoData;
+
+  char FilePsinfo[100] = {};
+  internal_snprintf(FilePsinfo, 100, "/proc/%d/psinfo", internal_getpid());
+  CHECK_EQ(internal_stat(FilePsinfo, &statData), 0);
+
+  const int fd = internal_open(FilePsinfo, O_RDONLY);
+  ssize_t readNum = internal_read(fd, &psinfoData, sizeof(psinfoData));
+  CHECK_GE(readNum, 0);
+
+  internal_close(fd);
+  char *binary_name = (reinterpret_cast<char ***>(psinfoData.pr_argv))[0][0];
+
+  // This is an absulate path.
+  if (binary_name[0] == '/')
+    return internal_snprintf(buf, buf_len, "%s", binary_name);
+
+  // This is a relative path to the binary, starts with ./ or ../
+  if (binary_name[0] == '.') {
+    char *path = nullptr;
+    if ((path = internal_getcwd(buf, buf_len)) != nullptr)
+      return internal_snprintf(buf + internal_strlen(path),
+                               buf_len - internal_strlen(path), "/%s",
+                               binary_name) +
+             internal_strlen(path);
+  }
+
+  // This is running a raw binary in the dir where it is from.
+  char *path = nullptr;
+  if ((path = internal_getcwd(buf, buf_len)) != nullptr) {
+    char fullName[kMaxPathLength] = {};
+    internal_snprintf(fullName, kMaxPathLength, "%s/%s", path, binary_name);
+    if (FileExists(fullName))
+      return internal_snprintf(buf + internal_strlen(path),
+                               buf_len - internal_strlen(path), "/%s",
+                               binary_name) +
+             internal_strlen(path);
+  }
+
+  // Find the binary in the env PATH.
+  if ((path = FindPathToBinaryOrLibrary(binary_name)) != nullptr)
+    return internal_snprintf(buf, buf_len, "%s", path);
+
+  return 0;
+}
+
+// https://www.ibm.com/docs/en/aix/7.3?topic=concepts-system-memory-allocation-using-malloc-subsystem
+uptr GetMaxVirtualAddress() {
+#  if SANITIZER_WORDSIZE == 64
+  return (1ULL << 60) - 1;
+#  else
+  return 0xffffffff;
+#  endif
+}
+
+uptr GetMaxUserVirtualAddress() { return GetMaxVirtualAddress(); }
+
+uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {
+  return ReadBinaryName(buf, buf_len);
+}
+
+void InitializePlatformCommonFlags(CommonFlags *cf) {}
+
+void InitializePlatformEarly() {
+  // Do nothing.
+}
+
+uptr GetPageSize() { return getpagesize(); }
+
+void CheckASLR() {
+  // Do nothing
+}
+
+HandleSignalMode GetHandleSignalMode(int signum) {
+  switch (signum) {
+    case SIGABRT:
+      return common_flags()->handle_abort;
+    case SIGILL:
+      return common_flags()->handle_sigill;
+    case SIGTRAP:
+      return common_flags()->handle_sigtrap;
+    case SIGFPE:
+      return common_flags()->handle_sigfpe;
+    case SIGSEGV:
+      return common_flags()->handle_segv;
+    case SIGBUS:
+      return common_flags()->handle_sigbus;
+  }
+  return kHandleSignalNo;
+}
+
+void InitTlsSize() {}
+
+bool FileExists(const char *filename) {
+  struct stat st;
+  if (internal_stat(filename, &st))
+    return false;
+  // Sanity check: filename is a regular file.
+  return S_ISREG(st.st_mode);
+}
+
+bool DirExists(const char *path) {
+  struct stat st;
+  if (internal_stat(path, &st))
+    return false;
+  return S_ISDIR(st.st_mode);
+}
+
+uptr GetTlsSize() {
+  // FIXME: implement this interface.
+  return 0;
+}
+
+SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
+  return SignalContext::Unknown;
+}
+
+bool SignalContext::IsTrueFaultingAddress() const { return true; }
+
+void SignalContext::InitPcSpBp() {
+  ucontext_t *ucontext = (ucontext_t *)context;
+  pc = ucontext->uc_mcontext.jmp_context.iar;
+  sp = ucontext->uc_mcontext.jmp_context.gpr[1];
+  // The powerpc{,64} ABIs do not specify r31 as the frame pointer, but compiler
+  // always uses r31 when we need a frame pointer.
+  bp = ucontext->uc_mcontext.jmp_context.gpr[31];
+}
+
+void SignalContext::DumpAllRegisters(void *context) {}
+
+char **GetEnviron() { return environ; }
+
+char **GetArgv() { return p_xargv; }
+
+void ListOfModules::init() {
+  clearOrInit();
+  MemoryMappingLayout memory_mapping(false);
+  memory_mapping.DumpListOfModules(&modules_);
+}
+
+void ListOfModules::fallbackInit() { clear(); }
+
+u64 MonotonicNanoTime() {
+  timespec ts;
+  internal_clock_gettime(CLOCK_MONOTONIC, &ts);
+  return (u64)ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec;
+}
+
+// FIXME implement on this platform.
+void GetMemoryProfile(fill_profile_f cb, uptr *stats) {}
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_AIX
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_aix.h b/compiler-rt/lib/sanitizer_common/sanitizer_aix.h
new file mode 100644
index 0000000000000..8854db9d3b259
--- /dev/null
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_aix.h
@@ -0,0 +1,47 @@
+//===-- sanitizer_aix.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries and
+// provides definitions for AIX-specific functions.
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_AIX_H
+#define SANITIZER_AIX_H
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_AIX
+#  include "sanitizer_common.h"
+#  include "sanitizer_posix.h"
+
+namespace __sanitizer {
+
+#  if SANITIZER_WORDSIZE == 32
+static const uptr InstructionStart = 0x10000000;
+#  else
+static const uptr InstructionStart = 0x100000000;
+#  endif
+
+struct ProcSelfMapsBuff {
+  char *data;
+  uptr mmaped_size;
+  uptr len;
+};
+
+struct MemoryMappingLayoutData {
+  ProcSelfMapsBuff proc_self_maps;
+  const char *current;
+};
+
+void ReadProcMaps(ProcSelfMapsBuff *proc_maps);
+
+char *internal_getcwd(char *buf, uptr size);
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_AIX
+#endif  // SANITIZER_AIX_H
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_common.cpp
index 6cd69a53093e7..3b5286bcf9be9 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common.cpp
@@ -275,6 +275,12 @@ const char *GetProcessName() {
   return process_name_cache_str;
 }
 
+const char *GetBinaryName() {
+  if (binary_name_cache_str[0] == '\0')
+    ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str));
+  return binary_name_cache_str;
+}
+
 static uptr ReadProcessName(/*out*/ char *buf, uptr buf_len) {
   ReadLongProcessName(buf, buf_len);
   char *s = const_cast<char *>(StripModuleName(buf));
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common.h b/compiler-rt/lib/sanitizer_common/sanitizer_common.h
index d9e7ded593feb..466790d335ac0 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common.h
@@ -290,6 +290,7 @@ uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len);
 uptr ReadBinaryDir(/*out*/ char *buf, uptr buf_len);
 uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len);
 const char *GetProcessName();
+const char *GetBinaryName();
 void UpdateProcessName();
 void CacheBinaryName();
 void DisableCoreDumperIfNecessary();
@@ -835,7 +8...
[truncated]

@jakeegan jakeegan requested a review from vitalybuka March 24, 2025 11:59
@jakeegan jakeegan changed the title [sanitizer_common] Add AIX specific functionality [sanitizer_common] Implement address sanitizer on AIX: add platform specific functionality (4/n) Mar 24, 2025
@jakeegan
Copy link
Member Author

Going to make some large changes concerning procmap and instruction starts, so I'll put the PR in draft state for now.

@jakeegan jakeegan marked this pull request as draft March 24, 2025 18:41
@jakeegan jakeegan marked this pull request as ready for review April 8, 2025 10:46
@jakeegan jakeegan changed the title [sanitizer_common] Implement address sanitizer on AIX: add platform specific functionality (4/n) [sanitizer_common] Implement address sanitizer on AIX: add platform specific functionality (4/6) Apr 29, 2025
@vitalybuka
Copy link
Collaborator

Every time I get here, I am skipping it till the better time.

Can we do this in more incremental way?
I'd split it into 10+ independent PRs, similar to what you already have in commits. Make them fix a particular issue.

Many pieces are straight forward which I ready to approve and land any time, but not entire thing.

Also it would be nice create an issue with feature request, and attach all PRs (including clang/llvm) to that issue.

// We don't have the full path to user libraries, so we use what we have
// available as the display name.
// TODO: Append the archive member name if it exists.
const char *displayPath = data_.proc_self_maps.data + mapIter->pr_pathoff;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if AIX can work without display name,
can we first implement in in the way consistent with other platforms, without display
and add display in follow up PR?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AIX can "work" without using the display name, but the unsymbolized output would end up with paths like /proc/30343910/object/a.out or /proc/30343910/object/jfs2.10.5.4242.

That is, the output would be inconsistent with other platforms and not easy to symbolize later.

That said, I believe that the tests are checking the symbolized output, which means that the display name support can probably be moved to another PR.

// PowerPC ABIs specify that the return address is saved at offset
// 16 of the *caller's* stack frame. Thus we must dereference the
// back chain to find the caller frame before extracting it.
# ifdef __powerpc__
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trivial code/comment moves like this either unnecessary or should go into separate [NFC] PRs

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we then replace regular name with display name?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we then replace regular name with display name?

Can you elaborate on what you mean? If we say that the "primary name" is what was already in the code before this patch, are you saying that you prefer for the implementation on AIX to use the display name as the "primary name" (and for AIX to have an additional "object path" that it uses when invoking the symbolizer)?

@jakeegan
Copy link
Member Author

jakeegan commented May 6, 2025

Split part of the PR to:
#138556
#138537
#138188

I'll also open an issue to attach all the PRs to.

@jakeegan jakeegan changed the title [sanitizer_common] Implement address sanitizer on AIX: add platform specific functionality (4/6) [sanitizer_common] Implement address sanitizer on AIX: add/update functions (4/6) May 7, 2025
@jakeegan jakeegan changed the title [sanitizer_common] Implement address sanitizer on AIX: add/update functions (4/6) [sanitizer_common] Implement address sanitizer on AIX: add/update functions (4/n) May 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants