SgAsmElfRelocSection * SgAsmElfRelocSection::parse() { SgAsmElfSection::parse(); SgAsmElfFileHeader *fhdr = get_elf_header(); ROSE_ASSERT(fhdr); size_t entry_size, struct_size, extra_size, nentries; calculate_sizes(&entry_size, &struct_size, &extra_size, &nentries); ROSE_ASSERT(extra_size==0); /* Parse each entry */ for (size_t i=0; i<nentries; i++) { SgAsmElfRelocEntry *entry = 0; if (4==fhdr->get_word_size()) { if (p_uses_addend) { SgAsmElfRelocEntry::Elf32RelaEntry_disk disk; read_content_local(i*entry_size, &disk, struct_size); entry = new SgAsmElfRelocEntry(this); entry->parse(fhdr->get_sex(), &disk); } else { SgAsmElfRelocEntry::Elf32RelEntry_disk disk; read_content_local(i*entry_size, &disk, struct_size); entry = new SgAsmElfRelocEntry(this); entry->parse(fhdr->get_sex(), &disk); } } else if (8==fhdr->get_word_size()) { if (p_uses_addend) { SgAsmElfRelocEntry::Elf64RelaEntry_disk disk; read_content_local(i*entry_size, &disk, struct_size); entry = new SgAsmElfRelocEntry(this); entry->parse(fhdr->get_sex(), &disk); } else { SgAsmElfRelocEntry::Elf64RelEntry_disk disk; read_content_local(i*entry_size, &disk, struct_size); entry = new SgAsmElfRelocEntry(this); entry->parse(fhdr->get_sex(), &disk); } } else { throw FormatError("unsupported ELF word size"); } if (extra_size>0) entry->get_extra() = read_content_local_ucl(i*entry_size+struct_size, extra_size); } return this; }
/** Write a note at the specified offset to the section containing the note. Returns the offset for the first byte past the end * of the note. */ rose_addr_t SgAsmElfNoteEntry::unparse(std::ostream &f, rose_addr_t at) { /* Find the section holding this note */ SgAsmElfNoteSection *notes = SageInterface::getEnclosingNode<SgAsmElfNoteSection>(this); ROSE_ASSERT(notes!=NULL); ROSE_ASSERT(at < notes->get_size()); SgAsmElfFileHeader *fhdr = dynamic_cast<SgAsmElfFileHeader*>(notes->get_header()); ROSE_ASSERT(fhdr!=NULL); /* Name size, including NUL termination */ uint32_t u32; host_to_disk(fhdr->get_sex(), p_name->get_string().size()+1, &u32); notes->write(f, at, 4, &u32); at += 4; /* Payload size */ host_to_disk(fhdr->get_sex(), p_payload.size(), &u32); notes->write(f, at, 4, &u32); at += 4; /* Type */ host_to_disk(fhdr->get_sex(), p_type, &u32); notes->write(f, at, 4, &u32); at += 4; /* Name with NUL termination and padded to a multiple of four bytes */ std::string name = p_name->get_string(); while ((name.size()+1) % 4) name += '\0'; notes->write(f, at, name.size()+1, name.c_str()); at += name.size()+1; /* Payload */ notes->write(f, at, p_payload); at += p_payload.size(); return at; }
/** Initialize a note by parsing it from the specified location in the note section. Return value is the offset to the * beginning of the next note. */ rose_addr_t SgAsmElfNoteEntry::parse(rose_addr_t at) { /* Find the section holding this note */ SgAsmElfNoteSection *notes = SageInterface::getEnclosingNode<SgAsmElfNoteSection>(this); ROSE_ASSERT(notes!=NULL); ROSE_ASSERT(at < notes->get_size()); SgAsmElfFileHeader *fhdr = dynamic_cast<SgAsmElfFileHeader*>(notes->get_header()); ROSE_ASSERT(fhdr!=NULL); /* Length of note entry name, including NUL termination */ uint32_t u32; notes->read_content_local(at, &u32, 4); size_t name_size = disk_to_host(fhdr->get_sex(), u32); at += 4; /* Length of note entry description (i.e., the payload) */ notes->read_content_local(at, &u32, 4); size_t payload_size = disk_to_host(fhdr->get_sex(), u32); at += 4; /* Type of note */ notes->read_content_local(at, &u32, 4); unsigned type = disk_to_host(fhdr->get_sex(), u32); at += 4; /* NUL-terminated name */ std::string note_name = notes->read_content_local_str(at); ROSE_ASSERT(note_name.size()+1 == name_size); at += name_size; at = (at+3) & ~0x3; /* payload is aligned on a four-byte offset */ /* Set properties */ get_name()->set_string(note_name); set_type(type); p_payload = notes->read_content_local_ucl(at, payload_size); return at + payload_size; }
void SgAsmElfRelocSection::unparse(std::ostream &f) const { SgAsmElfFileHeader *fhdr = get_elf_header(); ROSE_ASSERT(fhdr); ByteOrder::Endianness sex = fhdr->get_sex(); size_t entry_size, struct_size, extra_size, nentries; calculate_sizes(&entry_size, &struct_size, &extra_size, &nentries); /* Adjust the entry size stored in the ELF Section Table */ get_section_entry()->set_sh_entsize(entry_size); /* Write each entry's required part followed by the optional part */ for (size_t i=0; i<nentries; i++) { SgAsmElfRelocEntry::Elf32RelaEntry_disk diska32; SgAsmElfRelocEntry::Elf64RelaEntry_disk diska64; SgAsmElfRelocEntry::Elf32RelEntry_disk disk32; SgAsmElfRelocEntry::Elf64RelEntry_disk disk64; void *disk = NULL; SgAsmElfRelocEntry *entry = p_entries->get_entries()[i]; if (4==fhdr->get_word_size()) { if (p_uses_addend) { disk = entry->encode(sex, &diska32); } else { disk = entry->encode(sex, &disk32); } } else if (8==fhdr->get_word_size()) { if (p_uses_addend) { disk = entry->encode(sex, &diska64); } else { disk = entry->encode(sex, &disk64); } } else { ROSE_ASSERT(!"unsupported word size"); } rose_addr_t spos = i * entry_size; spos = write(f, spos, struct_size, disk); #if 0 /*FIXME: padding not supported here yet (RPM 2008-10-13)*/ if (entry->get_extra().size()>0) { ROSE_ASSERT(entry->get_extra().size()<=extra_size); write(f, spos, entry->get_extra()); } #endif } unparse_holes(f); }
/** Write the section table section back to disk */ void SgAsmElfSectionTable::unparse(std::ostream &f) const { SgAsmElfFileHeader *fhdr = dynamic_cast<SgAsmElfFileHeader*>(get_header()); ROSE_ASSERT(fhdr!=NULL); ByteOrder::Endianness sex = fhdr->get_sex(); SgAsmGenericSectionPtrList sections = fhdr->get_sectab_sections(); /* Write the sections first */ for (size_t i=0; i<sections.size(); i++) sections[i]->unparse(f); unparse_holes(f); /* Calculate sizes. The ELF File Header should have been updated in reallocate() prior to unparsing. */ size_t ent_size, struct_size, opt_size, nentries; calculate_sizes(&ent_size, &struct_size, &opt_size, &nentries); ROSE_ASSERT(fhdr->get_shextrasz()==opt_size); ROSE_ASSERT(fhdr->get_e_shnum()==nentries); /* Write the section table entries */ for (size_t i=0; i<sections.size(); ++i) { SgAsmElfSection *section = dynamic_cast<SgAsmElfSection*>(sections[i]); ROSE_ASSERT(section!=NULL); SgAsmElfSectionTableEntry *shdr = section->get_section_entry(); ROSE_ASSERT(shdr!=NULL); ROSE_ASSERT(shdr->get_sh_offset()==section->get_offset());/*section table entry should have been updated in reallocate()*/ int id = section->get_id(); ROSE_ASSERT(id>=0 && (size_t)id<nentries); SgAsmElfSectionTableEntry::Elf32SectionTableEntry_disk disk32; SgAsmElfSectionTableEntry::Elf64SectionTableEntry_disk disk64; void *disk = NULL; if (4==fhdr->get_word_size()) { disk = shdr->encode(sex, &disk32); } else if (8==fhdr->get_word_size()) { disk = shdr->encode(sex, &disk64); } else { ROSE_ASSERT(!"invalid word size"); } /* The disk struct */ rose_addr_t spos = write(f, id*ent_size, struct_size, disk); if (shdr->get_extra().size() > 0) { ROSE_ASSERT(shdr->get_extra().size()<=opt_size); write(f, spos, shdr->get_extra()); } } }
/** Initializes this ELF Symbol Section by parsing a file. */ SgAsmElfSymbolSection * SgAsmElfSymbolSection::parse() { SgAsmElfSection::parse(); SgAsmElfFileHeader *fhdr = get_elf_header(); ROSE_ASSERT(fhdr!=NULL); SgAsmElfSectionTableEntry *shdr = get_section_entry(); ROSE_ASSERT(shdr!=NULL); SgAsmElfStringSection *strsec = dynamic_cast<SgAsmElfStringSection*>(get_linked_section()); ROSE_ASSERT(strsec!=NULL); size_t entry_size, struct_size, extra_size, nentries; calculate_sizes(&entry_size, &struct_size, &extra_size, &nentries); ROSE_ASSERT(entry_size==shdr->get_sh_entsize()); /* Parse each entry */ for (size_t i=0; i<nentries; i++) { SgAsmElfSymbol *entry=0; if (4==fhdr->get_word_size()) { entry = new SgAsmElfSymbol(this); /*adds symbol to this symbol table*/ SgAsmElfSymbol::Elf32SymbolEntry_disk disk; read_content_local(i*entry_size, &disk, struct_size); entry->parse(fhdr->get_sex(), &disk); } else if (8==fhdr->get_word_size()) { entry = new SgAsmElfSymbol(this); /*adds symbol to this symbol table*/ SgAsmElfSymbol::Elf64SymbolEntry_disk disk; read_content_local(i*entry_size, &disk, struct_size); entry->parse(fhdr->get_sex(), &disk); } else { throw FormatError("unsupported ELF word size"); } if (extra_size>0) entry->get_extra() = read_content_local_ucl(i*entry_size+struct_size, extra_size); } return this; }
/** Write symbol table sections back to disk */ void SgAsmElfSymbolSection::unparse(std::ostream &f) const { SgAsmElfFileHeader *fhdr = get_elf_header(); ROSE_ASSERT(fhdr); ByteOrder sex = fhdr->get_sex(); size_t entry_size, struct_size, extra_size, nentries; calculate_sizes(&entry_size, &struct_size, &extra_size, &nentries); /* Adjust the entry size stored in the ELF Section Table */ get_section_entry()->set_sh_entsize(entry_size); /* Write each entry's required part followed by the optional part */ for (size_t i=0; i<nentries; i++) { SgAsmElfSymbol::Elf32SymbolEntry_disk disk32; SgAsmElfSymbol::Elf64SymbolEntry_disk disk64; void *disk=NULL; SgAsmElfSymbol *entry = p_symbols->get_symbols()[i]; if (4==fhdr->get_word_size()) { disk = entry->encode(sex, &disk32); } else if (8==fhdr->get_word_size()) { disk = entry->encode(sex, &disk64); } else { ROSE_ASSERT(!"unsupported word size"); } rose_addr_t spos = i * entry_size; spos = write(f, spos, struct_size, disk); if (entry->get_extra().size()>0) { ROSE_ASSERT(entry->get_extra().size()<=extra_size); write(f, spos, entry->get_extra()); } } unparse_holes(f); }
/** Parses an ELF Segment (Program Header) Table and constructs and parses all segments reachable from the table. The section * is extended as necessary based on the number of entries and teh size of each entry. */ SgAsmElfSegmentTable * SgAsmElfSegmentTable::parse() { SgAsmGenericSection::parse(); SgAsmElfFileHeader *fhdr = dynamic_cast<SgAsmElfFileHeader*>(get_header()); ROSE_ASSERT(fhdr!=NULL); ByteOrder sex = fhdr->get_sex(); size_t ent_size, struct_size, opt_size, nentries; calculate_sizes(&ent_size, &struct_size, &opt_size, &nentries); ROSE_ASSERT(opt_size==fhdr->get_phextrasz() && nentries==fhdr->get_e_phnum()); /* If the current size is very small (0 or 1 byte) then we're coming straight from the constructor and the parsing should * also extend this section to hold all the entries. Otherwise the caller must have assigned a specific size for a good * reason and we should leave that alone, reading zeros if the entries extend beyond the defined size. */ if (get_size()<=1 && get_size()<nentries*ent_size) extend(nentries*ent_size - get_size()); rose_addr_t offset=0; /* w.r.t. the beginning of this section */ for (size_t i=0; i<nentries; i++, offset+=ent_size) { /* Read/decode the segment header */ SgAsmElfSegmentTableEntry *shdr = NULL; if (4==fhdr->get_word_size()) { SgAsmElfSegmentTableEntry::Elf32SegmentTableEntry_disk disk; read_content_local(offset, &disk, struct_size); shdr = new SgAsmElfSegmentTableEntry(sex, &disk); } else { SgAsmElfSegmentTableEntry::Elf64SegmentTableEntry_disk disk; read_content_local(offset, &disk, struct_size); shdr = new SgAsmElfSegmentTableEntry(sex, &disk); } shdr->set_index(i); if (opt_size>0) shdr->get_extra() = read_content_local_ucl(offset+struct_size, opt_size); /* Null segments are just unused slots in the table; no real section to create */ if (SgAsmElfSegmentTableEntry::PT_NULL == shdr->get_type()) continue; /* Create SgAsmElfSection objects for each ELF Segment. However, if the ELF Segment Table describes a segment * that's the same offset and size as a section from the Elf Section Table (and the memory mappings are * consistent) then use the preexisting section instead of creating a new one. */ SgAsmElfSection *s = NULL; SgAsmGenericSectionPtrList possible = fhdr->get_file()->get_sections_by_offset(shdr->get_offset(), shdr->get_filesz()); for (size_t j=0; !s && j<possible.size(); j++) { if (possible[j]->get_offset()!=shdr->get_offset() || possible[j]->get_size()!=shdr->get_filesz()) continue; /*different file extent*/ if (possible[j]->is_mapped()) { if (possible[j]->get_mapped_preferred_rva()!=shdr->get_vaddr() || possible[j]->get_mapped_size()!=shdr->get_memsz()) continue; /*different mapped address or size*/ unsigned section_perms = (possible[j]->get_mapped_rperm() ? 0x01 : 0x00) | (possible[j]->get_mapped_wperm() ? 0x02 : 0x00) | (possible[j]->get_mapped_xperm() ? 0x04 : 0x00); unsigned segment_perms = (shdr->get_flags() & SgAsmElfSegmentTableEntry::PF_RPERM ? 0x01 : 0x00) | (shdr->get_flags() & SgAsmElfSegmentTableEntry::PF_WPERM ? 0x02 : 0x00) | (shdr->get_flags() & SgAsmElfSegmentTableEntry::PF_XPERM ? 0x04 : 0x00); if (section_perms != segment_perms) continue; /*different mapped permissions*/ } /* Found a match. Set memory mapping params only. */ s = dynamic_cast<SgAsmElfSection*>(possible[j]); if (!s) continue; /*potential match was not from the ELF Section or Segment table*/ if (s->get_segment_entry()) continue; /*potential match is assigned to some other segment table entry*/ s->init_from_segment_table(shdr, true); /*true=>set memory mapping params only*/ } /* Create a new segment if no matching section was found. */ if (!s) { if (SgAsmElfSegmentTableEntry::PT_NOTE == shdr->get_type()) { s = new SgAsmElfNoteSection(fhdr); } else { s = new SgAsmElfSection(fhdr); } s->init_from_segment_table(shdr); s->parse(); } } return this; }
/** Parses an ELF Section Table and constructs and parses all sections reachable from the table. The section is extended as * necessary based on the number of entries and the size of each entry. */ SgAsmElfSectionTable * SgAsmElfSectionTable::parse() { SgAsmGenericSection::parse(); SgAsmElfFileHeader *fhdr = dynamic_cast<SgAsmElfFileHeader*>(get_header()); ROSE_ASSERT(fhdr!=NULL); ByteOrder::Endianness sex = fhdr->get_sex(); size_t ent_size, struct_size, opt_size, nentries; calculate_sizes(&ent_size, &struct_size, &opt_size, &nentries); ROSE_ASSERT(opt_size==fhdr->get_shextrasz() && nentries==fhdr->get_e_shnum()); /* If the current size is very small (0 or 1 byte) then we're coming straight from the constructor and the parsing should * also extend this section to hold all the entries. Otherwise the caller must have assigned a specific size for a good * reason and we should leave that alone, reading zeros if the entries extend beyond the defined size. */ if (get_size()<=1 && get_size()<nentries*ent_size) extend(nentries*ent_size - get_size()); // Read all the section headers. Section headers are not essential to the Unix loader, which uses only segments. Therefore // we should be prepared to handle bad entries. std::vector<SgAsmElfSectionTableEntry*> entries; rose_addr_t offset = 0; try { for (size_t i=0; i<nentries; i++, offset+=ent_size) { SgAsmElfSectionTableEntry *shdr = NULL; if (4 == fhdr->get_word_size()) { SgAsmElfSectionTableEntry::Elf32SectionTableEntry_disk disk; read_content_local(offset, &disk, struct_size); shdr = new SgAsmElfSectionTableEntry(sex, &disk); } else { SgAsmElfSectionTableEntry::Elf64SectionTableEntry_disk disk; read_content_local(offset, &disk, struct_size); shdr = new SgAsmElfSectionTableEntry(sex, &disk); } if (opt_size>0) shdr->get_extra() = read_content_local_ucl(offset+struct_size, opt_size); entries.push_back(shdr); } } catch (const ShortRead &error) { mlog[ERROR] <<"short read for elf section header #" <<entries.size() <<" at file offset " <<StringUtility::addrToString(error.offset) <<" when reading " <<StringUtility::plural(error.size, "bytes") <<"\n"; mlog[ERROR] <<"expected " <<StringUtility::plural(nentries, "sections") <<", but bailing out early\n"; nentries = entries.size(); } /* This vector keeps track of which sections have already been parsed. We could get the same information by calling * fhdr->get_section_by_id() and passing the entry number since entry numbers and IDs are one and the same in ELF. However, * this is a bit easier. */ std::vector<SgAsmElfSection*> is_parsed; is_parsed.resize(entries.size(), NULL); /* All sections implicitly depend on the section string table for their names. */ SgAsmElfStringSection *section_name_strings=NULL; if (fhdr->get_e_shstrndx() > 0 && fhdr->get_e_shstrndx() < entries.size()) { SgAsmElfSectionTableEntry *entry = entries[fhdr->get_e_shstrndx()]; ASSERT_not_null(entry); section_name_strings = new SgAsmElfStringSection(fhdr); section_name_strings->init_from_section_table(entry, section_name_strings, fhdr->get_e_shstrndx()); section_name_strings->parse(); is_parsed[fhdr->get_e_shstrndx()] = section_name_strings; } /* Read all the sections. Some sections depend on other sections, so we read them in such an order that all dependencies * are satisfied first. */ while (1) { bool try_again=false; for (size_t i=0; i<entries.size(); i++) { SgAsmElfSectionTableEntry *entry = entries[i]; ROSE_ASSERT(entry->get_sh_link()<entries.size()); /* Some sections might reference another section through the sh_link member. */ bool need_linked = entry->get_sh_link() > 0; ROSE_ASSERT(!need_linked || entry->get_sh_link()<entries.size()); SgAsmElfSection *linked = need_linked ? is_parsed[entry->get_sh_link()] : NULL; /* Relocation sections might have a second linked section stored in sh_info. */ bool need_info_linked = (entry->get_sh_type() == SgAsmElfSectionTableEntry::SHT_REL || entry->get_sh_type() == SgAsmElfSectionTableEntry::SHT_RELA) && entry->get_sh_info() > 0; ROSE_ASSERT(!need_info_linked || entry->get_sh_info()<entries.size()); SgAsmElfSection *info_linked = need_info_linked ? is_parsed[entry->get_sh_info()] : NULL; if (is_parsed[i]) { /* This section has already been parsed. */ } else if ((need_linked && !linked) || (need_info_linked && !info_linked)) { /* Don't parse this section yet because it depends on something that's not parsed yet. */ try_again = true; } else { switch (entry->get_sh_type()) { case SgAsmElfSectionTableEntry::SHT_NULL: /* Null entry. We still create the section just to hold the section header. */ is_parsed[i] = new SgAsmElfSection(fhdr); break; case SgAsmElfSectionTableEntry::SHT_NOBITS: /* These types of sections don't occupy any file space (e.g., BSS) */ is_parsed[i] = new SgAsmElfSection(fhdr); break; case SgAsmElfSectionTableEntry::SHT_DYNAMIC: { SgAsmElfStringSection *strsec = dynamic_cast<SgAsmElfStringSection*>(linked); ROSE_ASSERT(strsec); is_parsed[i] = new SgAsmElfDynamicSection(fhdr, strsec); break; } case SgAsmElfSectionTableEntry::SHT_DYNSYM: { SgAsmElfStringSection *strsec = dynamic_cast<SgAsmElfStringSection*>(linked); ROSE_ASSERT(strsec); SgAsmElfSymbolSection *symsec = new SgAsmElfSymbolSection(fhdr, strsec); symsec->set_is_dynamic(true); is_parsed[i] = symsec; break; } case SgAsmElfSectionTableEntry::SHT_SYMTAB: { SgAsmElfStringSection *strsec = dynamic_cast<SgAsmElfStringSection*>(linked); ROSE_ASSERT(strsec); SgAsmElfSymbolSection *symsec = new SgAsmElfSymbolSection(fhdr, strsec); symsec->set_is_dynamic(false); is_parsed[i] = symsec; break; } case SgAsmElfSectionTableEntry::SHT_STRTAB: is_parsed[i] = new SgAsmElfStringSection(fhdr); break; case SgAsmElfSectionTableEntry::SHT_REL: { SgAsmElfSymbolSection *symbols = dynamic_cast<SgAsmElfSymbolSection*>(linked); SgAsmElfRelocSection *relocsec = new SgAsmElfRelocSection(fhdr, symbols, info_linked); relocsec->set_uses_addend(false); is_parsed[i] = relocsec; break; } case SgAsmElfSectionTableEntry::SHT_RELA: { SgAsmElfSymbolSection *symbols = dynamic_cast<SgAsmElfSymbolSection*>(linked); SgAsmElfRelocSection *relocsec = new SgAsmElfRelocSection(fhdr, symbols, info_linked); relocsec->set_uses_addend(true); is_parsed[i] = relocsec; break; } case SgAsmElfSectionTableEntry::SHT_PROGBITS: { if (!section_name_strings) { fprintf(stderr, "SgAsmElfSectionTable::parse(): no string table for section table\n"); is_parsed[i] = new SgAsmElfSection(fhdr); } else { std::string section_name = section_name_strings->read_content_local_str(entry->get_sh_name()); if (section_name == ".eh_frame") { is_parsed[i] = new SgAsmElfEHFrameSection(fhdr); } else { is_parsed[i] = new SgAsmElfSection(fhdr); } } break; } case SgAsmElfSectionTableEntry::SHT_GNU_versym: { is_parsed[i] = new SgAsmElfSymverSection(fhdr); break; } case SgAsmElfSectionTableEntry::SHT_GNU_verdef: { SgAsmElfStringSection *strsec = dynamic_cast<SgAsmElfStringSection*>(linked); ROSE_ASSERT(strsec); is_parsed[i] = new SgAsmElfSymverDefinedSection(fhdr,strsec); break; } case SgAsmElfSectionTableEntry::SHT_GNU_verneed: { SgAsmElfStringSection *strsec = dynamic_cast<SgAsmElfStringSection*>(linked); ROSE_ASSERT(strsec); is_parsed[i] = new SgAsmElfSymverNeededSection(fhdr,strsec); break; } default: is_parsed[i] = new SgAsmElfSection(fhdr); break; } is_parsed[i]->init_from_section_table(entry, section_name_strings, i); is_parsed[i]->parse(); } } if (!try_again) break; } /* Initialize links between sections */ for (size_t i = 0; i < entries.size(); i++) { SgAsmElfSectionTableEntry *shdr = entries[i]; if (shdr->get_sh_link() > 0) { SgAsmElfSection *source = isSgAsmElfSection(fhdr->get_file()->get_section_by_id(i)); SgAsmElfSection *target = isSgAsmElfSection(fhdr->get_file()->get_section_by_id(shdr->get_sh_link())); assert(source); /* because we created it above */ source->set_linked_section(target); } } /* Finish parsing sections now that we have basic info for all the sections. */ for (size_t i=0; i<is_parsed.size(); i++) is_parsed[i]->finish_parsing(); return this; }
/** Unparses the section into the optional output stream and returns the number of bytes written. If there is no output stream * we still go through the actions but don't write anything. This is the only way to determine the amount of memory required * to store the section since the section is run-length encoded. */ rose_addr_t SgAsmElfEHFrameSection::unparse(std::ostream *fp) const { SgAsmElfFileHeader *fhdr = get_elf_header(); ROSE_ASSERT(fhdr!=NULL); rose_addr_t at=0; uint32_t u32_disk; uint64_t u64_disk; for (size_t i=0; i<get_ci_entries()->get_entries().size(); i++) { rose_addr_t last_cie_offset = at; SgAsmElfEHFrameEntryCI *cie = get_ci_entries()->get_entries()[i]; std::string s = cie->unparse(this); if (s.size()<0xffffffff) { host_to_disk(fhdr->get_sex(), s.size(), &u32_disk); if (fp) write(*fp, at, 4, &u32_disk); at += 4; } else { u32_disk = 0xffffffff; if (fp) write(*fp, at, 4, &u32_disk); at += 4; host_to_disk(fhdr->get_sex(), s.size(), &u64_disk); if (fp) write(*fp, at, 8, &u64_disk); at += 8; } if (fp) write(*fp, at, s); at += s.size(); for (size_t j=0; j<cie->get_fd_entries()->get_entries().size(); j++) { SgAsmElfEHFrameEntryFD *fde = cie->get_fd_entries()->get_entries()[j]; std::string s = fde->unparse(this, cie); /* Record size, not counting run-length coded size field, but counting CIE back offset. */ rose_addr_t record_size = 4 + s.size(); if (record_size<0xffffffff) { host_to_disk(fhdr->get_sex(), record_size, &u32_disk); if (fp) write(*fp, at, 4, &u32_disk); at += 4; } else { u32_disk = 0xffffffff; if (fp) write(*fp, at, 4, &u32_disk); at += 4; host_to_disk(fhdr->get_sex(), record_size, &u64_disk); if (fp) write(*fp, at, 8, &u64_disk); at += 8; } /* CIE back offset. Number of bytes from the beginning of the current CIE record (including the Size fields) to * the beginning of the FDE record (excluding the Size fields but including the CIE back offset). */ rose_addr_t cie_back_offset = at - last_cie_offset; host_to_disk(fhdr->get_sex(), cie_back_offset, &u32_disk); if (fp) write(*fp, at, 4, &u32_disk); at += 4; /* The FDE record itself */ if (fp) write(*fp, at, s); at += s.size(); } } /* Write a zero length to indicate the end of the CIE list */ u32_disk = 0; if (fp) write(*fp, at, 4, &u32_disk); at += 4; return at; }
/** Initialize by parsing a file. */ SgAsmElfEHFrameSection * SgAsmElfEHFrameSection::parse() { SgAsmElfSection::parse(); SgAsmElfFileHeader *fhdr = get_elf_header(); ROSE_ASSERT(fhdr!=NULL); rose_addr_t record_offset=0; std::map<rose_addr_t, SgAsmElfEHFrameEntryCI*> cies; while (record_offset<get_size()) { rose_addr_t at = record_offset; unsigned char u8_disk; uint32_t u32_disk; uint64_t u64_disk; /* Length or extended length */ rose_addr_t length_field_size = 4; /*number of bytes not counted in length*/ read_content_local(at, &u32_disk, 4); at += 4; rose_addr_t record_size = disk_to_host(fhdr->get_sex(), u32_disk); if (record_size==0xffffffff) { read_content_local(at, &u64_disk, 8); at += 8; record_size = disk_to_host(fhdr->get_sex(), u64_disk); length_field_size += 8; /*FIXME: it's not entirely clear whether ExtendedLength includes this field*/ } if (0==record_size) break; /* Backward offset to CIE record, or zero if this is a CIE record. */ read_content_local(at, &u32_disk, 4); at += 4; rose_addr_t cie_back_offset = disk_to_host(fhdr->get_sex(), u32_disk); if (0==cie_back_offset) { /* This is a CIE record */ SgAsmElfEHFrameEntryCI *cie = new SgAsmElfEHFrameEntryCI(this); cies[record_offset] = cie; /* Version */ uint8_t cie_version; read_content_local(at++, &cie_version, 1); cie->set_version(cie_version); /* Augmentation String */ std::string astr = read_content_local_str(at); at += astr.size() + 1; cie->set_augmentation_string(astr); /* Alignment factors */ cie->set_code_alignment_factor(read_content_local_uleb128(&at)); cie->set_data_alignment_factor(read_content_local_sleb128(&at)); /* Augmentation data length. This is apparently the length of the data described by the Augmentation String plus * the Initial Instructions plus any padding. [RPM 2009-01-15] */ cie->set_augmentation_data_length(read_content_local_uleb128(&at)); /* Augmentation data. The format of the augmentation data in the CIE record is determined by reading the * characters of the augmentation string. */ if (!astr.empty() && astr[0]=='z') { for (size_t i=1; i<astr.size(); i++) { if ('L'==astr[i]) { read_content_local(at++, &u8_disk, 1); cie->set_lsda_encoding(u8_disk); } else if ('P'==astr[i]) { /* The first byte is an encoding method which describes the following bytes, which are the address of * a Personality Routine Handler. There appears to be very little documentation about these fields. */ read_content_local(at++, &u8_disk, 1); cie->set_prh_encoding(u8_disk); switch (cie->get_prh_encoding()) { case 0x05: /* See Ubuntu 32bit /usr/bin/aptitude */ case 0x06: /* See second CIE record for Gentoo-Amd64 /usr/bin/addftinfo */ case 0x07: /* See first CIE record for Gentoo-Amd64 /usr/bin/addftinfo */ read_content_local(at++, &u8_disk, 1); /* not sure what this is; arg for __gxx_personality_v0? */ cie->set_prh_arg(u8_disk); read_content_local(at, &u32_disk, 4); at+=4; /* address of <__gxx_personality_v0@plt> */ cie->set_prh_addr(ByteOrder::le_to_host(u32_disk)); break; case 0x09: /* *.o file generated by gcc-4.0.x */ /* FIXME: Cannot find any info about this entry. Fix SgAsmElfEHFrameSection::parse() if we * ever figure this out. [RPM 2009-09-29] */ /*fallthrough*/ default: { if (++nwarnings<=WARNING_LIMIT) { fprintf(stderr, "%s:%u: warning: ELF CIE 0x%08"PRIx64" has unknown PRH encoding 0x%02x\n", __FILE__, __LINE__, get_offset()+record_offset, cie->get_prh_encoding()); if (WARNING_LIMIT==nwarnings) fprintf(stderr, " (additional frame warnings will be suppressed)\n"); } break; } } } else if ('R'==astr[i]) { read_content_local(at++, &u8_disk, 1); cie->set_addr_encoding(u8_disk); } else if ('S'==astr[i]) { /* See http://lkml.indiana.edu/hypermail/linux/kernel/0602.3/1144.html and GCC PR #26208*/ cie->set_sig_frame(true); } else { /* Some stuff we don't handle yet. Warn about it and don't read anything. */ if (++nwarnings<=WARNING_LIMIT) { fprintf(stderr, "%s:%u: warning: ELF CIE 0x%08"PRIx64" has invalid augmentation string \"%s\"\n", __FILE__, __LINE__, get_offset()+record_offset, escapeString(astr).c_str()); if (WARNING_LIMIT==nwarnings) fprintf(stderr, " (additional frame warnings will be suppressed)\n"); } } } } /* Initial instructions. These are apparently included in the augmentation_data_length. The final instructions can * be zero padding (no-op instructions) to bring the record up to a multiple of the word size. */ rose_addr_t init_insn_size = (length_field_size + record_size) - (at - record_offset); cie->get_instructions() = read_content_local_ucl(at, init_insn_size); ROSE_ASSERT(cie->get_instructions().size()==init_insn_size); } else { /* This is a FDE record */ rose_addr_t cie_offset = record_offset + length_field_size - cie_back_offset; assert(cies.find(cie_offset)!=cies.end()); SgAsmElfEHFrameEntryCI *cie = cies[cie_offset]; SgAsmElfEHFrameEntryFD *fde = new SgAsmElfEHFrameEntryFD(cie); /* PC Begin (begin_rva) and size */ switch (cie->get_addr_encoding()) { case -1: /* No address encoding specified */ case 0x01: case 0x03: case 0x1b: /* Address doesn't look valid (e.g., 0xfffd74e8) but still four bytes [RPM 2008-01-16]*/ { read_content_local(at, &u32_disk, 4); at+=4; fde->set_begin_rva(ByteOrder::le_to_host(u32_disk)); read_content_local(at, &u32_disk, 4); at+=4; fde->set_size(ByteOrder::le_to_host(u32_disk)); break; } default: fprintf(stderr, "%s:%u: ELF CIE 0x%08"PRIx64", FDE 0x%08"PRIx64": unknown address encoding: 0x%02x\n", __FILE__, __LINE__, get_offset()+cie_offset, get_offset()+record_offset, cie->get_addr_encoding()); abort(); } /* Augmentation Data */ std::string astring = cie->get_augmentation_string(); if (astring.size()>0 && astring[0]=='z') { rose_addr_t aug_length = read_content_local_uleb128(&at); fde->get_augmentation_data() = read_content_local_ucl(at, aug_length); at += aug_length; ROSE_ASSERT(fde->get_augmentation_data().size()==aug_length); } /* Call frame instructions */ rose_addr_t cf_insn_size = (length_field_size + record_size) - (at - record_offset); fde->get_instructions() = read_content_local_ucl(at, cf_insn_size); ROSE_ASSERT(fde->get_instructions().size()==cf_insn_size); } record_offset += length_field_size + record_size; } return this; }
/** Initialize by parsing a file. */ SgAsmElfEHFrameSection * SgAsmElfEHFrameSection::parse() { SgAsmElfSection::parse(); SgAsmElfFileHeader *fhdr = get_elf_header(); ROSE_ASSERT(fhdr!=NULL); rose_addr_t record_offset=0; std::map<rose_addr_t, SgAsmElfEHFrameEntryCI*> cies; // This section consists of a Common Information Entry (CIE) followed by one or more Frame Description Entry (FDE) and this // pattern is repeated for the entire section or until a termination record is reached. There is typically one CIE per // object file and one FDE per function. The CIE and FDE together describe how to unwind the caller during an exception if // the current instruction pointer is in the range covered by the FDE. Sawyer::Optional<rose_addr_t> prevCieOffset; while (record_offset<get_size()) { rose_addr_t at = record_offset; unsigned char u8_disk; uint32_t u32_disk; uint64_t u64_disk; /* Length or extended length */ rose_addr_t length_field_size = 4; /*number of bytes not counted in length*/ read_content_local(at, &u32_disk, 4); at += 4; rose_addr_t record_size = disk_to_host(fhdr->get_sex(), u32_disk); if (record_size==0xffffffff) { read_content_local(at, &u64_disk, 8); at += 8; record_size = disk_to_host(fhdr->get_sex(), u64_disk); length_field_size += 8; // length field size is both "length" and "extended length" } if (0==record_size) { record_offset += length_field_size; break; } /* Backward offset to CIE record, or zero if this is a CIE record. */ read_content_local(at, &u32_disk, 4); at += 4; rose_addr_t cie_back_offset = disk_to_host(fhdr->get_sex(), u32_disk); if (0==cie_back_offset) { /* This is a CIE record */ SgAsmElfEHFrameEntryCI *cie = new SgAsmElfEHFrameEntryCI(this); cies[record_offset] = cie; prevCieOffset = record_offset; /* Version. This parser was written based on version 1 documentation. */ uint8_t cie_version; read_content_local(at++, &cie_version, 1); cie->set_version(cie_version); /* Augmentation String */ std::string astr = read_content_local_str(at); at += astr.size() + 1; cie->set_augmentation_string(astr); /* EH data: 4 or 8 bytes depending on word size, only present "if the augmentation string contains 'eh'". The word * "contains" in the documentation apparently means the string is _equal_ to "eh". */ if (astr == "eh") { uint64_t eh_data; if (4==fhdr->get_exec_format()->get_word_size()) { read_content_local(at, &u32_disk, 4); at += 4; eh_data = disk_to_host(fhdr->get_sex(), u32_disk); } else { read_content_local(at, &u64_disk, 8); at += 8; eh_data = disk_to_host(fhdr->get_sex(), u64_disk); } cie->set_eh_data(eh_data); } /* Alignment factors: * + code_alignment_factor: An unsigned LEB128 encoded value that is factored out of all advance location * instructions that are associated with this CIE or its FDEs. This value shall be multiplied by the delta * argument of an adavance location instruction to obtain the new location value. * + data_alignment_factor: A signed LEB128 encoded value that is factored out of all offset instructions that are * associated with this CIE or its FDEs. This value shall be multiplied by the register offset argument of an * offset instruction to obtain the new offset value. */ cie->set_code_alignment_factor(read_content_local_uleb128(&at)); cie->set_data_alignment_factor(read_content_local_sleb128(&at)); /* Augmentation data length. This is apparently the length of the data described by the Augmentation String plus * the Initial Instructions plus any padding [RPM 2009-01-15]. This field is present only if the augmentation * string starts with the character "z" [Robb P. Matzke 2014-12-15]. */ if (!astr.empty() && astr[0]=='z') cie->set_augmentation_data_length(read_content_local_uleb128(&at)); /* Augmentation data. The format of the augmentation data in the CIE record is determined by reading the * characters of the augmentation string. */ if (!astr.empty() && astr[0]=='z') { for (size_t i=1; i<astr.size(); i++) { if ('L'==astr[i]) { read_content_local(at++, &u8_disk, 1); cie->set_lsda_encoding(u8_disk); } else if ('P'==astr[i]) { /* The first byte is an encoding method which describes the following bytes, which are the address of * a Personality Routine Handler. There appears to be very little documentation about these fields. */ read_content_local(at++, &u8_disk, 1); cie->set_prh_encoding(u8_disk); switch (cie->get_prh_encoding()) { case 0x05: /* See Ubuntu 32bit /usr/bin/aptitude */ case 0x06: /* See second CIE record for Gentoo-Amd64 /usr/bin/addftinfo */ case 0x07: /* See first CIE record for Gentoo-Amd64 /usr/bin/addftinfo */ read_content_local(at++, &u8_disk, 1); /* not sure what this is; arg for __gxx_personality_v0? */ cie->set_prh_arg(u8_disk); read_content_local(at, &u32_disk, 4); at+=4; /* address of <__gxx_personality_v0@plt> */ cie->set_prh_addr(ByteOrder::le_to_host(u32_disk)); break; case 0x09: /* *.o file generated by gcc-4.0.x */ /* FIXME: Cannot find any info about this entry. Fix SgAsmElfEHFrameSection::parse() if we * ever figure this out. [RPM 2009-09-29] */ /*fallthrough*/ case 0x10: /* See 32-bit version of skype library */ /* FIXME[Robb P. Matzke 2014-12-15]: cannot find any info for this entry. */ /*fallthrough*/ default: { if (++nwarnings<=WARNING_LIMIT) { fprintf(stderr, "%s:%u: warning: ELF CIE 0x%08"PRIx64" has unknown PRH encoding 0x%02x\n", __FILE__, __LINE__, get_offset()+record_offset, cie->get_prh_encoding()); if (WARNING_LIMIT==nwarnings) fprintf(stderr, " (additional frame warnings will be suppressed)\n"); } break; } } } else if ('R'==astr[i]) { read_content_local(at++, &u8_disk, 1); cie->set_addr_encoding(u8_disk); } else if ('S'==astr[i]) { /* See http://lkml.indiana.edu/hypermail/linux/kernel/0602.3/1144.html and GCC PR #26208*/ cie->set_sig_frame(true); } else { /* Some stuff we don't handle yet. Warn about it and don't read anything. */ if (++nwarnings<=WARNING_LIMIT) { fprintf(stderr, "%s:%u: warning: ELF CIE 0x%08"PRIx64" has invalid augmentation string \"%s\"\n", __FILE__, __LINE__, get_offset()+record_offset, escapeString(astr).c_str()); if (WARNING_LIMIT==nwarnings) fprintf(stderr, " (additional frame warnings will be suppressed)\n"); } } } } /* Initial instructions. These are apparently included in the augmentation_data_length. The final instructions can * be zero padding (no-op instructions) to bring the record up to a multiple of the word size. */ rose_addr_t init_insn_size = (length_field_size + record_size) - (at - record_offset); cie->get_instructions() = read_content_local_ucl(at, init_insn_size); ROSE_ASSERT(cie->get_instructions().size()==init_insn_size); } else { /* This is a FDE record */ bool fde_parse_error = false; rose_addr_t cie_offset = record_offset + length_field_size - cie_back_offset; if (cies.find(cie_offset) == cies.end()) { mlog[ERROR] <<"bad CIE offset " <<cie_offset <<" in section \"" <<StringUtility::cEscape(get_name()->get_string()) <<"\"\n"; mlog[ERROR] <<" referenced by FDE at offset " <<record_offset <<" having CIE back offset " <<cie_back_offset <<"\n"; if (prevCieOffset) { mlog[ERROR] <<" previous CIE was at offset " <<*prevCieOffset <<"\n"; } else { mlog[ERROR] <<" there was no previous CIE in this section\n"; } mlog[ERROR] <<" bailing out early\n"; break; } assert(cies.find(cie_offset)!=cies.end()); SgAsmElfEHFrameEntryCI *cie = cies[cie_offset]; SgAsmElfEHFrameEntryFD *fde = new SgAsmElfEHFrameEntryFD(cie); /* PC Begin (begin_rva) and size */ switch (cie->get_addr_encoding()) { case -1: /* No address encoding specified */ case 0x01: case 0x03: case 0x1b: /* Address doesn't look valid (e.g., 0xfffd74e8) but still four bytes [RPM 2008-01-16]*/ { read_content_local(at, &u32_disk, 4); at+=4; fde->set_begin_rva(ByteOrder::le_to_host(u32_disk)); read_content_local(at, &u32_disk, 4); at+=4; fde->set_size(ByteOrder::le_to_host(u32_disk)); break; } default: fprintf(stderr, "%s:%u: warning: ELF CIE 0x%08"PRIx64", FDE 0x%08"PRIx64": unknown address encoding: 0x%02x\n", __FILE__, __LINE__, get_offset()+cie_offset, get_offset()+record_offset, cie->get_addr_encoding()); fde_parse_error = true; break; } // Location of following fields depends on having correctly unparsed the previous stuff which is RLE. if (!fde_parse_error) { /* Augmentation Data */ std::string astring = cie->get_augmentation_string(); if (astring.size()>0 && astring[0]=='z') { rose_addr_t aug_length = read_content_local_uleb128(&at); fde->get_augmentation_data() = read_content_local_ucl(at, aug_length); at += aug_length; ROSE_ASSERT(fde->get_augmentation_data().size()==aug_length); } /* Call frame instructions */ rose_addr_t cf_insn_size = (length_field_size + record_size) - (at - record_offset); fde->get_instructions() = read_content_local_ucl(at, cf_insn_size); ROSE_ASSERT(fde->get_instructions().size()==cf_insn_size); } } record_offset += length_field_size + record_size; } if (record_offset < get_size()) { mlog[WARN] <<"ELF error handling section \"" <<StringUtility::cEscape(get_name()->get_string()) <<"\"" <<" contains " <<StringUtility::plural(get_size()-record_offset, "more bytes") <<" that were not parsed\n"; } return this; }