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