• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 14345988482

08 Apr 2025 09:45PM UTC coverage: 71.913% (+0.006%) from 71.907%
14345988482

push

github

yuwata
test: Improve coverage in test-memfd-util and use ASSERT_OK() macro and friends

14 of 16 new or added lines in 1 file covered. (87.5%)

4696 existing lines in 76 files now uncovered.

296867 of 412812 relevant lines covered (71.91%)

666289.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.61
/src/basic/build-path.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <elf.h>
4
#include <link.h>
5
#include <sys/auxv.h>
6

7
#include "build-path.h"
8
#include "errno-list.h"
9
#include "errno-util.h"
10
#include "fd-util.h"
11
#include "macro.h"
12
#include "path-util.h"
13
#include "process-util.h"
14
#include "unistd.h"
15

16
static int get_runpath_from_dynamic(const ElfW(Dyn) *d, ElfW(Addr) bias, const char **ret) {
3,840✔
17
        size_t runpath_index = SIZE_MAX, rpath_index = SIZE_MAX;
3,840✔
18
        const char *strtab = NULL;
3,840✔
19

20
        assert(d);
3,840✔
21

22
        /* Iterates through the PT_DYNAMIC section to find the DT_RUNPATH/DT_RPATH entries */
23

24
        for (; d->d_tag != DT_NULL; d++) {
47,814✔
25

26
                switch (d->d_tag) {
47,814✔
27

28
                case DT_RUNPATH:
3,840✔
29
                        runpath_index = (size_t) d->d_un.d_val;
3,840✔
30
                        break;
3,840✔
31

32
                case DT_RPATH:
×
33
                        rpath_index = (size_t) d->d_un.d_val;
×
UNCOV
34
                        break;
×
35

36
                case DT_STRTAB:
3,840✔
37
                        /* On MIPS and RISC-V DT_STRTAB records an offset, not a valid address, so it has to be adjusted
38
                         * using the bias calculated earlier. */
39
                        if (d->d_un.d_val != 0)
3,840✔
40
                                strtab = (const char *) ((uintptr_t) d->d_un.d_val
3,840✔
41
#if defined(__mips__) || defined(__riscv)
42
                                         + bias
43
#endif
44
                                );
45
                        break;
46
                }
47

48
                /* runpath wins, hence if we have the table and runpath we can exit the loop early */
49
                if (strtab && runpath_index != SIZE_MAX)
47,814✔
50
                        break;
51
        }
52

53
        if (!strtab)
3,840✔
54
                return -ENOTRECOVERABLE;
55

56
        /* According to ld.so runpath wins if both runpath and rpath are defined. */
57
        if (runpath_index != SIZE_MAX) {
3,840✔
58
                if (ret)
3,840✔
59
                        *ret = strtab + runpath_index;
3,840✔
60
                return 1;
3,840✔
61
        }
62

63
        if (rpath_index != SIZE_MAX) {
×
64
                if (ret)
×
65
                        *ret = strtab + rpath_index;
×
UNCOV
66
                return 1;
×
67
        }
68

69
        if (ret)
×
UNCOV
70
                *ret = NULL;
×
71

72
        return 0;
73
}
74

