Add an ELF file parser 98/238798/2
authorMateusz Moscicki <m.moscicki2@partner.samsung.com>
Fri, 17 Jul 2020 09:28:03 +0000 (11:28 +0200)
committerMateusz Moscicki <m.moscicki2@partner.samsung.com>
Fri, 17 Jul 2020 10:25:52 +0000 (12:25 +0200)
Change-Id: Ib71d0de73a6dde20f5ccfca1a8b8f0d4e283a19a

src/shared/telf.c [new file with mode: 0644]
src/shared/telf.h [new file with mode: 0644]

diff --git a/src/shared/telf.c b/src/shared/telf.c
new file mode 100644 (file)
index 0000000..09508c1
--- /dev/null
@@ -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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <elf.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+
+#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 (file)
index 0000000..8007a94
--- /dev/null
@@ -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 <elf.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#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