summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/spack/spack/util/elf.py66
-rw-r--r--var/spack/repos/builtin/packages/gcc-runtime/package.py22
2 files changed, 63 insertions, 25 deletions
diff --git a/lib/spack/spack/util/elf.py b/lib/spack/spack/util/elf.py
index f0fda07787..86a4e141c0 100644
--- a/lib/spack/spack/util/elf.py
+++ b/lib/spack/spack/util/elf.py
@@ -7,7 +7,7 @@ import bisect
import re
import struct
from struct import calcsize, unpack, unpack_from
-from typing import BinaryIO, Dict, List, NamedTuple, Optional, Pattern, Tuple
+from typing import BinaryIO, Callable, Dict, List, NamedTuple, Optional, Pattern, Tuple
class ElfHeader(NamedTuple):
@@ -476,6 +476,31 @@ def get_interpreter(path: str) -> Optional[str]:
return None
+def _delete_dynamic_array_entry(
+ f: BinaryIO, elf: ElfFile, should_delete: Callable[[int, int], bool]
+) -> None:
+ f.seek(elf.pt_dynamic_p_offset)
+ dynamic_array_fmt = elf.byte_order + ("qQ" if elf.is_64_bit else "lL")
+ dynamic_array_size = calcsize(dynamic_array_fmt)
+ new_offset = elf.pt_dynamic_p_offset # points to the new dynamic array
+ old_offset = elf.pt_dynamic_p_offset # points to the current dynamic array
+ for _ in range(elf.pt_dynamic_p_filesz // dynamic_array_size):
+ data = read_exactly(f, dynamic_array_size, "Malformed dynamic array entry")
+ tag, val = unpack(dynamic_array_fmt, data)
+
+ if tag == ELF_CONSTANTS.DT_NULL or not should_delete(tag, val):
+ if new_offset != old_offset:
+ f.seek(new_offset)
+ f.write(data)
+ f.seek(old_offset + dynamic_array_size)
+ new_offset += dynamic_array_size
+
+ if tag == ELF_CONSTANTS.DT_NULL:
+ break
+
+ old_offset += dynamic_array_size
+
+
def delete_rpath(path: str) -> None:
"""Modifies a binary to remove the rpath. It zeros out the rpath string and also drops the
DT_R(UN)PATH entry from the dynamic section, so it doesn't show up in 'readelf -d file', nor
@@ -492,29 +517,22 @@ def delete_rpath(path: str) -> None:
f.seek(rpath_offset)
f.write(new_rpath_string)
- # Next update the dynamic array
- f.seek(elf.pt_dynamic_p_offset)
- dynamic_array_fmt = elf.byte_order + ("qQ" if elf.is_64_bit else "lL")
- dynamic_array_size = calcsize(dynamic_array_fmt)
- new_offset = elf.pt_dynamic_p_offset # points to the new dynamic array
- old_offset = elf.pt_dynamic_p_offset # points to the current dynamic array
- for _ in range(elf.pt_dynamic_p_filesz // dynamic_array_size):
- data = read_exactly(f, dynamic_array_size, "Malformed dynamic array entry")
- tag, _ = unpack(dynamic_array_fmt, data)
-
- # Overwrite any entry that is not DT_RPATH or DT_RUNPATH, including DT_NULL
- if tag != ELF_CONSTANTS.DT_RPATH and tag != ELF_CONSTANTS.DT_RUNPATH:
- if new_offset != old_offset:
- f.seek(new_offset)
- f.write(data)
- f.seek(old_offset + dynamic_array_size)
- new_offset += dynamic_array_size
-
- # End of the dynamic array
- if tag == ELF_CONSTANTS.DT_NULL:
- break
-
- old_offset += dynamic_array_size
+ # Delete DT_RPATH / DT_RUNPATH entries from the dynamic section
+ _delete_dynamic_array_entry(
+ f, elf, lambda tag, _: tag == ELF_CONSTANTS.DT_RPATH or tag == ELF_CONSTANTS.DT_RUNPATH
+ )
+
+
+def delete_needed_from_elf(f: BinaryIO, elf: ElfFile, needed: bytes) -> None:
+ """Delete a needed library from the dynamic section of an ELF file"""
+ if not elf.has_needed or needed not in elf.dt_needed_strs:
+ return
+
+ offset = elf.dt_needed_strtab_offsets[elf.dt_needed_strs.index(needed)]
+
+ _delete_dynamic_array_entry(
+ f, elf, lambda tag, val: tag == ELF_CONSTANTS.DT_NEEDED and val == offset
+ )
class CStringType:
diff --git a/var/spack/repos/builtin/packages/gcc-runtime/package.py b/var/spack/repos/builtin/packages/gcc-runtime/package.py
index 7046b8ff09..551aaa57cb 100644
--- a/var/spack/repos/builtin/packages/gcc-runtime/package.py
+++ b/var/spack/repos/builtin/packages/gcc-runtime/package.py
@@ -3,6 +3,7 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import glob
import os
import re
@@ -11,7 +12,7 @@ from macholib import MachO, mach_o
from llnl.util import tty
from spack.package import *
-from spack.util.elf import parse_elf
+from spack.util.elf import delete_needed_from_elf, parse_elf
class GccRuntime(Package):
@@ -72,6 +73,9 @@ class GccRuntime(Package):
for path, name in libraries:
install(path, os.path.join(prefix.lib, name))
+ if spec.platform in ("linux", "freebsd"):
+ _drop_libgfortran_zlib(prefix.lib)
+
def _get_libraries_macho(self):
"""Same as _get_libraries_elf but for Mach-O binaries"""
cc = Executable(self.compiler.cc)
@@ -124,6 +128,22 @@ class GccRuntime(Package):
return HeaderList([])
+def _drop_libgfortran_zlib(lib_dir: str) -> None:
+ """Due to a bug in GCC's autotools setup (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87182),
+ libz sometimes appears as a redundant system dependency of libgfortran. Delete it."""
+ libraries = glob.glob(os.path.join(lib_dir, "libgfortran*.so*"))
+ if len(libraries) == 0:
+ return
+ with open(libraries[0], "rb+") as f:
+ elf = parse_elf(f, dynamic_section=True)
+ if not elf.has_needed:
+ return
+ libz = next((x for x in elf.dt_needed_strs if x.startswith(b"libz.so")), None)
+ if libz is None:
+ return
+ delete_needed_from_elf(f, elf, libz)
+
+
def get_elf_libraries(compiler, libraries):
"""Get the GCC runtime libraries for ELF binaries"""
cc = Executable(compiler.cc)