omitrbp

Find kernel functions that omit the frame pointer
git clone git://git.margiolis.net/omitrbp.git
Log | Files | Refs | README | LICENSE

commit 5c021778106dc47481a2e2c57bb6d9a15325126e
Author: Christos Margiolis <christos@margiolis.net>
Date:   Sat, 18 Mar 2023 20:19:04 +0200

initial commit

Diffstat:
ALICENSE | 20++++++++++++++++++++
AMakefile | 8++++++++
AREADME | 4++++
Aomitrbp.1 | 23+++++++++++++++++++++++
Aomitrbp.c | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 239 insertions(+), 0 deletions(-)

diff --git a/LICENSE b/LICENSE @@ -0,0 +1,20 @@ +MIT License + +(c) 2023-Present Christos Margiolis <christos@FreeBSD.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +PROG= omitrbp +SRCS= ${PROG}.c +MAN= ${PROG}.1 +LDFLAGS+= -lelf + +.include <bsd.prog.mk> diff --git a/README b/README @@ -0,0 +1,4 @@ +omitrbp +======= + +omitrbp prints all kernel functions that omit their frame pointer (rbp). diff --git a/omitrbp.1 b/omitrbp.1 @@ -0,0 +1,23 @@ +.Dd March 18, 2023 +.Dt OMITRBP 1 +.Os +.Sh NAME +.Nm omitrbp +.Nd find kernel functions that omit the frame pointer +.Sh SYNOPSIS +.Nm +.Op Fl f +.Sh DESCRIPTION +.Nm +uses the kernel bootfile's ELF info to find all functions that omit the frame +pointer (rbp). If the +.Fl f +option is specified, +.Nm +will print only the functions that do not push the frame pointer in the the +first instruction. +.Sh SEE ALSO +.Xr elf 3 , +.Xr gelf 3 +.Sh AUTHORS +.An Christos Margiolis Aq Mt christos@FreeBSD.org diff --git a/omitrbp.c b/omitrbp.c @@ -0,0 +1,184 @@ +#include <sys/param.h> +#include <sys/sysctl.h> + +#include <err.h> +#include <fcntl.h> +#include <gelf.h> +#include <libelf.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +struct section { + GElf_Shdr sh; + Elf_Scn *scn; + const char *name; +}; + +static struct section *sl; +static size_t shnum; +static int first_instr = 0; + +static void +print_rbp_ommited(const char *func, uint64_t lo, uint64_t hi) +{ + Elf_Data *d; + struct section *s; + uint8_t *buf; + uint64_t addr; + int i; + + for (i = 1; i < shnum; i++) { + s = &sl[i]; + if (strcmp(s->name, ".text") != 0 || + s->sh.sh_type != SHT_PROGBITS) + continue; + (void)elf_errno(); + if ((d = elf_getdata(s->scn, NULL)) == NULL) { + if (elf_errno() != 0) + warnx("elf_getdata(): %s", elf_errmsg(-1)); + continue; + } + if (d->d_size <= 0 || d->d_buf == NULL) + continue; + buf = d->d_buf; + addr = s->sh.sh_addr + d->d_off; + while (addr != lo) { + addr++; + buf++; + } + if (first_instr) { + if (*buf != 0x55) + puts(func); + } else { + int found = 0; + + while (addr != hi) { + if (*buf == 0x55) + found = 1; + addr++; + buf++; + } + if (!found) + puts(func); + } + return; + } +} + +int +main(int argc, char *argv[]) +{ + Elf *elf; + Elf_Scn *scn; + Elf_Data *d; + GElf_Shdr sh; + GElf_Sym sym; + struct section *s; + uint64_t lo, hi; + uint32_t stab; + const char *name, *func; + char bootfile[BUFSIZ]; + size_t shstrndx, ndx, slen; + int fd, len, i, j, ch; + + while ((ch = getopt(argc, argv, "f")) != -1) { + switch (ch) { + case 'f': + first_instr = 1; + break; + case '?': + default: + fprintf(stderr, "usage: %s [-f]\n", argv[0]); + exit(1); + } + } + argc -= optind; + argv += optind; + + slen = sizeof(bootfile); + if (sysctlbyname("kern.bootfile", bootfile, &slen, NULL, 0) != 0) + strlcpy(bootfile, "/boot/kernel/kernel", sizeof(bootfile)); + + if (elf_version(EV_CURRENT) == EV_NONE) + errx(1, "elf_version(): %s", elf_errmsg(-1)); + if ((fd = open(bootfile, O_RDONLY)) < 0) + err(1, "open(%s)", bootfile); + if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) + errx(1, "elf_begin(): %s", elf_errmsg(-1)); + if (elf_kind(elf) == ELF_K_NONE) + errx(1, "not an ELF file: %s", bootfile); + + if (!elf_getshnum(elf, &shnum)) + errx(1, "elf_getshnum(): %s", elf_errmsg(-1)); + if ((sl = malloc(shnum * sizeof(struct section))) == NULL) + err(1, "malloc"); + if (!elf_getshstrndx(elf, &shstrndx)) + errx(1, "elf_getshstrndx(): %s", elf_errmsg(-1)); + if ((scn = elf_getscn(elf, 0)) == NULL) + errx(1, "elf_getscn(): %s", elf_errmsg(-1)); + (void)elf_errno(); + + do { + if (gelf_getshdr(scn, &sh) == NULL) { + warnx("gelf_getshdr(): %s", elf_errmsg(-1)); + (void)elf_errno(); + continue; + } + if ((name = elf_strptr(elf, shstrndx, sh.sh_name)) == NULL) + (void)elf_errno(); + if ((ndx = elf_ndxscn(scn)) == SHN_UNDEF && elf_errno() != 0) { + warnx("elf_ndxscn(): %s", elf_errmsg(-1)); + continue; + } + if (ndx >= shnum) + continue; + s = &sl[ndx]; + s->scn = scn; + s->sh = sh; + s->name = name; + } while ((scn = elf_nextscn(elf, scn)) != NULL); + if (elf_errno() != 0) + warnx("elf_nextscn(): %s", elf_errmsg(-1)); + + for (i = 1; i < shnum; i++) { + s = &sl[i]; + if (s->sh.sh_type != SHT_SYMTAB && s->sh.sh_type != SHT_DYNSYM) + continue; + if (s->sh.sh_link >= shnum) + continue; + stab = s->sh.sh_link; + (void)elf_errno(); + if ((d = elf_getdata(s->scn, NULL)) == NULL) { + if (elf_errno() != 0) + warnx("elf_getdata(): %s", elf_errmsg(-1)); + continue; + } + if (d->d_size <= 0) + continue; + if (s->sh.sh_entsize == 0) + continue; + len = (int)(s->sh.sh_size / s->sh.sh_entsize); + if (len > INT_MAX) + continue; + for (j = 0; j < len; j++) { + if (gelf_getsym(d, j, &sym) != &sym) { + warnx("gelf_getsym(): %s", elf_errmsg(-1)); + continue; + } + if (GELF_ST_TYPE(sym.st_info) != STT_FUNC) + continue; + lo = sym.st_value; + hi = sym.st_value + sym.st_size; + if ((func = elf_strptr(elf, stab, sym.st_name)) != NULL) + print_rbp_ommited(func, lo, hi); + } + } + + free(sl); + close(fd); + elf_end(elf); + + return (0); +}