75
static int get_runpath(const char **ret) {
3,840✔
76
        unsigned long phdr, phent, phnum;
3,840✔
77

78
        /* Finds the rpath/runpath in the program headers of the main executable we are running in */
79

80
        phdr = getauxval(AT_PHDR);      /* Start offset of phdr */
3,840✔
81
        if (phdr == 0)
3,840✔
82
                return -ENOTRECOVERABLE;
83

84
        phnum = getauxval(AT_PHNUM);    /* Number of entries in phdr */
3,840✔
85
        if (phnum == 0)
3,840✔
86
                return -ENOTRECOVERABLE;
87

88
        phent = getauxval(AT_PHENT);    /* Size of entries in phdr */
3,840✔
89
        if (phent < sizeof(ElfW(Phdr))) /* Safety check, that our idea of the structure matches the file */
3,840✔
90
                return -ENOTRECOVERABLE;
91

92
        ElfW(Addr) bias = 0, dyn = 0;
93
        bool found_bias = false, found_dyn = false;
94

95
        /* Iterate through the Phdr structures to find the PT_PHDR and PT_DYNAMIC sections */
96
        for (unsigned long i = 0; i < phnum; i++) {
26,880✔
97
                const ElfW(Phdr) *p = (const ElfW(Phdr)*) (phdr + (i * phent));
26,880✔
98

99
                switch (p->p_type) {
26,880✔
100

101
                case PT_PHDR:
3,840✔
102
                        if (p->p_vaddr > phdr) /* safety overflow check */
3,840✔
103
                                return -ENOTRECOVERABLE;
104

105
                        bias = (ElfW(Addr)) phdr - p->p_vaddr;
3,840✔
106
                        found_bias = true;
3,840✔
107
                        break;
3,840✔
108

109
                case PT_DYNAMIC:
3,840✔
110
                        dyn = p->p_vaddr;
3,840✔
111
                        found_dyn = true;
3,840✔
112
                        break;
3,840✔
113
                }
114

115
                if (found_bias && found_dyn)
26,880✔
116
                        break;
117
        }
118

119
        if (!found_dyn)
3,840✔
120
                return -ENOTRECOVERABLE;
121

122
        return get_runpath_from_dynamic((const ElfW(Dyn)*) (bias + dyn), bias, ret);
3,840✔
123
}
124

125
int get_build_exec_dir(char **ret) {
14,297✔
126
        int r;
14,297✔
127

128
        /* Returns the build execution directory if we are invoked in a build environment. Specifically, this
129
         * checks if the main program binary has an rpath/runpath set (i.e. an explicit directory where to
130
         * look for shared libraries) to $ORIGIN. If so we know that this is not a regular installed binary,
131
         * but one which shall acquire its libraries from below a directory it is located in, i.e. a build
132
         * directory or similar. In that case it typically makes sense to also search for our auxiliary
133
         * executables we fork() off in a directory close to our main program binary, rather than in the
134
         * system.
135
         *
136
         * This function is supposed to be used when looking for "callout" binaries that are closely related
137
         * to the main program (i.e. speak a specific protocol between each other). And where it's generally
138
         * a good idea to use the binary from the build tree (if there is one) instead of the system.
139
         *
140
         * Note that this does *not* actually return the rpath/runpath but the instead the directory the main
141
         * executable was found in. This follows the logic that the result is supposed to be used for
142
         * executable binaries (i.e. stuff in bindir), not for shared libraries (i.e. stuff in libdir), and
143
         * hence the literal shared library path would just be wrong.
144
         *
145
         * TLDR: if we look for callouts in this dir first, running binaries from the meson build tree
146
         * automatically uses the right callout.
147
         *
148
         * Returns:
149
         *     -ENOEXEC         → We are not running in an rpath/runpath $ORIGIN environment
150
         *     -ENOENT          → We don't know our own binary path
151
         *     -NOTRECOVERABLE  → Dynamic binary information missing?
152
         */
153

154
        static int runpath_cached = -ERRNO_MAX-1;
14,297✔
155
        if (runpath_cached == -ERRNO_MAX-1) {
14,297✔
156
                const char *runpath = NULL;
3,840✔
157

158
                runpath_cached = get_runpath(&runpath);
3,840✔
159

160
                /* We only care if the runpath starts with $ORIGIN/ */
161
                if (runpath_cached > 0 && !startswith(runpath, "$ORIGIN/"))
3,840✔
162
                        runpath_cached = 0;
3,840✔
163
        }
164
        if (runpath_cached < 0)
14,297✔
165
                return runpath_cached;
14,297✔
166
        if (runpath_cached == 0)
14,297✔
167
                return -ENOEXEC;
168

169
        _cleanup_free_ char *exe = NULL;
×
170
        r = get_process_exe(0, &exe);
×
171
        if (r < 0)
×
UNCOV
172
                return runpath_cached = r;
×
173

UNCOV
174
        return path_extract_directory(exe, ret);
×
175
}
176

