2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2019 The FreeBSD Foundation.
6 * This software was developed by Bora Ozarslan under sponsorship from
7 * the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
31 #include <sys/param.h>
32 #include <sys/elf_common.h>
33 #include <sys/endian.h>
52 __FBSDID("$FreeBSD$");
54 static bool convert_to_feature_val(char *, uint32_t *);
55 static bool edit_file_features(Elf *, int, int, char *);
56 static bool get_file_features(Elf *, int, int, uint32_t *, uint64_t *);
57 static void print_features(void);
58 static bool print_file_features(Elf *, int, int, char *);
59 static void usage(void);
61 struct ControlFeatures {
67 static struct ControlFeatures featurelist[] = {
68 { "aslr", NT_FREEBSD_FCTL_ASLR_DISABLE, "Disable ASLR" },
69 { "protmax", NT_FREEBSD_FCTL_PROTMAX_DISABLE,
70 "Disable implicit PROT_MAX" },
71 { "stackgap", NT_FREEBSD_FCTL_STKGAP_DISABLE, "Disable stack gap" },
72 { "wxneeded", NT_FREEBSD_FCTL_WXNEEDED, "Requires W+X mappings" },
73 #ifdef NT_FREEBSD_FCTL_LA48
74 { "la48", NT_FREEBSD_FCTL_LA48, "amd64: Limit user VA to 48bit" },
76 { "aslrstkgap", NT_FREEBSD_FCTL_ASG_DISABLE, "Disable ASLR stack gap" },
79 static struct option long_opts[] = {
80 { "help", no_argument, NULL, 'h' },
84 #if BYTE_ORDER == LITTLE_ENDIAN
85 #define SUPPORTED_ENDIAN ELFDATA2LSB
87 #define SUPPORTED_ENDIAN ELFDATA2MSB
93 main(int argc, char **argv)
100 bool editfeatures, lflag;
103 editfeatures = false;
107 if (elf_version(EV_CURRENT) == EV_NONE)
108 errx(EXIT_FAILURE, "elf_version error");
110 while ((ch = getopt_long(argc, argv, "hile:", long_opts, NULL)) != -1) {
134 warnx("no file(s) specified");
142 if ((fd = open(argv[0],
143 editfeatures ? O_RDWR : O_RDONLY, 0)) < 0) {
144 warn("error opening file %s", argv[0]);
149 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
150 warnx("elf_begin failed: %s", elf_errmsg(-1));
155 if ((kind = elf_kind(elf)) != ELF_K_ELF) {
156 if (kind == ELF_K_AR)
157 warnx("file '%s' is an archive", argv[0]);
159 warnx("file '%s' is not an ELF file", argv[0]);
164 if (gelf_getehdr(elf, &ehdr) == NULL) {
165 warnx("gelf_getehdr: %s", elf_errmsg(-1));
170 * XXX need to support cross-endian operation, but for now
171 * exit on error rather than misbehaving.
173 if (ehdr.e_ident[EI_DATA] != SUPPORTED_ENDIAN) {
174 warnx("file endianness must match host");
180 if (!print_file_features(elf, ehdr.e_phnum, fd,
185 } else if (!edit_file_features(elf, ehdr.e_phnum, fd,
204 #define USAGE_MESSAGE \
206 Usage: %s [options] file...\n\
207 Set or display the control features for an ELF object.\n\n\
208 Supported options are:\n\
209 -l List known control features.\n\
210 -i Ignore unknown features.\n\
211 -e [+-=]feature,list Edit features from a comma separated list.\n\
212 -h | --help Print a usage message and exit.\n"
218 fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
223 convert_to_feature_val(char *feature_str, uint32_t *feature_val)
231 operation = *feature_str;
233 len = nitems(featurelist);
234 while ((feature = strsep(&feature_str, ",")) != NULL) {
235 for (i = 0; i < len; ++i) {
236 if (strcmp(featurelist[i].alias, feature) == 0) {
237 input |= featurelist[i].value;
240 /* XXX Backwards compatibility for "no"-prefix flags. */
241 if (strncmp(featurelist[i].alias, "no", 2) == 0 &&
242 strcmp(featurelist[i].alias + 2, feature) == 0) {
243 input |= featurelist[i].value;
245 "interpreting %s as %s; please specify %s",
246 feature, featurelist[i].alias,
247 featurelist[i].alias);
252 if (isdigit(feature[0])) {
254 unsigned long long val;
257 val = strtoll(feature, &eptr, 0);
258 if (eptr == feature || *eptr != '\0')
260 else if (val > UINT32_MAX)
263 warn("%s invalid", feature);
268 warnx("%s is not a valid feature", feature);
275 if (operation == '+') {
276 *feature_val |= input;
277 } else if (operation == '=') {
278 *feature_val = input;
279 } else if (operation == '-') {
280 *feature_val &= ~input;
282 warnx("'%c' not an operator - use '+', '-', '='",
290 edit_file_features(Elf *elf, int phcount, int fd, char *val)
295 if (!get_file_features(elf, phcount, fd, &features, &off)) {
296 warnx("NT_FREEBSD_FEATURE_CTL note not found");
300 if (!convert_to_feature_val(val, &features))
303 if (lseek(fd, off, SEEK_SET) == -1 ||
304 write(fd, &features, sizeof(features)) <
305 (ssize_t)sizeof(features)) {
306 warnx("error writing feature value");
317 printf("Known features are:\n");
318 for (i = 0; i < nitems(featurelist); ++i)
319 printf("%-16s%s\n", featurelist[i].alias,
320 featurelist[i].desc);
324 print_file_features(Elf *elf, int phcount, int fd, char *filename)
329 if (!get_file_features(elf, phcount, fd, &features, NULL)) {
333 printf("File '%s' features:\n", filename);
334 for (i = 0; i < nitems(featurelist); ++i) {
335 printf("%-16s'%s' is ", featurelist[i].alias,
336 featurelist[i].desc);
338 if ((featurelist[i].value & features) == 0)
347 get_file_features(Elf *elf, int phcount, int fd, uint32_t *features,
352 unsigned long read_total;
353 int namesz, descsz, i;
357 * Go through each program header to find one that is of type PT_NOTE
358 * and has a note for feature control.
360 for (i = 0; i < phcount; ++i) {
361 if (gelf_getphdr(elf, i, &phdr) == NULL) {
362 warnx("gelf_getphdr failed: %s", elf_errmsg(-1));
366 if (phdr.p_type != PT_NOTE)
369 if (lseek(fd, phdr.p_offset, SEEK_SET) < 0) {
370 warn("lseek() failed:");
375 while (read_total < phdr.p_filesz) {
376 if (read(fd, ¬e, sizeof(note)) <
377 (ssize_t)sizeof(note)) {
378 warnx("elf note header too short");
381 read_total += sizeof(note);
384 * XXX: Name and descriptor are 4 byte aligned, however,
385 * the size given doesn't include the padding.
387 namesz = roundup2(note.n_namesz, 4);
388 name = malloc(namesz);
390 warn("malloc() failed.");
393 descsz = roundup2(note.n_descsz, 4);
394 if (read(fd, name, namesz) < namesz) {
395 warnx("elf note name too short");
399 read_total += namesz;
401 if (note.n_namesz != 8 ||
402 strncmp("FreeBSD", name, 7) != 0 ||
403 note.n_type != NT_FREEBSD_FEATURE_CTL) {
404 /* Not the right note. Skip the description */
405 if (lseek(fd, descsz, SEEK_CUR) < 0) {
406 warn("lseek() failed.");
410 read_total += descsz;
415 if (note.n_descsz < sizeof(uint32_t)) {
416 warnx("Feature descriptor can't "
417 "be less than 4 bytes");
423 * XXX: For now we look at only 4 bytes of the
424 * descriptor. This should respect descsz.
426 if (note.n_descsz > sizeof(uint32_t))
427 warnx("Feature note is bigger than expected");
428 if (read(fd, features, sizeof(uint32_t)) <
429 (ssize_t)sizeof(uint32_t)) {
430 warnx("feature note data too short");
435 *off = phdr.p_offset + read_total;
441 warnx("NT_FREEBSD_FEATURE_CTL note not found");