diff --git a/pkg/collector/python/containers.go b/pkg/collector/python/containers.go index 68b097c45f05..acb518756b1c 100644 --- a/pkg/collector/python/containers.go +++ b/pkg/collector/python/containers.go @@ -14,7 +14,8 @@ import ( /* #include -#cgo !windows LDFLAGS: -ldatadog-agent-rtloader -ldl +#cgo !aix,!windows LDFLAGS: -ldatadog-agent-rtloader -ldl +#cgo aix LDFLAGS: -ldl #cgo windows LDFLAGS: -ldatadog-agent-rtloader -lstdc++ -static */ import "C" diff --git a/pkg/collector/python/datadog_agent.go b/pkg/collector/python/datadog_agent.go index 95ea79bc00de..82a0b4aee756 100644 --- a/pkg/collector/python/datadog_agent.go +++ b/pkg/collector/python/datadog_agent.go @@ -31,7 +31,8 @@ import ( ) /* -#cgo !windows LDFLAGS: -ldatadog-agent-rtloader -ldl +#cgo !aix,!windows LDFLAGS: -ldatadog-agent-rtloader -ldl +#cgo aix LDFLAGS: -ldl #cgo windows LDFLAGS: -ldatadog-agent-rtloader -lstdc++ -static #include "datadog_agent_rtloader.h" diff --git a/pkg/collector/python/init.go b/pkg/collector/python/init.go index 3742a54d9184..583aace4b8dd 100644 --- a/pkg/collector/python/init.go +++ b/pkg/collector/python/init.go @@ -34,7 +34,10 @@ import ( ) /* -#cgo !windows LDFLAGS: -L${SRCDIR}/../../../rtloader/build/rtloader -ldatadog-agent-rtloader -ldl +// On AIX, Go's CGO requires shared libraries to be wrapped in .a archives. +// libdatadog-agent-rtloader.a is built from the .so file using "ar -X64 -r". +#cgo aix LDFLAGS: -L${SRCDIR}/../../../rtloader/build/rtloader -ldatadog-agent-rtloader -ldl +#cgo !aix,!windows LDFLAGS: -L${SRCDIR}/../../../rtloader/build/rtloader -ldatadog-agent-rtloader -ldl #cgo windows LDFLAGS: -L${SRCDIR}/../../../rtloader/build/rtloader -ldatadog-agent-rtloader -lstdc++ -static #cgo CFLAGS: -I "${SRCDIR}/../../../rtloader/include" -I "${SRCDIR}/../../../rtloader/common" diff --git a/pkg/collector/python/init_nix.go b/pkg/collector/python/init_nix.go index c9170fceb9ff..d3fda27b51ec 100644 --- a/pkg/collector/python/init_nix.go +++ b/pkg/collector/python/init_nix.go @@ -16,7 +16,8 @@ import ( ) /* -#cgo !windows LDFLAGS: -ldatadog-agent-rtloader -ldl +#cgo !aix,!windows LDFLAGS: -ldatadog-agent-rtloader -ldl +#cgo aix LDFLAGS: -ldl #include #include diff --git a/pkg/collector/python/kubeutil.go b/pkg/collector/python/kubeutil.go index f3d81277ce53..6f47cb712140 100644 --- a/pkg/collector/python/kubeutil.go +++ b/pkg/collector/python/kubeutil.go @@ -20,7 +20,8 @@ import ( /* #include -#cgo !windows LDFLAGS: -ldatadog-agent-rtloader -ldl +#cgo !aix,!windows LDFLAGS: -ldatadog-agent-rtloader -ldl +#cgo aix LDFLAGS: -ldl #cgo windows LDFLAGS: -ldatadog-agent-rtloader -lstdc++ -static */ import "C" diff --git a/pkg/collector/python/memory.go b/pkg/collector/python/memory.go index 8f95ad1d5f9f..2fe133c8c6bf 100644 --- a/pkg/collector/python/memory.go +++ b/pkg/collector/python/memory.go @@ -18,7 +18,8 @@ import ( ) /* -#cgo !windows LDFLAGS: -ldatadog-agent-rtloader -ldl +#cgo !aix,!windows LDFLAGS: -ldatadog-agent-rtloader -ldl +#cgo aix LDFLAGS: -ldl #cgo windows LDFLAGS: -ldatadog-agent-rtloader -lstdc++ -static #if defined(__linux__) || defined(_WIN32) diff --git a/pkg/collector/python/no_kubeutil.go b/pkg/collector/python/no_kubeutil.go index 66432f8be25c..475c7ee9f68a 100644 --- a/pkg/collector/python/no_kubeutil.go +++ b/pkg/collector/python/no_kubeutil.go @@ -9,7 +9,8 @@ package python /* #include -#cgo !windows LDFLAGS: -ldatadog-agent-rtloader -ldl +#cgo !aix,!windows LDFLAGS: -ldatadog-agent-rtloader -ldl +#cgo aix LDFLAGS: -ldl #cgo windows LDFLAGS: -ldatadog-agent-rtloader -lstdc++ -static */ import "C" diff --git a/pkg/collector/python/python_aix.go b/pkg/collector/python/python_aix.go new file mode 100644 index 000000000000..204bdb5a89cc --- /dev/null +++ b/pkg/collector/python/python_aix.go @@ -0,0 +1,73 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build aix && python + +package python + +// Force libpython.so into the agent binary's XCOFF startup-load chain. +// +// Background +// ---------- +// Python C extension modules (lib-dynload/_decimal.so, etc.) carry Python API +// symbols (PyArg_ParseTuple, PyBool_FromLong, …) with no explicit library +// dependency in their XCOFF loader section (IMPid = "."). On AIX, such +// unresolved symbols are looked up in the process-global symbol table, which +// is populated only by libraries that appear in the XCOFF startup-load chain +// (i.e. in the XCOFF loader section of the main executable or its startup- +// linked dependencies). +// +// Without this file, libpython.so is loaded only via dlopen, transitively +// through libdatadog-agent-three.so when rtloader initialises Python. A +// dlopen-loaded library does NOT add its symbols to the global symbol table. +// As a result every Python C extension fails with: +// +// ImportError: Symbol PyXxx is not exported from dependent module agent. +// +// Fix +// --- +// The init() function below calls C.Py_IsInitialized() once at process startup. +// Go's CGO layer sees this Go→C call and emits a //go:cgo_import_dynamic +// directive that places libpython.a(shr_64.o) in the agent binary's XCOFF +// loader section. Consequently libpython.so is loaded at process startup, +// before any Python C extension module is imported, and all Python API +// symbols enter the global symbol table where the extension modules can find them. +// +// Note: a C-internal reference (static void* holding &Py_IsInitialized) is NOT +// sufficient — Go's linker only generates XCOFF import entries for symbols that +// are called from the Go side through CGO. A direct Go→C call is required. +// +// Py_IsInitialized() returns 0 when called here (Python not yet started) and +// has no side effects — it is a safe, idempotent read of an internal flag. +// +// Part 2 — Exporting Python API symbols from the agent binary +// The -bE:python.exp flag (passed via CGO_LDFLAGS at build time, NOT via +// #cgo LDFLAGS which would be rejected by Go's CGO security filter) causes the +// linker to add all Python API symbols to the agent binary's own EXP +// (export) table. Extension modules have Python API symbols with IMPid="." +// in their XCOFF, meaning "find in the main program's EXP table". Without +// -bE, those symbols are not exported from the agent and every extension fails: +// +// ImportError: Symbol PyXxx is not exported from dependent module agent. +// +// With -bE:python.exp, the agent exports the symbols and extensions load cleanly. + +/* +#cgo LDFLAGS: -L${SRCDIR}/../../../embedded/lib -lpython3 + +// Forward declaration — we do not include Python.h to avoid pulling in +// the entire CPython header tree; Py_IsInitialized has a stable ABI. +extern int Py_IsInitialized(void); +*/ +import "C" + +func init() { + // Call Py_IsInitialized() to create a live Go→C reference. This forces + // the CGO linker to emit a //go:cgo_import_dynamic for Py_IsInitialized, + // placing libpython.a(shr_64.o) in the XCOFF startup-load chain. + // Python is not yet started here so the call always returns 0; we discard + // the result. There are no side effects. + _ = C.Py_IsInitialized() +} diff --git a/pkg/collector/python/tagger.go b/pkg/collector/python/tagger.go index 92354171fdac..9a074aed6fee 100644 --- a/pkg/collector/python/tagger.go +++ b/pkg/collector/python/tagger.go @@ -16,7 +16,8 @@ import ( ) /* -#cgo !windows LDFLAGS: -ldatadog-agent-rtloader -ldl +#cgo !aix,!windows LDFLAGS: -ldatadog-agent-rtloader -ldl +#cgo aix LDFLAGS: -ldl #cgo windows LDFLAGS: -ldatadog-agent-rtloader -lstdc++ -static #include "datadog_agent_rtloader.h" diff --git a/pkg/collector/python/util.go b/pkg/collector/python/util.go index 14be3f6d065c..9a27c0f405c0 100644 --- a/pkg/collector/python/util.go +++ b/pkg/collector/python/util.go @@ -9,7 +9,8 @@ package python /* #include -#cgo !windows LDFLAGS: -ldatadog-agent-rtloader -ldl +#cgo !aix,!windows LDFLAGS: -ldatadog-agent-rtloader -ldl +#cgo aix LDFLAGS: -ldl #cgo windows LDFLAGS: -ldatadog-agent-rtloader -lstdc++ -static */ import "C" diff --git a/rtloader/rtloader/api.cpp b/rtloader/rtloader/api.cpp index 9828e71dd622..0c62375399d6 100644 --- a/rtloader/rtloader/api.cpp +++ b/rtloader/rtloader/api.cpp @@ -42,6 +42,8 @@ # define DATADOG_AGENT_THREE "libdatadog-agent-three.dylib" #elif __FreeBSD__ # define DATADOG_AGENT_THREE "libdatadog-agent-three.so" +#elif _AIX +# define DATADOG_AGENT_THREE "libdatadog-agent-three.so" #elif _WIN32 # define DATADOG_AGENT_THREE "libdatadog-agent-three.dll" #else @@ -404,7 +406,11 @@ DATADOG_AGENT_RTLOADER_API int handle_crashes(const int enable_coredump, const i if (alt_stack == nullptr) { // Note: this memory is never freed, but it is necessary for the duration of the program alt_stack = _malloc(alt_stack_size); - stack_t new_stack{ .ss_sp = alt_stack, .ss_flags = 0, .ss_size = alt_stack_size }; + stack_t new_stack; + memset(&new_stack, 0, sizeof(new_stack)); + new_stack.ss_sp = (decltype(new_stack.ss_sp))alt_stack; + new_stack.ss_size = alt_stack_size; + new_stack.ss_flags = 0; int ret = sigaltstack(&new_stack, nullptr); if (ret != 0) { std::ostringstream err_msg;