about summary refs log tree commit diff
path: root/nixpkgs/pkgs/build-support/libredirect/default.nix
blob: 1ab4a0db827aa3aca47130a4a313ab330872051a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
{ lib, stdenv, bintools-unwrapped, llvmPackages, llvmPackages_13, coreutils }:

let
  # aarch64-darwin needs a clang that can build arm64e binaries, so make sure a version of LLVM
  # is used that can do that, but prefer the stdenv one if it is new enough.
  llvmPkgs = if (lib.versionAtLeast (lib.getVersion llvmPackages.clang) "13")
    then llvmPackages
    else llvmPackages_13;
  in
if stdenv.hostPlatform.isStatic
then throw ''
  libredirect is not available on static builds.

  Please fix your derivation to not depend on libredirect on static
  builds, using something like following:

    nativeBuildInputs =
      lib.optional (!stdenv.buildPlatform.isStatic) libredirect;

  and disable tests as necessary, although fixing tests to work without
  libredirect is even better.

  libredirect uses LD_PRELOAD feature of dynamic loader and does not
  work on static builds where dynamic loader is not used.
  ''
else stdenv.mkDerivation rec {
  pname = "libredirect";
  version = "0";

  unpackPhase = ''
    cp ${./libredirect.c} libredirect.c
    cp ${./test.c} test.c
  '';

  outputs = ["out" "hook"];

  libName = "libredirect" + stdenv.hostPlatform.extensions.sharedLibrary;

  buildPhase = ''
    runHook preBuild

    ${if stdenv.isDarwin && stdenv.isAarch64 then ''
    # We need the unwrapped binutils and clang:
    # We also want to build a fat library with x86_64, arm64, arm64e in there.
    # Because we use the unwrapped tools, we need to provide -isystem for headers
    # and the library search directory for libdl.
    # We can't build this on x86_64, because the libSystem we point to doesn't
    # like arm64(e).
    PATH=${bintools-unwrapped}/bin:${llvmPkgs.clang-unwrapped}/bin:$PATH \
      clang -arch x86_64 -arch arm64 -arch arm64e \
      -isystem ${llvmPkgs.clang.libc}/include \
      -isystem ${llvmPkgs.libclang.lib}/lib/clang/*/include \
      -L${llvmPkgs.clang.libc}/lib \
      -Wl,-install_name,$libName \
      -Wall -std=c99 -O3 -fPIC libredirect.c \
      -shared -o "$libName"
    '' else if stdenv.isDarwin then ''
    $CC -Wall -std=c99 -O3 -fPIC libredirect.c \
      -Wl,-install_name,$out/lib/$libName \
      -shared -o "$libName"
    '' else ''
    $CC -Wall -std=c99 -O3 -fPIC libredirect.c \
      -shared -o "$libName"
    ''}

    if [ -n "$doInstallCheck" ]; then
      $CC -Wall -std=c99 \
        ${lib.optionalString (!stdenv.isDarwin) "-D_GNU_SOURCE"} \
        -O3 test.c -o test
    fi

    runHook postBuild
  '';

  # We want to retain debugging info to be able to use GDB on libredirect.so
  # to more easily investigate which function overrides are missing or why
  # existing ones do not have the intended effect.
  dontStrip = true;

  installPhase = ''
    runHook preInstall

    install -vD "$libName" "$out/lib/$libName"

  '' + lib.optionalString (stdenv.isDarwin && stdenv.isAarch64) ''
    # dylib will be rejected unless dylib rpath gets explictly set
    install_name_tool \
      -change $libName $out/lib/$libName \
      $out/lib/$libName
  '' + ''
    # Provide a setup hook that injects our library into every process.
    mkdir -p "$hook/nix-support"
    cat <<SETUP_HOOK > "$hook/nix-support/setup-hook"
    ${if stdenv.isDarwin then ''
    export DYLD_INSERT_LIBRARIES="$out/lib/$libName"
    '' else ''
    export LD_PRELOAD="$out/lib/$libName"
    ''}
    SETUP_HOOK

    runHook postInstall
  '';

  doInstallCheck = true;

  installCheckPhase = ''
    (
      source "$hook/nix-support/setup-hook"
      NIX_REDIRECTS="/foo/bar/test=${coreutils}/bin/true:/bar/baz=$(mktemp -d)" ./test
    )
  '';

  meta = with lib; {
    platforms = platforms.unix;
    description = "An LD_PRELOAD library to intercept and rewrite the paths in glibc calls";
    longDescription = ''
      libredirect is an LD_PRELOAD library to intercept and rewrite the paths in
      glibc calls based on the value of $NIX_REDIRECTS, a colon-separated list
      of path prefixes to be rewritten, e.g. "/src=/dst:/usr/=/nix/store/".
    '';
  };
}