import os
from typing import Iterator
from ..context import Context
from ..package import Package
from ..util import join_env_paths, run
from .patchelf import PatchElf
from .prelink import Prelink
from .pyelftools import PyElfTools
[docs]class LibShrink(Package):
"""
Dependency package for `libshrink <https://github.com/vusec/libshrink>`_.
Libshrink shrinks the application address space to a maximum number of
bits. It moves the stack and TLS to a memory region that is within the
allowed bitrange, and prelinks all shared libraries as well so that they do
not exceed the address space limitations. It also defines a
:func:`run_wrapper` that should be put in ``ctx.target_run_wrapper`` by an
instance that uses libshrink.
:identifier: libshrink-<addrspace_bits>
:param addrspace_bits: maximum number of nonzero bits in any pointer
:param commit: branch or commit to clone
:param debug: whether to compile with debug symbols
"""
git_url = "https://github.com/vusec/libshrink.git"
def __init__(
self, addrspace_bits: int, commit: str = "master", debug: bool = False
):
self.addrspace_bits = addrspace_bits
self.commit = commit
self.debug = debug
def ident(self) -> str:
return f"libshrink-{self.addrspace_bits}"
def dependencies(self) -> Iterator[Package]:
yield Prelink("209")
yield PatchElf("0.9")
yield PyElfTools("0.24", "2.7")
def fetch(self, ctx: Context) -> None:
run(ctx, ["git", "clone", self.git_url, "src"])
os.chdir("src")
run(ctx, ["git", "checkout", self.commit])
def build(self, ctx: Context) -> None:
os.chdir("src")
run(
ctx,
[
"make",
f"-j{ctx.jobs}",
"OBJDIR=" + self.path(ctx, "obj"),
"DEBUG=" + ("1" if self.debug else "0"),
],
)
def install(self, ctx: Context) -> None:
pass
def is_fetched(self, ctx: Context) -> bool:
return os.path.exists("src")
def is_built(self, ctx: Context) -> bool:
return os.path.exists("obj/libshrink-static.a") and os.path.exists(
"obj/libshrink-preload.so"
)
def is_installed(self, ctx: Context) -> bool:
return self.is_built(ctx)
def _prelink_binary(self, ctx: Context, binary: str) -> None:
libpath = join_env_paths(ctx.runenv).get("LD_LIBRARY_PATH", "")
run(
ctx,
[
self.path(ctx, "src/prelink_binary.py"),
"--set-rpath",
"--in-place",
"--static-lib",
"--out-dir",
"prelink-" + os.path.basename(binary),
"--library-path",
libpath,
"--addrspace-bits",
self.addrspace_bits,
binary,
],
)
def _fix_preinit(self, ctx: Context, binary: str) -> None:
run(
ctx,
[
self.path(ctx, "src/fix_preinit.py"),
"--preinit-name",
"__shrinkaddrspace_preinit",
binary,
],
)
[docs] def run_wrapper(self, ctx: Context) -> str:
"""
Run wrapper for targets. Links to a script that sets the ``rpath``
before any libraries are loaded, so that any dependencies of shared
libraries loaded by the applications are also loaded from the directory
of prelinked libraries (which is created by a post-build hook).
:param ctx: the configuration context
"""
return self.path(ctx, "src/rpath_wrapper.sh")