]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/elfctl/elfctl.c
Merge llvm-project release/16.x llvmorg-16.0.3-0-gda3cd333bea5
[FreeBSD/FreeBSD.git] / usr.bin / elfctl / elfctl.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2019 The FreeBSD Foundation.
5  *
6  * This software was developed by Bora Ozarslan under sponsorship from
7  * the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include <sys/param.h>
32 #include <sys/elf_common.h>
33 #include <sys/endian.h>
34 #include <sys/stat.h>
35
36 #include <ctype.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <gelf.h>
41 #include <getopt.h>
42 #include <libelf.h>
43 #include <stdbool.h>
44 #include <stdint.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #include "_elftc.h"
51
52 __FBSDID("$FreeBSD$");
53
54 static bool convert_to_feature_val(const char *, uint32_t *);
55 static bool edit_file_features(Elf *, int, int, char *, bool);
56 static bool get_file_features(Elf *, int, int, uint32_t *, uint64_t *, bool);
57 static void print_features(void);
58 static bool print_file_features(Elf *, int, int, char *, bool);
59 static void usage(void);
60
61 struct ControlFeatures {
62         const char *alias;
63         unsigned long value;
64         const char *desc;
65 };
66
67 static struct ControlFeatures featurelist[] = {
68         { "noaslr",     NT_FREEBSD_FCTL_ASLR_DISABLE,   "Disable ASLR" },
69         { "noprotmax",  NT_FREEBSD_FCTL_PROTMAX_DISABLE,
70             "Disable implicit PROT_MAX" },
71         { "nostackgap", NT_FREEBSD_FCTL_STKGAP_DISABLE, "Disable stack gap" },
72         { "wxneeded",   NT_FREEBSD_FCTL_WXNEEDED, "Requires W+X mappings" },
73         { "la48",       NT_FREEBSD_FCTL_LA48, "amd64: Limit user VA to 48bit" },
74 };
75
76 static struct option long_opts[] = {
77         { "help",       no_argument,    NULL,   'h' },
78         { NULL,         0,              NULL,   0 }
79 };
80
81 #if BYTE_ORDER == LITTLE_ENDIAN
82 #define HOST_ENDIAN     ELFDATA2LSB
83 #define SWAP_ENDIAN     ELFDATA2MSB
84 #else
85 #define HOST_ENDIAN     ELFDATA2MSB
86 #define SWAP_ENDIAN     ELFDATA2LSB
87 #endif
88
89 static bool iflag;
90
91 int
92 main(int argc, char **argv)
93 {
94         GElf_Ehdr ehdr;
95         Elf *elf;
96         Elf_Kind kind;
97         int ch, fd, retval;
98         char *features;
99         bool editfeatures, lflag, endian_swap;
100
101         lflag = 0;
102         editfeatures = false;
103         retval = 0;
104         features = NULL;
105
106         if (elf_version(EV_CURRENT) == EV_NONE)
107                 errx(EXIT_FAILURE, "elf_version error");
108
109         while ((ch = getopt_long(argc, argv, "hile:", long_opts, NULL)) != -1) {
110                 switch (ch) {
111                 case 'i':
112                         iflag = true;
113                         break;
114                 case 'l':
115                         print_features();
116                         lflag = true;
117                         break;
118                 case 'e':
119                         if (features != NULL)
120                                 errx(1, "-e may be specified only once");
121                         features = optarg;
122                         editfeatures = true;
123                         break;
124                 case 'h':
125                 default:
126                         usage();
127                 }
128         }
129         argc -= optind;
130         argv += optind;
131         if (argc == 0) {
132                 if (lflag)
133                         exit(0);
134                 else {
135                         warnx("no file(s) specified");
136                         usage();
137                 }
138         }
139
140         while (argc) {
141                 elf = NULL;
142
143                 if ((fd = open(argv[0],
144                     editfeatures ? O_RDWR : O_RDONLY, 0)) < 0) {
145                         warn("error opening file %s", argv[0]);
146                         retval = 1;
147                         goto fail;
148                 }
149
150                 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
151                         warnx("elf_begin failed: %s", elf_errmsg(-1));
152                         retval = 1;
153                         goto fail;
154                 }
155
156                 if ((kind = elf_kind(elf)) != ELF_K_ELF) {
157                         if (kind == ELF_K_AR)
158                                 warnx("file '%s' is an archive", argv[0]);
159                         else
160                                 warnx("file '%s' is not an ELF file", argv[0]);
161                         retval = 1;
162                         goto fail;
163                 }
164
165                 if (gelf_getehdr(elf, &ehdr) == NULL) {
166                         warnx("gelf_getehdr: %s", elf_errmsg(-1));
167                         retval = 1;
168                         goto fail;
169                 }
170
171                 if (ehdr.e_ident[EI_DATA] == HOST_ENDIAN) {
172                         endian_swap = false;
173                 } else if (ehdr.e_ident[EI_DATA] == SWAP_ENDIAN) {
174                         endian_swap = true;
175                 } else {
176                         warnx("file endianness unknown");
177                         retval = 1;
178                         goto fail;
179                 }
180
181                 if (!editfeatures) {
182                         if (!print_file_features(elf, ehdr.e_phnum, fd,
183                             argv[0], endian_swap)) {
184                                 retval = 1;
185                                 goto fail;
186                         }
187                 } else if (!edit_file_features(elf, ehdr.e_phnum, fd,
188                     features, endian_swap)) {
189                         retval = 1;
190                         goto fail;
191                 }
192 fail:
193                 if (elf != NULL)
194                         elf_end(elf);
195
196                 if (fd >= 0)
197                         close(fd);
198
199                 argc--;
200                 argv++;
201         }
202
203         return (retval);
204 }
205
206 #define USAGE_MESSAGE \
207         "\
208 Usage: %s [options] file...\n\
209   Set or display the control features for an ELF object.\n\n\
210   Supported options are:\n\
211   -l                        List known control features.\n\
212   -i                        Ignore unknown features.\n\
213   -e [+-=]feature,list      Edit features from a comma separated list.\n\
214   -h | --help               Print a usage message and exit.\n"
215
216 static void
217 usage(void)
218 {
219
220         fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
221         exit(1);
222 }
223
224 static bool
225 convert_to_feature_val(const char *feature_str, uint32_t *feature_val)
226 {
227         char *feature, *feature_tmp;
228         int i, len;
229         uint32_t input;
230         char operation;
231
232         input = 0;
233         operation = *feature_str;
234         feature_str++;
235
236         if (operation != '+' && operation != '-' && operation != '=')
237                 errx(1, "'%c' not an operator - use '+', '-', '='", operation);
238
239         if ((feature_tmp = strdup(feature_str)) == NULL)
240                 err(1, "strdup");
241         len = nitems(featurelist);
242         while ((feature = strsep(&feature_tmp, ",")) != NULL) {
243                 for (i = 0; i < len; ++i) {
244                         if (strcmp(featurelist[i].alias, feature) == 0) {
245                                 input |= featurelist[i].value;
246                                 break;
247                         }
248                         /* XXX Backwards compatibility for "no"-prefix flags. */
249                         if (strncmp(featurelist[i].alias, "no", 2) == 0 &&
250                             strcmp(featurelist[i].alias + 2, feature) == 0) {
251                                 input |= featurelist[i].value;
252                                 warnx(
253                                     "interpreting %s as %s; please specify %s",
254                                     feature, featurelist[i].alias,
255                                     featurelist[i].alias);
256                                 break;
257                         }
258                 }
259                 if (i == len) {
260                         if (isdigit(feature[0])) {
261                                 char *eptr;
262                                 unsigned long long val;
263
264                                 errno = 0;
265                                 val = strtoll(feature, &eptr, 0);
266                                 if (eptr == feature || *eptr != '\0')
267                                         errno = EINVAL;
268                                 else if (val > UINT32_MAX)
269                                         errno = ERANGE;
270                                 if (errno != 0) {
271                                         warn("%s invalid", feature);
272                                         free(feature_tmp);
273                                         return (false);
274                                 }
275                                 input |= val;
276                         } else {
277                                 warnx("%s is not a valid feature", feature);
278                                 if (!iflag) {
279                                         free(feature_tmp);
280                                         return (false);
281                                 }
282                         }
283                 }
284         }
285
286         if (operation == '+') {
287                 *feature_val |= input;
288         } else if (operation == '=') {
289                 *feature_val = input;
290         } else if (operation == '-') {
291                 *feature_val &= ~input;
292         }
293         free(feature_tmp);
294         return (true);
295 }
296
297 static bool
298 edit_file_features(Elf *elf, int phcount, int fd, char *val, bool endian_swap)
299 {
300         uint32_t features, prev_features;
301         uint64_t off;
302
303         if (!get_file_features(elf, phcount, fd, &features, &off,
304             endian_swap)) {
305                 warnx("NT_FREEBSD_FEATURE_CTL note not found");
306                 return (false);
307         }
308
309         prev_features = features;
310         if (!convert_to_feature_val(val, &features))
311                 return (false);
312         /* Avoid touching file if no change. */
313         if (features == prev_features)
314                 return (true);
315
316         if (endian_swap)
317                 features = bswap32(features);
318
319         if (lseek(fd, off, SEEK_SET) == -1 ||
320             write(fd, &features, sizeof(features)) <
321             (ssize_t)sizeof(features)) {
322                 warnx("error writing feature value");
323                 return (false);
324         }
325         return (true);
326 }
327
328 static void
329 print_features(void)
330 {
331         size_t i;
332
333         printf("Known features are:\n");
334         for (i = 0; i < nitems(featurelist); ++i)
335                 printf("%-16s%s\n", featurelist[i].alias,
336                     featurelist[i].desc);
337 }
338
339 static bool
340 print_file_features(Elf *elf, int phcount, int fd, char *filename,
341     bool endian_swap)
342 {
343         uint32_t features;
344         unsigned long i;
345
346         if (!get_file_features(elf, phcount, fd, &features, NULL,
347             endian_swap)) {
348                 return (false);
349         }
350
351         printf("File '%s' features:\n", filename);
352         for (i = 0; i < nitems(featurelist); ++i) {
353                 printf("%-16s'%s' is ", featurelist[i].alias,
354                     featurelist[i].desc);
355
356                 if ((featurelist[i].value & features) == 0)
357                         printf("un");
358
359                 printf("set.\n");
360         }
361         return (true);
362 }
363
364 static bool
365 get_file_features(Elf *elf, int phcount, int fd, uint32_t *features,
366     uint64_t *off, bool endian_swap)
367 {
368         GElf_Phdr phdr;
369         Elf_Note note;
370         unsigned long read_total;
371         int namesz, descsz, i;
372         char *name;
373
374         /*
375          * Go through each program header to find one that is of type PT_NOTE
376          * and has a note for feature control.
377          */
378         for (i = 0; i < phcount; ++i) {
379                 if (gelf_getphdr(elf, i, &phdr) == NULL) {
380                         warnx("gelf_getphdr failed: %s", elf_errmsg(-1));
381                         return (false);
382                 }
383
384                 if (phdr.p_type != PT_NOTE)
385                         continue;
386
387                 if (lseek(fd, phdr.p_offset, SEEK_SET) < 0) {
388                         warn("lseek() failed:");
389                         return (false);
390                 }
391
392                 read_total = 0;
393                 while (read_total < phdr.p_filesz) {
394                         if (read(fd, &note, sizeof(note)) <
395                             (ssize_t)sizeof(note)) {
396                                 warnx("elf note header too short");
397                                 return (false);
398                         }
399                         read_total += sizeof(note);
400
401                         if (endian_swap) {
402                                 note.n_namesz = bswap32(note.n_namesz);
403                                 note.n_descsz = bswap32(note.n_descsz);
404                                 note.n_type = bswap32(note.n_type);
405                         }
406
407                         /*
408                          * XXX: Name and descriptor are 4 byte aligned, however,
409                          * the size given doesn't include the padding.
410                          */
411                         namesz = roundup2(note.n_namesz, 4);
412                         name = malloc(namesz);
413                         if (name == NULL) {
414                                 warn("malloc() failed.");
415                                 return (false);
416                         }
417                         descsz = roundup2(note.n_descsz, 4);
418                         if (read(fd, name, namesz) < namesz) {
419                                 warnx("elf note name too short");
420                                 free(name);
421                                 return (false);
422                         }
423                         read_total += namesz;
424
425                         if (note.n_namesz != 8 ||
426                             strncmp("FreeBSD", name, 7) != 0 ||
427                             note.n_type != NT_FREEBSD_FEATURE_CTL) {
428                                 /* Not the right note. Skip the description */
429                                 if (lseek(fd, descsz, SEEK_CUR) < 0) {
430                                         warn("lseek() failed.");
431                                         free(name);
432                                         return (false);
433                                 }
434                                 read_total += descsz;
435                                 free(name);
436                                 continue;
437                         }
438
439                         if (note.n_descsz < sizeof(uint32_t)) {
440                                 warnx("Feature descriptor can't "
441                                     "be less than 4 bytes");
442                                 free(name);
443                                 return (false);
444                         }
445
446                         /*
447                          * XXX: For now we look at only 4 bytes of the
448                          * descriptor. This should respect descsz.
449                          */
450                         if (note.n_descsz > sizeof(uint32_t))
451                                 warnx("Feature note is bigger than expected");
452                         if (read(fd, features, sizeof(uint32_t)) <
453                             (ssize_t)sizeof(uint32_t)) {
454                                 warnx("feature note data too short");
455                                 free(name);
456                                 return (false);
457                         }
458                         if (endian_swap)
459                                 *features = bswap32(*features);
460                         if (off != NULL)
461                                 *off = phdr.p_offset + read_total;
462                         free(name);
463                         return (true);
464                 }
465         }
466
467         warnx("NT_FREEBSD_FEATURE_CTL note not found");
468         return (false);
469 }