Skip to content

Commit c01233c

Browse files
committed
Try to fix SONAME...
1 parent 74b3b58 commit c01233c

File tree

4 files changed

+151
-4
lines changed

4 files changed

+151
-4
lines changed

.cargo/cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[target.x86_64-unknown-linux-gnu]
2+
rustflags = ["-C", "link-args=-Wl,-export-dynamic"]
3+
4+
[target.aarch64-unknown-linux-gnu]
5+
rustflags = ["-C", "link-args=-Wl,-export-dynamic"]

.github/workflows/CI.yml

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -326,15 +326,85 @@ jobs:
326326
uses: addnab/docker-run-action@v3
327327
with:
328328
image: ${{ steps.docker.outputs.IMAGE }}
329-
options: -v ${{ steps.docker.outputs.PNPM_STORE_PATH }}:${{ steps.docker.outputs.PNPM_STORE_PATH }} -v ${{ github.workspace }}:${{ github.workspace }} -w ${{ github.workspace }} --platform ${{ steps.docker.outputs.PLATFORM }}
329+
options: -v ${{ steps.docker.outputs.PNPM_STORE_PATH }}:${{ steps.docker.outputs.PNPM_STORE_PATH }} -v ${{ github.workspace }}:${{ github.workspace }} -w ${{ github.workspace }} --platform ${{ steps.docker.outputs.PLATFORM }} -e CI=true -e GITHUB_ACTIONS=true
330330
run: |
331-
# Install Python 3.9+ (any version will work with abi3-py39)
331+
# Install Python 3.x
332332
apt-get update -y
333-
apt-get install -y python3 python3-dev
333+
apt-get install -y python3 python3-dev patchelf
334+
335+
echo "=== Starting test setup ==="
336+
echo "Current directory: $(pwd)"
337+
echo "Python version: $(python3 --version)"
338+
echo "Patchelf version: $(patchelf --version)"
339+
echo "Using combined approach: SONAME patching + programmatic RTLD_GLOBAL"
340+
echo "CI environment: CI=$CI, GITHUB_ACTIONS=$GITHUB_ACTIONS"
341+
342+
# Check what .node files exist
343+
echo "=== Available .node files ==="
344+
ls -la *.node || echo "No .node files found"
345+
346+
# Check what .node files exist and patch Python dependencies
347+
echo "=== Checking .node file Python dependencies ==="
348+
for file in *.node; do
349+
if [ -f "$file" ]; then
350+
case "$file" in
351+
*linux*)
352+
echo "Checking $file..."
353+
echo "Python dependencies before patching:"
354+
ldd "$file" 2>/dev/null | grep python || echo "No Python dependencies found"
355+
356+
# Check if we need to patch SONAME
357+
current_python_lib=$(ldd "$file" 2>/dev/null | grep "libpython" | head -1 | awk '{print $1}')
358+
if [ -n "$current_python_lib" ]; then
359+
echo "Current Python library: $current_python_lib"
360+
361+
# Find the actual Python library on the system
362+
system_python_lib=$(find /usr/lib* -name "libpython3*.so.*" -type f 2>/dev/null | head -1)
363+
if [ -n "$system_python_lib" ]; then
364+
system_python_soname=$(basename "$system_python_lib")
365+
echo "System Python library: $system_python_soname"
366+
367+
# Only patch if they're different
368+
if [ "$current_python_lib" != "$system_python_soname" ]; then
369+
echo "Patching SONAME from $current_python_lib to $system_python_soname"
370+
patchelf --replace-needed "$current_python_lib" "$system_python_soname" "$file"
371+
echo "SONAME patching completed"
372+
else
373+
echo "SONAME already matches system Python"
374+
fi
375+
else
376+
echo "Warning: Could not find system Python library"
377+
fi
378+
else
379+
echo "No Python library dependency found"
380+
fi
381+
382+
echo "Python dependencies after patching:"
383+
ldd "$file" 2>/dev/null | grep python || echo "No Python dependencies found"
384+
echo "---"
385+
;;
386+
*)
387+
echo "Skipping non-Linux file: $file"
388+
;;
389+
esac
390+
fi
391+
done
334392
335393
# Install pnpm and run tests
394+
echo "=== Installing pnpm ==="
336395
corepack disable
337396
npm i -gf pnpm
397+
398+
echo "=== Running pnpm install ==="
399+
# Should be non-interactive in CI environment
400+
pnpm install --prefer-offline
401+
402+
echo "=== Setting up Python library path ==="
403+
export LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH
404+
echo "LD_LIBRARY_PATH: $LD_LIBRARY_PATH"
405+
406+
407+
echo "=== Running tests ==="
338408
pnpm test
339409
340410
publish:

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ http-rewriter = { git = "ssh://git@github.com/platformatic/http-rewriter.git" }
2727
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
2828
napi = { version = "3.0.0-beta.8", default-features = false, features = ["napi4"], optional = true }
2929
napi-derive = { version = "3.0.0-beta.8", optional = true }
30-
pyo3 = { version = "0.25.1", features = ["auto-initialize", "experimental-async", "abi3-py39"] }
30+
pyo3 = { version = "0.25.1", features = ["auto-initialize", "experimental-async"] }
3131
pyo3-async-runtimes = { version = "0.25.0", features = ["tokio-runtime"] }
3232
thiserror = "2.0.12"
3333
tokio = { version = "1.45.1", features = ["full"] }

