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