void get_syms(char *filename, mod_info_t *mi) { int fd; Elf *elf; if ((fd = open(filename, O_RDONLY)) == -1) { perror(filename); exit(ERR_SYSCALL); } if (elf_version(EV_CURRENT) == EV_NONE) { (void) fprintf(stderr, "%s: libelf out of date\n", cmdname); exit(ERR_ELF); } if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { (void) fprintf(stderr, "%s: elf_begin failed\n", cmdname); exit(ERR_ELF); } if (gelf_getclass(elf) != ELFCLASS64) { (void) fprintf(stderr, "%s: unsupported mon.out format for " "this class of object\n", cmdname); exit(ERR_ELF); } mi->txt_origin = get_txtorigin(elf, filename); fetch_symtab(elf, filename, mi); }
ElfCreator *elfcreator_begin(char *path, Elf *elf) { ElfCreator *ctor = NULL; GElf_Ehdr ehdr_mem, *ehdr; GElf_Half machine; if (!(ctor = calloc(1, sizeof(*ctor)))) return NULL; clear(ctor, 0); ctor->path = path; ctor->oldelf = elf; ehdr = gelf_getehdr(elf, &ehdr_mem); machine = ehdr->e_machine; if ((ctor->fd = open(path, O_RDWR|O_CREAT|O_TRUNC, 0755)) < 0) { err: clear(ctor, 1); free(ctor); return NULL; } if (!(ctor->elf = elf_begin(ctor->fd, ELF_C_WRITE_MMAP, elf))) goto err; gelf_newehdr(ctor->elf, gelf_getclass(elf)); gelf_update_ehdr(ctor->elf, ehdr); if (!(ctor->ehdr = gelf_getehdr(ctor->elf, &ctor->ehdr_mem))) goto err; return ctor; }
/** * perform checks on the initial ELF binary and create an ELF descriptor * to perform direct manipulations within the file. */ int ElfInject::prepareElfBin() { info("prepare ELF binary: %s", bin.name.c_str()); /* open binary file */ bin.fd = open(bin.name.c_str(), O_RDWR, S_IRWXU); if (bin.fd < 0) { error("cannot open %s", bin.name.c_str()); return -1; } /* fstat binary */ if (fstat(bin.fd, &bin.stats)) { error("cannot fstat %s", bin.name.c_str()); return -1; } /* initialize ELF library */ if(elf_version(EV_CURRENT) == EV_NONE ) { error("ELF library initialization failed"); return -1; } /* create elf descriptor */ if ((bin.elf = elf_begin(bin.fd, ELF_C_RDWR_MMAP, NULL)) == NULL) { error("cannot initialize elf"); return -1; } /* check, whether the file is an ELF-file */ if (elf_kind(bin.elf) != ELF_K_ELF) { error("%s is not an ELF file", bin.name.c_str()); return -1; } debug("° correct ELF type"); if (gelf_getclass(bin.elf) != ELFCLASS32) { error("%s is not a 32-bit binary", bin.name.c_str()); return -1; } debug("° compiled for 32-bit systems"); /* allocate space for binary */ // if ((bin.basePtr = (char*)malloc(bin.stats.st_size)) == NULL) { // error("cannot allocate enough memory" << lend ; // } /* create elf descriptor for mem region */ // if ((bin.elfMem = elf_memory(bin.basePtr, bin.stats.st_size)) == NULL) {; // error("cannot initialize elfMem"); // } return 0; }
static int decide(const char *file, int fd, Elf *elf, uint_t flags, const char *wname, int wfd, uchar_t osabi) { int r; if (gelf_getclass(elf) == ELFCLASS64) r = regular64(file, fd, elf, flags, wname, wfd, osabi); else r = regular32(file, fd, elf, flags, wname, wfd, osabi); return (r); }
Dwarf_Small _dwarf_elf_get_pointer_size(void *obj) { Dwarf_Elf_Object *e; e = obj; assert(e != NULL); if (gelf_getclass(e->eo_elf) == ELFCLASS32) return (4); else return (8); }
unsigned long long get_elf_entry(const char *path) { Elf *e; Elf_Kind ek; GElf_Ehdr ehdr; unsigned long long entrypoint; if (elf_version(EV_CURRENT) == EV_NONE) { fprintf(stderr, "Elf library initialization failed.\n"); exit(1); } int fd = open(path, O_RDONLY, 0); if (fd < 0) { fprintf(stderr, "Couldn't open the ELF at %s.\n", path); exit(1); } e = elf_begin(fd, ELF_C_READ, NULL); if (e == NULL) { fprintf(stderr, "elf_begin() failed.\n"); exit(1); } ek = elf_kind(e); if (ek != ELF_K_ELF) { fprintf(stderr, "Bad ELF file.\n"); exit(1); } if (gelf_getehdr(e, &ehdr) == NULL) { fprintf(stderr, "Couldn't get the ELF header.\n"); exit(1); } if (gelf_getclass(e) != ELFCLASS32) { fprintf(stderr, "Not a 32-bit ELF.\n"); exit(1); } entrypoint = ehdr.e_entry; elf_end(e); close(fd); return entrypoint; }
Dwarf_Small _dwarf_elf_get_length_size(void *obj) { Dwarf_Elf_Object *e; e = obj; assert(e != NULL); if (gelf_getclass(e->eo_elf) == ELFCLASS32) return (4); else if (e->eo_ehdr.e_machine == EM_MIPS) return (8); else return (4); }
int elf_utils_copy(Elf *dest, Elf *source) { GElf_Ehdr ehdr; Elf_Scn *dst_scn, *src_scn; GElf_Shdr shdr; Elf_Data *dst_data, *src_data; size_t segment_count, segndx; GElf_Phdr phdr; ELF_ASSERT(elf_flagelf(dest, ELF_C_SET, ELF_F_LAYOUT)); ELF_ASSERT(gelf_getehdr(source, &ehdr)); ELF_ASSERT(gelf_newehdr(dest, gelf_getclass(source))); ELF_ASSERT(gelf_update_ehdr(dest, &ehdr)); src_scn = NULL; while ((src_scn = elf_nextscn(source, src_scn)) != NULL) { ELF_ASSERT(gelf_getshdr(src_scn, &shdr)); ELF_ASSERT(dst_scn = elf_newscn(dest)); ELF_ASSERT(gelf_update_shdr(dst_scn, &shdr)); src_data = NULL; while ((src_data = elf_getdata(src_scn, src_data)) != NULL) { ELF_ASSERT(dst_data = elf_newdata(dst_scn)); memcpy(dst_data, src_data, sizeof(Elf_Data)); } } ELF_ASSERT(elf_getphdrnum(source, &segment_count) == 0); ELF_ASSERT(gelf_newphdr(dest, segment_count)); for (segndx = 0; segndx < segment_count; segndx++) { ELF_ASSERT(gelf_getphdr(source, segndx, &phdr)); ELF_ASSERT(gelf_update_phdr(dest, segndx, &phdr)); } return 1; failure: return 0; }
int elfu_eCheck(Elf *e) { int elfclass; GElf_Ehdr ehdr; GElf_Phdr *phdrs = NULL; GElf_Shdr *shdrs = NULL; size_t i, j, numPhdr, numShdr; int retval = 0; assert(e); elfclass = gelf_getclass(e); if (elfclass == ELFCLASSNONE) { ELFU_WARNELF("getclass"); goto ERROR; } if (!gelf_getehdr(e, &ehdr)) { ELFU_WARNELF("gelf_getehdr"); goto ERROR; } if (ehdr.e_machine != EM_386 && ehdr.e_machine != EM_X86_64) { ELFU_WARN("Sorry, only x86-32 and x86-64 ELF files are supported at the moment.\n"); goto ERROR; } if (elf_getphdrnum(e, &numPhdr)) { ELFU_WARNELF("elf_getphdrnum"); goto ERROR; } if (elf_getshdrnum(e, &numShdr)) { ELFU_WARNELF("elf_getshdrnum"); goto ERROR; } if (numPhdr > 0) { phdrs = malloc(numPhdr * sizeof(GElf_Phdr)); if (!phdrs) { ELFU_WARN("elfu_eCheck: malloc() failed for phdrs.\n"); goto ERROR; } /* Attempt to load all PHDRs at once to catch any errors early */ for (i = 0; i < numPhdr; i++) { GElf_Phdr phdr; if (gelf_getphdr(e, i, &phdr) != &phdr) { ELFU_WARN("gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1)); goto ERROR; } phdrs[i] = phdr; } /* Check that LOAD PHDR memory ranges do not overlap, and that others * are either fully contained in a LOAD range, or not at all. */ for (i = 0; i < numPhdr; i++) { if (phdrs[i].p_type != PT_LOAD) { continue; } for (j = 0; j < numPhdr; j++) { if (j == i || phdrs[j].p_type != PT_LOAD) { continue; } if (OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz, phdrs[j].p_vaddr, phdrs[j].p_memsz)) { if (phdrs[j].p_type == PT_LOAD) { ELFU_WARN("elfu_eCheck: Found LOAD PHDRs that overlap in memory.\n"); goto ERROR; } else if (!FULLY_OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz, phdrs[j].p_vaddr, phdrs[j].p_memsz)) { ELFU_WARN("elfu_eCheck: PHDRs %d and %d partially overlap in memory.\n", i, j); goto ERROR; } } } } } if (numShdr > 1) { /* SHDRs should not overlap with PHDRs. */ if (OVERLAPPING(ehdr.e_shoff, numShdr * ehdr.e_shentsize, ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) { ELFU_WARN("elfu_eCheck: SHDRs overlap with PHDRs.\n"); goto ERROR; } shdrs = malloc(numShdr * sizeof(GElf_Shdr)); if (!shdrs) { ELFU_WARN("elfu_eCheck: malloc() failed for shdrs.\n"); goto ERROR; } /* Attempt to load all SHDRs at once to catch any errors early */ for (i = 1; i < numShdr; i++) { Elf_Scn *scn; GElf_Shdr shdr; scn = elf_getscn(e, i); if (!scn) { ELFU_WARN("elf_getscn() failed for #%d: %s\n", i, elf_errmsg(-1)); } if (gelf_getshdr(scn, &shdr) != &shdr) { ELFU_WARNELF("gelf_getshdr"); goto ERROR; } shdrs[i] = shdr; } /* Check that Section memory ranges do not overlap. * NB: Section 0 is reserved and thus ignored. */ for (i = 1; i < numShdr; i++) { /* Section should not overlap with EHDR. */ if (shdrs[i].sh_offset == 0) { ELFU_WARN("elfu_eCheck: Section %d overlaps with EHDR.\n", i); goto ERROR; } /* Section should not overlap with PHDRs. */ if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]), ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) { ELFU_WARN("elfu_eCheck: Section %d overlaps with PHDR.\n", i); goto ERROR; } /* Section should not overlap with SHDRs. */ if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]), ehdr.e_shoff, numShdr * ehdr.e_shentsize)) { ELFU_WARN("elfu_eCheck: Section %d overlaps with SHDRs.\n", i); goto ERROR; } for (j = 1; j < numShdr; j++) { if (j == i) { continue; } /* Sections must not overlap in memory. */ if (shdrs[i].sh_addr != 0 && shdrs[j].sh_addr != 0 && OVERLAPPING(shdrs[i].sh_addr, shdrs[i].sh_size, shdrs[j].sh_addr, shdrs[j].sh_size)) { ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in memory.\n", i, j); goto ERROR; } /* Sections must not overlap in file. */ if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]), shdrs[j].sh_offset, SCNFILESIZE(&shdrs[j]))) { ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in file.\n", i, j); goto ERROR; } /* We may not have more than one symbol table */ if (shdrs[i].sh_type == SHT_SYMTAB && shdrs[j].sh_type == SHT_SYMTAB) { ELFU_WARN("elfu_eCheck: Found more than one SYMTAB section.\n"); goto ERROR; } /* We may not have more than one dynamic symbol table */ if (shdrs[i].sh_type == SHT_DYNSYM && shdrs[j].sh_type == SHT_DYNSYM) { ELFU_WARN("elfu_eCheck: Found more than one DYNSYM section.\n"); goto ERROR; } } /* Section addr/offset should match parent PHDR. * Find parent PHDR: */ for (j = 0; j < numPhdr; j++) { if (PHDR_CONTAINS_SCN_IN_MEMORY(&phdrs[j], &shdrs[i])) { GElf_Off shoff = phdrs[j].p_offset + (shdrs[i].sh_addr - phdrs[j].p_vaddr); if ((shdrs[i].sh_offset != shoff && shdrs[i].sh_type != SHT_NOBITS) || !PHDR_CONTAINS_SCN_IN_FILE(&phdrs[j], &shdrs[i])) { ELFU_WARN("elfu_eCheck: SHDR %d and PHDR %d report conflicting file/memory regions.\n", i, j); goto ERROR; } } } /* sh_link members should not point to sections out of range. */ if (shdrs[i].sh_link >= numShdr) { ELFU_WARN("elfu_eCheck: Bogus sh_link in SHDR %d.\n", i); } } } DONE: if (phdrs) { free(phdrs); } if (shdrs) { free(shdrs); } return retval; ERROR: ELFU_WARN("elfu_eCheck: Errors found.\n"); retval = -1; goto DONE; }
void c_mem_load_elf (char *elf_filename, const char *start_symbol, const char *exit_symbol) { int fd; // int n_initialized = 0; Elf *e; // Default start and exit symbols if (start_symbol == NULL) start_symbol = "_start"; if (exit_symbol == NULL) exit_symbol = "exit"; // Verify the elf library version if (elf_version (EV_CURRENT) == EV_NONE) { fprintf (stderr, "ERROR: c_mem_load_elf: Failed to initialize the libelfg library!\n"); exit (1); } // Open the file for reading fd = open (elf_filename, O_RDONLY, 0); if (fd < 0) { fprintf (stderr, "ERROR: c_mem_load_elf: could not open elf input file: %s\n", elf_filename); exit (1); } // Initialize the Elf pointer with the open file e = elf_begin (fd, ELF_C_READ, NULL); if (e == NULL) { fprintf (stderr, "ERROR: c_mem_load_elf: elf_begin() initialization failed!\n"); exit (1); } // Verify that the file is an ELF file if (elf_kind (e) != ELF_K_ELF) { elf_end (e); fprintf (stderr, "ERROR: c_mem_load_elf: specified file '%s' is not an ELF file!\n", elf_filename); exit (1); } // Get the ELF header GElf_Ehdr ehdr; if (gelf_getehdr (e, & ehdr) == NULL) { elf_end (e); fprintf (stderr, "ERROR: c_mem_load_elf: get_getehdr() failed: %s\n", elf_errmsg(-1)); exit (1); } // Is this a 32b or 64 ELF? if (gelf_getclass (e) == ELFCLASS32) { fprintf (stdout, "c_mem_load_elf: %s is a 32-bit ELF file\n", elf_filename); bitwidth = 32; } else if (gelf_getclass (e) == ELFCLASS64) { fprintf (stdout, "c_mem_load_elf: %s is a 64-bit ELF file\n", elf_filename); bitwidth = 64; } else { fprintf (stderr, "ERROR: c_mem_load_elf: ELF file '%s' is not 32b or 64b\n", elf_filename); elf_end (e); exit (1); } // Verify we are dealing with a RISC-V ELF if (ehdr.e_machine != 243) { // EM_RISCV is not defined, but this returns 243 when used with a valid elf file. elf_end (e); fprintf (stderr, "ERROR: c_mem_load_elf: %s is not a RISC-V ELF file\n", elf_filename); exit (1); } // Verify we are dealing with a little endian ELF if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) { elf_end (e); fprintf (stderr, "ERROR: c_mem_load_elf: %s is a big-endian 64-bit RISC-V executable which is not supported\n", elf_filename); exit (1); } // Grab the string section index size_t shstrndx; shstrndx = ehdr.e_shstrndx; // Iterate through each of the sections looking for code that should be loaded Elf_Scn *scn = 0; GElf_Shdr shdr; min_addr = 0xFFFFFFFFFFFFFFFFllu; max_addr = 0x0000000000000000llu; pc_start = 0xFFFFFFFFFFFFFFFFllu; pc_exit = 0xFFFFFFFFFFFFFFFFllu; while ((scn = elf_nextscn (e,scn)) != NULL) { // get the header information for this section gelf_getshdr (scn, & shdr); char *sec_name = elf_strptr (e, shstrndx, shdr.sh_name); fprintf (stdout, "Section %-16s: ", sec_name); Elf_Data *data = 0; // If we find a code/data section, load it into the model if ( ((shdr.sh_type == SHT_PROGBITS) || (shdr.sh_type == SHT_INIT_ARRAY) || (shdr.sh_type == SHT_FINI_ARRAY)) && ((shdr.sh_flags & SHF_WRITE) || (shdr.sh_flags & SHF_ALLOC) || (shdr.sh_flags & SHF_EXECINSTR))) { data = elf_getdata (scn, data); // n_initialized += data->d_size; if (shdr.sh_addr < min_addr) min_addr = shdr.sh_addr; if (max_addr < (shdr.sh_addr + data->d_size - 1)) // shdr.sh_size + 4)) max_addr = shdr.sh_addr + data->d_size - 1; // shdr.sh_size + 4; if (max_addr >= MAX_MEM_SIZE) { fprintf (stdout, "INTERNAL ERROR: max_addr (0x%0" PRIx64 ") > buffer size (0x%0" PRIx64 ")\n", max_addr, MAX_MEM_SIZE); fprintf (stdout, " Please increase the #define in this program, recompile, and run again\n"); fprintf (stdout, " Abandoning this run\n"); exit (1); } memcpy (& (mem_buf [shdr.sh_addr]), data->d_buf, data->d_size); fprintf (stdout, "addr %16" PRIx64 " to addr %16" PRIx64 "; size 0x%8lx (= %0ld) bytes\n", shdr.sh_addr, shdr.sh_addr + data->d_size, data->d_size, data->d_size); } // If we find the symbol table, search for the start address location else if (shdr.sh_type == SHT_SYMTAB) { fprintf (stdout, "Searching for addresses of '%s' and '%s' symbols\n", start_symbol, exit_symbol); // Get the section data data = elf_getdata (scn, data); // Get the number of symbols in this section int symbols = shdr.sh_size / shdr.sh_entsize; // search for the uart_default symbols we need to potentially modify. GElf_Sym sym; int i; for (i = 0; i < symbols; ++i) { // get the symbol data gelf_getsym (data, i, &sym); // get the name of the symbol char *name = elf_strptr (e, shdr.sh_link, sym.st_name); // Look for, and remember PC of the start symbol if (strcmp (name, start_symbol) == 0) { pc_start = sym.st_value; } // Look for, and remember PC of the exit symbol else if (strcmp (name, exit_symbol) == 0) { pc_exit = sym.st_value; } } if (pc_start == -1) fprintf (stdout, " No '_start' label found\n"); else fprintf (stdout, " '_start' label addr: %16" PRIx64 " (hex)\n", pc_start); if (pc_exit == -1) fprintf (stdout, " No 'exit' label found\n"); else fprintf (stdout, " 'exit' label addr: %16" PRIx64 " (hex)\n", pc_exit); } else { fprintf (stdout, "Ignored\n"); } } elf_end (e); fprintf (stdout, "Min addr: %16" PRIx64 " (hex)\n", min_addr); fprintf (stdout, "Max addr: %16" PRIx64 " (hex)\n", max_addr); }
int init(char* fname) { char *id, bytes[5]; if (elf_version (EV_CURRENT) == EV_NONE) { errx (EXIT_FAILURE, " ELF library initialization " " failed : %s", elf_errmsg (-1)); return -1; } int fd; if ((fd = open (fname, O_RDONLY, 0)) < 0) { err (EXIT_FAILURE, " open \"%s\" failed ", fname); return -1; } g.fd = fd; Elf *e; if ((e = elf_begin (fd, ELF_C_READ, NULL)) == NULL) { errx (EXIT_FAILURE, " elf_begin () failed : %s.", elf_errmsg (-1)); return -1; } if (elf_kind (e) != ELF_K_ELF) { errx (EXIT_FAILURE, " \"%s\" is not an ELF object .", fname); return -1; } g.e = e; GElf_Ehdr ehdr; if (gelf_getehdr (e, &ehdr) == NULL) { errx (EXIT_FAILURE, " getehdr () failed : %s.", elf_errmsg (-1)); return -1; } g.ehdr = ehdr; int i; if ((i = gelf_getclass (e)) == ELFCLASSNONE) { errx (EXIT_FAILURE, " getclass () failed : %s.", elf_errmsg (-1)); return -1; } if ((id = elf_getident (e, NULL)) == NULL) { errx (EXIT_FAILURE, " getident () failed : %s.", elf_errmsg (-1)); return -1; } size_t n; if (elf_getshdrnum (e, &n) != 0) { errx (EXIT_FAILURE, " getshdrnum () failed : %s.", elf_errmsg (-1)); return -1; } g.shdrnum = n; if (elf_getshdrstrndx (e, &n) != 0) { errx (EXIT_FAILURE, " getshdrstrndx () failed : %s.", elf_errmsg (-1)); return -1; } g.shstrndx = n; if (elf_getphdrnum (e, &n) != 0) { errx (EXIT_FAILURE, " getphdrnum () failed : %s.", elf_errmsg (-1)); return -1; } g.phdrnum = n; init_scns(); init_phdrs(); return 0; }
static int build_file(Elf *src_elf, GElf_Ehdr *src_ehdr, Cmd_Info *cmd_info) { Elf_Scn *src_scn; Elf_Scn *dst_scn; int new_sh_name = 0; /* to hold the offset for the new */ /* section's name */ Elf *dst_elf = 0; Elf_Data *elf_data; Elf_Data *data; int64_t scn_no, x; size_t no_of_symbols = 0; section_info_table *info; unsigned int c = 0; int fdtmp; GElf_Shdr src_shdr; GElf_Shdr dst_shdr; GElf_Ehdr dst_ehdr; GElf_Off new_offset = 0, r; size_t shnum, shstrndx; if (elf_getshnum(src_elf, &shnum) == NULL) { error_message(LIBELF_ERROR, LIBelf_ERROR, elf_errmsg(-1), prog); return (FAILURE); } if (elf_getshstrndx(src_elf, &shstrndx) == NULL) { error_message(LIBELF_ERROR, LIBelf_ERROR, elf_errmsg(-1), prog); return (FAILURE); } if ((fdtmp = open(elftmpfile, O_RDWR | O_TRUNC | O_CREAT, (mode_t)0666)) == -1) { error_message(OPEN_TEMP_ERROR, SYSTEM_ERROR, strerror(errno), prog, elftmpfile); return (FAILURE); } if ((dst_elf = elf_begin(fdtmp, ELF_C_WRITE, (Elf *) 0)) == NULL) { error_message(READ_ERROR, LIBelf_ERROR, elf_errmsg(-1), prog, elftmpfile); (void) close(fdtmp); return (FAILURE); } if (gelf_newehdr(dst_elf, gelf_getclass(src_elf)) == NULL) { error_message(LIBELF_ERROR, LIBelf_ERROR, elf_errmsg(-1), prog); return (FAILURE); } /* initialize dst_ehdr */ (void) gelf_getehdr(dst_elf, &dst_ehdr); dst_ehdr = *src_ehdr; /* * flush the changes to the ehdr so the * ident array is filled in. */ (void) gelf_update_ehdr(dst_elf, &dst_ehdr); if (src_ehdr->e_phnum != 0) { (void) elf_flagelf(dst_elf, ELF_C_SET, ELF_F_LAYOUT); if (gelf_newphdr(dst_elf, src_ehdr->e_phnum) == NULL) { error_message(LIBELF_ERROR, LIBelf_ERROR, elf_errmsg(-1), prog); return (FAILURE); } for (x = 0; x < src_ehdr->e_phnum; ++x) { GElf_Phdr dst; GElf_Phdr src; /* LINTED */ (void) gelf_getphdr(src_elf, (int)x, &src); /* LINTED */ (void) gelf_getphdr(dst_elf, (int)x, &dst); (void) memcpy(&dst, &src, sizeof (GElf_Phdr)); /* LINTED */ (void) gelf_update_phdr(dst_elf, (int)x, &dst); } x = location(dst_ehdr.e_phoff, 0, src_elf); if (x == AFTER) new_offset = (GElf_Off)src_ehdr->e_ehsize; } scn_no = 1; while ((src_scn = sec_table[scn_no].scn) != (Elf_Scn *) -1) { info = &sec_table[scn_no]; /* If section should be copied to new file NOW */ if ((info->secno != (GElf_Word)DELETED) && info->secno <= scn_no) { if ((dst_scn = elf_newscn(dst_elf)) == NULL) { error_message(LIBELF_ERROR, LIBelf_ERROR, elf_errmsg(-1), prog); return (FAILURE); } (void) gelf_getshdr(dst_scn, &dst_shdr); (void) gelf_getshdr(info->scn, &src_shdr); (void) memcpy(&dst_shdr, &src_shdr, sizeof (GElf_Shdr)); /* * Update link and info fields * The sh_link field may have special values so * check them first. */ if ((src_shdr.sh_link >= shnum) || (src_shdr.sh_link == 0)) dst_shdr.sh_link = src_shdr.sh_link; else if ((int)sec_table[src_shdr.sh_link].secno < 0) dst_shdr.sh_link = 0; else dst_shdr.sh_link = sec_table[src_shdr.sh_link].secno; if ((src_shdr.sh_type == SHT_REL) || (src_shdr.sh_type == SHT_RELA)) { if ((src_shdr.sh_info >= shnum) || ((int)sec_table[src_shdr. sh_info].secno < 0)) dst_shdr.sh_info = 0; else dst_shdr.sh_info = sec_table[src_shdr.sh_info].secno; } data = sec_table[scn_no].data; if ((elf_data = elf_newdata(dst_scn)) == NULL) { error_message(LIBELF_ERROR, LIBelf_ERROR, elf_errmsg(-1), prog); return (FAILURE); } *elf_data = *data; /* SHT_{DYNSYM, SYMTAB} might need some change */ if (((src_shdr.sh_type == SHT_SYMTAB) || (src_shdr.sh_type == SHT_DYNSYM)) && src_shdr.sh_entsize != 0 && (cmd_info->no_of_delete != 0 || cmd_info->no_of_nulled != 0)) { char *new_sym; no_of_symbols = src_shdr.sh_size / src_shdr.sh_entsize; new_sym = malloc(no_of_symbols * src_shdr.sh_entsize); if (new_sym == NULL) { error_message(MALLOC_ERROR, PLAIN_ERROR, (char *)0, prog); mcs_exit(FAILURE); } /* CSTYLED */ elf_data->d_buf = (void *) new_sym; for (c = 0; c < no_of_symbols; c++) { GElf_Sym csym; (void) gelf_getsym(data, c, &csym); if ((csym.st_shndx < SHN_LORESERVE) && (csym.st_shndx != SHN_UNDEF)) { section_info_table *i; i = &sec_table[csym.st_shndx]; if (((int)i->secno != DELETED) && ((int)i->secno != NULLED)) csym.st_shndx = i->secno; else { if (src_shdr.sh_type == SHT_SYMTAB) /* * The section which * this * symbol relates * to is removed. * There is no way to * specify this fact, * just change the shndx * to 1. */ csym.st_shndx = 1; else { /* * If this is in a * .dynsym, NULL it out. */ csym.st_shndx = 0; csym.st_name = 0; csym.st_value = 0; csym.st_size = 0; csym.st_info = 0; csym.st_other = 0; csym.st_shndx = 0; } } } (void) gelf_update_sym(elf_data, c, &csym); } } /* update SHT_SYMTAB_SHNDX */ if ((src_shdr.sh_type == SHT_SYMTAB_SHNDX) && (src_shdr.sh_entsize != 0) && ((cmd_info->no_of_delete != 0) || (cmd_info->no_of_nulled != 0))) { GElf_Word *oldshndx; GElf_Word *newshndx; uint_t entcnt; entcnt = src_shdr.sh_size / src_shdr.sh_entsize; oldshndx = data->d_buf; newshndx = malloc(entcnt * src_shdr.sh_entsize); if (newshndx == NULL) { error_message(MALLOC_ERROR, PLAIN_ERROR, (char *)0, prog); mcs_exit(FAILURE); } elf_data->d_buf = (void *)newshndx; for (c = 0; c < entcnt; c++) { if (oldshndx[c] != SHN_UNDEF) { section_info_table *i; i = &sec_table[oldshndx[c]]; if (((int)i->secno != DELETED) && ((int)i->secno != NULLED)) newshndx[c] = i->secno; else newshndx[c] = oldshndx[c]; } else newshndx[c] = oldshndx[c]; } } /* * If the section is to be updated, * do so. */ if (ISCANDIDATE(info->flags)) { if ((GET_LOC(info->flags) == PRIOR) && (((int)info->secno == NULLED) || ((int)info->secno == EXPANDED) || ((int)info->secno == SHRUNK))) { /* * The section is updated, * but the position is not too * good. Need to NULL this out. */ dst_shdr.sh_name = 0; dst_shdr.sh_type = SHT_PROGBITS; if ((int)info->secno != NULLED) { (cmd_info->no_of_moved)++; SET_MOVING(info->flags); } } else { /* * The section is positioned AFTER, * or there are no segments. * It is safe to update this section. */ data = sec_table[scn_no].mdata; *elf_data = *data; dst_shdr.sh_size = elf_data->d_size; } } /* add new section name to shstrtab? */ else if (!Sect_exists && (new_sec_string != NULL) && (scn_no == shstrndx) && (dst_shdr.sh_type == SHT_STRTAB) && ((src_ehdr->e_phnum == 0) || ((x = scn_location(dst_scn, dst_elf)) != IN) || (x != PRIOR))) { size_t sect_len; sect_len = strlen(SECT_NAME); if ((elf_data->d_buf = malloc((dst_shdr.sh_size + sect_len + 1))) == NULL) { error_message(MALLOC_ERROR, PLAIN_ERROR, (char *)0, prog); mcs_exit(FAILURE); } /* put original data plus new data in section */ (void) memcpy(elf_data->d_buf, data->d_buf, data->d_size); (void) memcpy(&((char *)elf_data->d_buf) [data->d_size], SECT_NAME, sect_len + 1); /* LINTED */ new_sh_name = (int)dst_shdr.sh_size; dst_shdr.sh_size += sect_len + 1; elf_data->d_size += sect_len + 1; } /* * Compute offsets. */ if (src_ehdr->e_phnum != 0) { /* * Compute section offset. */ if (off_table[scn_no] == 0) { if (dst_shdr.sh_addralign != 0) { r = new_offset % dst_shdr.sh_addralign; if (r) new_offset += dst_shdr.sh_addralign - r; } dst_shdr.sh_offset = new_offset; elf_data->d_off = 0; } else { if (nobits_table[scn_no] == 0) new_offset = off_table[scn_no]; } if (nobits_table[scn_no] == 0) new_offset += dst_shdr.sh_size; } } (void) gelf_update_shdr(dst_scn, &dst_shdr); /* flush changes */ scn_no++; } /* * This is the real new section. */ if (!Sect_exists && new_sec_string != NULL) { size_t string_size; string_size = strlen(new_sec_string) + 1; if ((dst_scn = elf_newscn(dst_elf)) == NULL) { error_message(LIBELF_ERROR, LIBelf_ERROR, elf_errmsg(-1), prog); return (FAILURE); } (void) gelf_getshdr(dst_scn, &dst_shdr); dst_shdr.sh_name = new_sh_name; dst_shdr.sh_type = SHT_PROGBITS; dst_shdr.sh_flags = 0; dst_shdr.sh_addr = 0; if (src_ehdr->e_phnum != NULL) dst_shdr.sh_offset = new_offset; else dst_shdr.sh_offset = 0; dst_shdr.sh_size = string_size + 1; dst_shdr.sh_link = 0; dst_shdr.sh_info = 0; dst_shdr.sh_addralign = 1; dst_shdr.sh_entsize = 0; (void) gelf_update_shdr(dst_scn, &dst_shdr); /* flush changes */ if ((elf_data = elf_newdata(dst_scn)) == NULL) { error_message(LIBELF_ERROR, LIBelf_ERROR, elf_errmsg(-1), prog); return (FAILURE); } elf_data->d_size = string_size + 1; if ((elf_data->d_buf = (char *) calloc(1, string_size + 1)) == NULL) { error_message(MALLOC_ERROR, PLAIN_ERROR, (char *)0, prog); mcs_exit(FAILURE); } (void) memcpy(&((char *)elf_data->d_buf)[1], new_sec_string, string_size); elf_data->d_align = 1; new_offset += string_size + 1; } /* * If there are sections which needed to be moved, * then do it here. */ if (cmd_info->no_of_moved != 0) { int cnt; info = &sec_table[0]; for (cnt = 0; cnt < shnum; cnt++, info++) { if ((GET_MOVING(info->flags)) == 0) continue; if ((src_scn = elf_getscn(src_elf, info->osecno)) == NULL) { error_message(LIBELF_ERROR, LIBelf_ERROR, elf_errmsg(-1), prog); return (FAILURE); } if (gelf_getshdr(src_scn, &src_shdr) == NULL) { error_message(LIBELF_ERROR, LIBelf_ERROR, elf_errmsg(-1), prog); return (FAILURE); } if ((dst_scn = elf_newscn(dst_elf)) == NULL) { error_message(LIBELF_ERROR, LIBelf_ERROR, elf_errmsg(-1), prog); return (FAILURE); } if (gelf_getshdr(dst_scn, &dst_shdr) == NULL) { error_message(LIBELF_ERROR, LIBelf_ERROR, elf_errmsg(-1), prog); return (FAILURE); } dst_shdr = src_shdr; data = info->mdata; dst_shdr.sh_offset = new_offset; /* UPDATE fields */ dst_shdr.sh_size = data->d_size; if ((shnum >= src_shdr.sh_link) || (src_shdr.sh_link == 0)) dst_shdr.sh_link = src_shdr.sh_link; else dst_shdr.sh_link = sec_table[src_shdr.sh_link].osecno; if ((shnum >= src_shdr.sh_info) || (src_shdr.sh_info == 0)) dst_shdr.sh_info = src_shdr.sh_info; else dst_shdr.sh_info = sec_table[src_shdr.sh_info].osecno; (void) gelf_update_shdr(dst_scn, &dst_shdr); if ((elf_data = elf_newdata(dst_scn)) == NULL) { error_message(LIBELF_ERROR, LIBelf_ERROR, elf_errmsg(-1), prog); return (FAILURE); } (void) memcpy(elf_data, data, sizeof (Elf_Data)); new_offset += data->d_size; } } /* * In the event that the position of the sting table has changed, * as a result of deleted sections, update the ehdr->e_shstrndx. */ if ((shstrndx > 0) && (shnum > 0) && (sec_table[shstrndx].secno < shnum)) { if (sec_table[shstrndx].secno < SHN_LORESERVE) { dst_ehdr.e_shstrndx = sec_table[dst_ehdr.e_shstrndx].secno; } else { Elf_Scn *_scn; GElf_Shdr shdr0; /* * If shstrndx requires 'Extended ELF Sections' * then it is stored in shdr[0].sh_link */ dst_ehdr.e_shstrndx = SHN_XINDEX; if ((_scn = elf_getscn(dst_elf, 0)) == NULL) { error_message(LIBELF_ERROR, LIBelf_ERROR, elf_errmsg(-1), prog); return (FAILURE); } (void) gelf_getshdr(_scn, &shdr0); shdr0.sh_link = sec_table[shstrndx].secno; (void) gelf_update_shdr(_scn, &shdr0); } } if (src_ehdr->e_phnum != 0) { size_t align = gelf_fsize(dst_elf, ELF_T_ADDR, 1, EV_CURRENT); /* UPDATE location of program header table */ if (location(dst_ehdr.e_phoff, 0, dst_elf) == AFTER) { r = new_offset % align; if (r) new_offset += align - r; dst_ehdr.e_phoff = new_offset; new_offset += dst_ehdr.e_phnum * dst_ehdr.e_phentsize; } /* UPDATE location of section header table */ if ((location(dst_ehdr.e_shoff, 0, src_elf) == AFTER) || ((location(dst_ehdr.e_shoff, 0, src_elf) == PRIOR) && (!Sect_exists && new_sec_string != NULL))) { r = new_offset % align; if (r) new_offset += align - r; dst_ehdr.e_shoff = new_offset; } free(b_e_seg_table); /* * The NOTE segment is the one segment whos * sections might get moved by mcs processing. * Make sure that the NOTE segments offset points * to the .note section. */ if ((notesegndx != -1) && (notesctndx != -1) && (sec_table[notesctndx].secno)) { Elf_Scn * notescn; GElf_Shdr nshdr; notescn = elf_getscn(dst_elf, sec_table[notesctndx].secno); (void) gelf_getshdr(notescn, &nshdr); if (gelf_getclass(dst_elf) == ELFCLASS32) { Elf32_Phdr * ph = elf32_getphdr(dst_elf) + notesegndx; /* LINTED */ ph->p_offset = (Elf32_Off)nshdr.sh_offset; } else { Elf64_Phdr * ph = elf64_getphdr(dst_elf) + notesegndx; ph->p_offset = (Elf64_Off)nshdr.sh_offset; } } } /* copy ehdr changes back into real ehdr */ (void) gelf_update_ehdr(dst_elf, &dst_ehdr); if (elf_update(dst_elf, ELF_C_WRITE) < 0) { error_message(LIBELF_ERROR, LIBelf_ERROR, elf_errmsg(-1), prog); return (FAILURE); } (void) elf_end(dst_elf); (void) close(fdtmp); return (SUCCESS); }
static int ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags) { GElf_Ehdr sehdr, dehdr; Elf_Scn *sscn, *dscn; Elf_Data *sdata, *ddata; GElf_Shdr shdr; int symtab_idx = -1; off_t new_offset = 0; off_t ctfnameoff = 0; int compress = (flags & CTF_ELFWRITE_F_COMPRESS); int *secxlate = NULL; int srcidx, dstidx, pad, i; int curnmoff = 0; int changing = 0; int ret; size_t nshdr, nphdr, strndx; void *strdatabuf = NULL, *symdatabuf = NULL; size_t strdatasz = 0, symdatasz = 0; void *cdata = NULL; size_t elfsize, asize; if ((flags & ~(CTF_ELFWRITE_F_COMPRESS)) != 0) { ret = ctf_set_errno(fp, EINVAL); goto out; } if (gelf_newehdr(dst, gelf_getclass(src)) == 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (gelf_getehdr(src, &sehdr) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } (void) memcpy(&dehdr, &sehdr, sizeof (GElf_Ehdr)); if (gelf_update_ehdr(dst, &dehdr) == 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } /* * Use libelf to get the number of sections and the string section to * deal with ELF files that may have a large number of sections. We just * always use this to make our live easier. */ if (elf_getphdrnum(src, &nphdr) != 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (elf_getshdrnum(src, &nshdr) != 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (elf_getshdrstrndx(src, &strndx) != 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } /* * Neither the existing debug sections nor the SUNW_ctf sections (new or * existing) are SHF_ALLOC'd, so they won't be in areas referenced by * program headers. As such, we can just blindly copy the program * headers from the existing file to the new file. */ if (nphdr != 0) { (void) elf_flagelf(dst, ELF_C_SET, ELF_F_LAYOUT); if (gelf_newphdr(dst, nphdr) == 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } for (i = 0; i < nphdr; i++) { GElf_Phdr phdr; if (gelf_getphdr(src, i, &phdr) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (gelf_update_phdr(dst, i, &phdr) == 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } } } secxlate = ctf_alloc(sizeof (int) * nshdr); for (srcidx = dstidx = 0; srcidx < nshdr; srcidx++) { Elf_Scn *scn = elf_getscn(src, srcidx); GElf_Shdr shdr; char *sname; if (gelf_getshdr(scn, &shdr) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } sname = elf_strptr(src, strndx, shdr.sh_name); if (sname == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (strcmp(sname, CTF_ELF_SCN_NAME) == 0) { secxlate[srcidx] = -1; } else { secxlate[srcidx] = dstidx++; curnmoff += strlen(sname) + 1; } new_offset = (off_t)dehdr.e_phoff; } for (srcidx = 1; srcidx < nshdr; srcidx++) { char *sname; sscn = elf_getscn(src, srcidx); if (gelf_getshdr(sscn, &shdr) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (secxlate[srcidx] == -1) { changing = 1; continue; } dscn = elf_newscn(dst); if (dscn == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } /* * If this file has program headers, we need to explicitly lay * out sections. If none of the sections prior to this one have * been removed, then we can just use the existing location. If * one or more sections have been changed, then we need to * adjust this one to avoid holes. */ if (changing && nphdr != 0) { pad = new_offset % shdr.sh_addralign; if (pad != 0) new_offset += shdr.sh_addralign - pad; shdr.sh_offset = new_offset; } shdr.sh_link = secxlate[shdr.sh_link]; if (shdr.sh_type == SHT_REL || shdr.sh_type == SHT_RELA) shdr.sh_info = secxlate[shdr.sh_info]; sname = elf_strptr(src, strndx, shdr.sh_name); if (sname == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if ((sdata = elf_getdata(sscn, NULL)) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if ((ddata = elf_newdata(dscn)) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } bcopy(sdata, ddata, sizeof (Elf_Data)); if (srcidx == strndx) { char seclen = strlen(CTF_ELF_SCN_NAME); strdatasz = ddata->d_size + shdr.sh_size + seclen + 1; ddata->d_buf = strdatabuf = ctf_alloc(strdatasz); if (ddata->d_buf == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size); (void) strcpy((caddr_t)ddata->d_buf + shdr.sh_size, CTF_ELF_SCN_NAME); ctfnameoff = (off_t)shdr.sh_size; shdr.sh_size += seclen + 1; ddata->d_size += seclen + 1; if (nphdr != 0) changing = 1; } if (shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) { int nsym = shdr.sh_size / shdr.sh_entsize; symtab_idx = secxlate[srcidx]; symdatasz = shdr.sh_size; ddata->d_buf = symdatabuf = ctf_alloc(symdatasz); if (ddata->d_buf == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } (void) bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size); for (i = 0; i < nsym; i++) { GElf_Sym sym; short newscn; (void) gelf_getsym(ddata, i, &sym); if (sym.st_shndx >= SHN_LORESERVE) continue; if ((newscn = secxlate[sym.st_shndx]) != sym.st_shndx) { sym.st_shndx = (newscn == -1 ? 1 : newscn); if (gelf_update_sym(ddata, i, &sym) == 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } } } } if (gelf_update_shdr(dscn, &shdr) == 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } new_offset = (off_t)shdr.sh_offset; if (shdr.sh_type != SHT_NOBITS) new_offset += shdr.sh_size; } if (symtab_idx == -1) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } /* Add the ctf section */ if ((dscn = elf_newscn(dst)) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (gelf_getshdr(dscn, &shdr) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } shdr.sh_name = ctfnameoff; shdr.sh_type = SHT_PROGBITS; shdr.sh_size = fp->ctf_size; shdr.sh_link = symtab_idx; shdr.sh_addralign = 4; if (changing && nphdr != 0) { pad = new_offset % shdr.sh_addralign; if (pad) new_offset += shdr.sh_addralign - pad; shdr.sh_offset = new_offset; new_offset += shdr.sh_size; } if ((ddata = elf_newdata(dscn)) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (compress != 0) { int err; if (ctf_zopen(&err) == NULL) { ret = ctf_set_errno(fp, err); goto out; } if ((err = ctf_compress(fp, &cdata, &asize, &elfsize)) != 0) { ret = ctf_set_errno(fp, err); goto out; } ddata->d_buf = cdata; ddata->d_size = elfsize; } else { ddata->d_buf = (void *)fp->ctf_base; ddata->d_size = fp->ctf_size; } ddata->d_align = shdr.sh_addralign; if (gelf_update_shdr(dscn, &shdr) == 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } /* update the section header location */ if (nphdr != 0) { size_t align = gelf_fsize(dst, ELF_T_ADDR, 1, EV_CURRENT); size_t r = new_offset % align; if (r) new_offset += align - r; dehdr.e_shoff = new_offset; } /* commit to disk */ if (sehdr.e_shstrndx == SHN_XINDEX) dehdr.e_shstrndx = SHN_XINDEX; else dehdr.e_shstrndx = secxlate[sehdr.e_shstrndx]; if (gelf_update_ehdr(dst, &dehdr) == 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (elf_update(dst, ELF_C_WRITE) < 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } ret = 0; out: if (strdatabuf != NULL) ctf_free(strdatabuf, strdatasz); if (symdatabuf != NULL) ctf_free(symdatabuf, symdatasz); if (cdata != NULL) ctf_data_free(cdata, fp->ctf_size); if (secxlate != NULL) ctf_free(secxlate, sizeof (int) * nshdr); return (ret); }
static const value_t * elf_get_ehdr(const value_t *this_fn, parser_state_t *state, const char *filename, int binfd, Elf *e, GElf_Ehdr *ehdr, void *arg) { int eclass = gelf_getclass(e); char *id = elf_getident(e, NULL); dir_t *einfo = dir_id_new(); dir_t *edir = dir_id_new(); size_t n; if (eclass == ELFCLASS32) dir_string_set(einfo, "class", value_int_new(32)); else if (eclass == ELFCLASS64) dir_string_set(einfo, "class", value_int_new(64)); else { dir_string_set(einfo, "class", &value_null); if (eclass != ELFCLASSNONE) parser_report(state, "ELF file has unknown class %d - %s\n", eclass, elf_errmsg(-1)); } if (id == NULL) parser_report(state, "ELF file has unknown identity - %s\n", elf_errmsg(-1)); else dir_string_set(einfo, "id", value_string_new(id, EI_NIDENT)); dir_string_set(einfo, "ehdr", dir_value(edir)); dir_string_set(edir,"type" , value_int_new(ehdr->e_type)); dir_string_set(edir,"machine", value_int_new(ehdr->e_machine)); dir_string_set(edir,"version", value_int_new(ehdr->e_version)); dir_string_set(edir,"entry" , value_int_new(ehdr->e_entry)); dir_string_set(edir,"phoff" , value_int_new(ehdr->e_phoff)); dir_string_set(edir,"shoff" , value_int_new(ehdr->e_shoff)); dir_string_set(edir,"flags" , value_int_new(ehdr->e_flags)); dir_string_set(edir,"ehsize" , value_int_new(ehdr->e_ehsize)); dir_string_set(edir,"phentsize", value_int_new(ehdr->e_phentsize)); dir_string_set(edir,"shentsize", value_int_new(ehdr->e_shentsize)); if (elf_getphdrnum(e, &n) != 0) parser_report(state,"ELF file has unknown number of segments - %s\n", elf_errmsg(-1)); else dir_string_set(einfo, "progsects", value_int_new(n)); if (elf_getshdrnum(e, &n) != 0) parser_report(state, "%s: ELF file has unknown number of sections - %s\n", elf_errmsg(-1)); else dir_string_set(einfo, "sections", value_int_new(n)); if (elf_getshdrstrndx(e, &n) != 0) parser_report(state, "%s: ELF file has no section names - %s\n", elf_errmsg(-1)); else dir_string_set(einfo, "shdrstrndx", value_int_new(n)); return dir_value(einfo); }
int main (int argc, char *argv[]) { if (argc < 3) error (EXIT_FAILURE, 0, "usage: %s FROMNAME TONAME", argv[0]); elf_version (EV_CURRENT); int infd = open (argv[1], O_RDONLY); if (infd == -1) error (EXIT_FAILURE, errno, "cannot open input file '%s'", argv[1]); Elf *inelf = elf_begin (infd, ELF_C_READ, NULL); if (inelf == NULL) error (EXIT_FAILURE, 0, "problems opening '%s' as ELF file: %s", argv[1], elf_errmsg (-1)); int outfd = creat (argv[2], 0666); if (outfd == -1) error (EXIT_FAILURE, errno, "cannot open output file '%s'", argv[2]); Elf *outelf = elf_begin (outfd, ELF_C_WRITE, NULL); if (outelf == NULL) error (EXIT_FAILURE, 0, "problems opening '%s' as ELF file: %s", argv[2], elf_errmsg (-1)); gelf_newehdr (outelf, gelf_getclass (inelf)); GElf_Ehdr ehdr_mem; GElf_Ehdr *ehdr; gelf_update_ehdr (outelf, (ehdr = gelf_getehdr (inelf, &ehdr_mem))); if (ehdr->e_phnum > 0) { int cnt; if (gelf_newphdr (outelf, ehdr->e_phnum) == 0) error (EXIT_FAILURE, 0, "cannot create program header: %s", elf_errmsg (-1)); for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) { GElf_Phdr phdr_mem; gelf_update_phdr (outelf, cnt, gelf_getphdr (inelf, cnt, &phdr_mem)); } } Elf_Scn *scn = NULL; while ((scn = elf_nextscn (inelf, scn)) != NULL) { Elf_Scn *newscn = elf_newscn (outelf); GElf_Shdr shdr_mem; gelf_update_shdr (newscn, gelf_getshdr (scn, &shdr_mem)); *elf_newdata (newscn) = *elf_getdata (scn, NULL); } elf_flagelf (outelf, ELF_C_SET, ELF_F_LAYOUT); if (elf_update (outelf, ELF_C_WRITE) == -1) error (EXIT_FAILURE, 0, "elf_update failed: %s", elf_errmsg (-1)); close (outfd); return 0; }
/* * Update our module cache by adding an entry for the specified module 'name'. * We create the dt_module_t and populate it using /system/object/<name>/. */ static void dt_module_update(dtrace_hdl_t *dtp, const char *name) { char fname[MAXPATHLEN]; struct stat64 st; int fd, err, bits; dt_module_t *dmp; const char *s; size_t shstrs; GElf_Shdr sh; Elf_Data *dp; Elf_Scn *sp; unsigned read_cmd = ELF_C_READ; if (0 == strcmp("mach_kernel", name)) { if (dtrace_kernel_path(fname, sizeof(fname)) != 0) { dt_dprintf("failed to retrieve the kernel binary, the module cache will not be updated\n"); return; } read_cmd = ELF_C_RDKERNTYPE; } if ((fd = open(fname, O_RDONLY)) == -1 || fstat64(fd, &st) == -1 || (dmp = dt_module_create(dtp, name)) == NULL) { dt_dprintf("failed to open %s: %s\n", fname, strerror(errno)); (void) close(fd); return; } /* * Since the module can unload out from under us (and /system/object * will return ENOENT), tell libelf to cook the entire file now and * then close the underlying file descriptor immediately. If this * succeeds, we know that we can continue safely using dmp->dm_elf. */ dmp->dm_elf = elf_begin(fd, read_cmd, NULL); err = elf_cntl(dmp->dm_elf, ELF_C_FDREAD); (void) close(fd); if (dmp->dm_elf == NULL || err == -1 || elf_getshstrndx(dmp->dm_elf, &shstrs) == 0) { dt_dprintf("failed to load %s: %s\n", fname, elf_errmsg(elf_errno())); dt_module_destroy(dtp, dmp); return; } switch (gelf_getclass(dmp->dm_elf)) { case ELFCLASS32: dmp->dm_ops = (dmp->dm_elf->ed_kind == ELF_K_MACHO ? &dt_modops_macho_32 : &dt_modops_32); bits = 32; break; case ELFCLASS64: dmp->dm_ops = (dmp->dm_elf->ed_kind == ELF_K_MACHO ? &dt_modops_macho_64 : &dt_modops_64); bits = 64; break; default: dt_dprintf("failed to load %s: unknown ELF class\n", fname); dt_module_destroy(dtp, dmp); return; } /* * Iterate over the section headers locating various sections of * interest and use their attributes to flesh out the dt_module_t. */ for (sp = NULL; (sp = elf_nextscn(dmp->dm_elf, sp)) != NULL; ) { if (gelf_getshdr(sp, &sh) == NULL || sh.sh_type == SHT_NULL || (s = elf_strptr(dmp->dm_elf, shstrs, sh.sh_name)) == NULL) continue; /* skip any malformed sections */ if (strcmp(s, ".text") == 0) { dmp->dm_text_size = sh.sh_size; dmp->dm_text_va = sh.sh_addr; } else if (strcmp(s, ".data") == 0) { dmp->dm_data_size = sh.sh_size; dmp->dm_data_va = sh.sh_addr; } else if (strcmp(s, ".bss") == 0) { dmp->dm_bss_size = sh.sh_size; dmp->dm_bss_va = sh.sh_addr; } else if (strcmp(s, ".info") == 0 && (dp = elf_getdata(sp, NULL)) != NULL) { bcopy(dp->d_buf, &dmp->dm_info, MIN(sh.sh_size, sizeof (dmp->dm_info))); } else if (strcmp(s, ".filename") == 0 && (dp = elf_getdata(sp, NULL)) != NULL) { (void) strlcpy(dmp->dm_file, dp->d_buf, sizeof (dmp->dm_file)); } } dmp->dm_flags |= DT_DM_KERNEL; dmp->dm_modid = (int)OBJFS_MODID(st.st_ino); if (dmp->dm_info.objfs_info_primary) dmp->dm_flags |= DT_DM_PRIMARY; dt_dprintf("opened %d-bit module %s (%s) [%d]\n", bits, dmp->dm_name, dmp->dm_file, dmp->dm_modid); }
/* * Print the symbol table according to the flags that were * set, if any. Input is an opened ELF file, the section name, * the section header, the section descriptor, and the filename. * First get the symbol table with a call to elf_getdata. * Then translate the symbol table data in memory by calling * readsyms(). This avoids duplication of function calls * and improves sorting efficiency. qsort is used when sorting * is requested. */ static void print_symtab(Elf *elf_file, unsigned int shstrndx, Elf_Scn *p_sd, GElf_Shdr *shdr, char *filename) { Elf_Data * sd; SYM *sym_data; SYM *s; GElf_Sxword count = 0; const int ndigits_arr[] = { 10, /* FMT_T_DEC */ 8, /* FMT_T_HEX */ 11, /* FMT_T_OCT */ }; int ndigits; /* * Determine # of digits to use for each numeric value. */ ndigits = ndigits_arr[fmt_flag]; if (gelf_getclass(elf_file) == ELFCLASS64) ndigits *= 2; /* * print header */ print_header(ndigits); /* * get symbol table data */ if (((sd = elf_getdata(p_sd, NULL)) == NULL) || (sd->d_size == 0)) { (void) fprintf(stderr, gettext("%s: %s: no symbol table data\n"), prog_name, filename); return; } count = shdr->sh_size / shdr->sh_entsize; /* * translate symbol table data */ sym_data = readsyms(sd, count, elf_file, shdr->sh_link, (unsigned int)elf_ndxscn(p_sd)); if (sym_data == NULL) { (void) fprintf(stderr, gettext( "%s: %s: problem reading symbol data\n"), prog_name, filename); return; } qsort((char *)sym_data, count-1, sizeof (SYM), (int (*)(const void *, const void *))compare); s = sym_data; while (count > 1) { #ifndef XPG4 if (u_flag) { /* * U_flag specified */ print_with_uflag(sym_data, filename); } else if (p_flag) #else if (p_flag) #endif print_with_pflag(ndigits, elf_file, shstrndx, sym_data, filename); else if (P_flag) print_with_Pflag(ndigits, elf_file, shstrndx, sym_data); else print_with_otherflags(ndigits, elf_file, shstrndx, sym_data, filename); sym_data++; count--; } free(s); /* allocated in readsym() */ }
int main (int argc, char *argv[]) { Elf *elf; int fd; GElf_Ehdr ehdr; int cnt; fd = open (argv[1], O_RDONLY); if (fd == -1) { printf ("cannot open \"%s\": %s\n", argv[1], strerror (errno)); exit (1); } elf_version (EV_CURRENT); elf = elf_begin (fd, ELF_C_READ, NULL); if (elf == NULL) { printf ("cannot open ELF file: %s\n", elf_errmsg (-1)); exit (1); } if (elf_kind (elf) != ELF_K_ELF) { printf ("\"%s\" is not an ELF file\n", argv[1]); exit (1); } if (gelf_getehdr (elf, &ehdr) == NULL) { printf ("cannot get the ELF header: %s\n", elf_errmsg (-1)); exit (1); } printf ("idx type %*s %*s %*s %*s %*s align flags\n", gelf_getclass (elf) == ELFCLASS32 ? 9 : 17, "offset", gelf_getclass (elf) == ELFCLASS32 ? 10 : 18, "vaddr", gelf_getclass (elf) == ELFCLASS32 ? 10 : 18, "paddr", gelf_getclass (elf) == ELFCLASS32 ? 9 : 12, "filesz", gelf_getclass (elf) == ELFCLASS32 ? 9 : 12, "memsz"); for (cnt = 0; cnt < ehdr.e_phnum; ++cnt) { static const char *typenames[] = { [PT_NULL] = "NULL", [PT_LOAD] = "LOAD", [PT_DYNAMIC] = "DYNAMIC", [PT_INTERP] = "INTERP", [PT_NOTE] = "NOTE", [PT_SHLIB] = "SHLIB", [PT_PHDR] = "PHDR" }; GElf_Phdr mem; GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &mem); char buf[19]; const char *p_type = typenames[phdr->p_type]; /* If we don't know the name of the type we use the number value. */ if (phdr->p_type >= PT_NUM) { snprintf (buf, sizeof (buf), "%x", phdr->p_type); p_type = buf; } printf ("%3d %-7s %#0*llx %#0*llx %#0*llx %#0*llx %#0*llx %#6llx ", cnt, p_type, gelf_getclass (elf) == ELFCLASS32 ? 9 : 17, (unsigned long long int) phdr->p_offset, gelf_getclass (elf) == ELFCLASS32 ? 10 : 18, (unsigned long long int) phdr->p_vaddr, gelf_getclass (elf) == ELFCLASS32 ? 10 : 18, (unsigned long long int) phdr->p_paddr, gelf_getclass (elf) == ELFCLASS32 ? 9 : 12, (unsigned long long int) phdr->p_filesz, gelf_getclass (elf) == ELFCLASS32 ? 9 : 12, (unsigned long long int) phdr->p_memsz, (unsigned long long int) phdr->p_align); putc_unlocked ((phdr->p_flags & PF_X) ? 'X' : ' ', stdout); putc_unlocked ((phdr->p_flags & PF_W) ? 'W' : ' ', stdout); putc_unlocked ((phdr->p_flags & PF_R) ? 'R' : ' ', stdout); putc_unlocked ('\n', stdout); if (phdr->p_type == PT_INTERP) { /* We can show the user the name of the interpreter. */ size_t maxsize; char *filedata = elf_rawfile (elf, &maxsize); if (filedata != NULL && phdr->p_offset < maxsize) printf ("\t[Requesting program interpreter: %s]\n", filedata + phdr->p_offset); } } if (elf_end (elf) != 0) { printf ("error while freeing ELF descriptor: %s\n", elf_errmsg (-1)); exit (1); } return 0; }
dt_module_update(dtrace_hdl_t *dtp, struct kld_file_stat *k_stat) #endif { char fname[MAXPATHLEN]; struct stat64 st; int fd, err, bits; dt_module_t *dmp; const char *s; size_t shstrs; GElf_Shdr sh; Elf_Data *dp; Elf_Scn *sp; #if defined(sun) (void) snprintf(fname, sizeof (fname), "%s/%s/object", OBJFS_ROOT, name); #else GElf_Ehdr ehdr; GElf_Phdr ph; char name[MAXPATHLEN]; uintptr_t mapbase, alignmask; int i = 0; int is_elf_obj; (void) strlcpy(name, k_stat->name, sizeof(name)); (void) strlcpy(fname, k_stat->pathname, sizeof(fname)); #endif if ((fd = open(fname, O_RDONLY)) == -1 || fstat64(fd, &st) == -1 || (dmp = dt_module_create(dtp, name)) == NULL) { dt_dprintf("failed to open %s: %s\n", fname, strerror(errno)); (void) close(fd); return; } /* * Since the module can unload out from under us (and /system/object * will return ENOENT), tell libelf to cook the entire file now and * then close the underlying file descriptor immediately. If this * succeeds, we know that we can continue safely using dmp->dm_elf. */ dmp->dm_elf = elf_begin(fd, ELF_C_READ, NULL); err = elf_cntl(dmp->dm_elf, ELF_C_FDREAD); (void) close(fd); if (dmp->dm_elf == NULL || err == -1 || elf_getshdrstrndx(dmp->dm_elf, &shstrs) == -1) { dt_dprintf("failed to load %s: %s\n", fname, elf_errmsg(elf_errno())); dt_module_destroy(dtp, dmp); return; } switch (gelf_getclass(dmp->dm_elf)) { case ELFCLASS32: dmp->dm_ops = &dt_modops_32; bits = 32; break; case ELFCLASS64: dmp->dm_ops = &dt_modops_64; bits = 64; break; default: dt_dprintf("failed to load %s: unknown ELF class\n", fname); dt_module_destroy(dtp, dmp); return; } #if defined(__FreeBSD__) mapbase = (uintptr_t)k_stat->address; gelf_getehdr(dmp->dm_elf, &ehdr); is_elf_obj = (ehdr.e_type == ET_REL); if (is_elf_obj) { dmp->dm_sec_offsets = malloc(ehdr.e_shnum * sizeof(*dmp->dm_sec_offsets)); if (dmp->dm_sec_offsets == NULL) { dt_dprintf("failed to allocate memory\n"); dt_module_destroy(dtp, dmp); return; } } #endif /* * Iterate over the section headers locating various sections of * interest and use their attributes to flesh out the dt_module_t. */ for (sp = NULL; (sp = elf_nextscn(dmp->dm_elf, sp)) != NULL; ) { if (gelf_getshdr(sp, &sh) == NULL || sh.sh_type == SHT_NULL || (s = elf_strptr(dmp->dm_elf, shstrs, sh.sh_name)) == NULL) continue; /* skip any malformed sections */ #if defined(__FreeBSD__) if (sh.sh_size == 0) continue; if (sh.sh_type == SHT_PROGBITS || sh.sh_type == SHT_NOBITS) { alignmask = sh.sh_addralign - 1; mapbase += alignmask; mapbase &= ~alignmask; sh.sh_addr = mapbase; if (is_elf_obj) dmp->dm_sec_offsets[elf_ndxscn(sp)] = sh.sh_addr; mapbase += sh.sh_size; } #endif if (strcmp(s, ".text") == 0) { dmp->dm_text_size = sh.sh_size; dmp->dm_text_va = sh.sh_addr; } else if (strcmp(s, ".data") == 0) { dmp->dm_data_size = sh.sh_size; dmp->dm_data_va = sh.sh_addr; } else if (strcmp(s, ".bss") == 0) { dmp->dm_bss_size = sh.sh_size; dmp->dm_bss_va = sh.sh_addr; } else if (strcmp(s, ".info") == 0 && (dp = elf_getdata(sp, NULL)) != NULL) { bcopy(dp->d_buf, &dmp->dm_info, MIN(sh.sh_size, sizeof (dmp->dm_info))); } else if (strcmp(s, ".filename") == 0 && (dp = elf_getdata(sp, NULL)) != NULL) { (void) strlcpy(dmp->dm_file, dp->d_buf, sizeof (dmp->dm_file)); } } dmp->dm_flags |= DT_DM_KERNEL; #if defined(sun) dmp->dm_modid = (int)OBJFS_MODID(st.st_ino); #else /* * Include .rodata and special sections into .text. * This depends on default section layout produced by GNU ld * for ELF objects and libraries: * [Text][R/O data][R/W data][Dynamic][BSS][Non loadable] */ dmp->dm_text_size = dmp->dm_data_va - dmp->dm_text_va; #if defined(__i386__) /* * Find the first load section and figure out the relocation * offset for the symbols. The kernel module will not need * relocation, but the kernel linker modules will. */ for (i = 0; gelf_getphdr(dmp->dm_elf, i, &ph) != NULL; i++) { if (ph.p_type == PT_LOAD) { dmp->dm_reloc_offset = k_stat->address - ph.p_vaddr; break; } } #endif #endif if (dmp->dm_info.objfs_info_primary) dmp->dm_flags |= DT_DM_PRIMARY; dt_dprintf("opened %d-bit module %s (%s) [%d]\n", bits, dmp->dm_name, dmp->dm_file, dmp->dm_modid); }
int inject_code(int fd, inject_data_t *inject, char **err) { elf_data_t elf; int ret; off_t align; size_t n; elf.fd = fd; elf.e = NULL; if(elf_version(EV_CURRENT) == EV_NONE) { (*err) = "failed to initialize libelf"; goto fail; } /* Use libelf to read the file, but do writes manually */ elf.e = elf_begin(elf.fd, ELF_C_READ, NULL); if(!elf.e) { (*err) = "failed to open elf file"; goto fail; } if(elf_kind(elf.e) != ELF_K_ELF) { (*err) = "not an elf executable"; goto fail; } ret = gelf_getclass(elf.e); switch(ret) { case ELFCLASSNONE: (*err) = "unknown elf class"; goto fail; case ELFCLASS32: elf.bits = 32; break; default: elf.bits = 64; break; } /* Get executable header */ if(!gelf_getehdr(elf.e, &elf.ehdr)) { (*err) = "failed to get executable header"; goto fail; } /* Find a rewritable program header */ if(find_rewritable_segment(&elf, inject, err) < 0) { goto fail; } /* Write the injected code to the binary */ if(write_code(&elf, inject, err) < 0) { goto fail; } if (inject->secaddr == 0) find_injected_secaddr(&elf,inject); /* Fix alignment of code address */ align = 0x1000; inject->secaddr = inject->secaddr - inject->len; n = (inject->off % align) - (inject->secaddr % align); if(n > 0) { inject->secaddr -= align; } inject->secaddr += n; fprintf(finfo,"Inject=%lx\n",inject->secaddr); /* Rewrite a section for the injected code */ if(rewrite_code_section(&elf, inject, err) < 0) { goto fail; } /* Update the name of the rewritten section */ if(rewrite_section_name(&elf, inject, err) < 0) { goto fail; } /* Rewrite a segment for the added code section */ if(rewrite_code_segment(&elf, inject, err) < 0) { goto fail; } /* Rewrite entry point if requested */ if(inject->entry >= 0) { if(rewrite_entry_point(&elf, inject, err) < 0) { goto fail; } } ret = 0; goto cleanup; fail: ret = -1; cleanup: if(elf.e) { elf_end(elf.e); } return ret; }
/* * scan the ELF image to build table of global symbold and the image * regions where they can be found (BSS and DATA) */ static inline int table_init_helper (void) { Elf *e = NULL; GElf_Ehdr ehdr; char *shstr_name = NULL; size_t shstrndx; Elf_Scn *scn = NULL; GElf_Shdr shdr; int ret = -1; #if 0 int (*getsi) (); /* look up name of elf_get... routine */ #endif /* unrecognized format */ if (elf_version (EV_CURRENT) == EV_NONE) { goto bail; } /* get the ELF object from already opened state */ e = elf_begin (GET_STATE (exe_fd), ELF_C_READ, NULL); if (e == NULL) { goto bail; } /* do some sanity checks */ if (elf_kind (e) != ELF_K_ELF) { goto bail; } if (gelf_getehdr (e, &ehdr) == NULL) { goto bail; } if (gelf_getclass (e) == ELFCLASSNONE) { goto bail; } /* * There are various elf_get* routines with different return values * in differnt libraries/versions - name of routine does not tell us * all we ned to know. So let it go here, and we'll mop up any * problems later on. */ /* * This routine is either "elf_getshdrstrndx" in newer ELF * libraries, or "elf_getshstrndx" in older ones. Hard-code older * one for now since it is in the newer libraries, although marked as * deprecated. This will be detected by autoconf later * * DEPRECATED */ (void) elf_getshstrndx (e, &shstrndx); /* walk sections, look for RO/BSS/DATA and symbol table */ scn = NULL; while ((scn = elf_nextscn (e, scn)) != NULL) { if (gelf_getshdr (scn, &shdr) != &shdr) { goto bail; } shstr_name = elf_strptr (e, shstrndx, shdr.sh_name); if (shstr_name == NULL) { goto bail; } /* found the read-only data */ if (shdr.sh_type == SHT_PROGBITS && strcmp (shstr_name, ".rodata") == 0) { elfro.start = shdr.sh_addr; elfro.end = elfro.start + shdr.sh_size; shmemi_trace (SHMEM_LOG_SYMBOLS, "ELF section .rodata for global variables = 0x%lX -> 0x%lX", elfro.start, elfro.end); continue; /* move to next scan */ } /* found the uninitialized globals */ if (shdr.sh_type == SHT_NOBITS && strcmp (shstr_name, ".bss") == 0) { elfbss.start = shdr.sh_addr; elfbss.end = elfbss.start + shdr.sh_size; shmemi_trace (SHMEM_LOG_SYMBOLS, "ELF section .bss for global variables = 0x%lX -> 0x%lX", elfbss.start, elfbss.end); continue; /* move to next scan */ } /* found the initialized globals */ if (shdr.sh_type == SHT_PROGBITS && strcmp (shstr_name, ".data") == 0) { elfdata.start = shdr.sh_addr; elfdata.end = elfdata.start + shdr.sh_size; shmemi_trace (SHMEM_LOG_SYMBOLS, "ELF section .data for global variables = 0x%lX -> 0x%lX", elfdata.start, elfdata.end); continue; /* move to next scan */ } /* keep looking until we find the symbol table */ if (shdr.sh_type == SHT_SYMTAB) { Elf_Data *data = NULL; while ((data = elf_getdata (scn, data)) != NULL) { GElf_Sym *es; GElf_Sym *last_es; es = (GElf_Sym *) data->d_buf; if (es == NULL) { continue; } /* find out how many entries to look for */ last_es = (GElf_Sym *) ((char *) data->d_buf + data->d_size); for (; es < last_es; es += 1) { char *name; /* * need visible global or local (Fortran save) object with * some kind of content */ if (es->st_value == 0 || es->st_size == 0) { continue; } /* * this macro handles a symbol that is present * in one libelf implementation but isn't in another * (elfutils vs. libelf) */ #ifndef GELF_ST_VISIBILITY #define GELF_ST_VISIBILITY(o) ELF64_ST_VISIBILITY(o) #endif if (GELF_ST_TYPE (es->st_info) != STT_OBJECT && GELF_ST_VISIBILITY (es->st_info) != STV_DEFAULT) { continue; } name = elf_strptr (e, shdr.sh_link, (size_t) es->st_name); if (name == NULL || *name == '\0') { continue; } /* put the symbol and info into the symbol hash table */ { globalvar_t *gv = (globalvar_t *) malloc (sizeof (*gv)); if (gv == NULL) { goto bail; } gv->name = strdup (name); if (gv->name == NULL) { free (gv); goto bail; } gv->addr = (void *) es->st_value; gv->size = es->st_size; HASH_ADD_PTR (gvp, addr, gv); } } } /* * pulled out all the global symbols => success, * don't need to scan further */ ret = 0; break; } } bail: if (elf_end (e) != 0) { ret = -1; } return ret; }
void clone_elf(Elf *elf, Elf *newelf, const char *elf_name, const char *newelf_name, bool *sym_filter, int num_symbols, int shady #ifdef SUPPORT_ANDROID_PRELINK_TAGS , int *prelinked, int *elf_little, long *prelink_addr #endif , bool rebuild_shstrtab, bool strip_debug, bool dry_run) { GElf_Ehdr ehdr_mem, *ehdr; /* store ELF header of original library */ size_t shstrndx; /* section-strings-section index */ size_t shnum; /* number of sections in the original file */ /* string table for section headers in new file */ struct Ebl_Strtab *shst = NULL; int dynamic_idx = -1; /* index in shdr_info[] of .dynamic section */ int dynsym_idx = -1; /* index in shdr_info[] of dynamic symbol table section */ unsigned int cnt; /* general-purpose counter */ /* This flag is true when at least one section is dropped or when the relative order of sections has changed, so that section indices in the resulting file will be different from those in the original. */ bool sections_dropped_or_rearranged; Elf_Scn *scn; /* general-purpose section */ size_t idx; /* general-purporse section index */ shdr_info_t *shdr_info = NULL; unsigned int shdr_info_len = 0; GElf_Phdr *phdr_info = NULL; /* Get the information from the old file. */ ehdr = gelf_getehdr (elf, &ehdr_mem); FAILIF_LIBELF(NULL == ehdr, gelf_getehdr); /* Create new program header for the elf file */ FAILIF(gelf_newehdr (newelf, gelf_getclass (elf)) == 0 || (ehdr->e_type != ET_REL && gelf_newphdr (newelf, ehdr->e_phnum) == 0), "Cannot create new file: %s", elf_errmsg (-1)); #ifdef SUPPORT_ANDROID_PRELINK_TAGS ASSERT(prelinked); ASSERT(prelink_addr); ASSERT(elf_little); *elf_little = (ehdr->e_ident[EI_DATA] == ELFDATA2LSB); *prelinked = check_prelinked(elf_name, *elf_little, prelink_addr); #endif INFO("\n\nCALCULATING MODIFICATIONS\n\n"); /* Copy out the old program header: notice that if the ELF file does not have a program header, this loop won't execute. */ INFO("Copying ELF program header...\n"); phdr_info = (GElf_Phdr *)CALLOC(ehdr->e_phnum, sizeof(GElf_Phdr)); for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) { INFO("\tRetrieving entry %d\n", cnt); FAILIF_LIBELF(NULL == gelf_getphdr(elf, cnt, phdr_info + cnt), gelf_getphdr); /* -- we update the header at the end FAILIF_LIBELF(gelf_update_phdr (newelf, cnt, phdr_info + cnt) == 0, gelf_update_phdr); */ } /* Get the section-header strings section. This section contains the strings used to name the other sections. */ FAILIF_LIBELF(elf_getshstrndx(elf, &shstrndx) < 0, elf_getshstrndx); /* Get the number of sections. */ FAILIF_LIBELF(elf_getshnum (elf, &shnum) < 0, elf_getshnum); INFO("Original ELF file has %zd sections.\n", shnum); /* Allocate the section-header-info buffer. We allocate one more entry for the section-strings section because we regenerate that one and place it at the very end of the file. Note that just because we create an extra entry in the shdr_info array, it does not mean that we create one more section the header. We just mark the old section for removal and create one as the last section. */ INFO("Allocating section-header info structure (%zd) bytes...\n", shnum*sizeof (shdr_info_t)); shdr_info_len = rebuild_shstrtab ? shnum + 1 : shnum; shdr_info = (shdr_info_t *)CALLOC(shdr_info_len, sizeof (shdr_info_t)); /* Iterate over all the sections and initialize the internal section-info array... */ INFO("Initializing section-header info structure...\n"); /* Gather information about the sections in this file. */ scn = NULL; cnt = 1; while ((scn = elf_nextscn (elf, scn)) != NULL) { ASSERT(elf_ndxscn(scn) == cnt); shdr_info[cnt].scn = scn; FAILIF_LIBELF(NULL == gelf_getshdr(scn, &shdr_info[cnt].shdr), gelf_getshdr); /* Get the name of the section. */ shdr_info[cnt].name = elf_strptr (elf, shstrndx, shdr_info[cnt].shdr.sh_name); INFO("\tname: %s\n", shdr_info[cnt].name); FAILIF(shdr_info[cnt].name == NULL, "Malformed file: section %d name is null\n", cnt); /* Mark them as present but not yet investigated. By "investigating" sections, we mean that we check to see if by stripping other sections, the sections under investigation will be compromised. For example, if we are removing a section of code, then we want to make sure that the symbol table does not contain symbols that refer to this code, so we investigate the symbol table. If we do find such symbols, we will not strip the code section. */ shdr_info[cnt].idx = 1; /* Remember the shdr.sh_link value. We need to remember this value for those sections that refer to other sections. For example, we need to remember it for relocation-entry sections, because if we modify the symbol table that a relocation-entry section is relative to, then we need to patch the relocation section. By the time we get to deciding whether we need to patch the relocation section, we will have overwritten its header's sh_link field with a new value. */ shdr_info[cnt].old_shdr = shdr_info[cnt].shdr; INFO("\t\toriginal sh_link: %08d\n", shdr_info[cnt].old_shdr.sh_link); INFO("\t\toriginal sh_addr: %lld\n", shdr_info[cnt].old_shdr.sh_addr); INFO("\t\toriginal sh_offset: %lld\n", shdr_info[cnt].old_shdr.sh_offset); INFO("\t\toriginal sh_size: %lld\n", shdr_info[cnt].old_shdr.sh_size); if (shdr_info[cnt].shdr.sh_type == SHT_DYNAMIC) { INFO("\t\tthis is the SHT_DYNAMIC section [%s] at index %d\n", shdr_info[cnt].name, cnt); dynamic_idx = cnt; } else if (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM) { INFO("\t\tthis is the SHT_DYNSYM section [%s] at index %d\n", shdr_info[cnt].name, cnt); dynsym_idx = cnt; } FAILIF(shdr_info[cnt].shdr.sh_type == SHT_SYMTAB_SHNDX, "Cannot handle sh_type SHT_SYMTAB_SHNDX!\n"); FAILIF(shdr_info[cnt].shdr.sh_type == SHT_GROUP, "Cannot handle sh_type SHT_GROUP!\n"); FAILIF(shdr_info[cnt].shdr.sh_type == SHT_GNU_versym, "Cannot handle sh_type SHT_GNU_versym!\n"); /* Increment the counter. */ ++cnt; } /* while */ /* Get the EBL handling. */ Ebl *ebl = ebl_openbackend (elf); FAILIF_LIBELF(NULL == ebl, ebl_openbackend); FAILIF_LIBELF(0 != arm_init(elf, ehdr->e_machine, ebl, sizeof(Ebl)), arm_init); if (strip_debug) { /* This will actually strip more than just sections. It will strip anything not essential to running the image. */ INFO("Finding debug sections to strip.\n"); /* Now determine which sections can go away. The general rule is that all sections which are not used at runtime are stripped out. But there are a few exceptions: - special sections named ".comment" and ".note" are kept - OS or architecture specific sections are kept since we might not know how to handle them - if a section is referred to from a section which is not removed in the sh_link or sh_info element it cannot be removed either */ for (cnt = 1; cnt < shnum; ++cnt) { /* Check whether the section can be removed. */ if (SECTION_STRIP_P (ebl, elf, ehdr, &shdr_info[cnt].shdr, shdr_info[cnt].name, 1, /* remove .comment sections */ 1 /* remove all debug sections */) || /* The macro above is broken--check for .comment explicitly */ !strcmp(".comment", shdr_info[cnt].name) #ifdef ARM_SPECIFIC_HACKS || /* We ignore this section, that's why we can remove it. */ !strcmp(".stack", shdr_info[cnt].name) #endif ) { /* For now assume this section will be removed. */ INFO("Section [%s] will be stripped from image.\n", shdr_info[cnt].name); shdr_info[cnt].idx = 0; } #ifdef STRIP_STATIC_SYMBOLS else if (shdr_info[cnt].shdr.sh_type == SHT_SYMTAB) { /* Mark the static symbol table for removal */ INFO("Section [%s] (static symbol table) will be stripped from image.\n", shdr_info[cnt].name); shdr_info[cnt].idx = 0; if (shdr_info[shdr_info[cnt].shdr.sh_link].shdr.sh_type == SHT_STRTAB) { /* Mark the symbol table's string table for removal. */ INFO("Section [%s] (static symbol-string table) will be stripped from image.\n", shdr_info[shdr_info[cnt].shdr.sh_link].name); shdr_info[shdr_info[cnt].shdr.sh_link].idx = 0; } else { ERROR("Expecting the sh_link field of a symbol table to point to" " associated symbol-strings table! This is not mandated by" " the standard, but is a common practice and the only way " " to know for sure which strings table corresponds to which" " symbol table!\n"); } } #endif } /* Mark the SHT_NULL section as handled. */ shdr_info[0].idx = 2; /* Handle exceptions: section groups and cross-references. We might have to repeat this a few times since the resetting of the flag might propagate. */ int exceptions_pass = 0; bool changes; do { changes = false; INFO("\nHandling exceptions, pass %d\n\n", exceptions_pass++); for (cnt = 1; cnt < shnum; ++cnt) { if (shdr_info[cnt].idx == 0) { /* If a relocation section is marked as being removed but the section it is relocating is not, then do not remove the relocation section. */ if ((shdr_info[cnt].shdr.sh_type == SHT_REL || shdr_info[cnt].shdr.sh_type == SHT_RELA) && shdr_info[shdr_info[cnt].shdr.sh_info].idx != 0) { PRINT("\tSection [%s] will not be removed because the " "section it is relocating (%s) stays.\n", shdr_info[cnt].name, shdr_info[shdr_info[cnt].shdr.sh_info].name); } } if (shdr_info[cnt].idx == 1) { INFO("Processing section [%s]...\n", shdr_info[cnt].name); /* The content of symbol tables we don't remove must not reference any section which we do remove. Otherwise we cannot remove the referred section. */ if (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM || shdr_info[cnt].shdr.sh_type == SHT_SYMTAB) { Elf_Data *symdata; size_t elsize; INFO("\tSection [%s] is a symbol table that's not being" " removed.\n\tChecking to make sure that no symbols" " refer to sections that are being removed.\n", shdr_info[cnt].name); /* Make sure the data is loaded. */ symdata = elf_getdata (shdr_info[cnt].scn, NULL); FAILIF_LIBELF(NULL == symdata, elf_getdata); /* Go through all symbols and make sure the section they reference is not removed. */ elsize = gelf_fsize (elf, ELF_T_SYM, 1, ehdr->e_version); /* Check the length of the dynamic-symbol filter. */ FAILIF(sym_filter != NULL && (size_t)num_symbols != symdata->d_size / elsize, "Length of dynsym filter (%d) must equal the number" " of dynamic symbols (%zd)!\n", num_symbols, symdata->d_size / elsize); size_t inner; for (inner = 0; inner < symdata->d_size / elsize; ++inner) { GElf_Sym sym_mem; GElf_Sym *sym; size_t scnidx; sym = gelf_getsymshndx (symdata, NULL, inner, &sym_mem, NULL); FAILIF_LIBELF(sym == NULL, gelf_getsymshndx); scnidx = sym->st_shndx; FAILIF(scnidx == SHN_XINDEX, "Can't handle SHN_XINDEX!\n"); if (scnidx == SHN_UNDEF || scnidx >= shnum || (scnidx >= SHN_LORESERVE && scnidx <= SHN_HIRESERVE) || GELF_ST_TYPE (sym->st_info) == STT_SECTION) { continue; } /* If the symbol is going to be thrown and it is a global or weak symbol that is defined (not imported), then continue. Since the symbol is going away, we do not care whether it refers to a section that is also going away. */ if (sym_filter && !sym_filter[inner]) { bool global_or_weak = ELF32_ST_BIND(sym->st_info) == STB_GLOBAL || ELF32_ST_BIND(sym->st_info) == STB_WEAK; if (!global_or_weak && sym->st_shndx != SHN_UNDEF) continue; } /* -- far too much output INFO("\t\t\tSymbol [%s] (%d)\n", elf_strptr(elf, shdr_info[cnt].shdr.sh_link, sym->st_name), shdr_info[cnt].shdr.sh_info); */ if (shdr_info[scnidx].idx == 0) { PRINT("\t\t\tSymbol [%s] refers to section [%s], " "which is being removed. Will keep that " "section.\n", elf_strptr(elf, shdr_info[cnt].shdr.sh_link, sym->st_name), shdr_info[scnidx].name); /* Mark this section as used. */ shdr_info[scnidx].idx = 1; changes |= scnidx < cnt; } } /* for each symbol */ } /* section type is SHT_DYNSYM or SHT_SYMTAB */ /* Cross referencing happens: - for the cases the ELF specification says. That are + SHT_DYNAMIC in sh_link to string table + SHT_HASH in sh_link to symbol table + SHT_REL and SHT_RELA in sh_link to symbol table + SHT_SYMTAB and SHT_DYNSYM in sh_link to string table + SHT_GROUP in sh_link to symbol table + SHT_SYMTAB_SHNDX in sh_link to symbol table Other (OS or architecture-specific) sections might as well use this field so we process it unconditionally. - references inside section groups - specially marked references in sh_info if the SHF_INFO_LINK flag is set */ if (shdr_info[shdr_info[cnt].shdr.sh_link].idx == 0) { shdr_info[shdr_info[cnt].shdr.sh_link].idx = 1; changes |= shdr_info[cnt].shdr.sh_link < cnt; } /* Handle references through sh_info. */ if (SH_INFO_LINK_P (&shdr_info[cnt].shdr) && shdr_info[shdr_info[cnt].shdr.sh_info].idx == 0) { PRINT("\tSection [%s] links to section [%s], which was " "marked for removal--it will not be removed.\n", shdr_info[cnt].name, shdr_info[shdr_info[cnt].shdr.sh_info].name); shdr_info[shdr_info[cnt].shdr.sh_info].idx = 1; changes |= shdr_info[cnt].shdr.sh_info < cnt; } /* Mark the section as investigated. */ shdr_info[cnt].idx = 2; } /* if (shdr_info[cnt].idx == 1) */ } /* for (cnt = 1; cnt < shnum; ++cnt) */ } while (changes); } else { INFO("Not stripping sections.\n"); /* Mark the SHT_NULL section as handled. */ shdr_info[0].idx = 2; } /* Mark the section header string table as unused, we will create a new one as the very last section in the new ELF file. */ shdr_info[shstrndx].idx = rebuild_shstrtab ? 0 : 2; /* We need a string table for the section headers. */ FAILIF_LIBELF((shst = ebl_strtabinit (1 /* null-terminated */)) == NULL, ebl_strtabinit); /* Assign new section numbers. */ INFO("Creating new sections...\n"); //shdr_info[0].idx = 0; for (cnt = idx = 1; cnt < shnum; ++cnt) { if (shdr_info[cnt].idx > 0) { shdr_info[cnt].idx = idx++; /* Create a new section. */ FAILIF_LIBELF((shdr_info[cnt].newscn = elf_newscn(newelf)) == NULL, elf_newscn); ASSERT(elf_ndxscn (shdr_info[cnt].newscn) == shdr_info[cnt].idx); /* Add this name to the section header string table. */ shdr_info[cnt].se = ebl_strtabadd (shst, shdr_info[cnt].name, 0); INFO("\tsection [%s] (old offset %lld, old size %lld) will have index %d " "(was %zd).\n", shdr_info[cnt].name, shdr_info[cnt].old_shdr.sh_offset, shdr_info[cnt].old_shdr.sh_size, shdr_info[cnt].idx, elf_ndxscn(shdr_info[cnt].scn)); } else { INFO("\tIgnoring section [%s] (offset %lld, size %lld, index %zd), " "it will be discarded.\n", shdr_info[cnt].name, shdr_info[cnt].shdr.sh_offset, shdr_info[cnt].shdr.sh_size, elf_ndxscn(shdr_info[cnt].scn)); } } /* for */ sections_dropped_or_rearranged = idx != cnt; Elf_Data *shstrtab_data = NULL; #if 0 /* Fail if sections are being dropped or rearranged (except for moving shstrtab) or the symbol filter is not empty, AND the file is an executable. */ FAILIF(((idx != cnt && !(cnt - idx == 1 && rebuild_shstrtab)) || sym_filter != NULL) && ehdr->e_type != ET_DYN, "You may not rearrange sections or strip symbols on an executable file!\n"); #endif INFO("\n\nADJUSTING ELF FILE\n\n"); adjust_elf(elf, elf_name, newelf, newelf_name, ebl, ehdr, /* store ELF header of original library */ sym_filter, num_symbols, shdr_info, shdr_info_len, phdr_info, idx, /* highest_scn_num */ shnum, shstrndx, shst, sections_dropped_or_rearranged, dynamic_idx, /* index in shdr_info[] of .dynamic section */ dynsym_idx, /* index in shdr_info[] of dynamic symbol table */ shady, &shstrtab_data, ehdr->e_type == ET_DYN, /* adjust section ofsets only when the file is a shared library */ rebuild_shstrtab); /* We have everything from the old file. */ FAILIF_LIBELF(elf_cntl(elf, ELF_C_FDDONE) != 0, elf_cntl); /* The ELF library better follows our layout when this is not a relocatable object file. */ elf_flagelf (newelf, ELF_C_SET, (ehdr->e_type != ET_REL ? ELF_F_LAYOUT : 0)); /* Finally write the file. */ FAILIF_LIBELF(!dry_run && elf_update(newelf, ELF_C_WRITE) == -1, elf_update); if (shdr_info != NULL) { /* For some sections we might have created an table to map symbol table indices. */ for (cnt = 1; cnt < shdr_info_len; ++cnt) { FREEIF(shdr_info[cnt].newsymidx); FREEIF(shdr_info[cnt].symse); if(shdr_info[cnt].dynsymst != NULL) ebl_strtabfree (shdr_info[cnt].dynsymst); } /* Free the memory. */ FREE (shdr_info); } FREEIF(phdr_info); ebl_closebackend(ebl); /* Free other resources. */ if (shst != NULL) ebl_strtabfree (shst); if (shstrtab_data != NULL) FREEIF(shstrtab_data->d_buf); }
/** * Load an abs file into DSP memory and get a reference to the entry point. * The entry point can only e considered valid if loading succeeds. You can later * use the obtained entry point to start program execution. * * Make sure you have set up any abs filters you need BEFORE calling this function. * * For a reasonable set of default flags use ABSLOAD_FLAG_DEFAULT. This should be * good for 99% of cases. * * Normally, you can only load code when the core is not running (cold or idle). * However, in some weird cases you may want to override this check. This can be * done via ABSLOAD_FLAG_FORCE. Just don't shoot yourself in the knee. * * @param h device handle * @param path file path * @param ep a pointer to uint32_t, will be used to store entry point if loading succeeds. * @param flags one or more ABSLOAD_FLAG_* * @return */ int easynmc_load_abs(struct easynmc_handle *h, const char *path, uint32_t* ep, int flags) { int i; char *id; GElf_Ehdr ehdr; GElf_Shdr shdr; Elf *elf; int fd; Elf_Scn *scn = NULL; const char *state = easynmc_state_name(easynmc_core_state(h)); if (!(flags & ABSLOAD_FLAG_FORCE)) if ((easynmc_core_state(h) == EASYNMC_CORE_RUNNING) || (easynmc_core_state(h) == EASYNMC_CORE_KILLABLE) || (easynmc_core_state(h) == EASYNMC_CORE_INVALID)) { err("ERROR: Attempt to load abs when core is '%s'\n", state); err("ERROR: Will not do that unless --force'd\n"); return 1; } FILE* rfd = fopen(path, "rb"); if (NULL==rfd) { perror("fopen"); return -1; } if ((fd = open(path, O_RDONLY)) == -1) { perror("open"); goto errfclose; } if(elf_version(EV_CURRENT) == EV_NONE) { err("WARNING: Elf Library is out of date!\n"); } if ((elf = elf_begin(fd, ELF_C_READ , NULL)) == NULL) { err("elf_begin() failed: %s.", elf_errmsg(-1)); goto errclose; } if (gelf_getehdr(elf, &ehdr) == NULL) { err( "getehdr() failed: %s.", elf_errmsg(-1)); goto errclose; } if ((i = gelf_getclass(elf)) == ELFCLASSNONE) { err( "getclass() failed: %s.", elf_errmsg(-1)); goto errclose; } dbg("ELF Looks like a %d-bit ELF object\n", i == ELFCLASS32 ? 32 : 64); if ((id = elf_getident(elf, NULL)) == NULL) { err( "getident() failed: %s.", elf_errmsg(-1)); goto errclose; } dbg("ELF Machine id: 0x%x\n", ehdr.e_machine); *ep = ehdr.e_entry; elf = elf_begin(fd, ELF_C_READ , NULL); h->argoffset = 0; while((scn = elf_nextscn(elf, scn)) != 0) { char* why_skip = NULL; gelf_getshdr(scn, &shdr); char* name = elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name); int addr = shdr.sh_addr << 2; if (shdr.sh_size == 0) /* Skip empty sections */ why_skip = "(empty section)"; if (0==strcmp(name,".memBankMap")) { why_skip = "(not needed)"; } if (0==strcmp(name,".shstrtab")) { why_skip = "(not needed)"; } if (shdr.sh_type == SHT_NOBITS) { why_skip = "(nobits)"; memset(&h->imem[addr], 0x0, shdr.sh_size); } if (0==strcmp(name,".bss")) { why_skip = "(cleansing)"; memset(&h->imem[addr], 0x0, shdr.sh_size); } dbg("%s section %s %s %ld bytes @ 0x%x\n", why_skip ? "Skipping" : "Uploading", name, why_skip ? why_skip : "", (unsigned long) shdr.sh_size, addr ); size_t ret; if (!why_skip) { ret = fseek(rfd, shdr.sh_offset, SEEK_SET); if (ret !=0 ) { err("Seek failed, bad elf\n"); goto errclose; } dbg("read to %d size %ld\n", addr, (unsigned long) shdr.sh_size); ret = fread(&h->imem[addr], 1, shdr.sh_size, rfd); if (ret != shdr.sh_size ) { err("Ooops, failed to read all data: want %ld got %zd\n", (unsigned long) shdr.sh_size, ret); perror("fread"); goto errclose; } } /* Now call the section filter chain */ struct easynmc_section_filter *f = h->sfilters; int handled = 0; while (!handled && f) { dbg("Aplying section filter %s\n", f->name); handled = f->handle_section(h, name, rfd, shdr); f = f->next; } } close(fd); fclose(rfd); dbg("Elvish loading done!\n"); return 0; errclose: close(fd); errfclose: fclose(rfd); return -1; }
/* Try to find a dynamic symbol table via phdrs. */ static void find_dynsym (Dwfl_Module *mod) { GElf_Ehdr ehdr_mem; GElf_Ehdr *ehdr = gelf_getehdr (mod->main.elf, &ehdr_mem); for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i) { GElf_Phdr phdr_mem; GElf_Phdr *phdr = gelf_getphdr (mod->main.elf, i, &phdr_mem); if (phdr == NULL) break; if (phdr->p_type == PT_DYNAMIC) { /* Examine the dynamic section for the pointers we need. */ Elf_Data *data = elf_getdata_rawchunk (mod->main.elf, phdr->p_offset, phdr->p_filesz, ELF_T_DYN); if (data == NULL) continue; enum { i_symtab, i_strtab, i_hash, i_gnu_hash, i_max }; GElf_Addr addrs[i_max] = { 0, }; GElf_Xword strsz = 0; size_t n = data->d_size / gelf_fsize (mod->main.elf, ELF_T_DYN, 1, EV_CURRENT); for (size_t j = 0; j < n; ++j) { GElf_Dyn dyn_mem; GElf_Dyn *dyn = gelf_getdyn (data, j, &dyn_mem); if (dyn != NULL) switch (dyn->d_tag) { case DT_SYMTAB: addrs[i_symtab] = dyn->d_un.d_ptr; continue; case DT_HASH: addrs[i_hash] = dyn->d_un.d_ptr; continue; case DT_GNU_HASH: addrs[i_gnu_hash] = dyn->d_un.d_ptr; continue; case DT_STRTAB: addrs[i_strtab] = dyn->d_un.d_ptr; continue; case DT_STRSZ: strsz = dyn->d_un.d_val; continue; default: continue; case DT_NULL: break; } break; } /* Translate pointers into file offsets. */ GElf_Off offs[i_max] = { 0, }; find_offsets (mod->main.elf, ehdr, i_max, addrs, offs); /* Figure out the size of the symbol table. */ if (offs[i_hash] != 0) { /* In the original format, .hash says the size of .dynsym. */ size_t entsz = SH_ENTSIZE_HASH (ehdr); data = elf_getdata_rawchunk (mod->main.elf, offs[i_hash] + entsz, entsz, entsz == 4 ? ELF_T_WORD : ELF_T_XWORD); if (data != NULL) mod->syments = (entsz == 4 ? *(const GElf_Word *) data->d_buf : *(const GElf_Xword *) data->d_buf); } if (offs[i_gnu_hash] != 0 && mod->syments == 0) { /* In the new format, we can derive it with some work. */ const struct { Elf32_Word nbuckets; Elf32_Word symndx; Elf32_Word maskwords; Elf32_Word shift2; } *header; data = elf_getdata_rawchunk (mod->main.elf, offs[i_gnu_hash], sizeof *header, ELF_T_WORD); if (data != NULL) { header = data->d_buf; Elf32_Word nbuckets = header->nbuckets; Elf32_Word symndx = header->symndx; GElf_Off buckets_at = (offs[i_gnu_hash] + sizeof *header + (gelf_getclass (mod->main.elf) * sizeof (Elf32_Word) * header->maskwords)); data = elf_getdata_rawchunk (mod->main.elf, buckets_at, nbuckets * sizeof (Elf32_Word), ELF_T_WORD); if (data != NULL && symndx < nbuckets) { const Elf32_Word *const buckets = data->d_buf; Elf32_Word maxndx = symndx; for (Elf32_Word bucket = 0; bucket < nbuckets; ++bucket) if (buckets[bucket] > maxndx) maxndx = buckets[bucket]; GElf_Off hasharr_at = (buckets_at + nbuckets * sizeof (Elf32_Word)); hasharr_at += (maxndx - symndx) * sizeof (Elf32_Word); do { data = elf_getdata_rawchunk (mod->main.elf, hasharr_at, sizeof (Elf32_Word), ELF_T_WORD); if (data != NULL && (*(const Elf32_Word *) data->d_buf & 1u)) { mod->syments = maxndx + 1; break; } ++maxndx; hasharr_at += sizeof (Elf32_Word); } while (data != NULL); } } } if (offs[i_strtab] > offs[i_symtab] && mod->syments == 0) mod->syments = ((offs[i_strtab] - offs[i_symtab]) / gelf_fsize (mod->main.elf, ELF_T_SYM, 1, EV_CURRENT)); if (mod->syments > 0) { mod->symdata = elf_getdata_rawchunk (mod->main.elf, offs[i_symtab], gelf_fsize (mod->main.elf, ELF_T_SYM, mod->syments, EV_CURRENT), ELF_T_SYM); if (mod->symdata != NULL) { mod->symstrdata = elf_getdata_rawchunk (mod->main.elf, offs[i_strtab], strsz, ELF_T_BYTE); if (mod->symstrdata == NULL) mod->symdata = NULL; } if (mod->symdata == NULL) mod->symerr = DWFL_E (LIBELF, elf_errno ()); else { mod->symfile = &mod->main; mod->symerr = DWFL_E_NOERROR; } return; } } } }
int analyze_elf_binary(int file_descriptor, unsigned char *file_data) { Elf *elf_handle; GElf_Ehdr elf_executable_header; Elf_Scn *section; GElf_Shdr section_header; char *section_name; int replacements; if (elf_version(EV_CURRENT) == EV_NONE) PRINT_ELF_ERRNO(); if ((elf_handle = elf_begin(file_descriptor, ELF_C_READ, NULL)) == NULL) PRINT_ELF_ERRNO(); if (gelf_getehdr(elf_handle, &elf_executable_header) == NULL) PRINT_ELF_ERRNO(); switch(elf_kind(elf_handle)) { case ELF_K_NUM: case ELF_K_NONE: PRINT_ERROR_MESSAGE("file type unknown", false); break; case ELF_K_COFF: PRINT_ERROR_MESSAGE("COFF binaries not supported", false); break; case ELF_K_AR: PRINT_ERROR_MESSAGE("AR archives not supported", false); break; case ELF_K_ELF: if (options.verbose) { if (gelf_getclass(elf_handle) == ELFCLASS32) printf("Reading 32-bit ELF binary"); else printf("Reading 64-bit ELF binary"); if (options.read_only) printf(" in read-only mode\n"); else printf("\n"); } break; } replacements = 0; section = NULL; while ((section = elf_nextscn(elf_handle, section)) != NULL) { if (gelf_getshdr(section, §ion_header) != §ion_header) PRINT_ELF_ERRNO(); if ((section_name = elf_strptr(elf_handle, elf_executable_header.e_shstrndx, section_header.sh_name)) == NULL) PRINT_ELF_ERRNO(); if (options.verbose && !(section_header.sh_flags & SHF_EXECINSTR)) printf("* Section %s\n", section_name); else if (options.verbose) printf("* Section %s is executable\n", section_name); if (section_header.sh_flags & SHF_EXECINSTR || options.patch_all_sections) { /* Avoid the `.bss' section, it doesn't exist in the binary file. */ if (strcmp(section_name, ".bss")) { replacements += replace_vendor_string(file_data + section_header.sh_offset, section_header.sh_size, (unsigned char *) section_header.sh_addr); } } } PRINT_ELF_ERRNO(); /* If there isn't elf_errno set, nothing will happend. */ elf_end(elf_handle); return replacements; }