]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/elfctl/elfctl.c
Update to bmake-20200902
[FreeBSD/FreeBSD.git] / usr.bin / elfctl / elfctl.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 <err.h>
37 #include <fcntl.h>
38 #include <gelf.h>
39 #include <getopt.h>
40 #include <libelf.h>
41 #include <stdbool.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #include "_elftc.h"
48
49 __FBSDID("$FreeBSD$");
50
51 static bool convert_to_feature_val(char *, uint32_t *);
52 static bool edit_file_features(Elf *, int, int, char *);
53 static bool get_file_features(Elf *, int, int, uint32_t *, uint64_t *);
54 static void print_features(void);
55 static bool print_file_features(Elf *, int, int, char *);
56 static void usage(void);
57
58 struct ControlFeatures {
59         const char *alias;
60         unsigned long value;
61         const char *desc;
62 };
63
64 static struct ControlFeatures featurelist[] = {
65         { "aslr",       NT_FREEBSD_FCTL_ASLR_DISABLE,   "Disable ASLR" },
66         { "protmax",    NT_FREEBSD_FCTL_PROTMAX_DISABLE,
67             "Disable implicit PROT_MAX" },
68         { "stackgap",   NT_FREEBSD_FCTL_STKGAP_DISABLE, "Disable stack gap" },
69         { "wxneeded",   NT_FREEBSD_FCTL_WXNEEDED, "Requires W+X mappings" },
70         { "la48",       NT_FREEBSD_FCTL_LA48, "amd64: Limit user VA to 48bit" },
71 };
72
73 static struct option long_opts[] = {
74         { "help",       no_argument,    NULL,   'h' },
75         { NULL,         0,              NULL,   0 }
76 };
77
78 #if BYTE_ORDER == LITTLE_ENDIAN
79 #define SUPPORTED_ENDIAN ELFDATA2LSB
80 #else
81 #define SUPPORTED_ENDIAN ELFDATA2MSB
82 #endif
83                 
84 int
85 main(int argc, char **argv)
86 {
87         GElf_Ehdr ehdr;
88         Elf *elf;
89         Elf_Kind kind;
90         int ch, fd, retval;
91         char *features;
92         bool editfeatures, lflag;
93
94         lflag = 0;
95         editfeatures = false;
96         retval = 0;
97         features = NULL;
98
99         if (elf_version(EV_CURRENT) == EV_NONE)
100                 errx(EXIT_FAILURE, "elf_version error");
101
102         while ((ch = getopt_long(argc, argv, "hle:", long_opts, NULL)) != -1) {
103                 switch (ch) {
104                 case 'l':
105                         print_features();
106                         lflag = true;
107                         break;
108                 case 'e':
109                         features = optarg;
110                         editfeatures = true;
111                         break;
112                 case 'h':
113                 default:
114                         usage();
115                 }
116         }
117         argc -= optind;
118         argv += optind;
119         if (argc == 0) {
120                 if (lflag)
121                         exit(0);
122                 else {
123                         warnx("no file(s) specified");
124                         usage();
125                 }
126         }
127
128         while (argc) {
129                 elf = NULL;
130
131                 if ((fd = open(argv[0],
132                     editfeatures ? O_RDWR : O_RDONLY, 0)) < 0) {
133                         warn("error opening file %s", argv[0]);
134                         retval = 1;
135                         goto fail;
136                 }
137
138                 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
139                         warnx("elf_begin failed: %s", elf_errmsg(-1));
140                         retval = 1;
141                         goto fail;
142                 }
143
144                 if ((kind = elf_kind(elf)) != ELF_K_ELF) {
145                         if (kind == ELF_K_AR)
146                                 warnx("file '%s' is an archive", argv[0]);
147                         else
148                                 warnx("file '%s' is not an ELF file", argv[0]);
149                         retval = 1;
150                         goto fail;
151                 }
152
153                 if (gelf_getehdr(elf, &ehdr) == NULL) {
154                         warnx("gelf_getehdr: %s", elf_errmsg(-1));
155                         retval = 1;
156                         goto fail;
157                 }
158                 /*
159                  * XXX need to support cross-endian operation, but for now
160                  * exit on error rather than misbehaving.
161                  */
162                 if (ehdr.e_ident[EI_DATA] != SUPPORTED_ENDIAN) {
163                         warnx("file endianness must match host");
164                         retval = 1;
165                         goto fail;
166                 }
167
168                 if (!editfeatures) {
169                         if (!print_file_features(elf, ehdr.e_phnum, fd,
170                             argv[0])) {
171                                 retval = 1;
172                                 goto fail;
173                         }
174                 } else if (!edit_file_features(elf, ehdr.e_phnum, fd,
175                     features)) {
176                         retval = 1;
177                         goto fail;
178                 }
179 fail:
180                 if (elf != NULL)
181                         elf_end(elf);
182
183                 if (fd >= 0)
184                         close(fd);
185
186                 argc--;
187                 argv++;
188         }
189
190         return (retval);
191 }
192
193 #define USAGE_MESSAGE \
194         "\
195 Usage: %s [options] file...\n\
196   Set or display the control features for an ELF object.\n\n\
197   Supported options are:\n\
198   -l                        List known control features.\n\
199   -e [+-=]feature,list      Edit features from a comma separated list.\n\
200   -h | --help               Print a usage message and exit.\n"
201
202 static void
203 usage(void)
204 {
205
206         fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
207         exit(1);
208 }
209
210 static bool
211 convert_to_feature_val(char *feature_str, uint32_t *feature_val)
212 {
213         char *feature;
214         int i, len;
215         uint32_t input;
216         char operation;
217
218         input = 0;
219         operation = *feature_str;
220         feature_str++;
221         len = nitems(featurelist);
222         while ((feature = strsep(&feature_str, ",")) != NULL) {
223                 for (i = 0; i < len; ++i) {
224                         if (strcmp(featurelist[i].alias, feature) == 0) {
225                                 input |= featurelist[i].value;
226                                 break;
227                         }
228                 }
229                 if (i == len) {
230                         warnx("%s is not a valid feature", feature);
231                         return (false);
232                 }
233         }
234
235         if (operation == '+') {
236                 *feature_val |= input;
237         } else if (operation == '=') {
238                 *feature_val = input;
239         } else if (operation == '-') {
240                 *feature_val &= ~input;
241         } else {
242                 warnx("'%c' not an operator - use '+', '-', '='",
243                     feature_str[0]);
244                 return (false);
245         }
246         return (true);
247 }
248
249 static bool
250 edit_file_features(Elf *elf, int phcount, int fd, char *val)
251 {
252         uint32_t features;
253         uint64_t off;
254
255         if (!get_file_features(elf, phcount, fd, &features, &off)) {
256                 warnx("NT_FREEBSD_FEATURE_CTL note not found");
257                 return (false);
258         }
259
260         if (!convert_to_feature_val(val, &features))
261                 return (false);
262
263         if (lseek(fd, off, SEEK_SET) == -1 ||
264             write(fd, &features, sizeof(features)) <
265             (ssize_t)sizeof(features)) {
266                 warnx("error writing feature value");
267                 return (false);
268         }
269         return (true);
270 }
271
272 static void
273 print_features(void)
274 {
275         size_t i;
276
277         printf("Known features are:\n");
278         for (i = 0; i < nitems(featurelist); ++i)
279                 printf("%-16s%s\n", featurelist[i].alias,
280                     featurelist[i].desc);
281 }
282
283 static bool
284 print_file_features(Elf *elf, int phcount, int fd, char *filename)
285 {
286         uint32_t features;
287         unsigned long i;
288
289         if (!get_file_features(elf, phcount, fd, &features, NULL)) {
290                 return (false);
291         }
292
293         printf("File '%s' features:\n", filename);
294         for (i = 0; i < nitems(featurelist); ++i) {
295                 printf("%-16s'%s' is ", featurelist[i].alias,
296                     featurelist[i].desc);
297
298                 if ((featurelist[i].value & features) == 0)
299                         printf("un");
300
301                 printf("set.\n");
302         }
303         return (true);
304 }
305
306 static bool
307 get_file_features(Elf *elf, int phcount, int fd, uint32_t *features,
308     uint64_t *off)
309 {
310         GElf_Phdr phdr;
311         Elf_Note note;
312         unsigned long read_total;
313         int namesz, descsz, i;
314         char *name;
315
316         /*
317          * Go through each program header to find one that is of type PT_NOTE
318          * and has a note for feature control.
319          */
320         for (i = 0; i < phcount; ++i) {
321                 if (gelf_getphdr(elf, i, &phdr) == NULL) {
322                         warnx("gelf_getphdr failed: %s", elf_errmsg(-1));
323                         return (false);
324                 }
325
326                 if (phdr.p_type != PT_NOTE)
327                         continue;
328
329                 if (lseek(fd, phdr.p_offset, SEEK_SET) < 0) {
330                         warn("lseek() failed:");
331                         return (false);
332                 }
333
334                 read_total = 0;
335                 while (read_total < phdr.p_filesz) {
336                         if (read(fd, &note, sizeof(note)) <
337                             (ssize_t)sizeof(note)) {
338                                 warnx("elf note header too short");
339                                 return (false);
340                         }
341                         read_total += sizeof(note);
342
343                         /*
344                          * XXX: Name and descriptor are 4 byte aligned, however,
345                          *      the size given doesn't include the padding.
346                          */
347                         namesz = roundup2(note.n_namesz, 4);
348                         name = malloc(namesz);
349                         if (name == NULL) {
350                                 warn("malloc() failed.");
351                                 return (false);
352                         }
353                         descsz = roundup2(note.n_descsz, 4);
354                         if (read(fd, name, namesz) < namesz) {
355                                 warnx("elf note name too short");
356                                 free(name);
357                                 return (false);
358                         }
359                         read_total += namesz;
360
361                         if (note.n_namesz != 8 ||
362                             strncmp("FreeBSD", name, 7) != 0 ||
363                             note.n_type != NT_FREEBSD_FEATURE_CTL) {
364                                 /* Not the right note. Skip the description */
365                                 if (lseek(fd, descsz, SEEK_CUR) < 0) {
366                                         warn("lseek() failed.");
367                                         free(name);
368                                         return (false);
369                                 }
370                                 read_total += descsz;
371                                 free(name);
372                                 continue;
373                         }
374
375                         if (note.n_descsz < sizeof(uint32_t)) {
376                                 warnx("Feature descriptor can't "
377                                     "be less than 4 bytes");
378                                 free(name);
379                                 return (false);
380                         }
381
382                         /*
383                          * XXX: For now we look at only 4 bytes of the
384                          *      descriptor. This should respect descsz.
385                          */
386                         if (note.n_descsz > sizeof(uint32_t))
387                                 warnx("Feature note is bigger than expected");
388                         if (read(fd, features, sizeof(uint32_t)) <
389                             (ssize_t)sizeof(uint32_t)) {
390                                 warnx("feature note data too short");
391                                 free(name);
392                                 return (false);
393                         }
394                         if (off != NULL)
395                                 *off = phdr.p_offset + read_total;
396                         free(name);
397                         return (true);
398                 }
399         }
400
401         warnx("NT_FREEBSD_FEATURE_CTL note not found");
402         return (false);
403 }