implement elf dynamic linking check

This commit is contained in:
Evgenii Alekseev 2023-06-01 11:28:58 +03:00
parent 2922bb9d72
commit 9449c95ad4
2 changed files with 96 additions and 0 deletions

View File

@ -33,6 +33,7 @@ setup(
"cerberus",
"inflection",
"passlib",
"pyelftools",
"requests",
"srcinfo",
],

View File

@ -0,0 +1,95 @@
#
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from dataclasses import dataclass
from pathlib import Path
from typing import IO
from elftools.elf.elffile import ELFFile
from elftools.elf.dynamic import DynamicSection
from pyalpm import Package # type: ignore[import]
from ahriman.core.util import walk
@dataclass
class PackageArchive:
"""
helper for package archives
"""
package: Package
@staticmethod
def get_depends_paths(package_base: str, root: Path) -> tuple[set[str], set[Path]]:
dependencies = set()
roots = set()
package_dir = root / "build" / package_base / "pkg"
for path in walk(package_dir):
dependencies.update(PackageArchive.get_dynamic(path))
roots.update(path.relative_to(package_dir).parents)
return dependencies, roots
@staticmethod
def get_dynamic(binary_path: Path) -> list[str]:
with binary_path.open("rb") as binary_file:
if not PackageArchive.is_elf(binary_file):
return []
elf_file = ELFFile(binary_file)
dynamic_section = next((section for section in elf_file.iter_sections() if isinstance(section, DynamicSection)), None)
if dynamic_section is None:
return []
return [tag.needed for tag in dynamic_section.iter_tags() if tag.entry.d_tag == "DT_NEEDED"]
@staticmethod
def get_filesystem(root: Path) -> dict[str, tuple[list[Path], list[Path]]]:
result = {}
for path in filter(lambda fn: fn.name == "files", walk(root / "var" / "lib" / "pacman" / "local")):
package, *_ = path.parent.name.rsplit("-", 2)
directories = []
files = []
for entry in path.read_text(encoding="utf8").splitlines():
if not entry or entry == "%FILES%":
continue # header
entry_path = Path(entry)
if entry_path.name == "":
directories.append(entry_path)
else:
files.append(entry_path)
result[package] = (directories, files)
return result
@staticmethod
def is_elf(descriptor: IO[bytes]) -> bool:
expected = b"\x7fELF"
length = len(expected)
magic_bytes = descriptor.read(length)
descriptor.seek(0) # reset reading position
return magic_bytes == expected