src/asgi/mod.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ use std::{
55
path::{Path, PathBuf},
66
};
77

8+
#[cfg(target_os = "linux")]
9+
use std::os::raw::c_void;
10+
11+
#[cfg(target_os = "linux")]
12+
unsafe extern "C" {
13+
fn dlopen(filename: *const i8, flag: i32) -> *mut c_void;
14+
}
15+
16+
#[cfg(target_os = "linux")]
17+
const RTLD_GLOBAL: i32 = 0x100;
18+
#[cfg(target_os = "linux")]
19+
const RTLD_NOW: i32 = 0x2;
20+
821
use bytes::BytesMut;
922
use http_handler::{Handler, Request, RequestExt, Response, extensions::DocumentRoot};
1023
use pyo3::exceptions::PyRuntimeError;
@@ -35,6 +48,62 @@ pub use websocket::{
3548
WebSocketConnectionScope, WebSocketReceiveMessage, WebSocketSendException, WebSocketSendMessage,
3649
};
3750

51+
/// Load Python library with RTLD_GLOBAL on Linux to make symbols available
52+
#[cfg(target_os = "linux")]
53+
fn ensure_python_symbols_global() {
54+
unsafe {
55+
// Try to find the system Python library dynamically
56+
use std::process::Command;
57+
58+
// First try to find the Python library using find command
59+
if let Ok(output) = Command::new("find")
60+
.args(&[
61+
"/usr/lib",
62+
"/usr/lib64",
63+
"/usr/local/lib",
64+
"-name",
65+
"libpython3*.so.*",
66+
"-type",
67+
"f",
68+
])
69+
.output()
70+
{
71+
let output_str = String::from_utf8_lossy(&output.stdout);
72+
for lib_path in output_str.lines() {
73+
if let Ok(lib_cstring) = CString::new(lib_path) {
74+
let handle = dlopen(lib_cstring.as_ptr(), RTLD_NOW | RTLD_GLOBAL);
75+
if !handle.is_null() {
76+
// Successfully loaded Python library with RTLD_GLOBAL
77+
return;
78+
}
79+
}
80+
}
81+
}
82+
83+
// Fallback to common library names if find command fails
84+
let python_libs = [
85+
"libpython3.12.so.1.0\0",
86+
"libpython3.11.so.1.0\0",
87+
"libpython3.10.so.1.0\0",
88+
"libpython3.9.so.1.0\0",
89+
"libpython3.8.so.1.0\0",
90+
];
91+
92+
for lib_name in &python_libs {
93+
let handle = dlopen(lib_name.as_ptr() as *const i8, RTLD_NOW | RTLD_GLOBAL);
94+
if !handle.is_null() {
95+
// Successfully loaded Python library with RTLD_GLOBAL
96+
break;
97+
}
98+
}
99+
}
100+
}
101+
102+
#[cfg(not(target_os = "linux"))]
103+
fn ensure_python_symbols_global() {
104+
// On non-Linux platforms, this is typically not needed
105+
}
106+
38107
/// Core ASGI handler that loads and manages a Python ASGI application
39108
pub struct Asgi {
40109
app_function: PyObject,
@@ -47,6 +116,9 @@ impl Asgi {
47116
docroot: Option<String>,
48117
app_target: Option<PythonHandlerTarget>,
49118
) -> Result<Self, HandlerError> {
119+
// Ensure Python symbols are globally available before initializing
120+
ensure_python_symbols_global();
121+
50122
// Determine document root
51123
let docroot = PathBuf::from(if let Some(docroot) = docroot {
52124
docroot

0 commit comments

Comments
 (0)