1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import pytest
import llnl.util.filesystem as fs
import spack.platforms
from spack.hooks.absolutify_elf_sonames import SharedLibrariesVisitor, find_and_patch_sonames
from spack.util.executable import Executable
def skip_unless_linux(f):
return pytest.mark.skipif(
str(spack.platforms.real_host()) != "linux", reason="only tested on linux for now"
)(f)
class ExecutableIntercept:
def __init__(self):
self.calls = []
def __call__(self, *args, **kwargs):
self.calls.append(args)
@property
def returncode(self):
return 0
@pytest.mark.requires_executables("gcc")
@skip_unless_linux
def test_shared_libraries_visitor(tmpdir):
"""Integration test for soname rewriting"""
gcc = Executable("gcc")
# Create a directory structure like this:
# ./no-soname.so # just a shared library without a soname
# ./soname.so # a shared library with a soname
# ./executable.so # an executable masquerading as a shared lib
# ./libskipme.so # a shared library with a soname
# ./mydir/parent_dir -> .. # a symlinked dir, causing a cycle
# ./mydir/skip_symlink -> ../libskipme # a symlink to a library
with fs.working_dir(str(tmpdir)):
with open("hello.c", "w") as f:
f.write("int main(){return 0;}")
gcc("hello.c", "-o", "no-soname.so", "--shared")
gcc("hello.c", "-o", "soname.so", "--shared", "-Wl,-soname,example.so")
gcc("hello.c", "-pie", "-o", "executable.so")
gcc("hello.c", "-o", "libskipme.so", "-Wl,-soname,libskipme.so")
os.mkdir("my_dir")
os.symlink("..", os.path.join("my_dir", "parent_dir"))
os.symlink(os.path.join("..", "libskipme.so"), os.path.join("my_dir", "skip_symlink"))
# Visit the whole prefix, but exclude `skip_symlink`
visitor = SharedLibrariesVisitor(exclude_list=["skip_symlink"])
fs.visit_directory_tree(str(tmpdir), visitor)
relative_paths = visitor.get_shared_libraries_relative_paths()
assert "no-soname.so" in relative_paths
assert "soname.so" in relative_paths
assert "executable.so" not in relative_paths
assert "libskipme.so" not in relative_paths
# Run the full hook of finding libs and setting sonames.
patchelf = ExecutableIntercept()
find_and_patch_sonames(str(tmpdir), ["skip_symlink"], patchelf)
assert len(patchelf.calls) == 2
elf_1 = tmpdir.join("no-soname.so")
elf_2 = tmpdir.join("soname.so")
assert ("--set-soname", elf_1, elf_1) in patchelf.calls
assert ("--set-soname", elf_2, elf_2) in patchelf.calls
|