Skip to content

Commit 4709edb

Browse files
fbrennenthomaslima
andauthored
Append to RPATH instead of overwriting (#245)
Respect RPATHs already set in extension modules, which point inside the wheel directory instead of always overwriting them. Co-authored-by: Thomas Ferreira de Lima <[email protected]>
1 parent a9b4794 commit 4709edb

File tree

18 files changed

+352
-10
lines changed

18 files changed

+352
-10
lines changed

.coveragerc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@
22
source =
33
auditwheel/
44
/auditwheel_src/auditwheel
5+
6+
[report]
7+
exclude_lines =
8+
raise NotImplementedError

auditwheel/patcher.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ def set_rpath(self,
2020
rpath: str) -> None:
2121
raise NotImplementedError
2222

23+
def get_rpath(self,
24+
file_name: str) -> str:
25+
raise NotImplementedError
26+
2327

2428
def _verify_patchelf() -> None:
2529
"""This function looks for the ``patchelf`` external binary in the PATH,
@@ -64,3 +68,9 @@ def set_rpath(self,
6468
check_call(['patchelf', '--remove-rpath', file_name])
6569
check_call(['patchelf', '--force-rpath', '--set-rpath',
6670
rpath, file_name])
71+
72+
def get_rpath(self,
73+
file_name: str) -> str:
74+
75+
return check_output(['patchelf', '--print-rpath',
76+
file_name]).decode('utf-8').strip()

auditwheel/repair.py

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import itertools
22
import logging
33
import os
4+
import platform
45
import re
56
import stat
67
import shutil
7-
from os.path import exists, basename, abspath, isabs
8+
from collections import OrderedDict
9+
from os.path import exists, basename, abspath, isabs, dirname
810
from os.path import join as pjoin
911
from typing import Dict, Optional
1012

1113
from auditwheel.patcher import ElfPatcher
12-
from .elfutils import elf_read_rpaths, elf_read_dt_needed
14+
from .elfutils import elf_read_rpaths, elf_read_dt_needed, is_subdir
1315
from .hashfile import hashfile
1416
from .policy import get_replace_platforms
1517
from .wheel_abi import get_wheel_elfdata
@@ -66,7 +68,7 @@ def repair_wheel(wheel_path: str, abi: str, lib_sdir: str, out_dir: str,
6668
if len(ext_libs) > 0:
6769
new_rpath = os.path.relpath(dest_dir, os.path.dirname(fn))
6870
new_rpath = os.path.join('$ORIGIN', new_rpath)
69-
patcher.set_rpath(fn, new_rpath)
71+
append_rpath_within_wheel(fn, new_rpath, ctx.name, patcher)
7072

7173
# we grafted in a bunch of libraries and modified their sonames, but
7274
# they may have internal dependencies (DT_NEEDED) on one another, so
@@ -124,3 +126,68 @@ def copylib(src_path, dest_dir, patcher):
124126
patcher.set_rpath(dest_path, dest_dir)
125127

126128
return new_soname, dest_path
129+
130+
131+
def append_rpath_within_wheel(lib_name: str,
132+
rpath: str,
133+
wheel_base_dir: str,
134+
patcher: ElfPatcher) -> None:
135+
"""Add a new rpath entry to a file while preserving as many existing
136+
rpath entries as possible.
137+
138+
In order to preserve an rpath entry it must:
139+
140+
1) Point to a location within wheel_base_dir.
141+
2) Not be a duplicate of an already-existing rpath entry.
142+
"""
143+
if not isabs(lib_name):
144+
lib_name = abspath(lib_name)
145+
lib_dir = dirname(lib_name)
146+
if not isabs(wheel_base_dir):
147+
wheel_base_dir = abspath(wheel_base_dir)
148+
149+
def is_valid_rpath(rpath: str) -> bool:
150+
return _is_valid_rpath(rpath, lib_dir, wheel_base_dir)
151+
152+
old_rpaths = patcher.get_rpath(lib_name)
153+
rpaths = filter(is_valid_rpath, old_rpaths.split(':'))
154+
# Remove duplicates while preserving ordering
155+
# Fake an OrderedSet using OrderedDict
156+
rpaths = OrderedDict([(old_rpath, '') for old_rpath in rpaths])
157+
rpaths[rpath] = ''
158+
159+
patcher.set_rpath(lib_name, ':'.join(rpaths))
160+
161+
162+
def _is_valid_rpath(rpath: str,
163+
lib_dir: str,
164+
wheel_base_dir: str) -> bool:
165+
full_rpath_entry = _resolve_rpath_tokens(rpath, lib_dir)
166+
if not isabs(full_rpath_entry):
167+
logger.debug('rpath entry {} could not be resolved to an absolute '
168+
'path -- discarding it.'.format(rpath))
169+
return False
170+
elif not is_subdir(full_rpath_entry, wheel_base_dir):
171+
logger.debug('rpath entry {} points outside the wheel -- discarding '
172+
'it.'.format(rpath))
173+
return False
174+
else:
175+
logger.debug('Preserved rpath entry {}'.format(rpath))
176+
return True
177+
178+
179+
def _resolve_rpath_tokens(rpath: str,
180+
lib_base_dir: str) -> str:
181+
# See https://www.man7.org/linux/man-pages/man8/ld.so.8.html#DESCRIPTION
182+
if platform.architecture()[0] == '64bit':
183+
system_lib_dir = 'lib64'
184+
else:
185+
system_lib_dir = 'lib'
186+
system_processor_type = platform.machine()
187+
token_replacements = {'ORIGIN': lib_base_dir,
188+
'LIB': system_lib_dir,
189+
'PLATFORM': system_processor_type}
190+
for token, target in token_replacements.items():
191+
rpath = rpath.replace('${}'.format(token), target) # $TOKEN
192+
rpath = rpath.replace('${{{}}}'.format(token), target) # ${TOKEN}
193+
return rpath
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
include pyproject.toml
2+
graft lib
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
all: lib-src/b/libb.so lib-src/a/liba.so
2+
3+
4+
lib-src/b/libb.so: lib-src/b/b.c
5+
gcc -fPIC -shared -o lib-src/b/libb.so lib-src/b/b.c
6+
7+
8+
lib-src/a/liba.so: lib-src/a/a.c
9+
gcc -fPIC -shared -o lib-src/a/liba.so lib-src/a/a.c

tests/integration/internal_rpath/internal_rpath/__init__.py

Whitespace-only changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
int fa(void) {
2+
return 11;
3+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
int fa(void);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
int fb(void) {
2+
return 10;
3+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
int fb(void);

0 commit comments

Comments
 (0)