From f5e3189dbf2e522f6d5d0644294d73a366ed2167 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Fri, 17 Jul 2020 11:28:03 +0200 Subject: [PATCH] Add an ELF file parser Change-Id: Ib71d0de73a6dde20f5ccfca1a8b8f0d4e283a19a --- src/shared/telf.c | 463 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/shared/telf.h | 88 +++++++++++ 2 files changed, 551 insertions(+) create mode 100644 src/shared/telf.c create mode 100644 src/shared/telf.h diff --git a/src/shared/telf.c b/src/shared/telf.c new file mode 100644 index 0000000..09508c1 --- /dev/null +++ b/src/shared/telf.c @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "telf.h" + +static void set_error(Elf *elf, const char *fmt, ...) +{ + assert(elf); + + va_list args; + va_start(args, fmt); + vsnprintf(elf->last_error, sizeof(elf->last_error), fmt, args); + va_end(args); +} + +static bool set_offset(Elf *elf, off64_t offset) +{ + assert(elf); + + if (lseek64(elf->fd, offset, SEEK_SET) == (off64_t)-1) { + set_error(elf, "set file offset error: %m"); + return false; + } + return true; +} + +static bool readehdr(Elf *elf, Elf64_Ehdr *ehdr) +{ + assert(elf); + assert(ehdr); + + if (teu_class(elf) == ELFCLASS32) { + Elf32_Ehdr *ehdr32 = elf->data; + + memcpy(ehdr->e_ident, ehdr32->e_ident, sizeof(ehdr->e_ident)); + ehdr->e_type = ehdr32->e_type; + ehdr->e_machine = ehdr32->e_machine; + ehdr->e_version = ehdr32->e_version; + ehdr->e_entry = (Elf64_Addr)ehdr32->e_entry; + ehdr->e_phoff = (Elf64_Off)ehdr32->e_phoff; + ehdr->e_shoff = (Elf64_Off)ehdr32->e_shoff; + ehdr->e_flags = ehdr32->e_flags; + ehdr->e_ehsize = ehdr32->e_ehsize; + ehdr->e_phentsize = ehdr32->e_phentsize; + ehdr->e_phnum = ehdr32->e_phnum; + ehdr->e_shentsize = ehdr32->e_shentsize; + ehdr->e_shnum = ehdr32->e_shnum; + ehdr->e_shstrndx = ehdr32->e_shstrndx; + + } else if (teu_class(elf) == ELFCLASS64) { + memcpy(ehdr, elf->data, sizeof(*ehdr)); + } else { + set_error(elf, "Unknown ELF class"); + return false; + } + + return true; +} + +char *teu_errmsg(Elf *elf) +{ + assert(elf); + return elf->last_error; +} + +bool teu_iself(Elf *elf) +{ + assert(elf); + + return elf->ident[EI_MAG0] == ELFMAG0 && + elf->ident[EI_MAG1] == ELFMAG1 && + elf->ident[EI_MAG2] == ELFMAG2 && + elf->ident[EI_MAG3] == ELFMAG3; +} + +uint8_t teu_class(Elf *elf) +{ + assert(elf); + + return (uint8_t)elf->ident[EI_CLASS]; +} + +off64_t get_file_size(Elf *elf, int fd) +{ + off64_t end = lseek64(fd, 0, SEEK_END); + + if (end == (off64_t)-1) { + set_error(elf, "set file offset error: %m"); + return (off64_t)-1; + } + + if (lseek64(fd, 0, SEEK_SET) == (off64_t)-1) { + set_error(elf, "set file offset error: %m"); + return (off64_t)-1; + } + + return end; +} + +static bool teu_init(Elf *elf) +{ + elf->ident = elf->data; + + if (!readehdr(elf, &elf->ehdr)) + return false; + + return true; +} + +bool teu_begin(int fd, Elf *elf) +{ + assert(elf); + assert(fd >= 0); + + elf->fd = fd; + + if (!set_offset(elf, 0)) + return false; + + elf->data_size = get_file_size(elf, fd); + if (elf->data_size == (off64_t)-1) + return false; + + elf->data = mmap(NULL, (elf->data_size + (1024-1)) & ~(1024-1), + PROT_READ, MAP_PRIVATE | MAP_NORESERVE, fd, 0); + if (elf->data == NULL) { + set_error(elf, "Can not allocate memory for file: %m"); + return false; + } + + elf->status = TEU_FILE; + + if (!teu_init(elf)) + return false; + + return true; +} + +bool teu_begin_memory(void *memory, Elf *elf) +{ + assert(memory); + assert(elf); + + elf->status = TEU_MEMORY; + + if (!teu_init(elf)) + return false; + + return true; +} + +bool teu_getehdr(Elf *elf, Elf64_Ehdr *ehdr) +{ + assert(elf); + assert(ehdr); + + memcpy(ehdr, &elf->ehdr, sizeof(*ehdr)); + return true; +} + +bool teu_getphdr(Elf *elf, uint64_t index, Elf64_Phdr *phdr) +{ + assert(elf); + assert(phdr); + + if (index >= elf->ehdr.e_phnum) { + set_error(elf, "index is out of range of program headers"); + return false; + } + + void *phdr_data = elf->data + elf->ehdr.e_phoff + index*elf->ehdr.e_phentsize; + + if (teu_class(elf) == ELFCLASS32) { + Elf32_Phdr *phdr32 = phdr_data; + + phdr->p_type = phdr32->p_type; + phdr->p_flags = phdr32->p_flags; + phdr->p_offset = (Elf64_Off)phdr32->p_offset; + phdr->p_vaddr = (Elf64_Addr)phdr32->p_vaddr; + phdr->p_paddr = (Elf64_Addr)phdr32->p_paddr; + phdr->p_filesz = (uint64_t)phdr32->p_filesz; + phdr->p_memsz = (uint64_t)phdr32->p_memsz; + phdr->p_align = (uint64_t)phdr32->p_align; + } else if (teu_class(elf) == ELFCLASS64) { + memcpy(phdr, phdr_data, elf->ehdr.e_phentsize); + } else { + set_error(elf, "Unknown ELF class"); + return false; + } + + return true; +} + +bool teu_getshdr(Elf *elf, uint64_t index, Elf64_Shdr *shdr) +{ + assert(elf); + assert(shdr); + + if (index >= elf->ehdr.e_shnum) { + set_error(elf, "index is out of range of section headers"); + return false; + } + + void *shdr_data = elf->data + elf->ehdr.e_shoff + index*elf->ehdr.e_shentsize; + + if (teu_class(elf) == ELFCLASS32) { + Elf32_Shdr *shdr32 = shdr_data; + + shdr->sh_name = shdr32->sh_name; + shdr->sh_type = shdr32->sh_type; + shdr->sh_flags = (uint64_t)shdr32->sh_flags; + shdr->sh_addr = (Elf64_Addr)shdr32->sh_addr; + shdr->sh_offset = (Elf64_Off)shdr32->sh_offset; + shdr->sh_size = (uint64_t)shdr32->sh_size; + shdr->sh_link = shdr32->sh_link; + shdr->sh_info = shdr32->sh_info; + shdr->sh_addralign = (uint64_t)shdr32->sh_addralign; + shdr->sh_entsize = (uint64_t)shdr32->sh_entsize; + } else if (teu_class(elf) == ELFCLASS64) { + memcpy(shdr, shdr_data, elf->ehdr.e_shentsize); + } else { + set_error(elf, "Unknown ELF class"); + return false; + } + + return true; +} + +char *teu_secname(Elf *elf, Elf64_Shdr *shdr) +{ + assert(elf); + assert(shdr); + + Elf64_Shdr strsection; + if (!teu_getshdr(elf, elf->ehdr.e_shstrndx, &strsection)) { + set_error(elf, "getshdr error"); + return false; + } + + return (char*)(elf->data + strsection.sh_offset + shdr->sh_name); +} + +void *teu_getsdata(Elf *elf, Elf64_Shdr *shdr) +{ + assert(elf); + assert(shdr); + + return elf->data + shdr->sh_offset; +} + +bool teu_getsym(Elf *elf, Elf64_Shdr *shdr, int index, void *data, Elf64_Sym *sym) +{ + assert(elf); + assert(shdr); + assert(data); + assert(sym); + + if (index*shdr->sh_entsize >= shdr->sh_size) { + set_error(elf, "index is out of range"); + return false; + } + + if (teu_class(elf) == ELFCLASS32) { + Elf32_Sym sym32; + memcpy(&sym32, data + index*(shdr->sh_entsize), shdr->sh_entsize); + sym->st_name = sym32.st_name; + sym->st_info = sym32.st_info; + sym->st_other = sym32.st_other; + sym->st_shndx = sym32.st_shndx; + sym->st_value = (Elf64_Addr)sym32.st_value; + sym->st_size = (uint64_t)sym32.st_size; + } else if (teu_class(elf) == ELFCLASS64) { + memcpy(sym, data + index*(shdr->sh_entsize), shdr->sh_entsize); + } else { + set_error(elf, "Unknown ELF class"); + return false; + } + + return true; +} + +char *teu_strptr(Elf *elf, int sindex, off64_t offset) +{ + assert(elf); + + Elf64_Shdr strsection; + if (!teu_getshdr(elf, sindex, &strsection)) { + set_error(elf, "getshdr error"); + return false; + } + + return (char*)(elf->data + strsection.sh_offset + offset); +} + +bool teu_close(Elf *elf) +{ + assert(elf); + + if (elf->status == TEU_MEMORY) + munmap(elf->data, elf->data_size); + + return true; +} + +bool teu_getdata(Elf *elf, off64_t offset, uint64_t size, void *data) +{ + assert(elf); + assert(data); + + memcpy(data, elf->data + offset, size); + + return true; +} + +off64_t teu_getnote(Elf *elf, void *data, off64_t offset, + Elf64_Nhdr *nhdr, off64_t *name_offset, off64_t *desc_offset) +{ + assert(elf); + assert(data); + assert(nhdr); + assert(name_offset); + assert(desc_offset); + + if (teu_class(elf) == ELFCLASS32) { + Elf32_Nhdr *nhdr32 = data + offset; + nhdr->n_descsz = (Elf64_Word)nhdr32->n_descsz; + nhdr->n_namesz = (Elf64_Word)nhdr32->n_namesz; + nhdr->n_type = (Elf64_Word)nhdr32->n_type; + *name_offset = offset + sizeof(nhdr32); + } else if (teu_class(elf) == ELFCLASS64) { + memcpy(nhdr, data + offset, sizeof(*nhdr)); + *name_offset = offset + sizeof(*nhdr); + } else { + set_error(elf, "Uknown ELF class"); + return (off64_t)-1; + } + + *desc_offset = *name_offset + ((nhdr->n_namesz + 3) & ~3); + return *desc_offset + ((nhdr->n_descsz + 3) & ~3); +} + +// int main(int argc, char *argv[]) +// { +// struct teu_elf elf; +// int fd = open(argv[1], O_LARGEFILE); +// teu_begin(fd, &elf); +// Elf64_Ehdr ehdr; +// if (teu_class(&elf) == ELFCLASS32) +// printf("ELFCLASS32\n"); +// +// if (teu_class(&elf) == ELFCLASS64) +// printf("ELFCLASS64\n"); +// +// if (!teu_getehdr(&elf, &ehdr)) { +// printf("err"); +// return 1; +// } +// printf("PHnum: %d SHnum: %d\n", ehdr.e_phnum, ehdr.e_shnum); +// +// printf("PHDRS:\n"); +// for (int i = 0; i < ehdr.e_phnum; i++) { +// Elf64_Phdr phdr; +// if (!teu_getphdr(&elf, i, &phdr)) { +// return 1; +// } +// printf("Type: %u Filesz: %lu Memsz: %lu\n", phdr.p_type, phdr.p_filesz, phdr.p_memsz); +// if (phdr.p_type == PT_NOTE) { +// printf("Note!!!\n"); +// char data[phdr.p_filesz]; +// if (!teu_getdata(&elf, phdr.p_offset, phdr.p_filesz, data)) +// return 1; +// +// off64_t offset = 0; +// while (offset < phdr.p_filesz) { +// Elf64_Nhdr nhdr; +// off64_t noff, doff; +// +// offset = teu_getnote(&elf, data, offset, &nhdr, &noff, &doff); +// printf("name: %s\n", (char*)&data[noff]); +// for (int i = 0; i < nhdr.n_descsz; i++) { +// char c = data[doff+i]; +// printf("%x", (uint8_t)c); +// } +// printf("\n"); +// } +// +// } +// } +// +// printf("SHDRS:\n"); +// for (int i = 0; i < ehdr.e_shnum; i++) { +// Elf64_Shdr shdr; +// if (!teu_getshdr(&elf, i, &shdr)) { +// return 1; +// } +// printf("Name: %u Type: %u Size: %lu Entsize: %lu\n", shdr.sh_name, shdr.sh_type, shdr.sh_size, shdr.sh_entsize); +// if (shdr.sh_type == SHT_SYMTAB) +// printf("SHT_SYMTAB!!!\n"); +// if (shdr.sh_type == SHT_DYNSYM) +// printf("SHT_DYNSYM!!!\n"); +// teu_secname(&elf, &shdr); +// +// char data[shdr.sh_size]; +// if (!teu_getsdata(&elf, &shdr, data)) { +// printf("getsdata error\n"); +// return 1; +// } +// if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) { +// printf("ENTSIZE! count: %lu\n", shdr.sh_size/shdr.sh_entsize); +// for (int i = 0; i < shdr.sh_size/shdr.sh_entsize; i++) { +// Elf64_Sym sym; +// if (!teu_getsym(&elf, &shdr, i, data, &sym)) { +// printf("get sym error\n"); +// return 1; +// } +// // printf("sym info: %d name: %d shndx: %d size: %lu \n", +// // sym.st_info, +// // sym.st_name, +// // sym.st_shndx, +// // sym.st_size); +// char *n = teu_strptr(&elf, shdr.sh_link, sym.st_name); +// if (n == NULL) { +// printf("strptr err\n"); +// return 1; +// } +// printf("n: stname: %d %s\n", sym.st_name, n); +// } +// } +// } +// return 0; +// } diff --git a/src/shared/telf.h b/src/shared/telf.h new file mode 100644 index 0000000..8007a94 --- /dev/null +++ b/src/shared/telf.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#ifndef __TELF_H +#define __TELF_H +#include +#include +#include + +#define ERR_MSG_LEN 1024 + +enum TEU_STATUS {TEU_NOINIT = 0, + TEU_FILE, + TEU_MEMORY}; + +struct teu_elf { + enum TEU_STATUS status; + int fd; + char *ident; + off64_t saved_offset; + Elf64_Ehdr ehdr; + char last_error[ERR_MSG_LEN]; + void *data; + off64_t data_size; +}; + +typedef struct teu_elf Elf; + +#ifdef __cplusplus +extern "C" { +#endif + +char *teu_errmsg(Elf *elf); + +bool teu_iself(Elf *elf); + +uint8_t teu_class(Elf *elf); + +bool teu_internal_getehdr(Elf *elf, Elf64_Ehdr *ehdr); + +bool teu_begin(int fd, Elf *elf); + +bool teu_begin_memory(void *memory, Elf *elf); + +bool teu_getehdr(Elf *elf, Elf64_Ehdr *ehdr); + +bool teu_getphdr(Elf *elf, uint64_t index, Elf64_Phdr *phdr); + +bool teu_getshdr(Elf *elf, uint64_t index, Elf64_Shdr *shdr); + +char *teu_secname(Elf *elf, Elf64_Shdr *shdr); + +void *teu_getsdata(Elf *elf, Elf64_Shdr *shdr); + +bool teu_getsym(Elf *elf, Elf64_Shdr *shdr, int index, void *data, Elf64_Sym *sym); + +char *teu_strptr(Elf *elf, int sindex, off64_t offset); + +bool teu_close(Elf *elf); + +bool teu_getdata(Elf *elf, off64_t offset, uint64_t size, void *data); + +off64_t teu_getnote(Elf *elf, void *data, off64_t offset, Elf64_Nhdr *nhdr, off64_t *name_offset, off64_t *desc_offset); + +#ifdef __cplusplus +} +#endif + +#endif // __TELF_H -- 2.7.4