177
static int find_build_dir_binary(const char *fn, char **ret) {
14,296✔
178
        int r;
14,296✔
179

180
        assert(fn);
14,296✔
181
        assert(ret);
14,296✔
182

183
        _cleanup_free_ char *build_dir = NULL;
14,296✔
184
        r = get_build_exec_dir(&build_dir);
14,296✔
185
        if (r < 0)
14,296✔
186
                return r;
187

188
        _cleanup_free_ char *np = path_join(build_dir, fn);
×
UNCOV
189
        if (!np)
×
190
                return -ENOMEM;
191

192
        *ret = TAKE_PTR(np);
×
UNCOV
193
        return 0;
×
194
}
195

196
static int find_environment_binary(const char *fn, const char **ret) {
14,296✔
197

198
        /* If a path such as /usr/lib/systemd/systemd-foobar is specified, then this will check for an
199
         * environment variable SYSTEMD_FOOBAR_PATH and return it if set. */
200

201
        _cleanup_free_ char *s = strdup(fn);
14,296✔
202
        if (!s)
14,296✔
203
                return -ENOMEM;
204

205
        ascii_strupper(s);
14,296✔
206
        string_replace_char(s, '-', '_');
14,296✔
207

208
        if (!strextend(&s, "_PATH"))
14,296✔
209
                return -ENOMEM;
210

211
        const char *e;
14,296✔
212
        e = secure_getenv(s);
14,296✔
213
        if (!e)
14,296✔
214
                return -ENXIO;
215

216
        *ret = e;
×
UNCOV
217
        return 0;
×
218
}
219

220
int invoke_callout_binary(const char *path, char *const argv[]) {
2,359✔
221
        int r;
2,359✔
222

223
        assert(path);
2,359✔
224

225
        /* Just like execv(), but tries to execute the specified binary in the build dir instead, if known */
226

227
        _cleanup_free_ char *fn = NULL;
2,359✔
228
        r = path_extract_filename(path, &fn);
2,359✔
229
        if (r < 0)
2,359✔
230
                return r;
231
        if (r == O_DIRECTORY) /* Uh? */
2,359✔
232
                return -EISDIR;
233

234
        const char *e;
2,359✔
235
        if (find_environment_binary(fn, &e) >= 0) {
2,359✔
236
                /* If there's an explicit environment variable set for this binary, prefer it */
237
                execv(e, argv);
×
UNCOV
238
                return -errno; /* The environment variable counts, let's fail otherwise */
×
239
        }
240

241
        _cleanup_free_ char *np = NULL;
2,359✔
242
        if (find_build_dir_binary(fn, &np) >= 0)
2,359✔
UNCOV
243
                execv(np, argv);
×
244

245
        execvp(path, argv);
2,359✔
246
        return -errno;
2,359✔
247
}
248

249
int pin_callout_binary(const char *path, char **ret_path) {
11,937✔
250
        int r, fd;
11,937✔
251

252
        assert(path);
11,937✔
253

254
        /* Similar to invoke_callout_binary(), but pins (i.e. O_PATH opens) the binary instead of executing
255
         * it, also optionally provides the path to the binary. */
256

257
        _cleanup_free_ char *fn = NULL;
11,937✔
258
        r = path_extract_filename(path, &fn);
11,937✔
259
        if (r < 0)
11,937✔
260
                return r;
261
        if (r == O_DIRECTORY) /* Uh? */
11,937✔
262
                return -EISDIR;
263

264
        const char *e;
11,937✔
265
        if (find_environment_binary(fn, &e) >= 0) {
11,937✔
266
                /* The environment variable counts. We'd fail if the executable is not available/invalid. */
UNCOV
267
                r = open_and_check_executable(e, /* root = */ NULL, ret_path, &fd);
×
268
                if (r < 0)
×
269
                        return r;
270

UNCOV
271
                return fd;
×
272
        }
273

274
        _cleanup_free_ char *np = NULL;
11,937✔
275
        if (find_build_dir_binary(fn, &np) >= 0) {
11,937✔
UNCOV
276
                r = open_and_check_executable(np, /* root = */ NULL, ret_path, &fd);
×
UNCOV
277
                if (r >= 0)
×
UNCOV
278
                        return fd;
×
279
        }
280

281
        r = find_executable_full(path, /* root = */ NULL,
11,937✔
282
                                 /* exec_search_path = */ NULL, /* use_path_envvar = */ true,
283
                                 ret_path, &fd);
284
        if (r < 0)
11,937✔
285
                return r;
286

287
        return fd;
11,937✔
288
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc