int elf_utils_shift_contents(Elf *e, int start_offset, int shift_amount) { GElf_Ehdr ehdr; Elf_Scn *scn; GElf_Shdr shdr; size_t segment_count = 0, segndx; GElf_Phdr phdr; int bottom_section_offset = 0; ELF_ASSERT(gelf_getehdr(e, &ehdr)); if (ehdr.e_shoff >= start_offset) { ehdr.e_shoff += shift_amount; ELF_ASSERT(gelf_update_ehdr(e, &ehdr)); } scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { ELF_ASSERT(gelf_getshdr(scn, &shdr)); if (shdr.sh_offset >= start_offset) { shdr.sh_offset += shift_amount; ELF_ASSERT(gelf_update_shdr(scn, &shdr)); } if (shdr.sh_offset + shdr.sh_size > bottom_section_offset) { bottom_section_offset = shdr.sh_offset + shdr.sh_size; } } if (bottom_section_offset > ehdr.e_shoff) { ELF_ASSERT(gelf_getehdr(e, &ehdr)); ehdr.e_shoff = bottom_section_offset; ELF_ASSERT(gelf_update_ehdr(e, &ehdr)); } /* A bug in libelf means that getphdrnum will report failure in a new file. * However, it will still set segment_count, so we'll use it. */ ELF_ASSERT((elf_getphdrnum(e, &segment_count), segment_count > 0)); for (segndx = 0; segndx < segment_count; segndx++) { ELF_ASSERT(gelf_getphdr(e, segndx, &phdr)); if (phdr.p_offset >= start_offset) { phdr.p_offset += shift_amount; ELF_ASSERT(gelf_update_phdr(e, segndx, &phdr)); } } return 1; failure: return 0; }
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; }
static AsmCtx_t * prepare_binary_output (AsmCtx_t *result, int machine, int klass, int data) { GElf_Ehdr *ehdr; GElf_Ehdr ehdr_mem; /* Create the ELF descriptor for the file. */ result->out.elf = elf_begin (result->fd, ELF_C_WRITE_MMAP, NULL); if (result->out.elf == NULL) { err_libelf: unlink (result->tmp_fname); close (result->fd); free (result); __libasm_seterrno (ASM_E_LIBELF); return NULL; } /* Create the ELF header for the output file. */ if (gelf_newehdr (result->out.elf, klass) == 0) goto err_libelf; ehdr = gelf_getehdr (result->out.elf, &ehdr_mem); /* If this failed we are in trouble. */ assert (ehdr != NULL); /* We create an object file. */ ehdr->e_type = ET_REL; /* Set the ELF version. */ ehdr->e_version = EV_CURRENT; /* Use the machine value the user provided. */ ehdr->e_machine = machine; /* Same for the class and endianness. */ ehdr->e_ident[EI_CLASS] = klass; ehdr->e_ident[EI_DATA] = data; memcpy (&ehdr->e_ident[EI_MAG0], ELFMAG, SELFMAG); /* Write the ELF header information back. */ (void) gelf_update_ehdr (result->out.elf, ehdr); /* No section so far. */ result->section_list = NULL; /* Initialize the hash table. */ asm_symbol_tab_init (&result->symbol_tab, 67); result->nsymbol_tab = 0; /* And the string tables. */ result->section_strtab = ebl_strtabinit (true); result->symbol_strtab = ebl_strtabinit (true); /* We have no section groups so far. */ result->groups = NULL; result->ngroups = 0; return result; }
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 write_ehdr(elf_data_t *elf, char **err) { off_t off; size_t n, ehdr_size; void *ehdr_buf; if(!gelf_update_ehdr(elf->e, &elf->ehdr)) { (*err) = "failed to update executable header"; return -1; } if(elf->bits == 32) { ehdr_buf = elf32_getehdr(elf->e); ehdr_size = sizeof(Elf32_Ehdr); } else { ehdr_buf = elf64_getehdr(elf->e); ehdr_size = sizeof(Elf64_Ehdr); } if(!ehdr_buf) { (*err) = "failed to get executable header"; return -1; } off = lseek(elf->fd, 0, SEEK_SET); if(off < 0) { (*err) = "lseek failed"; return -1; } n = write(elf->fd, ehdr_buf, ehdr_size); if(n != ehdr_size) { (*err) = "write failed"; return -1; } 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); }
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; }
int main(int argc, const char *argv[]) { ssize_t bytes_written; // only used to silence compiler warnings if (argc < 2) errx(EX_USAGE, "Usage: PatchEntry <binary>"); if (elf_version(EV_CURRENT) == EV_NONE) errx(EX_SOFTWARE, "Invalid ELF version: %s", elf_errmsg(-1)); int fd = open(argv[1], O_RDWR); if (fd == -1) err(EX_NOINPUT, "open() failed"); Elf *e; e = elf_begin(fd, ELF_C_RDWR, NULL); if (e == nullptr) errx(EX_SOFTWARE, "Failed to read from ELF file: %s", elf_errmsg(-1)); if (elf_kind(e) != ELF_K_ELF) errx(EX_SOFTWARE, "File is not an ELF object"); // Read the ELF header to find the entry point // FIXME: check the class/architecture, make sure it's the same as ours GElf_Ehdr elf_hdr; if (gelf_getehdr(e, &elf_hdr) == NULL) errx(EX_SOFTWARE, "Failed to read ELF header: %s", elf_errmsg(-1)); auto dt_init = FindDynamicInit(e); auto dt_init_ofs = 0; if (dt_init) { dt_init_ofs = dt_init->info.second.sh_offset + dt_init->data->d_off + dt_init->index * sizeof(Elf_Dyn) + offsetof(Elf_Dyn, d_un); } auto pit_sym = FindSymbol(e, kProgramInfoTableName); auto pit_info = FindSectionDataByAddr(e, pit_sym.st_value); auto pit_ofs = std::get<0>(pit_info).second.sh_offset + // Section file offset std::get<1>(pit_info)->d_off + // Elf_Data offset std::get<2>(pit_info); // Offset relative to Elf_Data #if 1 if (dt_init) printf("Old ELF entry:%p DT_INIT:%p@%x PIT:%p@%lx\n", (void*) elf_hdr.e_entry, (void*) dt_init->dyn_section.d_un.d_ptr, dt_init_ofs, (void*) pit_sym.st_value, pit_ofs); else printf("Old ELF entry:%p DT_INIT:NULL PIT:%p@%lx\n", (void*) elf_hdr.e_entry, (void*) pit_sym.st_value, pit_ofs); #endif // Replace the ProgramInfoTable values auto pit_data = std::get<1>(pit_info); auto orig_pit = reinterpret_cast<TrapProgramInfoTable*>( reinterpret_cast<uint8_t*>(pit_data->d_buf) + std::get<2>(pit_info)); TrapProgramInfoTable pit = *orig_pit; // Need to copy it here, since elf_end releases it if (pit.num_sections != 0) errx(EX_USAGE, "Binary already contains full ProgramInfoTable structure"); if (dt_init) pit.orig_dt_init = dt_init->dyn_section.d_un.d_ptr; else pit.orig_dt_init = 0; pit.orig_entry = elf_hdr.e_entry; // Find executable sections in the binary (.text/.plt/others) // then copy them over to the PIT pit.num_sections = TRAP_NUM_SECTIONS; for (size_t i = 0; i < TRAP_NUM_SECTIONS; i++) { auto sec_info = FindSectionByName(e, kExecSections[i][0]); if (sec_info.first != nullptr) { pit.sections[i].start = sec_info.second.sh_addr; pit.sections[i].size = sec_info.second.sh_size; } auto trap_sec_info = FindSectionByName(e, kExecSections[i][1]); if (trap_sec_info.first != nullptr) { pit.sections[i].trap = trap_sec_info.second.sh_addr; pit.sections[i].trap_size = trap_sec_info.second.sh_size; } } // Set the new entry point addresses auto entry_trampoline_sym = FindSymbol(e, kEntryTrampolineName); auto init_trampoline_sym = FindSymbol(e, kInitTrampolineName); elf_hdr.e_entry = entry_trampoline_sym.st_value; auto new_dt_init = static_cast<ArchPointer>(init_trampoline_sym.st_value); // Find export trampolines auto xptramp_info = FindSectionByName(e, ".xptramp"); DynamicSymbolMap dyn_sym_map; ExportTrampolineVector xptramp_vec; size_t xptramp_shndx = 0; if (xptramp_info.first != nullptr) { pit.xptramp_start = xptramp_info.second.sh_addr; pit.xptramp_size = xptramp_info.second.sh_size; dyn_sym_map = FindDynamicSymbols(e); xptramp_vec = FindExportTrampolines(e, xptramp_info); xptramp_shndx = elf_ndxscn(xptramp_info.first); } // Update the ELF header, then write it out // There's some pretty ugly behavior from libelf here: // if we let it handle the layout, it mis-aligns the data sections // It also doesn't seem to update the contents correctly, // so we do that manually elf_flagelf(e, ELF_C_SET, ELF_F_LAYOUT); gelf_update_ehdr(e, &elf_hdr); if (elf_update(e, ELF_C_WRITE) <= 0) errx(EX_SOFTWARE, "Couldn't update ELF file: %s", elf_errmsg(-1)); elf_end(e); // FIXME: libelf shenanigans force us to // write data out to the file manually // 1) DT_INIT inside .dynamic if (dt_init) { printf("writing new DT_INIT: 0x%lu\n", new_dt_init); lseek(fd, dt_init_ofs, SEEK_SET); bytes_written = write(fd, &new_dt_init, sizeof(new_dt_init)); } // 2) The ProgramInfoTable lseek(fd, pit_ofs, SEEK_SET); bytes_written = write(fd, &pit, sizeof(pit)); // 3) Exported symbols WriteExportSymbols(fd, xptramp_info, xptramp_vec, xptramp_shndx, dyn_sym_map); close(fd); return 0; }
int main(int argc, char **argv) { GElf_Ehdr ehdr; Elf *elf; Elf_Kind kind; int type = ELFOSABI_NONE; int retval = 0; int ch, change = 0, verbose = 0, force = 0, listed = 0; if (elf_version(EV_CURRENT) == EV_NONE) errx(EXIT_FAILURE, "elf_version error"); while ((ch = getopt_long(argc, argv, "Vf:hlt:v", brandelf_longopts, NULL)) != -1) switch (ch) { case 'f': if (change) errx(EXIT_FAILURE, "ERROR: the -f option is " "incompatible with the -t option."); force = 1; type = atoi(optarg); if (errno == ERANGE || type < 0 || type > 255) { warnx("ERROR: invalid argument to option " "-f: %s", optarg); usage(); } break; case 'h': usage(); break; case 'l': printelftypes(); listed = 1; break; case 'v': verbose = 1; break; case 't': if (force) errx(EXIT_FAILURE, "the -t option is " "incompatible with the -f option."); if ((type = elftype(optarg)) == -1) { warnx("ERROR: invalid ELF type '%s'", optarg); usage(); } change = 1; break; case 'V': printversion(); break; default: usage(); } argc -= optind; argv += optind; if (!argc) { if (listed) exit(0); else { warnx("no file(s) specified"); usage(); } } while (argc) { int fd; elf = NULL; if ((fd = open(argv[0], (change || force) ? O_RDWR : O_RDONLY, 0)) < 0) { warn("error opening file %s", argv[0]); retval = 1; goto fail; } if ((elf = elf_begin(fd, (change || force) ? ELF_C_RDWR : ELF_C_READ, NULL)) == NULL) { warnx("elf_begin failed: %s", elf_errmsg(-1)); retval = 1; goto fail; } if ((kind = elf_kind(elf)) != ELF_K_ELF) { if (kind == ELF_K_AR) warnx("file '%s' is an archive.", argv[0]); else warnx("file '%s' is not an ELF file.", argv[0]); retval = 1; goto fail; } if (gelf_getehdr(elf, &ehdr) == NULL) { warnx("gelf_getehdr: %s", elf_errmsg(-1)); retval = 1; goto fail; } if (!change && !force) { fprintf(stdout, "File '%s' is of brand '%s' (%u).\n", argv[0], iselftype(ehdr.e_ident[EI_OSABI]), ehdr.e_ident[EI_OSABI]); if (!iselftype(type)) { warnx("ELF ABI Brand '%u' is unknown", type); printelftypes(); } } else { /* * Keep the existing layout of the ELF object. */ if (elf_flagelf(elf, ELF_C_SET, ELF_F_LAYOUT) == 0) { warnx("elf_flagelf failed: %s", elf_errmsg(-1)); retval = 1; goto fail; } /* * Update the ABI type. */ ehdr.e_ident[EI_OSABI] = type; if (gelf_update_ehdr(elf, &ehdr) == 0) { warnx("gelf_update_ehdr error: %s", elf_errmsg(-1)); retval = 1; goto fail; } /* * Write back changes. */ if (elf_update(elf, ELF_C_WRITE) == -1) { warnx("elf_update error: %s", elf_errmsg(-1)); retval = 1; goto fail; } } fail: if (elf) elf_end(elf); if (fd >= 0 && close(fd) == -1) { warnx("%s: close error", argv[0]); retval = 1; } argc--; argv++; } return (retval); }