gcc-runtime: remove libz.so from libgfortran.so if present (#47812)

This commit is contained in:
Harmen Stoppels 2024-11-27 22:32:37 +01:00 committed by GitHub
parent 41be2f5899
commit 3194be2e92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 60 additions and 22 deletions

View File

@ -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:

View File

@ -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)