static void add_reloc(Elf *elf) { char name[100]; for (int i = 0; i < LIST_LEN(elf->sections); i++) { Section *sect = LIST_REF(elf->sections, i); if (LIST_LEN(sect->rels) == 0) continue; String *b = make_string(); for (int j = 0; j < LIST_LEN(sect->rels); j++) { Reloc *rel = LIST_REF(sect->rels, j); o8(b, rel->off); if (rel->sym) { o8(b, ELF64_R_INFO(find_symbol(elf, rel->sym)->index, rel->type)); } else { o8(b, ELF64_R_INFO(rel->section->symindex, rel->type)); } o8(b, rel->addend); } strcpy(name, ".rela"); strcpy(name + 5, sect->name); Section *relsec = make_section(name, SHT_RELA); relsec->link = elf->symtabnum; relsec->info = i + 1; relsec->body = b; relsec->entsize = 24; relsec->align = 4; add_section(elf, relsec); } }
int elfrw_read_Rela(FILE *fp, Elf64_Rela *in) { int r; if (is64bit_form()) { r = fread(in, sizeof *in, 1, fp); if (r == 1) { if (!native_form()) { revinplc_64xword(&in->r_offset); revinplc_64xword(&in->r_info); revinplc_64xword(&in->r_addend); } } } else { Elf32_Rela in32; r = fread(&in32, sizeof in32, 1, fp); if (r == 1) { if (native_form()) { in->r_offset = in32.r_offset; in->r_info = ELF64_R_INFO(ELF32_R_SYM(in32.r_info), ELF32_R_TYPE(in32.r_info)); in->r_addend = in32.r_addend; } else { in->r_offset = rev_32word(in32.r_offset); revinplc_32word(&in32.r_info); in->r_info = ELF64_R_INFO(ELF32_R_SYM(in32.r_info), ELF32_R_TYPE(in32.r_info)); in->r_addend = rev_32word(in32.r_addend); } } } return r; }
void Elf64Output::sort_symtab() { // With ELF, the local symbols must come first in the symbol table. // Annoying, but true. As a result, this function is necessary to // reorder the symbols with the local ones first. std::vector<Elf64_Sym> symtmp; std::vector<size_t> redirect(sym_.size()); // Mapping from old => new position of the symbol in the symbol table. for (int i = 0; i < sym_.size(); ++i) { if (ELF64_ST_BIND(sym_[i].st_info) == STB_LOCAL) { redirect[i] = symtmp.size(); symtmp.push_back(sym_[i]); } } local_syms_ = symtmp.size(); for (int i = 0; i < sym_.size(); ++i) { if (ELF64_ST_BIND(sym_[i].st_info) == STB_GLOBAL) { redirect[i] = symtmp.size(); symtmp.push_back(sym_[i]); } } for (int i = 0; i < text_reloc_.size(); ++i) { Elf64_Word type = ELF64_R_TYPE(text_reloc_[i].r_info); Elf64_Word sym = redirect[ELF64_R_SYM(text_reloc_[i].r_info)]; text_reloc_[i].r_info = ELF64_R_INFO(sym, type); } for (int i = 0; i < data_reloc_.size(); ++i) { Elf64_Word type = ELF64_R_TYPE(data_reloc_[i].r_info); Elf64_Word sym = redirect[ELF64_R_SYM(data_reloc_[i].r_info)]; assert(sym<sym_.size()); data_reloc_[i].r_info = ELF64_R_INFO(sym, type); } sym_ = symtmp; }
GElf_Rel * gelf_getrel(Elf_Data *ed, int ndx, GElf_Rel *dst) { int ec; Elf *e; size_t msz; Elf_Scn *scn; uint32_t sh_type; Elf32_Rel *rel32; Elf64_Rel *rel64; struct _Libelf_Data *d; d = (struct _Libelf_Data *) ed; if (d == NULL || ndx < 0 || dst == NULL || (scn = d->d_scn) == NULL || (e = scn->s_elf) == NULL) { LIBELF_SET_ERROR(ARGUMENT, 0); return (NULL); } ec = e->e_class; assert(ec == ELFCLASS32 || ec == ELFCLASS64); if (ec == ELFCLASS32) sh_type = scn->s_shdr.s_shdr32.sh_type; else sh_type = scn->s_shdr.s_shdr64.sh_type; if (_libelf_xlate_shtype(sh_type) != ELF_T_REL) { LIBELF_SET_ERROR(ARGUMENT, 0); return (NULL); } msz = _libelf_msize(ELF_T_REL, ec, e->e_version); assert(msz > 0); if (msz * ndx >= d->d_data.d_size) { LIBELF_SET_ERROR(ARGUMENT, 0); return (NULL); } if (ec == ELFCLASS32) { rel32 = (Elf32_Rel *) d->d_data.d_buf + ndx; dst->r_offset = (Elf64_Addr) rel32->r_offset; dst->r_info = ELF64_R_INFO( (Elf64_Xword) ELF32_R_SYM(rel32->r_info), ELF32_R_TYPE(rel32->r_info)); } else { rel64 = (Elf64_Rel *) d->d_data.d_buf + ndx; *dst = *rel64; } return (dst); }
void Elf64Output::ref(String* label, RelocType rtype) { // Refer to a symbol by name at the current text/data output location. // This function adds an entry to the relocation table. The size can be // either 4 or 8 bytes. A 4-byte entry indicates the instruction uses // RIP-relative addressing for x64. std::map<String::Ptr,size_t>::iterator i = symbol_.find(label); size_t symnum = 0; if (i == symbol_.end()) { symbol_.insert(std::make_pair(label, sym_.size())); sym_.resize(sym_.size()+1); Elf64_Sym* const sym = &sym_.back(); sym->st_name = string_->bytes(); sym->st_info = ELF64_ST_INFO(STB_GLOBAL, STT_NOTYPE); sym->st_other = 0; sym->st_shndx = SHN_UNDEF; sym->st_value = 0; sym->st_size = 0; // Unknown size string_->buffer(label->string().c_str(), label->string().size()+1); symnum = sym_.size()-1; } else { symnum = i->second; } Elf64_Rela reloc; if (REF_TEXT == rtype) { reloc.r_offset = text_->bytes(); reloc.r_info = ELF64_R_INFO(symnum, R_X86_64_64); reloc.r_addend = 0; text_reloc_.push_back(reloc); } else if (REF_DATA == rtype || REF_VTABLE == rtype) { reloc.r_offset = data_->bytes(); reloc.r_info = ELF64_R_INFO(symnum, R_X86_64_64); reloc.r_addend = 0; data_reloc_.push_back(reloc); } else if (REF_BRANCH == rtype || REF_CALL == rtype) { reloc.r_offset = text_->bytes(); reloc.r_info = ELF64_R_INFO(symnum, R_X86_64_PC32); reloc.r_addend = -sizeof(uint32_t); text_reloc_.push_back(reloc); } else if (REF_SIGNED == rtype) { reloc.r_offset = text_->bytes(); reloc.r_info = ELF64_R_INFO(symnum, R_X86_64_PC32); reloc.r_addend = -sizeof(uint32_t); text_reloc_.push_back(reloc); } }
void add_entry( Elf64_Addr offset, Elf_Word symbol, unsigned char type, Elf_Sxword addend ) { Elf_Xword info; if ( elf_file.get_class() == ELFCLASS32 ) { info = ELF32_R_INFO( symbol, type ); } else { info = ELF64_R_INFO( symbol, type ); } add_entry( offset, info, addend ); }
static void addRel64(elfull o,elfull a,elfull i,elfull r) { if (RELA) { struct Rela64Node *rn = mymalloc(sizeof(struct Rela64Node)); setval(be,rn->r.r_offset,8,o); setval(be,rn->r.r_addend,8,a); setval(be,rn->r.r_info,8,ELF64_R_INFO(i,r)); addtail(&relalist,&(rn->n)); } else { struct Rel64Node *rn = mymalloc(sizeof(struct Rel64Node)); setval(be,rn->r.r_offset,8,o); setval(be,rn->r.r_info,8,ELF64_R_INFO(i,r)); addtail(&relalist,&(rn->n)); } }
static void addgotsym(Sym *s) { Sym *got, *rela; if(s->got >= 0) return; adddynsym(s); got = lookup(".got", 0); s->got = got->size; adduint64(got, 0); if(iself) { rela = lookup(".rela", 0); addaddrplus(rela, got, s->got); adduint64(rela, ELF64_R_INFO(s->dynid, R_X86_64_GLOB_DAT)); adduint64(rela, 0); } else if(HEADTYPE == Hdarwin) { adduint32(lookup(".linkedit.got", 0), s->dynid); } else { diag("addgotsym: unsupported binary format"); } }
static void addpltsym(Sym *s) { if(s->plt >= 0) return; adddynsym(s); if(iself) { Sym *plt, *got, *rela; plt = lookup(".plt", 0); got = lookup(".got.plt", 0); rela = lookup(".rela.plt", 0); if(plt->size == 0) elfsetupplt(); // jmpq *got+size(IP) adduint8(plt, 0xff); adduint8(plt, 0x25); addpcrelplus(plt, got, got->size); // add to got: pointer to current pos in plt addaddrplus(got, plt, plt->size); // pushq $x adduint8(plt, 0x68); adduint32(plt, (got->size-24-8)/8); // jmpq .plt adduint8(plt, 0xe9); adduint32(plt, -(plt->size+4)); // rela addaddrplus(rela, got, got->size-8); adduint64(rela, ELF64_R_INFO(s->dynid, R_X86_64_JMP_SLOT)); adduint64(rela, 0); s->plt = plt->size - 16; } else if(HEADTYPE == Hdarwin) { // To do lazy symbol lookup right, we're supposed // to tell the dynamic loader which library each // symbol comes from and format the link info // section just so. I'm too lazy (ha!) to do that // so for now we'll just use non-lazy pointers, // which don't need to be told which library to use. // // http://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html // has details about what we're avoiding. Sym *plt; addgotsym(s); plt = lookup(".plt", 0); adduint32(lookup(".linkedit.plt", 0), s->dynid); // jmpq *got+size(IP) s->plt = plt->size; adduint8(plt, 0xff); adduint8(plt, 0x25); addpcrelplus(plt, lookup(".got", 0), s->got); } else { diag("addpltsym: unsupported binary format"); } }
void adddynrel(Sym *s, Reloc *r) { Sym *targ, *rela, *got; targ = r->sym; cursym = s; switch(r->type) { default: if(r->type >= 256) { diag("unexpected relocation type %d", r->type); return; } break; // Handle relocations found in ELF object files. case 256 + R_X86_64_PC32: if(targ->dynimpname != nil && !targ->dynexport) diag("unexpected R_X86_64_PC32 relocation for dynamic symbol %s", targ->name); if(targ->type == 0 || targ->type == SXREF) diag("unknown symbol %s in pcrel", targ->name); r->type = D_PCREL; r->add += 4; return; case 256 + R_X86_64_PLT32: r->type = D_PCREL; r->add += 4; if(targ->dynimpname != nil && !targ->dynexport) { addpltsym(targ); r->sym = lookup(".plt", 0); r->add += targ->plt; } return; case 256 + R_X86_64_GOTPCREL: if(targ->dynimpname == nil || targ->dynexport) { // have symbol if(r->off >= 2 && s->p[r->off-2] == 0x8b) { // turn MOVQ of GOT entry into LEAQ of symbol itself s->p[r->off-2] = 0x8d; r->type = D_PCREL; r->add += 4; return; } // fall back to using GOT and hope for the best (CMOV*) // TODO: just needs relocation, no need to put in .dynsym targ->dynimpname = targ->name; } addgotsym(targ); r->type = D_PCREL; r->sym = lookup(".got", 0); r->add += 4; r->add += targ->got; return; case 256 + R_X86_64_64: if(targ->dynimpname != nil && !targ->dynexport) diag("unexpected R_X86_64_64 relocation for dynamic symbol %s", targ->name); r->type = D_ADDR; return; // Handle relocations found in Mach-O object files. case 512 + MACHO_X86_64_RELOC_UNSIGNED*2 + 0: case 512 + MACHO_X86_64_RELOC_SIGNED*2 + 0: case 512 + MACHO_X86_64_RELOC_BRANCH*2 + 0: // TODO: What is the difference between all these? r->type = D_ADDR; if(targ->dynimpname != nil && !targ->dynexport) diag("unexpected reloc for dynamic symbol %s", targ->name); return; case 512 + MACHO_X86_64_RELOC_BRANCH*2 + 1: if(targ->dynimpname != nil && !targ->dynexport) { addpltsym(targ); r->sym = lookup(".plt", 0); r->add = targ->plt; r->type = D_PCREL; return; } // fall through case 512 + MACHO_X86_64_RELOC_UNSIGNED*2 + 1: case 512 + MACHO_X86_64_RELOC_SIGNED*2 + 1: case 512 + MACHO_X86_64_RELOC_SIGNED_1*2 + 1: case 512 + MACHO_X86_64_RELOC_SIGNED_2*2 + 1: case 512 + MACHO_X86_64_RELOC_SIGNED_4*2 + 1: r->type = D_PCREL; if(targ->dynimpname != nil && !targ->dynexport) diag("unexpected pc-relative reloc for dynamic symbol %s", targ->name); return; case 512 + MACHO_X86_64_RELOC_GOT_LOAD*2 + 1: if(targ->dynimpname == nil || targ->dynexport) { // have symbol // turn MOVQ of GOT entry into LEAQ of symbol itself if(r->off < 2 || s->p[r->off-2] != 0x8b) { diag("unexpected GOT_LOAD reloc for non-dynamic symbol %s", targ->name); return; } s->p[r->off-2] = 0x8d; r->type = D_PCREL; return; } // fall through case 512 + MACHO_X86_64_RELOC_GOT*2 + 1: if(targ->dynimpname == nil || targ->dynexport) diag("unexpected GOT reloc for non-dynamic symbol %s", targ->name); addgotsym(targ); r->type = D_PCREL; r->sym = lookup(".got", 0); r->add += targ->got; return; } // Handle references to ELF symbols from our own object files. if(targ->dynimpname == nil || targ->dynexport) return; switch(r->type) { case D_PCREL: addpltsym(targ); r->sym = lookup(".plt", 0); r->add = targ->plt; return; case D_ADDR: if(s->type != SDATA) break; if(iself) { adddynsym(targ); rela = lookup(".rela", 0); addaddrplus(rela, s, r->off); if(r->siz == 8) adduint64(rela, ELF64_R_INFO(targ->dynid, R_X86_64_64)); else adduint64(rela, ELF64_R_INFO(targ->dynid, R_X86_64_32)); adduint64(rela, r->add); r->type = 256; // ignore during relocsym return; } if(HEADTYPE == Hdarwin && s->size == PtrSize && r->off == 0) { // Mach-O relocations are a royal pain to lay out. // They use a compact stateful bytecode representation // that is too much bother to deal with. // Instead, interpret the C declaration // void *_Cvar_stderr = &stderr; // as making _Cvar_stderr the name of a GOT entry // for stderr. This is separate from the usual GOT entry, // just in case the C code assigns to the variable, // and of course it only works for single pointers, // but we only need to support cgo and that's all it needs. adddynsym(targ); got = lookup(".got", 0); s->type = got->type | SSUB; s->outer = got; s->sub = got->sub; got->sub = s; s->value = got->size; adduint64(got, 0); adduint32(lookup(".linkedit.got", 0), targ->dynid); r->type = 256; // ignore during relocsym return; } break; } cursym = s; diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); }
static int reloc_read(const struct buffer *in, struct parsed_elf *pelf, struct xdr *xdr, int bit64) { struct buffer b; Elf64_Word i; Elf64_Ehdr *ehdr; ehdr = &pelf->ehdr; pelf->relocs = calloc(ehdr->e_shnum, sizeof(Elf64_Rela *)); /* Allocate array for each section that contains relocation entries. */ for (i = 0; i < ehdr->e_shnum; i++) { Elf64_Shdr *shdr; Elf64_Rela *rela; Elf64_Xword j; Elf64_Xword nrelocs; int is_rela; shdr = &pelf->shdr[i]; /* Only process REL and RELA sections. */ if (shdr->sh_type != SHT_REL && shdr->sh_type != SHT_RELA) continue; DEBUG("Checking relocation section %u\n", i); /* Ensure the section that relocations apply is a valid. */ if (shdr->sh_info >= ehdr->e_shnum || shdr->sh_info == SHN_UNDEF) { ERROR("Relocations apply to an invalid section: %u\n", shdr[i].sh_info); return -1; } is_rela = shdr->sh_type == SHT_RELA; /* Determine the number relocations in this section. */ nrelocs = shdr->sh_size / shdr->sh_entsize; pelf->relocs[i] = calloc(nrelocs, sizeof(Elf64_Rela)); buffer_splice(&b, in, shdr->sh_offset, shdr->sh_size); if (check_size(in, shdr->sh_offset, buffer_size(&b), "relocation section")) { ERROR("Relocation section %u failed.\n", i); return -1; } rela = pelf->relocs[i]; for (j = 0; j < nrelocs; j++) { if (bit64) { rela->r_offset = xdr->get64(&b); rela->r_info = xdr->get64(&b); if (is_rela) rela->r_addend = xdr->get64(&b); } else { uint32_t r_info; rela->r_offset = xdr->get32(&b); r_info = xdr->get32(&b); rela->r_info = ELF64_R_INFO(ELF32_R_SYM(r_info), ELF32_R_TYPE(r_info)); if (is_rela) rela->r_addend = xdr->get32(&b); } rela++; } } return 0; }
void RelocationAddend64::setSymbolInfo(uint32_t sym){ entry.r_info = ELF64_R_INFO(sym, getType()); }