gnu: ld-wrapper: Add '-rpath' flag only for libraries that are in the store.
[jackhill/guix/guix.git] / gnu / packages / ld-wrapper.scm
CommitLineData
dfb53ee2 1#!@BASH@
82dc2b9a
LC
2# -*- mode: scheme; coding: utf-8; -*-
3
4# XXX: We have to go through Bash because there's no command-line switch to
5# augment %load-compiled-path, and because of the silly 127-byte limit for
6# the shebang line in Linux.
7# Use `load-compiled' because `load' (and `-l') doesn't otherwise load our
8# .go file (see <http://bugs.gnu.org/12519>).
9
dfb53ee2 10main="(@ (gnu build-support ld-wrapper) ld-wrapper)"
9bab6bea 11exec @GUILE@ -c "(load-compiled \"@SELF@.go\") (apply $main (cdr (command-line)))" "$@"
82dc2b9a 12!#
4155e2a9 13;;; GNU Guix --- Functional package management for GNU
d8491ba5 14;;; Copyright © 2012, 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org>
82dc2b9a 15;;;
4155e2a9 16;;; This file is part of GNU Guix.
82dc2b9a 17;;;
4155e2a9 18;;; GNU Guix is free software; you can redistribute it and/or modify it
82dc2b9a
LC
19;;; under the terms of the GNU General Public License as published by
20;;; the Free Software Foundation; either version 3 of the License, or (at
21;;; your option) any later version.
22;;;
4155e2a9 23;;; GNU Guix is distributed in the hope that it will be useful, but
82dc2b9a
LC
24;;; WITHOUT ANY WARRANTY; without even the implied warranty of
25;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26;;; GNU General Public License for more details.
27;;;
28;;; You should have received a copy of the GNU General Public License
4155e2a9 29;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
82dc2b9a 30
dfb53ee2 31(define-module (gnu build-support ld-wrapper)
82dc2b9a 32 #:use-module (srfi srfi-1)
d8491ba5 33 #:use-module (ice-9 match)
82dc2b9a
LC
34 #:export (ld-wrapper))
35
36;;; Commentary:
37;;;
38;;; This is a wrapper for the linker. Its purpose is to inspect the -L and
39;;; -l switches passed to the linker, add corresponding -rpath arguments, and
40;;; invoke the actual linker with this new set of arguments.
41;;;
42;;; The alternatives to this hack would be:
43;;;
44;;; 1. Using $LD_RUN_PATH. However, that would tend to include more than
45;;; needed in the RPATH; for instance, given a package with `libfoo' as
46;;; an input, all its binaries would have libfoo in their RPATH,
47;;; regardless of whether they actually NEED it.
48;;;
49;;; 2. Use a GCC "lib" spec string such as `%{L*:-rpath %*}', which adds a
50;;; `-rpath LIBDIR' argument for each occurrence of `-L LIBDIR'.
51;;; However, this doesn't work when $LIBRARY_PATH is used, because the
52;;; additional `-L' switches are not matched by the above rule, because
53;;; the rule only matches explicit user-provided switches. See
54;;; <http://gcc.gnu.org/ml/gcc-help/2012-09/msg00110.html> for details.
55;;;
56;;; As a bonus, this wrapper checks for "impurities"--i.e., references to
57;;; libraries outside the store.
58;;;
59;;; Code:
60
61(define %real-ld
62 ;; Name of the linker that we wrap.
63 "@LD@")
64
65(define %store-directory
66 ;; File name of the store.
8be3b8a3 67 (or (getenv "NIX_STORE") "/gnu/store"))
82dc2b9a
LC
68
69(define %temporary-directory
70 ;; Temporary directory.
71 (or (getenv "TMPDIR") "/tmp"))
72
73(define %build-directory
74 ;; Top build directory when run from a builder.
75 (getenv "NIX_BUILD_TOP"))
76
77(define %allow-impurities?
78 ;; Whether to allow references to libraries outside the store.
79 (getenv "GUIX_LD_WRAPPER_ALLOW_IMPURITIES"))
80
81(define %debug?
82 ;; Whether to emit debugging output.
83 (getenv "GUIX_LD_WRAPPER_DEBUG"))
84
41fc0eb9
LC
85(define (readlink* file)
86 ;; Call 'readlink' until the result is not a symlink.
cfbf7877
LC
87 (define %max-symlink-depth 50)
88
89 (let loop ((file file)
90 (depth 0))
41fc0eb9
LC
91 (catch 'system-error
92 (lambda ()
93 (if (>= depth %max-symlink-depth)
94 file
95 (loop (readlink file) (+ depth 1))))
96 (lambda args
97 (if (= EINVAL (system-error-errno args))
98 file
99 (apply throw args))))))
100
101(define (dereference-symlinks file)
102 ;; Same as 'readlink*' but return FILE if the symlink target is invalid or
103 ;; FILE does not exist.
104 (catch 'system-error
105 (lambda ()
106 ;; When used from a user environment, FILE may refer to
107 ;; ~/.guix-profile/lib/libfoo.so, which is itself a symlink to the
108 ;; store. Check whether this is the case.
109 (readlink* file))
110 (lambda args
111 (if (= ENOENT (system-error-errno args))
112 file
113 (apply throw args)))))
114
115(define (pure-file-name? file)
116 ;; Return #t when FILE is the name of a file either within the store
117 ;; (possibly via a symlink) or within the build directory.
118 (let ((file (dereference-symlinks file)))
cfbf7877
LC
119 (or (not (string-prefix? "/" file))
120 (string-prefix? %store-directory file)
121 (string-prefix? %temporary-directory file)
41fc0eb9
LC
122 (and %build-directory
123 (string-prefix? %build-directory file)))))
82dc2b9a 124
51d0cd9b
LC
125(define (store-file-name? file)
126 ;; Return #t when FILE is a store file, possibly indirectly.
127 (string-prefix? %store-directory (dereference-symlinks file)))
128
f307947e
LC
129(define (shared-library? file)
130 ;; Return #t when FILE denotes a shared library.
131 (or (string-suffix? ".so" file)
132 (let ((index (string-contains file ".so.")))
133 ;; Since we cannot use regexps during bootstrap, roll our own.
134 (and index
135 (string-every (char-set-union (char-set #\.) char-set:digit)
136 (string-drop file (+ index 3)))))))
137
82dc2b9a
LC
138(define (library-files-linked args)
139 ;; Return the file names of shared libraries explicitly linked against via
d8491ba5
LC
140 ;; `-l' or with an absolute file name in ARGS.
141 (define path+files
142 (fold (lambda (argument result)
143 (match result
144 ((library-path . library-files)
145 (cond ((string-prefix? "-L" argument) ;augment the search path
146 (cons (append library-path
147 (list (string-drop argument 2)))
148 library-files))
149 ((string-prefix? "-l" argument) ;add library
150 (let* ((lib (string-append "lib"
151 (string-drop argument 2)
152 ".so"))
153 (full (search-path library-path lib)))
154 (if full
155 (cons library-path
156 (cons full library-files))
157 result)))
158 ((and (string-prefix? %store-directory argument)
f307947e 159 (shared-library? argument)) ;add library
d8491ba5
LC
160 (cons library-path
161 (cons argument library-files)))
162 (else
163 result)))))
164 (cons '() '())
165 args))
166
167 (match path+files
168 ((path . files)
169 (reverse files))))
170
171(define (rpath-arguments library-files)
172 ;; Return the `-rpath' argument list for each of LIBRARY-FILES, a list of
173 ;; absolute file names.
82dc2b9a 174 (fold-right (lambda (file args)
51d0cd9b
LC
175 ;; Add '-rpath' if and only if FILE is in the store; we don't
176 ;; want to add '-rpath' for files under %BUILD-DIRECTORY or
177 ;; %TEMPORARY-DIRECTORY because that could leak to installed
178 ;; files.
179 (cond ((store-file-name? file)
180 (cons* "-rpath" (dirname file) args))
181 ((or %allow-impurities?
182 (pure-file-name? file))
183 args)
184 (else
185 (begin
186 (format (current-error-port)
187 "ld-wrapper: error: attempt to use \
188impure library ~s~%"
189 file)
190 (exit 1)))))
82dc2b9a
LC
191 '()
192 library-files))
193
194(define (ld-wrapper . args)
195 ;; Invoke the real `ld' with ARGS, augmented with `-rpath' switches.
d8491ba5
LC
196 (let* ((libs (library-files-linked args))
197 (args (append args (rpath-arguments libs))))
198 (when %debug?
199 (format (current-error-port)
200 "ld-wrapper: invoking `~a' with ~s~%"
201 %real-ld args))
82dc2b9a
LC
202 (apply execl %real-ld (basename %real-ld) args)))
203
204;;; ld-wrapper.scm ends here