gcc-runtime: remove libz.so from libgfortran.so if present (#47812)
This commit is contained in:
parent
41be2f5899
commit
3194be2e92
@ -7,7 +7,7 @@
|
||||
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)
|
||||
# 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
|
||||
)
|
||||
|
||||
# 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
|
||||
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
|
||||
|
||||
old_offset += dynamic_array_size
|
||||
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:
|
||||
|
@ -3,6 +3,7 @@
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
|
||||
@ -11,7 +12,7 @@
|
||||
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 @@ def install(self, spec, prefix):
|
||||
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 @@ def headers(self):
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user