From 4a27bf128b108d90412190c06a54ebac36a8ca2e Mon Sep 17 00:00:00 2001 From: Marcin Wojtas Date: Fri, 21 May 2021 11:19:31 +0200 Subject: [PATCH] usr.bin/elfctl: Allow for cross-endian operations. Detect if host endian is different than target endian and swap byte order of ELF note fields instead of failing. Submitted by: Dawid Gorecki Reviewed by: imp Obtained from: Semihalf Sponsored by: Stormshield Differential Revision: https://reviews.freebsd.org/D29550 --- usr.bin/elfctl/elfctl.c | 55 +++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/usr.bin/elfctl/elfctl.c b/usr.bin/elfctl/elfctl.c index bcdd1be394a9..41660fc4acd5 100644 --- a/usr.bin/elfctl/elfctl.c +++ b/usr.bin/elfctl/elfctl.c @@ -52,10 +52,10 @@ __FBSDID("$FreeBSD$"); static bool convert_to_feature_val(char *, uint32_t *); -static bool edit_file_features(Elf *, int, int, char *); -static bool get_file_features(Elf *, int, int, uint32_t *, uint64_t *); +static bool edit_file_features(Elf *, int, int, char *, bool); +static bool get_file_features(Elf *, int, int, uint32_t *, uint64_t *, bool); static void print_features(void); -static bool print_file_features(Elf *, int, int, char *); +static bool print_file_features(Elf *, int, int, char *, bool); static void usage(void); struct ControlFeatures { @@ -81,9 +81,11 @@ static struct option long_opts[] = { }; #if BYTE_ORDER == LITTLE_ENDIAN -#define SUPPORTED_ENDIAN ELFDATA2LSB +#define HOST_ENDIAN ELFDATA2LSB +#define SWAP_ENDIAN ELFDATA2MSB #else -#define SUPPORTED_ENDIAN ELFDATA2MSB +#define HOST_ENDIAN ELFDATA2MSB +#define SWAP_ENDIAN ELFDATA2LSB #endif static bool iflag; @@ -96,7 +98,7 @@ main(int argc, char **argv) Elf_Kind kind; int ch, fd, retval; char *features; - bool editfeatures, lflag; + bool editfeatures, lflag, endian_swap; lflag = 0; editfeatures = false; @@ -165,24 +167,25 @@ main(int argc, char **argv) retval = 1; goto fail; } - /* - * XXX need to support cross-endian operation, but for now - * exit on error rather than misbehaving. - */ - if (ehdr.e_ident[EI_DATA] != SUPPORTED_ENDIAN) { - warnx("file endianness must match host"); + + if (ehdr.e_ident[EI_DATA] == HOST_ENDIAN) { + endian_swap = false; + } else if (ehdr.e_ident[EI_DATA] == SWAP_ENDIAN) { + endian_swap = true; + } else { + warnx("file endianness unknown"); retval = 1; goto fail; } if (!editfeatures) { if (!print_file_features(elf, ehdr.e_phnum, fd, - argv[0])) { + argv[0], endian_swap)) { retval = 1; goto fail; } } else if (!edit_file_features(elf, ehdr.e_phnum, fd, - features)) { + features, endian_swap)) { retval = 1; goto fail; } @@ -286,12 +289,13 @@ convert_to_feature_val(char *feature_str, uint32_t *feature_val) } static bool -edit_file_features(Elf *elf, int phcount, int fd, char *val) +edit_file_features(Elf *elf, int phcount, int fd, char *val, bool endian_swap) { uint32_t features; uint64_t off; - if (!get_file_features(elf, phcount, fd, &features, &off)) { + if (!get_file_features(elf, phcount, fd, &features, &off, + endian_swap)) { warnx("NT_FREEBSD_FEATURE_CTL note not found"); return (false); } @@ -299,6 +303,9 @@ edit_file_features(Elf *elf, int phcount, int fd, char *val) if (!convert_to_feature_val(val, &features)) return (false); + if (endian_swap) + features = bswap32(features); + if (lseek(fd, off, SEEK_SET) == -1 || write(fd, &features, sizeof(features)) < (ssize_t)sizeof(features)) { @@ -320,12 +327,14 @@ print_features(void) } static bool -print_file_features(Elf *elf, int phcount, int fd, char *filename) +print_file_features(Elf *elf, int phcount, int fd, char *filename, + bool endian_swap) { uint32_t features; unsigned long i; - if (!get_file_features(elf, phcount, fd, &features, NULL)) { + if (!get_file_features(elf, phcount, fd, &features, NULL, + endian_swap)) { return (false); } @@ -344,7 +353,7 @@ print_file_features(Elf *elf, int phcount, int fd, char *filename) static bool get_file_features(Elf *elf, int phcount, int fd, uint32_t *features, - uint64_t *off) + uint64_t *off, bool endian_swap) { GElf_Phdr phdr; Elf_Note note; @@ -379,6 +388,12 @@ get_file_features(Elf *elf, int phcount, int fd, uint32_t *features, } read_total += sizeof(note); + if (endian_swap) { + note.n_namesz = bswap32(note.n_namesz); + note.n_descsz = bswap32(note.n_descsz); + note.n_type = bswap32(note.n_type); + } + /* * XXX: Name and descriptor are 4 byte aligned, however, * the size given doesn't include the padding. @@ -430,6 +445,8 @@ get_file_features(Elf *elf, int phcount, int fd, uint32_t *features, free(name); return (false); } + if (endian_swap) + *features = bswap32(*features); if (off != NULL) *off = phdr.p_offset + read_total; free(name);