From b0d9819d1ee456736ba3d4eced0d4722bc1c255e Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 24 Oct 2017 10:48:18 +0100 Subject: [PATCH] Update plthook to latest code from original repo @ 774b270 * This includes several fixes notably arm/aarch64 support. Although it's unlikely to be used in practice it's useful to be able to compile successfully. --- renderdoc/3rdparty/plthook/plthook.h | 1 + renderdoc/3rdparty/plthook/plthook_elf.c | 725 +++++++++++++---------- 2 files changed, 409 insertions(+), 317 deletions(-) diff --git a/renderdoc/3rdparty/plthook/plthook.h b/renderdoc/3rdparty/plthook/plthook.h index 1324642c2..2ef827ed0 100644 --- a/renderdoc/3rdparty/plthook/plthook.h +++ b/renderdoc/3rdparty/plthook/plthook.h @@ -43,6 +43,7 @@ #define PLTHOOK_INVALID_ARGUMENT 4 #define PLTHOOK_OUT_OF_MEMORY 5 #define PLTHOOK_INTERNAL_ERROR 6 +#define PLTHOOK_NOT_IMPLEMENTED 7 typedef struct plthook plthook_t; diff --git a/renderdoc/3rdparty/plthook/plthook_elf.c b/renderdoc/3rdparty/plthook/plthook_elf.c index 9d6d37174..612f689d6 100644 --- a/renderdoc/3rdparty/plthook/plthook_elf.c +++ b/renderdoc/3rdparty/plthook/plthook_elf.c @@ -33,23 +33,30 @@ * or implied, of the authors. * */ +#if defined(__sun) && defined(_XOPEN_SOURCE) && !defined(__EXTENSIONS__) +#define __EXTENSIONS__ +#endif +#if defined(__linux__) && !defined(_GNU_SOURCE) #define _GNU_SOURCE +#endif #include #include #include #include #include #include -#include -#include #include -#include #include #include #ifdef __sun -#include +#include #define ELF_TARGET_ALL #endif /* __sun */ +#ifdef __FreeBSD__ +#include +#include +#include +#endif #include #include #include "plthook.h" @@ -58,40 +65,47 @@ #define __attribute__(arg) #endif -#if defined __linux__ -#define ELF_OSABI ELFOSABI_SYSV -#define ELF_OSABI_ALT ELFOSABI_LINUX -#elif defined __sun -#define ELF_OSABI ELFOSABI_SOLARIS -#elif defined __FreeBSD__ -#define ELF_OSABI ELFOSABI_FREEBSD -#if defined __i386__ && __ELF_WORD_SIZE == 64 +#if defined __FreeBSD__ && defined __i386__ && __ELF_WORD_SIZE == 64 #error 32-bit application on 64-bit OS is not supported. #endif -#else -#error unsupported OS + +#if !defined(R_X86_64_JUMP_SLOT) && defined(R_X86_64_JMP_SLOT) +#define R_X86_64_JUMP_SLOT R_X86_64_JMP_SLOT #endif #if defined __x86_64__ || defined __x86_64 -#define ELF_DATA ELFDATA2LSB -#define E_MACHINE EM_X86_64 -#ifdef R_X86_64_JUMP_SLOT #define R_JUMP_SLOT R_X86_64_JUMP_SLOT -#else -#define R_JUMP_SLOT R_X86_64_JMP_SLOT -#endif -#define SHT_PLT_REL SHT_RELA #define Elf_Plt_Rel Elf_Rela -#define PLT_SECTION_NAME ".rela.plt" +#define PLT_DT_REL DT_RELA +#define R_GLOBAL_DATA R_X86_64_GLOB_DAT #elif defined __i386__ || defined __i386 -#define ELF_DATA ELFDATA2LSB -#define E_MACHINE EM_386 #define R_JUMP_SLOT R_386_JMP_SLOT -#define SHT_PLT_REL SHT_REL #define Elf_Plt_Rel Elf_Rel -#define PLT_SECTION_NAME ".rel.plt" +#define PLT_DT_REL DT_REL +#define R_GLOBAL_DATA R_386_GLOB_DAT +#elif defined __arm__ || defined __arm +#define R_JUMP_SLOT R_ARM_JUMP_SLOT +#define Elf_Plt_Rel Elf_Rel +#elif defined __aarch64__ || defined __aarch64 /* ARM64 */ +#define R_JUMP_SLOT R_AARCH64_JUMP_SLOT +#define Elf_Plt_Rel Elf_Rela +#elif defined __powerpc64__ +#define R_JUMP_SLOT R_PPC64_JMP_SLOT +#define Elf_Plt_Rel Elf_Rela +#elif defined __powerpc__ +#define R_JUMP_SLOT R_PPC_JMP_SLOT +#define Elf_Plt_Rel Elf_Rela +#elif 0 /* disabled because not tested */ && (defined __sparcv9 || defined __sparc_v9__) +#define R_JUMP_SLOT R_SPARC_JMP_SLOT +#define Elf_Plt_Rel Elf_Rela +#elif 0 /* disabled because not tested */ && (defined __sparc || defined __sparc__) +#define R_JUMP_SLOT R_SPARC_JMP_SLOT +#define Elf_Plt_Rel Elf_Rela +#elif 0 /* disabled because not tested */ && (defined __ia64 || defined __ia64__) +#define R_JUMP_SLOT R_IA64_IPLTMSB +#define Elf_Plt_Rel Elf_Rela #else -#error E_MACHINE is not defined. +#error unsupported OS #endif #if defined __LP64__ @@ -99,12 +113,16 @@ #define ELF_CLASS ELFCLASS64 #endif #define SIZE_T_FMT "lu" +#define ELF_WORD_FMT "u" +#define ELF_XWORD_FMT "lu" +#define ELF_SXWORD_FMT "ld" #define Elf_Half Elf64_Half -#define Elf_Addr Elf64_Addr +#define Elf_Xword Elf64_Xword +#define Elf_Sxword Elf64_Sxword #define Elf_Ehdr Elf64_Ehdr #define Elf_Phdr Elf64_Phdr -#define Elf_Shdr Elf64_Shdr #define Elf_Sym Elf64_Sym +#define Elf_Dyn Elf64_Dyn #define Elf_Rel Elf64_Rel #define Elf_Rela Elf64_Rela #ifndef ELF_R_SYM @@ -118,12 +136,22 @@ #define ELF_CLASS ELFCLASS32 #endif #define SIZE_T_FMT "u" +#ifdef __sun +#define ELF_WORD_FMT "lu" +#define ELF_XWORD_FMT "lu" +#define ELF_SXWORD_FMT "ld" +#else +#define ELF_WORD_FMT "u" +#define ELF_XWORD_FMT "u" +#define ELF_SXWORD_FMT "d" +#endif #define Elf_Half Elf32_Half -#define Elf_Addr Elf32_Addr +#define Elf_Xword Elf32_Word +#define Elf_Sxword Elf32_Sword #define Elf_Ehdr Elf32_Ehdr #define Elf_Phdr Elf32_Phdr -#define Elf_Shdr Elf32_Shdr #define Elf_Sym Elf32_Sym +#define Elf_Dyn Elf32_Dyn #define Elf_Rel Elf32_Rel #define Elf_Rela Elf32_Rela #ifndef ELF_R_SYM @@ -134,21 +162,22 @@ #endif #endif /* __LP64__ */ +#if defined(PT_GNU_RELRO) && !defined(__sun) +#define SUPPORT_RELRO /* RELRO (RELocation Read-Only) */ +#if !defined(DF_1_NOW) && defined(DF_1_BIND_NOW) +#define DF_1_NOW DF_1_BIND_NOW +#endif +#endif + struct plthook { - const char *base; - const Elf_Phdr *phdr; - size_t phnum; - Elf_Shdr *shdr; - size_t shnum; - char *shstrtab; - size_t shstrtab_size; const Elf_Sym *dynsym; - size_t dynsym_cnt; const char *dynstr; size_t dynstr_size; + const char *plt_addr_base; const Elf_Plt_Rel *plt; size_t plt_cnt; -#ifdef PT_GNU_RELRO + Elf_Xword r_type; +#ifdef SUPPORT_RELRO const char *relro_start; const char *relro_end; #endif @@ -156,15 +185,18 @@ struct plthook { static char errmsg[512]; -#ifdef PT_GNU_RELRO +#ifdef SUPPORT_RELRO static size_t page_size; #endif static int plthook_open_executable(plthook_t **plthook_out); static int plthook_open_shared_library(plthook_t **plthook_out, const char *filename); -static int plthook_open_real(plthook_t **plthook_out, const char *base, const char *filename); +static const Elf_Dyn *find_dyn_by_tag(const Elf_Dyn *dyn, Elf_Sxword tag); +#ifdef SUPPORT_RELRO +static int set_relro_members(plthook_t *plthook, struct link_map *lmap); +#endif +static int plthook_open_real(plthook_t **plthook_out, struct link_map *lmap); static int check_elf_header(const Elf_Ehdr *ehdr); -static int find_section(plthook_t *image, const char *name, const Elf_Shdr **out); static void set_errmsg(const char *fmt, ...) __attribute__((__format__ (__printf__, 1, 2))); int plthook_open(plthook_t **plthook_out, const char *filename) @@ -189,72 +221,59 @@ int plthook_open_by_handle(plthook_t **plthook_out, void *hndl) set_errmsg("dlinfo error"); return PLTHOOK_FILE_NOT_FOUND; } - return plthook_open_real(plthook_out, (const char*)lmap->l_addr, lmap->l_name); + return plthook_open_real(plthook_out, lmap); } int plthook_open_by_address(plthook_t **plthook_out, void *address) { +#if defined __FreeBSD__ + return PLTHOOK_NOT_IMPLEMENTED; +#else Dl_info info; + struct link_map *lmap = NULL; *plthook_out = NULL; - if (dladdr(address, &info) == 0) { + if (dladdr1(address, &info, (void**)&lmap, RTLD_DL_LINKMAP) == 0) { set_errmsg("dladdr error"); return PLTHOOK_FILE_NOT_FOUND; } - return plthook_open_real(plthook_out, info.dli_fbase, info.dli_fname); + return plthook_open_real(plthook_out, lmap); +#endif } static int plthook_open_executable(plthook_t **plthook_out) { #if defined __linux__ - /* Open the main program. */ - char buf[128]; - FILE *fp = fopen("/proc/self/maps", "r"); - unsigned long base; + return plthook_open_real(plthook_out, _r_debug.r_map); +#elif defined __sun + const char *auxv_file = "/proc/self/auxv"; +#define NUM_AUXV_CNT 10 + FILE *fp = fopen(auxv_file, "r"); + auxv_t auxv; + struct r_debug *r_debug = NULL; if (fp == NULL) { - set_errmsg("Could not open /proc/self/maps: %s", + set_errmsg("Could not open %s: %s", auxv_file, strerror(errno)); return PLTHOOK_INTERNAL_ERROR; } - if (fgets(buf, sizeof(buf), fp) == NULL) { - set_errmsg("Could not read /proc/self/maps: %s", - strerror(errno)); - fclose(fp); - return PLTHOOK_INTERNAL_ERROR; + while (fread(&auxv, sizeof(auxv_t), 1, fp) == 1) { + if (auxv.a_type == AT_SUN_LDDATA) { + r_debug = (struct r_debug *)auxv.a_un.a_ptr; + break; + } } fclose(fp); - if (sscanf(buf, "%lx-%*x r-xp %*x %*x:%*x %*u ", &base) != 1) { - set_errmsg("invalid /proc/self/maps format: %s", buf); + if (r_debug == NULL) { + set_errmsg("Could not find r_debug"); return PLTHOOK_INTERNAL_ERROR; } - return plthook_open_real(plthook_out, (const char*)base, "/proc/self/exe"); -#elif defined __sun - prmap_t prmap; - pid_t pid = getpid(); - char fname[128]; - int fd; - - sprintf(fname, "/proc/%d/map", pid); - fd = open(fname, O_RDONLY); - if (fd == -1) { - set_errmsg("Could not open %s: %s", fname, - strerror(errno)); - return PLTHOOK_INTERNAL_ERROR; - } - if (read(fd, &prmap, sizeof(prmap)) != sizeof(prmap)) { - set_errmsg("Could not read %s: %s", fname, - strerror(errno)); - close(fd); - return PLTHOOK_INTERNAL_ERROR; - } - close(fd); - sprintf(fname, "/proc/%d/object/a.out", pid); - return plthook_open_real(plthook_out, (const char*)prmap.pr_vaddr, fname); + return plthook_open_real(plthook_out, r_debug->r_map); #elif defined __FreeBSD__ return plthook_open_shared_library(plthook_out, NULL); #else -#error unsupported OS + set_errmsg("Opening the main program is not supported on this platform."); + return PLTHOOK_NOT_IMPLEMENTED; #endif } @@ -273,187 +292,339 @@ static int plthook_open_shared_library(plthook_t **plthook_out, const char *file return PLTHOOK_FILE_NOT_FOUND; } dlclose(hndl); - return plthook_open_real(plthook_out, (const char*)lmap->l_addr, lmap->l_name); + return plthook_open_real(plthook_out, lmap); } -static int plthook_open_real(plthook_t **plthook_out, const char *base, const char *filename) +static const Elf_Dyn *find_dyn_by_tag(const Elf_Dyn *dyn, Elf_Sxword tag) { - const Elf_Ehdr *ehdr = (Elf_Ehdr *)base; - const Elf_Shdr *shdr; - size_t shdr_size; - int fd = -1; - off_t offset; - plthook_t *plthook; - int rv; -#ifdef PT_GNU_RELRO - size_t idx; + while (dyn->d_tag != DT_NULL) { + if (dyn->d_tag == tag) { + return dyn; + } + dyn++; + } + return NULL; +} + +#ifdef SUPPORT_RELRO +#if defined __linux__ +static const char *get_mapped_file(const void *address, char *buf, int *err) +{ + unsigned long addr = (unsigned long)address; + FILE *fp; + + fp = fopen("/proc/self/maps", "r"); + if (fp == NULL) { + set_errmsg("failed to open /proc/self/maps"); + *err = PLTHOOK_INTERNAL_ERROR; + return NULL; + } + while (fgets(buf, PATH_MAX, fp) != NULL) { + unsigned long start, end; + int offset = 0; + + sscanf(buf, "%lx-%lx %*s %*x %*x:%*x %*u %n", &start, &end, &offset); + if (offset == 0) { + continue; + } + if (start < addr && addr < end) { + char *p = buf + offset; + while (*p == ' ') { + p++; + } + if (*p != '/') { + continue; + } + p[strlen(p) - 1] = '\0'; /* remove '\n' */ + fclose(fp); + return p; + } + } + fclose(fp); + set_errmsg("Could not find a mapped file reagion containing %p", address); + *err = PLTHOOK_INTERNAL_ERROR; + return NULL; +} +#elif defined __FreeBSD__ +static const char *get_mapped_file(const void *address, char *buf, int *err) +{ + uint64_t addr = (uint64_t)address; + struct kinfo_vmentry *top; + int i, cnt; + + top = kinfo_getvmmap(getpid(), &cnt); + if (top == NULL) { + fprintf(stderr, "failed to call kinfo_getvmmap()\n"); + *err = PLTHOOK_INTERNAL_ERROR; + return NULL; + } + for (i = 0; i < cnt; i++) { + struct kinfo_vmentry *kve = top + i; + + if (kve->kve_start < addr && addr < kve->kve_end) { + strncpy(buf, kve->kve_path, PATH_MAX); + free(top); + return buf; + } + } + free(top); + set_errmsg("Could not find a mapped file reagion containing %p", address); + *err = PLTHOOK_INTERNAL_ERROR; + return NULL; +} +#else +static const char *get_mapped_file(const void *address, char *buf, int *err) +{ + set_errmsg("Could not find a mapped file reagion containing %p", address); + *err = PLTHOOK_INTERNAL_ERROR; + return NULL; +} #endif - if (base == NULL) { - set_errmsg("The base address is zero."); - return PLTHOOK_FILE_NOT_FOUND; +static int set_relro_members(plthook_t *plthook, struct link_map *lmap) +{ + char fnamebuf[PATH_MAX]; + const char *fname; + FILE *fp; + Elf_Ehdr ehdr; + Elf_Half idx; + int rv; + + if (lmap->l_name[0] == '/') { + fname = lmap->l_name; + } else { + int err; + + fname = get_mapped_file(plthook->dynstr, fnamebuf, &err); + if (fname == NULL) { + return err; + } + } + fp = fopen(fname, "r"); + if (fp == NULL) { + set_errmsg("failed to open %s", fname); + return PLTHOOK_INTERNAL_ERROR; + } + if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) { + set_errmsg("failed to read the ELF header."); + fclose(fp); + return PLTHOOK_INVALID_FILE_FORMAT; + } + rv = check_elf_header(&ehdr); + if (rv != 0) { + fclose(fp); + return rv; } - if (filename == NULL) { - set_errmsg("failed to get the file name on the disk."); - return PLTHOOK_FILE_NOT_FOUND; + fseek(fp, ehdr.e_phoff, SEEK_SET); + + for (idx = 0; idx < ehdr.e_phnum; idx++) { + Elf_Phdr phdr; + + if (fread(&phdr, sizeof(phdr), 1, fp) != 1) { + set_errmsg("failed to read the program header table."); + fclose(fp); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (phdr.p_type == PT_GNU_RELRO) { + plthook->relro_start = plthook->plt_addr_base + phdr.p_vaddr; + plthook->relro_end = plthook->relro_start + phdr.p_memsz; + break; + } + } + fclose(fp); + return 0; +} +#endif + +static int plthook_open_real(plthook_t **plthook_out, struct link_map *lmap) +{ + plthook_t plthook = {NULL,}; + const Elf_Dyn *dyn; + const char *dyn_addr_base = NULL; + +#if defined __linux__ + plthook.plt_addr_base = (char*)lmap->l_addr; +#elif defined __FreeBSD__ || defined __sun + const Elf_Ehdr *ehdr = (const Elf_Ehdr*)lmap->l_addr; + int rv = check_elf_header(ehdr); + if (rv != 0) { + return rv; + } + if (ehdr->e_type == ET_DYN) { + dyn_addr_base = (const char*)lmap->l_addr; + plthook.plt_addr_base = (const char*)lmap->l_addr; + } +#else +#error unsupported OS +#endif + + /* get .dynsym section */ + dyn = find_dyn_by_tag(lmap->l_ld, DT_SYMTAB); + if (dyn == NULL) { + set_errmsg("failed to find DT_SYMTAB"); + return PLTHOOK_INTERNAL_ERROR; + } + plthook.dynsym = (const Elf_Sym*)(dyn_addr_base + dyn->d_un.d_ptr); + + /* Check sizeof(Elf_Sym) */ + dyn = find_dyn_by_tag(lmap->l_ld, DT_SYMENT); + if (dyn == NULL) { + set_errmsg("failed to find DT_SYMTAB"); + return PLTHOOK_INTERNAL_ERROR; + } + if (dyn->d_un.d_val != sizeof(Elf_Sym)) { + set_errmsg("DT_SYMENT size %" ELF_XWORD_FMT " != %" SIZE_T_FMT, dyn->d_un.d_val, sizeof(Elf_Sym)); + return PLTHOOK_INTERNAL_ERROR; } - plthook = calloc(1, sizeof(plthook_t)); - if (plthook == NULL) { + /* get .dynstr section */ + dyn = find_dyn_by_tag(lmap->l_ld, DT_STRTAB); + if (dyn == NULL) { + set_errmsg("failed to find DT_STRTAB"); + return PLTHOOK_INTERNAL_ERROR; + } + plthook.dynstr = dyn_addr_base + dyn->d_un.d_ptr; + + /* get .dynstr size */ + dyn = find_dyn_by_tag(lmap->l_ld, DT_STRSZ); + if (dyn == NULL) { + set_errmsg("failed to find DT_STRSZ"); + return PLTHOOK_INTERNAL_ERROR; + } + plthook.dynstr_size = dyn->d_un.d_val; + + /* get .rela.plt or .rel.plt section */ + dyn = find_dyn_by_tag(lmap->l_ld, DT_JMPREL); + plthook.r_type = R_JUMP_SLOT; +#ifdef PLT_DT_REL + if (dyn == NULL) { + /* get .rela.dyn or .rel.dyn section */ + dyn = find_dyn_by_tag(lmap->l_ld, PLT_DT_REL); + plthook.r_type = R_GLOBAL_DATA; + } +#endif + if (dyn == NULL) { + set_errmsg("failed to find DT_JMPREL"); + return PLTHOOK_INTERNAL_ERROR; + } + plthook.plt = (const Elf_Plt_Rel *)(dyn_addr_base + dyn->d_un.d_ptr); + + if (plthook.r_type == R_JUMP_SLOT) { + /* get total size of .rela.plt or .rel.plt */ + dyn = find_dyn_by_tag(lmap->l_ld, DT_PLTRELSZ); + if (dyn == NULL) { + set_errmsg("failed to find DT_PLTRELSZ"); + return PLTHOOK_INTERNAL_ERROR; + } + + plthook.plt_cnt = dyn->d_un.d_val / sizeof(Elf_Plt_Rel); +#ifdef PLT_DT_REL + } else { + int total_size_tag = PLT_DT_REL == DT_RELA ? DT_RELASZ : DT_RELSZ; + int elem_size_tag = PLT_DT_REL == DT_RELA ? DT_RELAENT : DT_RELENT; + size_t total_size, elem_size; + + dyn = find_dyn_by_tag(lmap->l_ld, total_size_tag); + if (dyn == NULL) { + set_errmsg("failed to find 0x%x", total_size_tag); + return PLTHOOK_INTERNAL_ERROR; + } + total_size = dyn->d_un.d_ptr; + + dyn = find_dyn_by_tag(lmap->l_ld, elem_size_tag); + if (dyn == NULL) { + set_errmsg("failed to find 0x%x", elem_size_tag); + return PLTHOOK_INTERNAL_ERROR; + } + elem_size = dyn->d_un.d_ptr; + plthook.plt_cnt = total_size / elem_size; +#endif + } + +#ifdef SUPPORT_RELRO + dyn = find_dyn_by_tag(lmap->l_ld, DT_FLAGS_1); + if (dyn != NULL && (dyn->d_un.d_val & DF_1_NOW)) { + int rv = set_relro_members(&plthook, lmap); + if (rv != 0) { + return rv; + } + if (page_size == 0) { + page_size = sysconf(_SC_PAGESIZE); + } + } +#endif + + *plthook_out = malloc(sizeof(plthook_t)); + if (*plthook_out == NULL) { set_errmsg("failed to allocate memory: %" SIZE_T_FMT " bytes", sizeof(plthook_t)); return PLTHOOK_OUT_OF_MEMORY; } - - /* sanity check */ - rv = check_elf_header(ehdr); - if (rv != 0) { - goto error_exit; - } - if (ehdr->e_type == ET_DYN) { - plthook->base = base; - } - plthook->phdr = (const Elf_Phdr *)(plthook->base + ehdr->e_phoff); - plthook->phnum = ehdr->e_phnum; - fd = open(filename, O_RDONLY, 0); - if (fd == -1) { - set_errmsg("Could not open %s: %s", filename, strerror(errno)); - rv = PLTHOOK_FILE_NOT_FOUND; - goto error_exit; - } - shdr_size = ehdr->e_shnum * ehdr->e_shentsize; - plthook->shdr = calloc(1, shdr_size); - if (plthook->shdr == NULL) { - set_errmsg("failed to allocate memory: %" SIZE_T_FMT " bytes", shdr_size); - rv = PLTHOOK_OUT_OF_MEMORY; - goto error_exit; - } - offset = ehdr->e_shoff; - if ((rv = lseek(fd, offset, SEEK_SET)) != offset) { - set_errmsg("failed to seek to the section header table."); - rv = PLTHOOK_INVALID_FILE_FORMAT; - goto error_exit; - } - if (read(fd, plthook->shdr, shdr_size) != shdr_size) { - set_errmsg("failed to read the section header table."); - rv = PLTHOOK_INVALID_FILE_FORMAT; - goto error_exit; - } - plthook->shnum = ehdr->e_shnum; - plthook->shstrtab_size = plthook->shdr[ehdr->e_shstrndx].sh_size; - plthook->shstrtab = malloc(plthook->shstrtab_size); - if (plthook->shstrtab == NULL) { - set_errmsg("failed to allocate memory: %" SIZE_T_FMT " bytes", plthook->shstrtab_size); - rv = PLTHOOK_OUT_OF_MEMORY; - goto error_exit; - } - offset = plthook->shdr[ehdr->e_shstrndx].sh_offset; - if (lseek(fd, offset, SEEK_SET) != offset) { - set_errmsg("failed to seek to the section header string table."); - rv = PLTHOOK_INVALID_FILE_FORMAT; - goto error_exit; - } - if (read(fd, plthook->shstrtab, plthook->shstrtab_size) != plthook->shstrtab_size) { - set_errmsg("failed to read the section header string table."); - rv = PLTHOOK_INVALID_FILE_FORMAT; - goto error_exit; - } -#ifdef PT_GNU_RELRO - if (page_size == 0) { - page_size = sysconf(_SC_PAGESIZE); - } - offset = ehdr->e_phoff; - if ((rv = lseek(fd, offset, SEEK_SET)) != offset) { - set_errmsg("failed to seek to the program header table."); - rv = PLTHOOK_INVALID_FILE_FORMAT; - goto error_exit; - } - for (idx = 0; idx < ehdr->e_phnum; idx++) { - Elf_Phdr phdr; - if (read(fd, &phdr, sizeof(phdr)) != sizeof(phdr)) { - set_errmsg("failed to read the program header table."); - rv = PLTHOOK_INVALID_FILE_FORMAT; - goto error_exit; - } - if (phdr.p_type == PT_GNU_RELRO) { - plthook->relro_start = plthook->base + phdr.p_vaddr; - plthook->relro_end = plthook->relro_start + phdr.p_memsz; - } - } -#endif - close(fd); - fd = -1; - - rv = find_section(plthook, ".dynsym", &shdr); - if (rv != 0) { - goto error_exit; - } - if (shdr->sh_type != SHT_DYNSYM) { - set_errmsg("The type of .dynsym section should be SHT_DYNSYM but %d.", shdr->sh_type); - rv = PLTHOOK_INVALID_FILE_FORMAT; - goto error_exit; - } - if (shdr->sh_entsize != sizeof(Elf_Sym)) { - set_errmsg("The size of a section header entry should be sizeof(Elf_Sym)(%" SIZE_T_FMT ") but %" SIZE_T_FMT ".", - sizeof(Elf_Sym), shdr->sh_entsize); - rv = PLTHOOK_INVALID_FILE_FORMAT; - goto error_exit; - } - plthook->dynsym = (const Elf_Sym*)(plthook->base + shdr->sh_addr); - plthook->dynsym_cnt = shdr->sh_size / shdr->sh_entsize; - - rv = find_section(plthook, ".dynstr", &shdr); - if (rv != 0) { - goto error_exit; - } - if (shdr->sh_type != SHT_STRTAB) { - set_errmsg("The type of .dynstrx section should be SHT_STRTAB but %d.", shdr->sh_type); - rv = PLTHOOK_INVALID_FILE_FORMAT; - goto error_exit; - } - plthook->dynstr = (const char*)(plthook->base + shdr->sh_addr); - plthook->dynstr_size = shdr->sh_size; - - rv = find_section(plthook, PLT_SECTION_NAME, &shdr); - if (rv != 0) { - goto error_exit; - } - if (shdr->sh_entsize != sizeof(Elf_Plt_Rel)) { - set_errmsg("invalid " PLT_SECTION_NAME " table entry size: %" SIZE_T_FMT, shdr->sh_entsize); - rv = PLTHOOK_INVALID_FILE_FORMAT; - goto error_exit; - } - plthook->plt = (Elf_Plt_Rel *)(plthook->base + shdr->sh_addr); - plthook->plt_cnt = shdr->sh_size / sizeof(Elf_Plt_Rel); - - *plthook_out = plthook; + **plthook_out = plthook; return 0; - error_exit: - if (fd != -1) { - close(fd); +} + +static int check_elf_header(const Elf_Ehdr *ehdr) +{ + static const unsigned short s = 1; + /* Check endianness at runtime. */ + unsigned char elfdata = (*(const char*)&s) ? ELFDATA2LSB : ELFDATA2MSB; + + if (ehdr == NULL) { + set_errmsg("invalid elf header address: NULL"); + return PLTHOOK_INTERNAL_ERROR; } - plthook_close(plthook); - return rv; + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) { + set_errmsg("invalid file signature: 0x%02x,0x%02x,0x%02x,0x%02x", + ehdr->e_ident[0], ehdr->e_ident[1], ehdr->e_ident[2], ehdr->e_ident[3]); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) { + set_errmsg("invalid elf class: 0x%02x", ehdr->e_ident[EI_CLASS]); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (ehdr->e_ident[EI_DATA] != elfdata) { + set_errmsg("invalid elf data: 0x%02x", ehdr->e_ident[EI_DATA]); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) { + set_errmsg("invalid elf version: 0x%02x", ehdr->e_ident[EI_VERSION]); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) { + set_errmsg("invalid file type: 0x%04x", ehdr->e_type); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (ehdr->e_version != EV_CURRENT) { + set_errmsg("invalid object file version: %" ELF_WORD_FMT, ehdr->e_version); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (ehdr->e_ehsize != sizeof(Elf_Ehdr)) { + set_errmsg("invalid elf header size: %u", ehdr->e_ehsize); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (ehdr->e_phentsize != sizeof(Elf_Phdr)) { + set_errmsg("invalid program header table entry size: %u", ehdr->e_phentsize); + return PLTHOOK_INVALID_FILE_FORMAT; + } + return 0; } int plthook_enum(plthook_t *plthook, unsigned int *pos, const char **name_out, void ***addr_out) { while (*pos < plthook->plt_cnt) { const Elf_Plt_Rel *plt = plthook->plt + *pos; - if (ELF_R_TYPE(plt->r_info) == R_JUMP_SLOT) { + if (ELF_R_TYPE(plt->r_info) == plthook->r_type) { size_t idx = ELF_R_SYM(plt->r_info); - if (idx >= plthook->dynsym_cnt) { - set_errmsg(".dynsym index %" SIZE_T_FMT " should be less than %" SIZE_T_FMT ".", idx, plthook->dynsym_cnt); - return PLTHOOK_INVALID_FILE_FORMAT; - } idx = plthook->dynsym[idx].st_name; if (idx + 1 > plthook->dynstr_size) { set_errmsg("too big section header string table index: %" SIZE_T_FMT, idx); return PLTHOOK_INVALID_FILE_FORMAT; } *name_out = plthook->dynstr + idx; - *addr_out = (void**)(plthook->base + plt->r_offset); + *addr_out = (void**)(plthook->plt_addr_base + plt->r_offset); (*pos)++; return 0; } @@ -479,7 +650,7 @@ int plthook_replace(plthook_t *plthook, const char *funcname, void *funcaddr, vo while ((rv = plthook_enum(plthook, &pos, &name, &addr)) == 0) { if (strncmp(name, funcname, funcnamelen) == 0) { if (name[funcnamelen] == '\0' || name[funcnamelen] == '@') { -#ifdef PT_GNU_RELRO +#ifdef SUPPORT_RELRO void *maddr = NULL; if (plthook->relro_start <= (char*)addr && (char*)addr < plthook->relro_end) { maddr = (void*)((size_t)addr & ~(page_size - 1)); @@ -494,7 +665,7 @@ int plthook_replace(plthook_t *plthook, const char *funcname, void *funcaddr, vo *oldfunc = *addr; } *addr = funcaddr; -#ifdef PT_GNU_RELRO +#ifdef SUPPORT_RELRO if (maddr != NULL) { mprotect(maddr, page_size, PROT_READ); } @@ -513,8 +684,6 @@ int plthook_replace(plthook_t *plthook, const char *funcname, void *funcaddr, vo void plthook_close(plthook_t *plthook) { if (plthook != NULL) { - free(plthook->shdr); - free(plthook->shstrtab); free(plthook); } } @@ -524,84 +693,6 @@ const char *plthook_error(void) return errmsg; } -static int check_elf_header(const Elf_Ehdr *ehdr) -{ - if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) { - set_errmsg("invalid file signature: 0x%02x,0x%02x,0x%02x,0x%02x", - ehdr->e_ident[0], ehdr->e_ident[1], ehdr->e_ident[2], ehdr->e_ident[3]); - return PLTHOOK_INVALID_FILE_FORMAT; - } - if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) { - set_errmsg("invalid elf class: 0x%02x", ehdr->e_ident[EI_CLASS]); - return PLTHOOK_INVALID_FILE_FORMAT; - } - if (ehdr->e_ident[EI_DATA] != ELF_DATA) { - set_errmsg("invalid elf data: 0x%02x", ehdr->e_ident[EI_DATA]); - return PLTHOOK_INVALID_FILE_FORMAT; - } - if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) { - set_errmsg("invalid elf version: 0x%02x", ehdr->e_ident[EI_VERSION]); - return PLTHOOK_INVALID_FILE_FORMAT; - } - if (ehdr->e_ident[EI_OSABI] != ELF_OSABI) { -#ifdef ELF_OSABI_ALT - if (ehdr->e_ident[EI_OSABI] != ELF_OSABI_ALT) { - set_errmsg("invalid OS ABI: 0x%02x", ehdr->e_ident[EI_OSABI]); - return PLTHOOK_INVALID_FILE_FORMAT; - } -#else - set_errmsg("invalid OS ABI: 0x%02x", ehdr->e_ident[EI_OSABI]); - return PLTHOOK_INVALID_FILE_FORMAT; -#endif - } - if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) { - set_errmsg("invalid file type: 0x%04x", ehdr->e_type); - return PLTHOOK_INVALID_FILE_FORMAT; - } - if (ehdr->e_machine != E_MACHINE) { - set_errmsg("invalid machine type: %u", ehdr->e_machine); - return PLTHOOK_INVALID_FILE_FORMAT; - } - if (ehdr->e_version != EV_CURRENT) { - set_errmsg("invalid object file version: %u", ehdr->e_version); - return PLTHOOK_INVALID_FILE_FORMAT; - } - if (ehdr->e_ehsize != sizeof(Elf_Ehdr)) { - set_errmsg("invalid elf header size: %u", ehdr->e_ehsize); - return PLTHOOK_INVALID_FILE_FORMAT; - } - if (ehdr->e_phentsize != sizeof(Elf_Phdr)) { - set_errmsg("invalid program header table entry size: %u", ehdr->e_phentsize); - return PLTHOOK_INVALID_FILE_FORMAT; - } - if (ehdr->e_shentsize != sizeof(Elf_Shdr)) { - set_errmsg("invalid section header table entry size: %u", ehdr->e_shentsize); - return PLTHOOK_INVALID_FILE_FORMAT; - } - return 0; -} - -static int find_section(plthook_t *image, const char *name, const Elf_Shdr **out) -{ - const Elf_Shdr *shdr = image->shdr; - const Elf_Shdr *shdr_end = shdr + image->shnum; - size_t namelen = strlen(name); - - while (shdr < shdr_end) { - if (shdr->sh_name + namelen >= image->shstrtab_size) { - set_errmsg("too big section header string table index: %u", shdr->sh_name); - return PLTHOOK_INVALID_FILE_FORMAT; - } - if (strcmp(image->shstrtab + shdr->sh_name, name) == 0) { - *out = shdr; - return 0; - } - shdr++; - } - set_errmsg("failed to find the section header: %s", name); - return PLTHOOK_INVALID_FILE_FORMAT; -} - static void set_errmsg(const char *fmt, ...) { va_list ap;