Skip to content

Commit 987c549

Browse files
committed
using @rpath was a bust; switched replacing olddir with newdir; add option to Darwin ld to pad rpath entries as much as possible for long relocation paths:
1 parent 5f85533 commit 987c549

File tree

2 files changed

+63
-52
lines changed

2 files changed

+63
-52
lines changed

lib/spack/env/cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ if [[ $mode == ld && "$SPACK_SHORT_SPEC" =~ "darwin" ]]; then
182182
break
183183
fi
184184
done
185+
args=("${args[@]}" "-headerpad_max_install_names")
185186
fi
186187

187188
# Save original command for debug logging

lib/spack/spack/relocate.py

Lines changed: 62 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# "Benedikt Hegner (CERN)"
2+
# "Patrick Gartung (FNAL)"
23

34
import os
45
import stat
@@ -10,32 +11,11 @@
1011
import llnl.util.tty as tty
1112

1213

13-
def get_existing_rpath(path_name, patchelf_executable):
14+
def get_existing_elf_rpaths(path_name, old_dir, new_dir):
1415
"""
15-
Return the RPATHS in given file as a list of strings.
16+
Return the RPATHS in given elf file as a list of strings.
1617
"""
17-
if platform.system() == 'Darwin':
18-
command = which('otool')
19-
output = command("-l", path_name, output=str, err=str)
20-
if command.returncode != 0:
21-
tty.warn('failed reading rpath for %s.' % path_name)
22-
return False
23-
last_cmd = None
24-
path = set()
25-
for line in output.split('\n'):
26-
match = re.search('( *[a-zA-Z]+ )(.*)', line)
27-
if match:
28-
lhs = match.group(1).lstrip().rstrip()
29-
rhs = match.group(2)
30-
match2 = re.search('(.*) \(.*\)', rhs)
31-
if match2:
32-
rhs = match2.group(1)
33-
if lhs == 'cmd':
34-
last_cmd = rhs
35-
if lhs == 'path' and last_cmd == 'LC_RPATH':
36-
path.add(rhs)
37-
return path
38-
elif platform.system() == 'Linux':
18+
if platform.system() == 'Linux':
3919
command = '%s --print-rpath %s ' % (patchelf_executable, path_name)
4020
status, output = getstatusoutput(command)
4121
if status != 0:
@@ -46,17 +26,42 @@ def get_existing_rpath(path_name, patchelf_executable):
4626
tty.die('relocation not supported for this platform')
4727
return retval
4828

49-
def change_dylib(path_name, rpaths):
29+
def modify_macho_object(path_name, old_dir, new_dir):
5030
"""
51-
Return the RPATHS in given file as a list of strings.
31+
Modify MachO binaries by changing rpaths,and id and dependency lib paths.
32+
33+
Examines the output of otool -l for these three patterns
34+
35+
cmd LC_ID_DYLIB
36+
cmdsize 160
37+
name /Users/gartung/spack-macdev/opt/spack/darwin-x86_64/clang-7.0.2-apple/tcl-8.6.5-xfeydlhaojmei6iws2rnxndvriym242k/lib/libtcl8.6.dylib (offset 24)
38+
39+
cmd LC_LOAD_DYLIB
40+
cmdsize 160
41+
name /Users/gartung/spack-macdev/opt/spack/darwin-x86_64/clang-7.0.2-apple/zlib-1.2.8-cyvcqvrzlgurne424y55hxvfucvz2354/lib/libz.1.dylib (offset 24)
42+
43+
cmd LC_RPATH
44+
cmdsize 128
45+
path /Users/gartung/spack-macdev/opt/spack/darwin-x86_64/clang-7.0.2-apple/xz-5.2.2-d4ecxpuzf2g3ycz3cnj3xmdj7zdnuqwb/lib (offset 12)
46+
47+
the old install dir in LC_LOAD_DYLIB is replace with the new install dir using
48+
install_name_tool -id newid binary
49+
50+
the old install dir in LC_LOAD_DYLIB is replaced with the new install dir using
51+
install_name_tool -change old new binary
52+
53+
the old install dir in LC_RPATH is replaced with the new install dir using
54+
install_name_tool -rpath old new binary
55+
5256
"""
5357
command = which('otool')
5458
output = command("-l", path_name, output=str, err=str)
5559
if command.returncode != 0:
5660
tty.warn('failed reading rpath for %s.' % path_name)
5761
return False
5862
last_cmd = None
59-
path = ''
63+
idpath = ''
64+
rpaths = []
6065
deps = []
6166
for line in output.split('\n'):
6267
match = re.search('( *[a-zA-Z]+ )(.*)', line)
@@ -68,19 +73,21 @@ def change_dylib(path_name, rpaths):
6873
rhs = match2.group(1)
6974
if lhs == 'cmd':
7075
last_cmd = rhs
76+
if lhs == 'path' and last_cmd == 'LC_RPATH':
77+
rpaths.append(rhs)
7178
if lhs == 'name' and last_cmd == 'LC_ID_DYLIB':
72-
path=rhs
79+
idpath=rhs
7380
if lhs == 'name' and last_cmd == 'LC_LOAD_DYLIB':
7481
deps.append(rhs)
75-
id = path
82+
id = idpath.replace(old_dir,new_dir)
7683
ndeps = []
84+
nrpaths = []
7785
for rpath in rpaths:
78-
if re.match(rpath,path):
79-
id = '@rpath'+re.split(rpath,path)[1]
86+
nrpath = rpath.replace(old_dir,new_dir)
87+
nrpaths.append(nrpath)
8088
for dep in deps:
81-
if re.match(rpath,dep):
82-
ndep = '@rpath'+re.split(rpath,dep)[1]
83-
ndeps.append(ndep)
89+
ndep = dep.replace(old_dir,new_dir)
90+
ndeps.append(ndep)
8491

