/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ART_LIBELFFILE_ELF_ELF_BUILDER_H_ #define ART_LIBELFFILE_ELF_ELF_BUILDER_H_ #include #include #include "arch/instruction_set.h" #include "base/array_ref.h" #include "base/bit_utils.h" #include "base/casts.h" #include "base/leb128.h" #include "base/unix_file/fd_file.h" #include "elf/elf_utils.h" #include "stream/error_delaying_output_stream.h" namespace art { // Writes ELF file. // // The basic layout of the elf file: // Elf_Ehdr - The ELF header. // Elf_Phdr[] - Program headers for the linker. // .note.gnu.build-id - Optional build ID section (SHA-1 digest). // .rodata - Oat metadata. // .text - Compiled code. // .bss - Zero-initialized writeable section. // .dex - Reserved NOBITS space for dex-related data. // .dynstr - Names for .dynsym. // .dynsym - A few oat-specific dynamic symbols. // .hash - Hash-table for .dynsym. // .dynamic - Tags which let the linker locate .dynsym. // .strtab - Names for .symtab. // .symtab - Debug symbols. // .debug_frame - Unwind information (CFI). // .debug_info - Debug information. // .debug_abbrev - Decoding information for .debug_info. // .debug_str - Strings for .debug_info. // .debug_line - Line number tables. // .shstrtab - Names of ELF sections. // Elf_Shdr[] - Section headers. // // Some section are optional (the debug sections in particular). // // We try write the section data directly into the file without much // in-memory buffering. This means we generally write sections based on the // dependency order (e.g. .dynamic points to .dynsym which points to .text). // // In the cases where we need to buffer, we write the larger section first // and buffer the smaller one (e.g. .strtab is bigger than .symtab). // // The debug sections are written last for easier stripping. // template class ElfBuilder final { public: static constexpr size_t kMaxProgramHeaders = 16; // SHA-1 digest. Not using SHA_DIGEST_LENGTH from openssl/sha.h to avoid // spreading this header dependency for just this single constant. static constexpr size_t kBuildIdLen = 20; using Elf_Addr = typename ElfTypes::Addr; using Elf_Off = typename ElfTypes::Off; using Elf_Word = typename ElfTypes::Word; using Elf_Sword = typename ElfTypes::Sword; using Elf_Ehdr = typename ElfTypes::Ehdr; using Elf_Shdr = typename ElfTypes::Shdr; using Elf_Sym = typename ElfTypes::Sym; using Elf_Phdr = typename ElfTypes::Phdr; using Elf_Dyn = typename ElfTypes::Dyn; // Base class of all sections. class Section : public OutputStream { public: Section(ElfBuilder* owner, const std::string& name, Elf_Word type, Elf_Word flags, const Section* link, Elf_Word info, Elf_Word align, Elf_Word entsize) : OutputStream(name), owner_(owner), header_(), section_index_(0), name_(name), link_(link), phdr_flags_(PF_R), phdr_type_(0) { DCHECK_GE(align, 1u); header_.sh_type = type; header_.sh_flags = flags; header_.sh_info = info; header_.sh_addralign = align; header_.sh_entsize = entsize; } // Allocate chunk of virtual memory for this section from the owning ElfBuilder. // This must be done at the start for all SHF_ALLOC sections (i.e. mmaped by linker). // It is fine to allocate section but never call Start/End() (e.g. the .bss section). void AllocateVirtualMemory(Elf_Word size) { AllocateVirtualMemory(owner_->virtual_address_, size); } void AllocateVirtualMemory(Elf_Addr addr, Elf_Word size) { CHECK_NE(header_.sh_flags & SHF_ALLOC, 0u); Elf_Word align = AddSection(); CHECK_EQ(header_.sh_addr, 0u); header_.sh_addr = RoundUp(addr, align); CHECK(header_.sh_size == 0u || header_.sh_size == size); header_.sh_size = size; CHECK_LE(owner_->virtual_address_, header_.sh_addr); owner_->virtual_address_ = header_.sh_addr + header_.sh_size; } // Start writing file data of this section. void Start() { CHECK(owner_->current_section_ == nullptr); Elf_Word align = AddSection(); CHECK_EQ(header_.sh_offset, 0u); header_.sh_offset = owner_->AlignFileOffset(align); owner_->current_section_ = this; } // Finish writing file data of this section. void End() { CHECK(owner_->current_section_ == this); Elf_Word position = GetPosition(); CHECK(header_.sh_size == 0u || header_.sh_size == position); header_.sh_size = position; owner_->current_section_ = nullptr; } // Get the number of bytes written so far. // Only valid while writing the section. Elf_Word GetPosition() const { CHECK(owner_->current_section_ == this); off_t file_offset = owner_->stream_.Seek(0, kSeekCurrent); DCHECK_GE(file_offset, (off_t)header_.sh_offset); return file_offset - header_.sh_offset; } // Get the location of this section in virtual memory. Elf_Addr GetAddress() const { DCHECK_NE(header_.sh_flags & SHF_ALLOC, 0u); DCHECK_NE(header_.sh_addr, 0u); return header_.sh_addr; } // This function always succeeds to simplify code. // Use builder's Good() to check the actual status. bool WriteFully(const void* buffer, size_t byte_count) override { CHECK(owner_->current_section_ == this); return owner_->stream_.WriteFully(buffer, byte_count); } // This function always succeeds to simplify code. // Use builder's Good() to check the actual status. off_t Seek(off_t offset, Whence whence) override { // Forward the seek as-is and trust the caller to use it reasonably. return owner_->stream_.Seek(offset, whence); } // This function flushes the output and returns whether it succeeded. // If there was a previous failure, this does nothing and returns false, i.e. failed. bool Flush() override { return owner_->stream_.Flush(); } Elf_Word GetSectionIndex() const { DCHECK_NE(section_index_, 0u); return section_index_; } // Returns true if this section has been added. bool Exists() const { return section_index_ != 0; } protected: // Add this section to the list of generated ELF sections (if not there already). // It also ensures the alignment is sufficient to generate valid program headers, // since that depends on the previous section. It returns the required alignment. Elf_Word AddSection() { if (section_index_ == 0) { std::vector& sections = owner_->sections_; Elf_Word last = sections.empty() ? PF_R : sections.back()->phdr_flags_; if (phdr_flags_ != last) { header_.sh_addralign = kPageSize; // Page-align if R/W/X flags changed. } sections.push_back(this); section_index_ = sections.size(); // First ELF section has index 1. } return owner_->write_program_headers_ ? header_.sh_addralign : 1; } ElfBuilder* owner_; Elf_Shdr header_; Elf_Word section_index_; const std::string name_; const Section* const link_; Elf_Word phdr_flags_; Elf_Word phdr_type_; friend class ElfBuilder; DISALLOW_COPY_AND_ASSIGN(Section); }; class CachedSection : public Section { public: CachedSection(ElfBuilder* owner, const std::string& name, Elf_Word type, Elf_Word flags, const Section* link, Elf_Word info, Elf_Word align, Elf_Word entsize) : Section(owner, name, type, flags, link, info, align, entsize), cache_() { } Elf_Word Add(const void* data, size_t length) { Elf_Word offset = cache_.size(); const uint8_t* d = reinterpret_cast(data); cache_.insert(cache_.end(), d, d + length); return offset; } Elf_Word GetCacheSize() { return cache_.size(); } void Write() { this->WriteFully(cache_.data(), cache_.size()); cache_.clear(); cache_.shrink_to_fit(); } void WriteCachedSection() { this->Start(); Write(); this->End(); } private: std::vector cache_; }; // Writer of .dynstr section. class CachedStringSection final : public CachedSection { public: CachedStringSection(ElfBuilder* owner, const std::string& name, Elf_Word flags, Elf_Word align) : CachedSection(owner, name, SHT_STRTAB, flags, /* link= */ nullptr, /* info= */ 0, align, /* entsize= */ 0) { } Elf_Word Add(const std::string& name) { if (CachedSection::GetCacheSize() == 0u) { DCHECK(name.empty()); } return CachedSection::Add(name.c_str(), name.length() + 1); } }; // Writer of .strtab and .shstrtab sections. class StringSection final : public Section { public: StringSection(ElfBuilder* owner, const std::string& name, Elf_Word flags, Elf_Word align) : Section(owner, name, SHT_STRTAB, flags, /* link= */ nullptr, /* info= */ 0, align, /* entsize= */ 0) { Reset(); } void Reset() { current_offset_ = 0; last_name_ = ""; last_offset_ = 0; } Elf_Word Write(const std::string& name) { if (current_offset_ == 0) { DCHECK(name.empty()); } else if (name == last_name_) { return last_offset_; // Very simple string de-duplication. } last_name_ = name; last_offset_ = current_offset_; this->WriteFully(name.c_str(), name.length() + 1); current_offset_ += name.length() + 1; return last_offset_; } private: Elf_Word current_offset_; std::string last_name_; Elf_Word last_offset_; }; // Writer of .dynsym and .symtab sections. class SymbolSection final : public Section { public: SymbolSection(ElfBuilder* owner, const std::string& name, Elf_Word type, Elf_Word flags, Section* strtab) : Section(owner, name, type, flags, strtab, /* info= */ 1, sizeof(Elf_Off), sizeof(Elf_Sym)) { syms_.push_back(Elf_Sym()); // The symbol table always has to start with NULL symbol. } // Buffer symbol for this section. It will be written later. void Add(Elf_Word name, const Section* section, Elf_Addr addr, Elf_Word size, uint8_t binding, uint8_t type) { Elf_Sym sym = Elf_Sym(); sym.st_name = name; sym.st_value = addr; sym.st_size = size; sym.st_other = 0; sym.st_info = (binding << 4) + (type & 0xf); Add(sym, section); } // Buffer symbol for this section. It will be written later. void Add(Elf_Sym sym, const Section* section) { DCHECK(section != nullptr); DCHECK_LE(section->GetAddress(), sym.st_value); DCHECK_LE(sym.st_value, section->GetAddress() + section->header_.sh_size); sym.st_shndx = section->GetSectionIndex(); syms_.push_back(sym); } Elf_Word GetCacheSize() { return syms_.size() * sizeof(Elf_Sym); } void WriteCachedSection() { auto is_local = [](const Elf_Sym& sym) { return ELF_ST_BIND(sym.st_info) == STB_LOCAL; }; auto less_then = [is_local](const Elf_Sym& a, const Elf_Sym b) { auto tuple_a = std::make_tuple(!is_local(a), a.st_value, a.st_name); auto tuple_b = std::make_tuple(!is_local(b), b.st_value, b.st_name); return tuple_a < tuple_b; // Locals first, then sort by address and name offset. }; if (!std::is_sorted(syms_.begin(), syms_.end(), less_then)) { std::sort(syms_.begin(), syms_.end(), less_then); } auto locals_end = std::partition_point(syms_.begin(), syms_.end(), is_local); this->header_.sh_info = locals_end - syms_.begin(); // Required by the spec. this->Start(); for (; !syms_.empty(); syms_.pop_front()) { this->WriteFully(&syms_.front(), sizeof(Elf_Sym)); } this->End(); } private: std::deque syms_; // Buffered/cached content of the whole section. }; class BuildIdSection final : public Section { public: BuildIdSection(ElfBuilder* owner, const std::string& name, Elf_Word type, Elf_Word flags, const Section* link, Elf_Word info, Elf_Word align, Elf_Word entsize) : Section(owner, name, type, flags, link, info, align, entsize), digest_start_(-1) { } Elf_Word GetSize() { return 16 + kBuildIdLen; } void Write() { // The size fields are 32-bit on both 32-bit and 64-bit systems, confirmed // with the 64-bit linker and libbfd code. The size of name and desc must // be a multiple of 4 and it currently is. this->WriteUint32(4); // namesz. this->WriteUint32(kBuildIdLen); // descsz. this->WriteUint32(3); // type = NT_GNU_BUILD_ID. this->WriteFully("GNU", 4); // name. digest_start_ = this->Seek(0, kSeekCurrent); static_assert(kBuildIdLen % 4 == 0, "expecting a mutliple of 4 for build ID length"); this->WriteFully(std::string(kBuildIdLen, '\0').c_str(), kBuildIdLen); // desc. DCHECK_EQ(this->GetPosition(), GetSize()); } off_t GetDigestStart() { CHECK_GT(digest_start_, 0); return digest_start_; } private: bool WriteUint32(uint32_t v) { return this->WriteFully(&v, sizeof(v)); } // File offset where the build ID digest starts. // Populated with zeros first, then updated with the actual value as the // very last thing in the output file creation. off_t digest_start_; }; ElfBuilder(InstructionSet isa, OutputStream* output) : isa_(isa), stream_(output), rodata_(this, ".rodata", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), text_(this, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, nullptr, 0, kPageSize, 0), data_bimg_rel_ro_( this, ".data.bimg.rel.ro", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), bss_(this, ".bss", SHT_NOBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), dex_(this, ".dex", SHT_NOBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), dynstr_(this, ".dynstr", SHF_ALLOC, kPageSize), dynsym_(this, ".dynsym", SHT_DYNSYM, SHF_ALLOC, &dynstr_), hash_(this, ".hash", SHT_HASH, SHF_ALLOC, &dynsym_, 0, sizeof(Elf_Word), sizeof(Elf_Word)), dynamic_(this, ".dynamic", SHT_DYNAMIC, SHF_ALLOC, &dynstr_, 0, kPageSize, sizeof(Elf_Dyn)), strtab_(this, ".strtab", 0, 1), symtab_(this, ".symtab", SHT_SYMTAB, 0, &strtab_), debug_frame_(this, ".debug_frame", SHT_PROGBITS, 0, nullptr, 0, sizeof(Elf_Addr), 0), debug_frame_hdr_( this, ".debug_frame_hdr.android", SHT_PROGBITS, 0, nullptr, 0, sizeof(Elf_Addr), 0), debug_info_(this, ".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0), debug_line_(this, ".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0), shstrtab_(this, ".shstrtab", 0, 1), build_id_(this, ".note.gnu.build-id", SHT_NOTE, SHF_ALLOC, nullptr, 0, 4, 0), current_section_(nullptr), started_(false), finished_(false), write_program_headers_(false), loaded_size_(0u), virtual_address_(0) { text_.phdr_flags_ = PF_R | PF_X; data_bimg_rel_ro_.phdr_flags_ = PF_R | PF_W; // Shall be made read-only at run time. bss_.phdr_flags_ = PF_R | PF_W; dex_.phdr_flags_ = PF_R; dynamic_.phdr_flags_ = PF_R | PF_W; dynamic_.phdr_type_ = PT_DYNAMIC; build_id_.phdr_type_ = PT_NOTE; } ~ElfBuilder() {} InstructionSet GetIsa() { return isa_; } BuildIdSection* GetBuildId() { return &build_id_; } Section* GetRoData() { return &rodata_; } Section* GetText() { return &text_; } Section* GetDataBimgRelRo() { return &data_bimg_rel_ro_; } Section* GetBss() { return &bss_; } Section* GetDex() { return &dex_; } StringSection* GetStrTab() { return &strtab_; } SymbolSection* GetSymTab() { return &symtab_; } Section* GetDebugFrame() { return &debug_frame_; } Section* GetDebugFrameHdr() { return &debug_frame_hdr_; } Section* GetDebugInfo() { return &debug_info_; } Section* GetDebugLine() { return &debug_line_; } void WriteSection(const char* name, const std::vector* buffer) { std::unique_ptr
s(new Section(this, name, SHT_PROGBITS, 0, nullptr, 0, 1, 0)); s->Start(); s->WriteFully(buffer->data(), buffer->size()); s->End(); other_sections_.push_back(std::move(s)); } // Reserve space for ELF header and program headers. // We do not know the number of headers until later, so // it is easiest to just reserve a fixed amount of space. // Program headers are required for loading by the linker. // It is possible to omit them for ELF files used for debugging. void Start(bool write_program_headers = true) { int size = sizeof(Elf_Ehdr); if (write_program_headers) { size += sizeof(Elf_Phdr) * kMaxProgramHeaders; } stream_.Seek(size, kSeekSet); started_ = true; virtual_address_ += size; write_program_headers_ = write_program_headers; } off_t End() { DCHECK(started_); DCHECK(!finished_); finished_ = true; // Note: loaded_size_ == 0 for tests that don't write .rodata, .text, .bss, // .dynstr, dynsym, .hash and .dynamic. These tests should not read loaded_size_. CHECK(loaded_size_ == 0 || loaded_size_ == RoundUp(virtual_address_, kPageSize)) << loaded_size_ << " " << virtual_address_; // Write section names and finish the section headers. shstrtab_.Start(); shstrtab_.Write(""); for (auto* section : sections_) { section->header_.sh_name = shstrtab_.Write(section->name_); if (section->link_ != nullptr) { section->header_.sh_link = section->link_->GetSectionIndex(); } if (section->header_.sh_offset == 0) { section->header_.sh_type = SHT_NOBITS; } } shstrtab_.End(); // Write section headers at the end of the ELF file. std::vector shdrs; shdrs.reserve(1u + sections_.size()); shdrs.push_back(Elf_Shdr()); // NULL at index 0. for (auto* section : sections_) { shdrs.push_back(section->header_); } Elf_Off section_headers_offset; section_headers_offset = AlignFileOffset(sizeof(Elf_Off)); stream_.WriteFully(shdrs.data(), shdrs.size() * sizeof(shdrs[0])); off_t file_size = stream_.Seek(0, kSeekCurrent); // Flush everything else before writing the program headers. This should prevent // the OS from reordering writes, so that we don't end up with valid headers // and partially written data if we suddenly lose power, for example. stream_.Flush(); // The main ELF header. Elf_Ehdr elf_header = MakeElfHeader(isa_); elf_header.e_shoff = section_headers_offset; elf_header.e_shnum = shdrs.size(); elf_header.e_shstrndx = shstrtab_.GetSectionIndex(); // Program headers (i.e. mmap instructions). std::vector phdrs; if (write_program_headers_) { phdrs = MakeProgramHeaders(); CHECK_LE(phdrs.size(), kMaxProgramHeaders); elf_header.e_phoff = sizeof(Elf_Ehdr); elf_header.e_phnum = phdrs.size(); } stream_.Seek(0, kSeekSet); stream_.WriteFully(&elf_header, sizeof(elf_header)); stream_.WriteFully(phdrs.data(), phdrs.size() * sizeof(phdrs[0])); stream_.Flush(); return file_size; } // This has the same effect as running the "strip" command line tool. // It removes all debugging sections (but it keeps mini-debug-info). // It returns the ELF file size (as the caller needs to truncate it). off_t Strip() { DCHECK(finished_); finished_ = false; Elf_Off end = 0; std::vector non_debug_sections; for (Section* section : sections_) { if (section == &shstrtab_ || // Section names will be recreated. section == &symtab_ || section == &strtab_ || section->name_.find(".debug_") == 0) { section->header_.sh_offset = 0; section->header_.sh_size = 0; section->section_index_ = 0; } else { if (section->header_.sh_type != SHT_NOBITS) { DCHECK_LE(section->header_.sh_offset, end + kPageSize) << "Large gap between sections"; end = std::max(end, section->header_.sh_offset + section->header_.sh_size); } non_debug_sections.push_back(section); } } shstrtab_.Reset(); // Write the non-debug section headers, program headers, and ELF header again. sections_ = std::move(non_debug_sections); stream_.Seek(end, kSeekSet); return End(); } // The running program does not have access to section headers // and the loader is not supposed to use them either. // The dynamic sections therefore replicates some of the layout // information like the address and size of .rodata and .text. // It also contains other metadata like the SONAME. // The .dynamic section is found using the PT_DYNAMIC program header. void PrepareDynamicSection(const std::string& elf_file_path, Elf_Word rodata_size, Elf_Word text_size, Elf_Word data_bimg_rel_ro_size, Elf_Word bss_size, Elf_Word bss_methods_offset, Elf_Word bss_roots_offset, Elf_Word dex_size) { std::string soname(elf_file_path); size_t directory_separator_pos = soname.rfind('/'); if (directory_separator_pos != std::string::npos) { soname = soname.substr(directory_separator_pos + 1); } // Allocate all pre-dynamic sections. rodata_.AllocateVirtualMemory(rodata_size); text_.AllocateVirtualMemory(text_size); if (data_bimg_rel_ro_size != 0) { data_bimg_rel_ro_.AllocateVirtualMemory(data_bimg_rel_ro_size); } if (bss_size != 0) { bss_.AllocateVirtualMemory(bss_size); } if (dex_size != 0) { dex_.AllocateVirtualMemory(dex_size); } // Cache .dynstr, .dynsym and .hash data. dynstr_.Add(""); // dynstr should start with empty string. Elf_Word oatdata = dynstr_.Add("oatdata"); dynsym_.Add(oatdata, &rodata_, rodata_.GetAddress(), rodata_size, STB_GLOBAL, STT_OBJECT); if (text_size != 0u) { // The runtime does not care about the size of this symbol (it uses the "lastword" symbol). // We use size 0 (meaning "unknown size" in ELF) to prevent overlap with the debug symbols. Elf_Word oatexec = dynstr_.Add("oatexec"); dynsym_.Add(oatexec, &text_, text_.GetAddress(), /* size= */ 0, STB_GLOBAL, STT_OBJECT); Elf_Word oatlastword = dynstr_.Add("oatlastword"); Elf_Word oatlastword_address = text_.GetAddress() + text_size - 4; dynsym_.Add(oatlastword, &text_, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT); } else if (rodata_size != 0) { // rodata_ can be size 0 for dwarf_test. Elf_Word oatlastword = dynstr_.Add("oatlastword"); Elf_Word oatlastword_address = rodata_.GetAddress() + rodata_size - 4; dynsym_.Add(oatlastword, &rodata_, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT); } if (data_bimg_rel_ro_size != 0u) { Elf_Word oatdatabimgrelro = dynstr_.Add("oatdatabimgrelro"); dynsym_.Add(oatdatabimgrelro, &data_bimg_rel_ro_, data_bimg_rel_ro_.GetAddress(), data_bimg_rel_ro_size, STB_GLOBAL, STT_OBJECT); Elf_Word oatdatabimgrelrolastword = dynstr_.Add("oatdatabimgrelrolastword"); Elf_Word oatdatabimgrelrolastword_address = data_bimg_rel_ro_.GetAddress() + data_bimg_rel_ro_size - 4; dynsym_.Add(oatdatabimgrelrolastword, &data_bimg_rel_ro_, oatdatabimgrelrolastword_address, 4, STB_GLOBAL, STT_OBJECT); } DCHECK_LE(bss_roots_offset, bss_size); if (bss_size != 0u) { Elf_Word oatbss = dynstr_.Add("oatbss"); dynsym_.Add(oatbss, &bss_, bss_.GetAddress(), bss_roots_offset, STB_GLOBAL, STT_OBJECT); DCHECK_LE(bss_methods_offset, bss_roots_offset); DCHECK_LE(bss_roots_offset, bss_size); // Add a symbol marking the start of the methods part of the .bss, if not empty. if (bss_methods_offset != bss_roots_offset) { Elf_Word bss_methods_address = bss_.GetAddress() + bss_methods_offset; Elf_Word bss_methods_size = bss_roots_offset - bss_methods_offset; Elf_Word oatbssroots = dynstr_.Add("oatbssmethods"); dynsym_.Add( oatbssroots, &bss_, bss_methods_address, bss_methods_size, STB_GLOBAL, STT_OBJECT); } // Add a symbol marking the start of the GC roots part of the .bss, if not empty. if (bss_roots_offset != bss_size) { Elf_Word bss_roots_address = bss_.GetAddress() + bss_roots_offset; Elf_Word bss_roots_size = bss_size - bss_roots_offset; Elf_Word oatbssroots = dynstr_.Add("oatbssroots"); dynsym_.Add( oatbssroots, &bss_, bss_roots_address, bss_roots_size, STB_GLOBAL, STT_OBJECT); } Elf_Word oatbsslastword = dynstr_.Add("oatbsslastword"); Elf_Word bsslastword_address = bss_.GetAddress() + bss_size - 4; dynsym_.Add(oatbsslastword, &bss_, bsslastword_address, 4, STB_GLOBAL, STT_OBJECT); } if (dex_size != 0u) { Elf_Word oatdex = dynstr_.Add("oatdex"); dynsym_.Add(oatdex, &dex_, dex_.GetAddress(), /* size= */ 0, STB_GLOBAL, STT_OBJECT); Elf_Word oatdexlastword = dynstr_.Add("oatdexlastword"); Elf_Word oatdexlastword_address = dex_.GetAddress() + dex_size - 4; dynsym_.Add(oatdexlastword, &dex_, oatdexlastword_address, 4, STB_GLOBAL, STT_OBJECT); } Elf_Word soname_offset = dynstr_.Add(soname); // We do not really need a hash-table since there is so few entries. // However, the hash-table is the only way the linker can actually // determine the number of symbols in .dynsym so it is required. int count = dynsym_.GetCacheSize() / sizeof(Elf_Sym); // Includes NULL. std::vector hash; hash.push_back(1); // Number of buckets. hash.push_back(count); // Number of chains. // Buckets. Having just one makes it linear search. hash.push_back(1); // Point to first non-NULL symbol. // Chains. This creates linked list of symbols. hash.push_back(0); // Placeholder entry for the NULL symbol. for (int i = 1; i < count - 1; i++) { hash.push_back(i + 1); // Each symbol points to the next one. } hash.push_back(0); // Last symbol terminates the chain. hash_.Add(hash.data(), hash.size() * sizeof(hash[0])); // Allocate all remaining sections. dynstr_.AllocateVirtualMemory(dynstr_.GetCacheSize()); dynsym_.AllocateVirtualMemory(dynsym_.GetCacheSize()); hash_.AllocateVirtualMemory(hash_.GetCacheSize()); Elf_Dyn dyns[] = { { .d_tag = DT_HASH, .d_un = { .d_ptr = hash_.GetAddress() }, }, { .d_tag = DT_STRTAB, .d_un = { .d_ptr = dynstr_.GetAddress() }, }, { .d_tag = DT_SYMTAB, .d_un = { .d_ptr = dynsym_.GetAddress() }, }, { .d_tag = DT_SYMENT, .d_un = { .d_ptr = sizeof(Elf_Sym) }, }, { .d_tag = DT_STRSZ, .d_un = { .d_ptr = dynstr_.GetCacheSize() }, }, { .d_tag = DT_SONAME, .d_un = { .d_ptr = soname_offset }, }, { .d_tag = DT_NULL, .d_un = { .d_ptr = 0 }, }, }; dynamic_.Add(&dyns, sizeof(dyns)); dynamic_.AllocateVirtualMemory(dynamic_.GetCacheSize()); loaded_size_ = RoundUp(virtual_address_, kPageSize); } void WriteDynamicSection() { dynstr_.WriteCachedSection(); dynsym_.WriteCachedSection(); hash_.WriteCachedSection(); dynamic_.WriteCachedSection(); } Elf_Word GetLoadedSize() { CHECK_NE(loaded_size_, 0u); return loaded_size_; } void WriteBuildIdSection() { build_id_.Start(); build_id_.Write(); build_id_.End(); } void WriteBuildId(uint8_t build_id[kBuildIdLen]) { stream_.Seek(build_id_.GetDigestStart(), kSeekSet); stream_.WriteFully(build_id, kBuildIdLen); stream_.Flush(); } // Returns true if all writes and seeks on the output stream succeeded. bool Good() { return stream_.Good(); } // Returns the builder's internal stream. OutputStream* GetStream() { return &stream_; } off_t AlignFileOffset(size_t alignment) { return stream_.Seek(RoundUp(stream_.Seek(0, kSeekCurrent), alignment), kSeekSet); } private: static Elf_Ehdr MakeElfHeader(InstructionSet isa) { Elf_Ehdr elf_header = Elf_Ehdr(); switch (isa) { case InstructionSet::kArm: // Fall through. case InstructionSet::kThumb2: { elf_header.e_machine = EM_ARM; elf_header.e_flags = EF_ARM_EABI_VER5; break; } case InstructionSet::kArm64: { elf_header.e_machine = EM_AARCH64; elf_header.e_flags = 0; break; } case InstructionSet::kX86: { elf_header.e_machine = EM_386; elf_header.e_flags = 0; break; } case InstructionSet::kX86_64: { elf_header.e_machine = EM_X86_64; elf_header.e_flags = 0; break; } case InstructionSet::kNone: { LOG(FATAL) << "No instruction set"; break; } default: { LOG(FATAL) << "Unknown instruction set " << isa; } } elf_header.e_ident[EI_MAG0] = ELFMAG0; elf_header.e_ident[EI_MAG1] = ELFMAG1; elf_header.e_ident[EI_MAG2] = ELFMAG2; elf_header.e_ident[EI_MAG3] = ELFMAG3; elf_header.e_ident[EI_CLASS] = (sizeof(Elf_Addr) == sizeof(Elf32_Addr)) ? ELFCLASS32 : ELFCLASS64; elf_header.e_ident[EI_DATA] = ELFDATA2LSB; elf_header.e_ident[EI_VERSION] = EV_CURRENT; elf_header.e_ident[EI_OSABI] = ELFOSABI_LINUX; elf_header.e_ident[EI_ABIVERSION] = 0; elf_header.e_type = ET_DYN; elf_header.e_version = 1; elf_header.e_entry = 0; elf_header.e_ehsize = sizeof(Elf_Ehdr); elf_header.e_phentsize = sizeof(Elf_Phdr); elf_header.e_shentsize = sizeof(Elf_Shdr); return elf_header; } // Create program headers based on written sections. std::vector MakeProgramHeaders() { CHECK(!sections_.empty()); std::vector phdrs; { // The program headers must start with PT_PHDR which is used in // loaded process to determine the number of program headers. Elf_Phdr phdr = Elf_Phdr(); phdr.p_type = PT_PHDR; phdr.p_flags = PF_R; phdr.p_offset = phdr.p_vaddr = phdr.p_paddr = sizeof(Elf_Ehdr); phdr.p_filesz = phdr.p_memsz = 0; // We need to fill this later. phdr.p_align = sizeof(Elf_Off); phdrs.push_back(phdr); // Tell the linker to mmap the start of file to memory. Elf_Phdr load = Elf_Phdr(); load.p_type = PT_LOAD; load.p_flags = PF_R; load.p_offset = load.p_vaddr = load.p_paddr = 0; load.p_filesz = load.p_memsz = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * kMaxProgramHeaders; load.p_align = kPageSize; phdrs.push_back(load); } // Create program headers for sections. for (auto* section : sections_) { const Elf_Shdr& shdr = section->header_; if ((shdr.sh_flags & SHF_ALLOC) != 0 && shdr.sh_size != 0) { DCHECK(shdr.sh_addr != 0u) << "Allocate virtual memory for the section"; // PT_LOAD tells the linker to mmap part of the file. // The linker can only mmap page-aligned sections. // Single PT_LOAD may contain several ELF sections. Elf_Phdr& prev = phdrs.back(); Elf_Phdr load = Elf_Phdr(); load.p_type = PT_LOAD; load.p_flags = section->phdr_flags_; load.p_offset = shdr.sh_offset; load.p_vaddr = load.p_paddr = shdr.sh_addr; load.p_filesz = (shdr.sh_type != SHT_NOBITS ? shdr.sh_size : 0u); load.p_memsz = shdr.sh_size; load.p_align = shdr.sh_addralign; if (prev.p_type == load.p_type && prev.p_flags == load.p_flags && prev.p_filesz == prev.p_memsz && // Do not merge .bss load.p_filesz == load.p_memsz) { // Do not merge .bss // Merge this PT_LOAD with the previous one. Elf_Word size = shdr.sh_offset + shdr.sh_size - prev.p_offset; prev.p_filesz = size; prev.p_memsz = size; } else { // If we are adding new load, it must be aligned. CHECK_EQ(shdr.sh_addralign, (Elf_Word)kPageSize); phdrs.push_back(load); } } } for (auto* section : sections_) { const Elf_Shdr& shdr = section->header_; if ((shdr.sh_flags & SHF_ALLOC) != 0 && shdr.sh_size != 0) { // Other PT_* types allow the program to locate interesting // parts of memory at runtime. They must overlap with PT_LOAD. if (section->phdr_type_ != 0) { Elf_Phdr phdr = Elf_Phdr(); phdr.p_type = section->phdr_type_; phdr.p_flags = section->phdr_flags_; phdr.p_offset = shdr.sh_offset; phdr.p_vaddr = phdr.p_paddr = shdr.sh_addr; phdr.p_filesz = phdr.p_memsz = shdr.sh_size; phdr.p_align = shdr.sh_addralign; phdrs.push_back(phdr); } } } // Set the size of the initial PT_PHDR. CHECK_EQ(phdrs[0].p_type, (Elf_Word)PT_PHDR); phdrs[0].p_filesz = phdrs[0].p_memsz = phdrs.size() * sizeof(Elf_Phdr); return phdrs; } InstructionSet isa_; ErrorDelayingOutputStream stream_; Section rodata_; Section text_; Section data_bimg_rel_ro_; Section bss_; Section dex_; CachedStringSection dynstr_; SymbolSection dynsym_; CachedSection hash_; CachedSection dynamic_; StringSection strtab_; SymbolSection symtab_; Section debug_frame_; Section debug_frame_hdr_; Section debug_info_; Section debug_line_; StringSection shstrtab_; BuildIdSection build_id_; std::vector> other_sections_; // List of used section in the order in which they were written. std::vector sections_; Section* current_section_; // The section which is currently being written. bool started_; bool finished_; bool write_program_headers_; // The size of the memory taken by the ELF file when loaded. size_t loaded_size_; // Used for allocation of virtual address space. Elf_Addr virtual_address_; DISALLOW_COPY_AND_ASSIGN(ElfBuilder); }; } // namespace art #endif // ART_LIBELFFILE_ELF_ELF_BUILDER_H_