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 <dgr@semihalf.com>
Reviewed by: imp
Obtained from: Semihalf
Sponsored by: Stormshield
Differential Revision: https://reviews.freebsd.org/D29550
This commit is contained in:
Marcin Wojtas 2021-05-21 11:19:31 +02:00
parent 4eac63af23
commit 4a27bf128b

View File

@ -52,10 +52,10 @@
__FBSDID("$FreeBSD$"); __FBSDID("$FreeBSD$");
static bool convert_to_feature_val(char *, uint32_t *); static bool convert_to_feature_val(char *, uint32_t *);
static bool edit_file_features(Elf *, int, int, char *); static bool edit_file_features(Elf *, int, int, char *, bool);
static bool get_file_features(Elf *, int, int, uint32_t *, uint64_t *); static bool get_file_features(Elf *, int, int, uint32_t *, uint64_t *, bool);
static void print_features(void); 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); static void usage(void);
struct ControlFeatures { struct ControlFeatures {
@ -81,9 +81,11 @@ static struct option long_opts[] = {
}; };
#if BYTE_ORDER == LITTLE_ENDIAN #if BYTE_ORDER == LITTLE_ENDIAN
#define SUPPORTED_ENDIAN ELFDATA2LSB #define HOST_ENDIAN ELFDATA2LSB
#define SWAP_ENDIAN ELFDATA2MSB
#else #else
#define SUPPORTED_ENDIAN ELFDATA2MSB #define HOST_ENDIAN ELFDATA2MSB
#define SWAP_ENDIAN ELFDATA2LSB
#endif #endif
static bool iflag; static bool iflag;
@ -96,7 +98,7 @@ main(int argc, char **argv)
Elf_Kind kind; Elf_Kind kind;
int ch, fd, retval; int ch, fd, retval;
char *features; char *features;
bool editfeatures, lflag; bool editfeatures, lflag, endian_swap;
lflag = 0; lflag = 0;
editfeatures = false; editfeatures = false;
@ -165,24 +167,25 @@ main(int argc, char **argv)
retval = 1; retval = 1;
goto fail; goto fail;
} }
/*
* XXX need to support cross-endian operation, but for now if (ehdr.e_ident[EI_DATA] == HOST_ENDIAN) {
* exit on error rather than misbehaving. endian_swap = false;
*/ } else if (ehdr.e_ident[EI_DATA] == SWAP_ENDIAN) {
if (ehdr.e_ident[EI_DATA] != SUPPORTED_ENDIAN) { endian_swap = true;
warnx("file endianness must match host"); } else {
warnx("file endianness unknown");
retval = 1; retval = 1;
goto fail; goto fail;
} }
if (!editfeatures) { if (!editfeatures) {
if (!print_file_features(elf, ehdr.e_phnum, fd, if (!print_file_features(elf, ehdr.e_phnum, fd,
argv[0])) { argv[0], endian_swap)) {
retval = 1; retval = 1;
goto fail; goto fail;
} }
} else if (!edit_file_features(elf, ehdr.e_phnum, fd, } else if (!edit_file_features(elf, ehdr.e_phnum, fd,
features)) { features, endian_swap)) {
retval = 1; retval = 1;
goto fail; goto fail;
} }
@ -286,12 +289,13 @@ convert_to_feature_val(char *feature_str, uint32_t *feature_val)
} }
static bool 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; uint32_t features;
uint64_t off; 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"); warnx("NT_FREEBSD_FEATURE_CTL note not found");
return (false); return (false);
} }
@ -299,6 +303,9 @@ edit_file_features(Elf *elf, int phcount, int fd, char *val)
if (!convert_to_feature_val(val, &features)) if (!convert_to_feature_val(val, &features))
return (false); return (false);
if (endian_swap)
features = bswap32(features);
if (lseek(fd, off, SEEK_SET) == -1 || if (lseek(fd, off, SEEK_SET) == -1 ||
write(fd, &features, sizeof(features)) < write(fd, &features, sizeof(features)) <
(ssize_t)sizeof(features)) { (ssize_t)sizeof(features)) {
@ -320,12 +327,14 @@ print_features(void)
} }
static bool 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; uint32_t features;
unsigned long i; 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); return (false);
} }
@ -344,7 +353,7 @@ print_file_features(Elf *elf, int phcount, int fd, char *filename)
static bool static bool
get_file_features(Elf *elf, int phcount, int fd, uint32_t *features, get_file_features(Elf *elf, int phcount, int fd, uint32_t *features,
uint64_t *off) uint64_t *off, bool endian_swap)
{ {
GElf_Phdr phdr; GElf_Phdr phdr;
Elf_Note note; Elf_Note note;
@ -379,6 +388,12 @@ get_file_features(Elf *elf, int phcount, int fd, uint32_t *features,
} }
read_total += sizeof(note); 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, * XXX: Name and descriptor are 4 byte aligned, however,
* the size given doesn't include the padding. * 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); free(name);
return (false); return (false);
} }
if (endian_swap)
*features = bswap32(*features);
if (off != NULL) if (off != NULL)
*off = phdr.p_offset + read_total; *off = phdr.p_offset + read_total;
free(name); free(name);