8592
st = os.stat(path_name)
8693
wmode = os.access(path_name, os.W_OK)
@@ -93,13 +100,23 @@ def change_dylib(path_name, rpaths):
93100
if status != 0:
94101
tty.warn('failed writing id for %s.' % path_name)
95102
tty.warn(output)
103+
96104
for orig, new in zip(deps, ndeps):
97105
command = ("install_name_tool -change %s %s %s" %
98106
(orig, new, path_name))
99107
status, output = getstatusoutput(command)
100108
if status != 0:
101109
tty.warn('failed writing dep for %s.' % path_name)
102110
tty.warn(output)
111+
112+
for orig, new in zip(rpaths, nrpaths):
113+
command= ("install_name_tool -rpath %s %s %s" %
114+
(orig,new,path_name) )
115+
status, output = getstatusoutput(command)
116+
if status != 0:
117+
tty.warn('failed writing id for %s.' % path_name)
118+
tty.warn(output)
119+
103120
os.chmod(path_name, st.st_mode)
104121
return
105122

@@ -117,25 +134,15 @@ def get_filetype(path_name):
117134
return output.strip()
118135

119136

120-
def modify_rpath(path_name, orig_rpath, new_rpath, patchelf_executable):
137+
def modify_elf_object(path_name, orig_rpath, new_rpath, patchelf_executable):
121138
"""
122-
Replace RPATH in given binary
139+
Replace RPATH's in given elf object
123140
"""
124141
st = os.stat(path_name)
125142
wmode = os.access(path_name, os.W_OK)
126143
if not wmode:
127144
os.chmod(path_name, st.st_mode | stat.S_IWUSR)
128-
if platform.system() == 'Darwin':
129-
for orig, new in zip(orig_rpath, new_rpath):
130-
command = which("install_name_tool")
131-
output = command("-rpath", "%s" % orig,
132-
"%s" % new,
133-
"%s" % path_name,
134-
output=str,
135-
err=str)
136-
if command.returncode != 0:
137-
tty.warn('failed writing rpath for %s.' % path_name)
138-
elif platform.system() == 'Linux':
145+
if platform.system() == 'Linux':
139146
new_joined = ':'.join(new_rpath)
140147
command = "%s --force-rpath --set-rpath '%s' '%s'" % \
141148
(patchelf_executable, new_joined, path_name)
@@ -172,13 +179,16 @@ def needs_text_relocation(filetype):
172179

173180
def relocate_binary(path_name, old_dir, new_dir, patchelf_executable):
174181
"""
175-
Change RPATHs in given file
182+
Change RPATHs in given elf or mach-o file
176183
"""
177-
orig_rpath = get_existing_rpath(path_name, patchelf_executable)
178-
new_rpath = substitute_rpath(orig_rpath, old_dir, new_dir)
179-
modify_rpath(path_name, orig_rpath, new_rpath, patchelf_executable)
180184
if platform.system() == 'Darwin':
181-
change_dylib(path_name, orig_rpath)
185+
modify_macho_object(path_name, old_dir, new_dir)
186+
elif platform.system() == 'Linux':
187+
orig_rpaths = get_existing_elf_rpaths(path_name, patchelf_executable)
188+
new_rpaths = substitute_rpath(orig_rpath, old_dir, new_dir)
189+
modify_elf_object(path_name, orig_rpaths, new_rpaths, patchelf_executable)
190+
else:
191+
tty.die("Relocation not implemented for %s" % platform.system())
182192

183193

184194
def relocate_text(path_name, old_dir, new_dir):

0 commit comments

Comments
 (0)