Refactor linker files from compiler/ to dex2oat/.
This shifts some code from the libart-compiler.so to dex2oat
and reduces memory needed for JIT. We also avoid loading the
libart-dexlayout.so for JIT but the memory savings are
minimal (one shared clean page, two shared dirty pages and
some per-app kernel mmap data) as the code has never been
needed in memory by JIT.
aosp_angler-userdebug file sizes (stripped):
lib64/libart-compiler.so: 2989112 -> 2671888 (-310KiB)
lib/libart-compiler.so: 2160816 -> 1939276 (-216KiB)
bin/dex2oat: 141868 -> 368808 (+222KiB)
LOAD/executable elf mapping sizes:
lib64/libart-compiler.so: 2866308 -> 2555500 (-304KiB)
lib/libart-compiler.so: 2050960 -> 1834836 (-211KiB)
bin/dex2oat: 129316 -> 345916 (+212KiB)
Test: m test-art-host-gtest
Test: testrunner.py --host
Test: cd art/; mma; cd -
Change-Id: If62f02847a6cbb208eaf7e1f3e91af4663fa4a5f
diff --git a/compiler/linker/buffered_output_stream.cc b/compiler/linker/buffered_output_stream.cc
index 4c66c76..07066b7 100644
--- a/compiler/linker/buffered_output_stream.cc
+++ b/compiler/linker/buffered_output_stream.cc
@@ -19,6 +19,7 @@
#include <string.h>
namespace art {
+namespace linker {
BufferedOutputStream::BufferedOutputStream(std::unique_ptr<OutputStream> out)
: OutputStream(out->GetLocation()), // Before out is moved to out_.
@@ -67,4 +68,5 @@
return out_->Seek(offset, whence);
}
+} // namespace linker
} // namespace art
diff --git a/compiler/linker/buffered_output_stream.h b/compiler/linker/buffered_output_stream.h
index a2eefbb..66994e8 100644
--- a/compiler/linker/buffered_output_stream.h
+++ b/compiler/linker/buffered_output_stream.h
@@ -24,6 +24,7 @@
#include "globals.h"
namespace art {
+namespace linker {
class BufferedOutputStream FINAL : public OutputStream {
public:
@@ -49,6 +50,7 @@
DISALLOW_COPY_AND_ASSIGN(BufferedOutputStream);
};
+} // namespace linker
} // namespace art
#endif // ART_COMPILER_LINKER_BUFFERED_OUTPUT_STREAM_H_
diff --git a/compiler/linker/elf_builder.h b/compiler/linker/elf_builder.h
new file mode 100644
index 0000000..7941237
--- /dev/null
+++ b/compiler/linker/elf_builder.h
@@ -0,0 +1,1028 @@
+/*
+ * 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_COMPILER_LINKER_ELF_BUILDER_H_
+#define ART_COMPILER_LINKER_ELF_BUILDER_H_
+
+#include <vector>
+
+#include "arch/instruction_set.h"
+#include "arch/mips/instruction_set_features_mips.h"
+#include "base/array_ref.h"
+#include "base/bit_utils.h"
+#include "base/casts.h"
+#include "base/unix_file/fd_file.h"
+#include "elf_utils.h"
+#include "leb128.h"
+#include "linker/error_delaying_output_stream.h"
+
+namespace art {
+namespace linker {
+
+// 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 - DEX files and oat metadata.
+// .text - Compiled code.
+// .bss - Zero-initialized writeable section.
+// .MIPS.abiflags - MIPS specific section.
+// .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.
+// .eh_frame - Unwind information (CFI).
+// .eh_frame_hdr - Index of .eh_frame.
+// .debug_frame - Unwind information (CFI).
+// .debug_frame.oat_patches - Addresses for relocation.
+// .debug_info - Debug information.
+// .debug_info.oat_patches - Addresses for relocation.
+// .debug_abbrev - Decoding information for .debug_info.
+// .debug_str - Strings for .debug_info.
+// .debug_line - Line number tables.
+// .debug_line.oat_patches - Addresses for relocation.
+// .text.oat_patches - Addresses for relocation.
+// .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 <typename ElfTypes>
+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<ElfTypes>* 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),
+ started_(false),
+ finished_(false),
+ 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;
+ }
+
+ // Start writing of this section.
+ void Start() {
+ CHECK(!started_);
+ CHECK(!finished_);
+ started_ = true;
+ auto& sections = owner_->sections_;
+ // Check that the previous section is complete.
+ CHECK(sections.empty() || sections.back()->finished_);
+ // The first ELF section index is 1. Index 0 is reserved for NULL.
+ section_index_ = sections.size() + 1;
+ // Page-align if we switch between allocated and non-allocated sections,
+ // or if we change the type of allocation (e.g. executable vs non-executable).
+ if (!sections.empty()) {
+ if (header_.sh_flags != sections.back()->header_.sh_flags) {
+ header_.sh_addralign = kPageSize;
+ }
+ }
+ // Align file position.
+ if (header_.sh_type != SHT_NOBITS) {
+ header_.sh_offset = owner_->AlignFileOffset(header_.sh_addralign);
+ } else {
+ header_.sh_offset = 0;
+ }
+ // Align virtual memory address.
+ if ((header_.sh_flags & SHF_ALLOC) != 0) {
+ header_.sh_addr = owner_->AlignVirtualAddress(header_.sh_addralign);
+ } else {
+ header_.sh_addr = 0;
+ }
+ // Push this section on the list of written sections.
+ sections.push_back(this);
+ }
+
+ // Finish writing of this section.
+ void End() {
+ CHECK(started_);
+ CHECK(!finished_);
+ finished_ = true;
+ if (header_.sh_type == SHT_NOBITS) {
+ CHECK_GT(header_.sh_size, 0u);
+ } else {
+ // Use the current file position to determine section size.
+ off_t file_offset = owner_->stream_.Seek(0, kSeekCurrent);
+ CHECK_GE(file_offset, (off_t)header_.sh_offset);
+ header_.sh_size = file_offset - header_.sh_offset;
+ }
+ if ((header_.sh_flags & SHF_ALLOC) != 0) {
+ owner_->virtual_address_ += header_.sh_size;
+ }
+ }
+
+ // Get the location of this section in virtual memory.
+ Elf_Addr GetAddress() const {
+ CHECK(started_);
+ return header_.sh_addr;
+ }
+
+ // Returns the size of the content of this section.
+ Elf_Word GetSize() const {
+ if (finished_) {
+ return header_.sh_size;
+ } else {
+ CHECK(started_);
+ CHECK_NE(header_.sh_type, (Elf_Word)SHT_NOBITS);
+ return owner_->stream_.Seek(0, kSeekCurrent) - header_.sh_offset;
+ }
+ }
+
+ // Write this section as "NOBITS" section. (used for the .bss section)
+ // This means that the ELF file does not contain the initial data for this section
+ // and it will be zero-initialized when the ELF file is loaded in the running program.
+ void WriteNoBitsSection(Elf_Word size) {
+ DCHECK_NE(header_.sh_flags & SHF_ALLOC, 0u);
+ header_.sh_type = SHT_NOBITS;
+ Start();
+ header_.sh_size = size;
+ End();
+ }
+
+ // 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(started_);
+ CHECK(!finished_);
+ 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(started_);
+ DCHECK_NE(section_index_, 0u);
+ return section_index_;
+ }
+
+ private:
+ ElfBuilder<ElfTypes>* owner_;
+ Elf_Shdr header_;
+ Elf_Word section_index_;
+ const std::string name_;
+ const Section* const link_;
+ bool started_;
+ bool finished_;
+ Elf_Word phdr_flags_;
+ Elf_Word phdr_type_;
+
+ friend class ElfBuilder;
+
+ DISALLOW_COPY_AND_ASSIGN(Section);
+ };
+
+ class CachedSection : public Section {
+ public:
+ CachedSection(ElfBuilder<ElfTypes>* 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<const uint8_t*>(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<uint8_t> cache_;
+ };
+
+ // Writer of .dynstr section.
+ class CachedStringSection FINAL : public CachedSection {
+ public:
+ CachedStringSection(ElfBuilder<ElfTypes>* 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<ElfTypes>* owner,
+ const std::string& name,
+ Elf_Word flags,
+ Elf_Word align)
+ : Section(owner,
+ name,
+ SHT_STRTAB,
+ flags,
+ /* link */ nullptr,
+ /* info */ 0,
+ align,
+ /* entsize */ 0),
+ current_offset_(0) {
+ }
+
+ Elf_Word Write(const std::string& name) {
+ if (current_offset_ == 0) {
+ DCHECK(name.empty());
+ }
+ Elf_Word offset = current_offset_;
+ this->WriteFully(name.c_str(), name.length() + 1);
+ current_offset_ += name.length() + 1;
+ return offset;
+ }
+
+ private:
+ Elf_Word current_offset_;
+ };
+
+ // Writer of .dynsym and .symtab sections.
+ class SymbolSection FINAL : public CachedSection {
+ public:
+ SymbolSection(ElfBuilder<ElfTypes>* owner,
+ const std::string& name,
+ Elf_Word type,
+ Elf_Word flags,
+ Section* strtab)
+ : CachedSection(owner,
+ name,
+ type,
+ flags,
+ strtab,
+ /* info */ 0,
+ sizeof(Elf_Off),
+ sizeof(Elf_Sym)) {
+ // The symbol table always has to start with NULL symbol.
+ Elf_Sym null_symbol = Elf_Sym();
+ CachedSection::Add(&null_symbol, sizeof(null_symbol));
+ }
+
+ // Buffer symbol for this section. It will be written later.
+ // If the symbol's section is null, it will be considered absolute (SHN_ABS).
+ // (we use this in JIT to reference code which is stored outside the debug ELF file)
+ void Add(Elf_Word name,
+ const Section* section,
+ Elf_Addr addr,
+ Elf_Word size,
+ uint8_t binding,
+ uint8_t type) {
+ Elf_Word section_index;
+ if (section != nullptr) {
+ DCHECK_LE(section->GetAddress(), addr);
+ DCHECK_LE(addr, section->GetAddress() + section->GetSize());
+ section_index = section->GetSectionIndex();
+ } else {
+ section_index = static_cast<Elf_Word>(SHN_ABS);
+ }
+ Add(name, section_index, addr, size, binding, type);
+ }
+
+ void Add(Elf_Word name,
+ Elf_Word section_index,
+ 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_shndx = section_index;
+ sym.st_info = (binding << 4) + (type & 0xf);
+ CachedSection::Add(&sym, sizeof(sym));
+ }
+ };
+
+ class AbiflagsSection FINAL : public Section {
+ public:
+ // Section with Mips abiflag info.
+ static constexpr uint8_t MIPS_AFL_REG_NONE = 0; // no registers
+ static constexpr uint8_t MIPS_AFL_REG_32 = 1; // 32-bit registers
+ static constexpr uint8_t MIPS_AFL_REG_64 = 2; // 64-bit registers
+ static constexpr uint32_t MIPS_AFL_FLAGS1_ODDSPREG = 1; // Uses odd single-prec fp regs
+ static constexpr uint8_t MIPS_ABI_FP_DOUBLE = 1; // -mdouble-float
+ static constexpr uint8_t MIPS_ABI_FP_XX = 5; // -mfpxx
+ static constexpr uint8_t MIPS_ABI_FP_64A = 7; // -mips32r* -mfp64 -mno-odd-spreg
+
+ AbiflagsSection(ElfBuilder<ElfTypes>* owner,
+ const std::string& name,
+ Elf_Word type,
+ Elf_Word flags,
+ const Section* link,
+ Elf_Word info,
+ Elf_Word align,
+ Elf_Word entsize,
+ InstructionSet isa,
+ const InstructionSetFeatures* features)
+ : Section(owner, name, type, flags, link, info, align, entsize) {
+ if (isa == kMips || isa == kMips64) {
+ bool fpu32 = false; // assume mips64 values
+ uint8_t isa_rev = 6; // assume mips64 values
+ if (isa == kMips) {
+ // adjust for mips32 values
+ fpu32 = features->AsMipsInstructionSetFeatures()->Is32BitFloatingPoint();
+ isa_rev = features->AsMipsInstructionSetFeatures()->IsR6()
+ ? 6
+ : features->AsMipsInstructionSetFeatures()->IsMipsIsaRevGreaterThanEqual2()
+ ? (fpu32 ? 2 : 5)
+ : 1;
+ }
+ abiflags_.version = 0; // version of flags structure
+ abiflags_.isa_level = (isa == kMips) ? 32 : 64;
+ abiflags_.isa_rev = isa_rev;
+ abiflags_.gpr_size = (isa == kMips) ? MIPS_AFL_REG_32 : MIPS_AFL_REG_64;
+ abiflags_.cpr1_size = fpu32 ? MIPS_AFL_REG_32 : MIPS_AFL_REG_64;
+ abiflags_.cpr2_size = MIPS_AFL_REG_NONE;
+ // Set the fp_abi to MIPS_ABI_FP_64A for mips32 with 64-bit FPUs (ie: mips32 R5 and R6).
+ // Otherwise set to MIPS_ABI_FP_DOUBLE.
+ abiflags_.fp_abi = (isa == kMips && !fpu32) ? MIPS_ABI_FP_64A : MIPS_ABI_FP_DOUBLE;
+ abiflags_.isa_ext = 0;
+ abiflags_.ases = 0;
+ // To keep the code simple, we are not using odd FP reg for single floats for both
+ // mips32 and mips64 ART. Therefore we are not setting the MIPS_AFL_FLAGS1_ODDSPREG bit.
+ abiflags_.flags1 = 0;
+ abiflags_.flags2 = 0;
+ }
+ }
+
+ Elf_Word GetSize() const {
+ return sizeof(abiflags_);
+ }
+
+ void Write() {
+ this->WriteFully(&abiflags_, sizeof(abiflags_));
+ }
+
+ private:
+ struct {
+ uint16_t version; // version of this structure
+ uint8_t isa_level, isa_rev, gpr_size, cpr1_size, cpr2_size;
+ uint8_t fp_abi;
+ uint32_t isa_ext, ases, flags1, flags2;
+ } abiflags_;
+ };
+
+ class BuildIdSection FINAL : public Section {
+ public:
+ BuildIdSection(ElfBuilder<ElfTypes>* 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) {
+ }
+
+ 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.
+ }
+
+ 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, const InstructionSetFeatures* features, OutputStream* output)
+ : isa_(isa),
+ features_(features),
+ 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),
+ bss_(this, ".bss", 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)),
+ eh_frame_(this, ".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0),
+ eh_frame_hdr_(this, ".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0),
+ 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_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),
+ abiflags_(this, ".MIPS.abiflags", SHT_MIPS_ABIFLAGS, SHF_ALLOC, nullptr, 0, kPageSize, 0,
+ isa, features),
+ build_id_(this, ".note.gnu.build-id", SHT_NOTE, SHF_ALLOC, nullptr, 0, 4, 0),
+ started_(false),
+ write_program_headers_(false),
+ loaded_size_(0u),
+ virtual_address_(0) {
+ text_.phdr_flags_ = PF_R | PF_X;
+ bss_.phdr_flags_ = PF_R | PF_W;
+ dynamic_.phdr_flags_ = PF_R | PF_W;
+ dynamic_.phdr_type_ = PT_DYNAMIC;
+ eh_frame_hdr_.phdr_type_ = PT_GNU_EH_FRAME;
+ abiflags_.phdr_type_ = PT_MIPS_ABIFLAGS;
+ build_id_.phdr_type_ = PT_NOTE;
+ }
+ ~ElfBuilder() {}
+
+ InstructionSet GetIsa() { return isa_; }
+ Section* GetRoData() { return &rodata_; }
+ Section* GetText() { return &text_; }
+ Section* GetBss() { return &bss_; }
+ StringSection* GetStrTab() { return &strtab_; }
+ SymbolSection* GetSymTab() { return &symtab_; }
+ Section* GetEhFrame() { return &eh_frame_; }
+ Section* GetEhFrameHdr() { return &eh_frame_hdr_; }
+ Section* GetDebugFrame() { return &debug_frame_; }
+ Section* GetDebugInfo() { return &debug_info_; }
+ Section* GetDebugLine() { return &debug_line_; }
+
+ // Encode patch locations as LEB128 list of deltas between consecutive addresses.
+ // (exposed publicly for tests)
+ static void EncodeOatPatches(const ArrayRef<const uintptr_t>& locations,
+ std::vector<uint8_t>* buffer) {
+ buffer->reserve(buffer->size() + locations.size() * 2); // guess 2 bytes per ULEB128.
+ uintptr_t address = 0; // relative to start of section.
+ for (uintptr_t location : locations) {
+ DCHECK_GE(location, address) << "Patch locations are not in sorted order";
+ EncodeUnsignedLeb128(buffer, dchecked_integral_cast<uint32_t>(location - address));
+ address = location;
+ }
+ }
+
+ void WritePatches(const char* name, const ArrayRef<const uintptr_t>& patch_locations) {
+ std::vector<uint8_t> buffer;
+ EncodeOatPatches(patch_locations, &buffer);
+ std::unique_ptr<Section> s(new Section(this, name, SHT_OAT_PATCH, 0, nullptr, 0, 1, 0));
+ s->Start();
+ s->WriteFully(buffer.data(), buffer.size());
+ s->End();
+ other_sections_.push_back(std::move(s));
+ }
+
+ void WriteSection(const char* name, const std::vector<uint8_t>* buffer) {
+ std::unique_ptr<Section> 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;
+ }
+
+ void End() {
+ DCHECK(started_);
+
+ // 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_.
+ // TODO: Either refactor the .eh_frame creation so that it counts towards loaded_size_,
+ // or remove all support for .eh_frame. (The currently unused .eh_frame counts towards
+ // the virtual_address_ but we don't consider it for 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();
+ }
+ }
+ shstrtab_.End();
+
+ // Write section headers at the end of the ELF file.
+ std::vector<Elf_Shdr> 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]));
+
+ // 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_, features_);
+ 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<Elf_Phdr> 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();
+ }
+
+ // 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 bss_size,
+ Elf_Word bss_methods_offset,
+ Elf_Word bss_roots_offset) {
+ 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);
+ }
+
+ // Calculate addresses of .text, .bss and .dynstr.
+ DCHECK_EQ(rodata_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize));
+ DCHECK_EQ(text_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize));
+ DCHECK_EQ(bss_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize));
+ DCHECK_EQ(dynstr_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize));
+ Elf_Word rodata_address = rodata_.GetAddress();
+ Elf_Word text_address = RoundUp(rodata_address + rodata_size, kPageSize);
+ Elf_Word bss_address = RoundUp(text_address + text_size, kPageSize);
+ Elf_Word abiflags_address = RoundUp(bss_address + bss_size, kPageSize);
+ Elf_Word abiflags_size = 0;
+ if (isa_ == kMips || isa_ == kMips64) {
+ abiflags_size = abiflags_.GetSize();
+ }
+ Elf_Word dynstr_address = RoundUp(abiflags_address + abiflags_size, kPageSize);
+
+ // Cache .dynstr, .dynsym and .hash data.
+ dynstr_.Add(""); // dynstr should start with empty string.
+ Elf_Word rodata_index = rodata_.GetSectionIndex();
+ Elf_Word oatdata = dynstr_.Add("oatdata");
+ dynsym_.Add(oatdata, rodata_index, rodata_address, rodata_size, STB_GLOBAL, STT_OBJECT);
+ if (text_size != 0u) {
+ Elf_Word text_index = rodata_index + 1u;
+ Elf_Word oatexec = dynstr_.Add("oatexec");
+ dynsym_.Add(oatexec, text_index, text_address, text_size, STB_GLOBAL, STT_OBJECT);
+ Elf_Word oatlastword = dynstr_.Add("oatlastword");
+ Elf_Word oatlastword_address = text_address + text_size - 4;
+ dynsym_.Add(oatlastword, text_index, 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_address + rodata_size - 4;
+ dynsym_.Add(oatlastword, rodata_index, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT);
+ }
+ DCHECK_LE(bss_roots_offset, bss_size);
+ if (bss_size != 0u) {
+ Elf_Word bss_index = rodata_index + 1u + (text_size != 0 ? 1u : 0u);
+ Elf_Word oatbss = dynstr_.Add("oatbss");
+ dynsym_.Add(oatbss, bss_index, bss_address, 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_address + bss_methods_offset;
+ Elf_Word bss_methods_size = bss_roots_offset - bss_methods_offset;
+ Elf_Word oatbssroots = dynstr_.Add("oatbssmethods");
+ dynsym_.Add(
+ oatbssroots, bss_index, 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_address + bss_roots_offset;
+ Elf_Word bss_roots_size = bss_size - bss_roots_offset;
+ Elf_Word oatbssroots = dynstr_.Add("oatbssroots");
+ dynsym_.Add(
+ oatbssroots, bss_index, bss_roots_address, bss_roots_size, STB_GLOBAL, STT_OBJECT);
+ }
+ Elf_Word oatbsslastword = dynstr_.Add("oatbsslastword");
+ Elf_Word bsslastword_address = bss_address + bss_size - 4;
+ dynsym_.Add(oatbsslastword, bss_index, bsslastword_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<Elf_Word> 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); // Dummy 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]));
+
+ // Calculate addresses of .dynsym, .hash and .dynamic.
+ DCHECK_EQ(dynstr_.header_.sh_flags, dynsym_.header_.sh_flags);
+ DCHECK_EQ(dynsym_.header_.sh_flags, hash_.header_.sh_flags);
+ Elf_Word dynsym_address =
+ RoundUp(dynstr_address + dynstr_.GetCacheSize(), dynsym_.header_.sh_addralign);
+ Elf_Word hash_address =
+ RoundUp(dynsym_address + dynsym_.GetCacheSize(), hash_.header_.sh_addralign);
+ DCHECK_EQ(dynamic_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize));
+ Elf_Word dynamic_address = RoundUp(hash_address + dynsym_.GetCacheSize(), kPageSize);
+
+ Elf_Dyn dyns[] = {
+ { DT_HASH, { hash_address } },
+ { DT_STRTAB, { dynstr_address } },
+ { DT_SYMTAB, { dynsym_address } },
+ { DT_SYMENT, { sizeof(Elf_Sym) } },
+ { DT_STRSZ, { dynstr_.GetCacheSize() } },
+ { DT_SONAME, { soname_offset } },
+ { DT_NULL, { 0 } },
+ };
+ dynamic_.Add(&dyns, sizeof(dyns));
+
+ loaded_size_ = RoundUp(dynamic_address + dynamic_.GetCacheSize(), kPageSize);
+ }
+
+ void WriteDynamicSection() {
+ dynstr_.WriteCachedSection();
+ dynsym_.WriteCachedSection();
+ hash_.WriteCachedSection();
+ dynamic_.WriteCachedSection();
+
+ CHECK_EQ(loaded_size_, RoundUp(dynamic_.GetAddress() + dynamic_.GetSize(), kPageSize));
+ }
+
+ Elf_Word GetLoadedSize() {
+ CHECK_NE(loaded_size_, 0u);
+ return loaded_size_;
+ }
+
+ void WriteMIPSabiflagsSection() {
+ abiflags_.Start();
+ abiflags_.Write();
+ abiflags_.End();
+ }
+
+ 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);
+ }
+
+ // 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);
+ }
+
+ Elf_Addr AlignVirtualAddress(size_t alignment) {
+ return virtual_address_ = RoundUp(virtual_address_, alignment);
+ }
+
+ private:
+ static Elf_Ehdr MakeElfHeader(InstructionSet isa, const InstructionSetFeatures* features) {
+ Elf_Ehdr elf_header = Elf_Ehdr();
+ switch (isa) {
+ case kArm:
+ // Fall through.
+ case kThumb2: {
+ elf_header.e_machine = EM_ARM;
+ elf_header.e_flags = EF_ARM_EABI_VER5;
+ break;
+ }
+ case kArm64: {
+ elf_header.e_machine = EM_AARCH64;
+ elf_header.e_flags = 0;
+ break;
+ }
+ case kX86: {
+ elf_header.e_machine = EM_386;
+ elf_header.e_flags = 0;
+ break;
+ }
+ case kX86_64: {
+ elf_header.e_machine = EM_X86_64;
+ elf_header.e_flags = 0;
+ break;
+ }
+ case kMips: {
+ elf_header.e_machine = EM_MIPS;
+ elf_header.e_flags = (EF_MIPS_NOREORDER |
+ EF_MIPS_PIC |
+ EF_MIPS_CPIC |
+ EF_MIPS_ABI_O32 |
+ (features->AsMipsInstructionSetFeatures()->IsR6()
+ ? EF_MIPS_ARCH_32R6
+ : EF_MIPS_ARCH_32R2));
+ break;
+ }
+ case kMips64: {
+ elf_header.e_machine = EM_MIPS;
+ elf_header.e_flags = (EF_MIPS_NOREORDER |
+ EF_MIPS_PIC |
+ EF_MIPS_CPIC |
+ EF_MIPS_ARCH_64R6);
+ break;
+ }
+ case 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);
+ elf_header.e_phoff = sizeof(Elf_Ehdr);
+ return elf_header;
+ }
+
+ // Create program headers based on written sections.
+ std::vector<Elf_Phdr> MakeProgramHeaders() {
+ CHECK(!sections_.empty());
+ std::vector<Elf_Phdr> 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) {
+ // 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_;
+ const InstructionSetFeatures* features_;
+
+ ErrorDelayingOutputStream stream_;
+
+ Section rodata_;
+ Section text_;
+ Section bss_;
+ CachedStringSection dynstr_;
+ SymbolSection dynsym_;
+ CachedSection hash_;
+ CachedSection dynamic_;
+ Section eh_frame_;
+ Section eh_frame_hdr_;
+ StringSection strtab_;
+ SymbolSection symtab_;
+ Section debug_frame_;
+ Section debug_info_;
+ Section debug_line_;
+ StringSection shstrtab_;
+ AbiflagsSection abiflags_;
+ BuildIdSection build_id_;
+ std::vector<std::unique_ptr<Section>> other_sections_;
+
+ // List of used section in the order in which they were written.
+ std::vector<Section*> sections_;
+
+ bool started_;
+ 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 linker
+} // namespace art
+
+#endif // ART_COMPILER_LINKER_ELF_BUILDER_H_
diff --git a/compiler/linker/error_delaying_output_stream.h b/compiler/linker/error_delaying_output_stream.h
index 99410e4..33e6b5a 100644
--- a/compiler/linker/error_delaying_output_stream.h
+++ b/compiler/linker/error_delaying_output_stream.h
@@ -22,6 +22,7 @@
#include "base/logging.h"
namespace art {
+namespace linker {
// OutputStream wrapper that delays reporting an error until Flush().
class ErrorDelayingOutputStream FINAL : public OutputStream {
@@ -96,6 +97,7 @@
off_t output_offset_; // Keep track of the current position in the stream.
};
+} // namespace linker
} // namespace art
#endif // ART_COMPILER_LINKER_ERROR_DELAYING_OUTPUT_STREAM_H_
diff --git a/compiler/linker/file_output_stream.cc b/compiler/linker/file_output_stream.cc
index bbfbdfd..477846e 100644
--- a/compiler/linker/file_output_stream.cc
+++ b/compiler/linker/file_output_stream.cc
@@ -22,6 +22,7 @@
#include "base/unix_file/fd_file.h"
namespace art {
+namespace linker {
FileOutputStream::FileOutputStream(File* file) : OutputStream(file->GetPath()), file_(file) {}
@@ -37,4 +38,5 @@
return file_->Flush() == 0;
}
+} // namespace linker
} // namespace art
diff --git a/compiler/linker/file_output_stream.h b/compiler/linker/file_output_stream.h
index f2d8453..28296a4 100644
--- a/compiler/linker/file_output_stream.h
+++ b/compiler/linker/file_output_stream.h
@@ -22,6 +22,7 @@
#include "os.h"
namespace art {
+namespace linker {
class FileOutputStream FINAL : public OutputStream {
public:
@@ -41,6 +42,7 @@
DISALLOW_COPY_AND_ASSIGN(FileOutputStream);
};
+} // namespace linker
} // namespace art
#endif // ART_COMPILER_LINKER_FILE_OUTPUT_STREAM_H_
diff --git a/compiler/linker/multi_oat_relative_patcher.cc b/compiler/linker/multi_oat_relative_patcher.cc
deleted file mode 100644
index 4ae75d6..0000000
--- a/compiler/linker/multi_oat_relative_patcher.cc
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#include "multi_oat_relative_patcher.h"
-
-#include "base/bit_utils.h"
-#include "base/logging.h"
-#include "globals.h"
-
-namespace art {
-namespace linker {
-
-MultiOatRelativePatcher::MultiOatRelativePatcher(InstructionSet instruction_set,
- const InstructionSetFeatures* features)
- : method_offset_map_(),
- relative_patcher_(
- linker::RelativePatcher::Create(instruction_set, features, &method_offset_map_)),
- adjustment_(0u),
- instruction_set_(instruction_set),
- start_size_code_alignment_(0u),
- start_size_relative_call_thunks_(0u),
- start_size_misc_thunks_(0u) {
-}
-
-void MultiOatRelativePatcher::StartOatFile(uint32_t adjustment) {
- DCHECK_ALIGNED(adjustment, kPageSize);
- adjustment_ = adjustment;
-
- start_size_code_alignment_ = relative_patcher_->CodeAlignmentSize();
- start_size_relative_call_thunks_ = relative_patcher_->RelativeCallThunksSize();
- start_size_misc_thunks_ = relative_patcher_->MiscThunksSize();
-}
-
-uint32_t MultiOatRelativePatcher::CodeAlignmentSize() const {
- DCHECK_GE(relative_patcher_->CodeAlignmentSize(), start_size_code_alignment_);
- return relative_patcher_->CodeAlignmentSize() - start_size_code_alignment_;
-}
-
-uint32_t MultiOatRelativePatcher::RelativeCallThunksSize() const {
- DCHECK_GE(relative_patcher_->RelativeCallThunksSize(), start_size_relative_call_thunks_);
- return relative_patcher_->RelativeCallThunksSize() - start_size_relative_call_thunks_;
-}
-
-uint32_t MultiOatRelativePatcher::MiscThunksSize() const {
- DCHECK_GE(relative_patcher_->MiscThunksSize(), start_size_misc_thunks_);
- return relative_patcher_->MiscThunksSize() - start_size_misc_thunks_;
-}
-
-std::pair<bool, uint32_t> MultiOatRelativePatcher::MethodOffsetMap::FindMethodOffset(
- MethodReference ref) {
- auto it = map.find(ref);
- if (it == map.end()) {
- return std::pair<bool, uint32_t>(false, 0u);
- } else {
- return std::pair<bool, uint32_t>(true, it->second);
- }
-}
-} // namespace linker
-} // namespace art
diff --git a/compiler/linker/multi_oat_relative_patcher.h b/compiler/linker/multi_oat_relative_patcher.h
deleted file mode 100644
index 02cd4b0..0000000
--- a/compiler/linker/multi_oat_relative_patcher.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2016 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_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
-#define ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
-
-#include "arch/instruction_set.h"
-#include "debug/method_debug_info.h"
-#include "method_reference.h"
-#include "relative_patcher.h"
-#include "safe_map.h"
-
-namespace art {
-
-class CompiledMethod;
-class LinkerPatch;
-class InstructionSetFeatures;
-
-namespace linker {
-
-// MultiOatRelativePatcher is a helper class for handling patching across
-// any number of oat files. It provides storage for method code offsets
-// and wraps RelativePatcher calls, adjusting relative offsets according
-// to the value set by SetAdjustment().
-class MultiOatRelativePatcher FINAL {
- public:
- using const_iterator = SafeMap<MethodReference, uint32_t>::const_iterator;
-
- MultiOatRelativePatcher(InstructionSet instruction_set, const InstructionSetFeatures* features);
-
- // Mark the start of a new oat file (for statistics retrieval) and set the
- // adjustment for a new oat file to apply to all relative offsets that are
- // passed to the MultiOatRelativePatcher.
- //
- // The adjustment should be the global offset of the base from which relative
- // offsets are calculated, such as the start of .rodata for the current oat file.
- // It must must never point directly to a method's code to avoid relative offsets
- // with value 0 because this value is used as a missing offset indication in
- // GetOffset() and an error indication in WriteThunks(). Additionally, it must be
- // page-aligned, so that it does not skew alignment calculations, say arm64 ADRP.
- void StartOatFile(uint32_t adjustment);
-
- // Get relative offset. Returns 0 when the offset has not been set yet.
- uint32_t GetOffset(MethodReference method_ref) {
- auto it = method_offset_map_.map.find(method_ref);
- return (it != method_offset_map_.map.end()) ? it->second - adjustment_ : 0u;
- }
-
- // Set the offset.
- void SetOffset(MethodReference method_ref, uint32_t offset) {
- method_offset_map_.map.Put(method_ref, offset + adjustment_);
- }
-
- // Wrapper around RelativePatcher::ReserveSpace(), doing offset adjustment.
- uint32_t ReserveSpace(uint32_t offset,
- const CompiledMethod* compiled_method,
- MethodReference method_ref) {
- offset += adjustment_;
- offset = relative_patcher_->ReserveSpace(offset, compiled_method, method_ref);
- offset -= adjustment_;
- return offset;
- }
-
- // Wrapper around RelativePatcher::ReserveSpaceEnd(), doing offset adjustment.
- uint32_t ReserveSpaceEnd(uint32_t offset) {
- offset += adjustment_;
- offset = relative_patcher_->ReserveSpaceEnd(offset);
- offset -= adjustment_;
- return offset;
- }
-
- // Wrapper around RelativePatcher::WriteThunks(), doing offset adjustment.
- uint32_t WriteThunks(OutputStream* out, uint32_t offset) {
- offset += adjustment_;
- offset = relative_patcher_->WriteThunks(out, offset);
- if (offset != 0u) { // 0u indicates write error.
- offset -= adjustment_;
- }
- return offset;
- }
-
- // Wrapper around RelativePatcher::PatchCall(), doing offset adjustment.
- void PatchCall(std::vector<uint8_t>* code,
- uint32_t literal_offset,
- uint32_t patch_offset,
- uint32_t target_offset) {
- patch_offset += adjustment_;
- target_offset += adjustment_;
- relative_patcher_->PatchCall(code, literal_offset, patch_offset, target_offset);
- }
-
- // Wrapper around RelativePatcher::PatchPcRelativeReference(), doing offset adjustment.
- void PatchPcRelativeReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) {
- patch_offset += adjustment_;
- target_offset += adjustment_;
- relative_patcher_->PatchPcRelativeReference(code, patch, patch_offset, target_offset);
- }
-
- void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset) {
- patch_offset += adjustment_;
- relative_patcher_->PatchBakerReadBarrierBranch(code, patch, patch_offset);
- }
-
- std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(size_t executable_offset) {
- executable_offset += adjustment_;
- return relative_patcher_->GenerateThunkDebugInfo(executable_offset);
- }
-
- // Wrappers around RelativePatcher for statistics retrieval.
- uint32_t CodeAlignmentSize() const;
- uint32_t RelativeCallThunksSize() const;
- uint32_t MiscThunksSize() const;
-
- private:
- // Map method reference to assigned offset.
- // Wrap the map in a class implementing linker::RelativePatcherTargetProvider.
- class MethodOffsetMap : public linker::RelativePatcherTargetProvider {
- public:
- std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) OVERRIDE;
- SafeMap<MethodReference, uint32_t> map;
- };
-
- MethodOffsetMap method_offset_map_;
- std::unique_ptr<RelativePatcher> relative_patcher_;
- uint32_t adjustment_;
- InstructionSet instruction_set_;
-
- uint32_t start_size_code_alignment_;
- uint32_t start_size_relative_call_thunks_;
- uint32_t start_size_misc_thunks_;
-
- friend class MultiOatRelativePatcherTest;
-
- DISALLOW_COPY_AND_ASSIGN(MultiOatRelativePatcher);
-};
-
-} // namespace linker
-} // namespace art
-
-#endif // ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
diff --git a/compiler/linker/multi_oat_relative_patcher_test.cc b/compiler/linker/multi_oat_relative_patcher_test.cc
deleted file mode 100644
index 5c359dc..0000000
--- a/compiler/linker/multi_oat_relative_patcher_test.cc
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#include "multi_oat_relative_patcher.h"
-
-#include "compiled_method.h"
-#include "debug/method_debug_info.h"
-#include "gtest/gtest.h"
-#include "vector_output_stream.h"
-
-namespace art {
-namespace linker {
-
-static const MethodReference kNullMethodRef = MethodReference(nullptr, 0u);
-
-class MultiOatRelativePatcherTest : public testing::Test {
- protected:
- class MockPatcher : public RelativePatcher {
- public:
- MockPatcher() { }
-
- uint32_t ReserveSpace(uint32_t offset,
- const CompiledMethod* compiled_method ATTRIBUTE_UNUSED,
- MethodReference method_ref) OVERRIDE {
- last_reserve_offset_ = offset;
- last_reserve_method_ = method_ref;
- offset += next_reserve_adjustment_;
- next_reserve_adjustment_ = 0u;
- return offset;
- }
-
- uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE {
- last_reserve_offset_ = offset;
- last_reserve_method_ = kNullMethodRef;
- offset += next_reserve_adjustment_;
- next_reserve_adjustment_ = 0u;
- return offset;
- }
-
- uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE {
- last_write_offset_ = offset;
- if (next_write_alignment_ != 0u) {
- offset += next_write_alignment_;
- bool success = WriteCodeAlignment(out, next_write_alignment_);
- CHECK(success);
- next_write_alignment_ = 0u;
- }
- if (next_write_call_thunk_ != 0u) {
- offset += next_write_call_thunk_;
- std::vector<uint8_t> thunk(next_write_call_thunk_, 'c');
- bool success = WriteThunk(out, ArrayRef<const uint8_t>(thunk));
- CHECK(success);
- next_write_call_thunk_ = 0u;
- }
- if (next_write_misc_thunk_ != 0u) {
- offset += next_write_misc_thunk_;
- std::vector<uint8_t> thunk(next_write_misc_thunk_, 'm');
- bool success = WriteMiscThunk(out, ArrayRef<const uint8_t>(thunk));
- CHECK(success);
- next_write_misc_thunk_ = 0u;
- }
- return offset;
- }
-
- void PatchCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
- uint32_t literal_offset,
- uint32_t patch_offset,
- uint32_t target_offset) OVERRIDE {
- last_literal_offset_ = literal_offset;
- last_patch_offset_ = patch_offset;
- last_target_offset_ = target_offset;
- }
-
- void PatchPcRelativeReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) OVERRIDE {
- last_literal_offset_ = patch.LiteralOffset();
- last_patch_offset_ = patch_offset;
- last_target_offset_ = target_offset;
- }
-
- void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
- const LinkerPatch& patch ATTRIBUTE_UNUSED,
- uint32_t patch_offset ATTRIBUTE_UNUSED) {
- LOG(FATAL) << "UNIMPLEMENTED";
- }
-
- std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(
- uint32_t executable_offset ATTRIBUTE_UNUSED) {
- LOG(FATAL) << "UNIMPLEMENTED";
- UNREACHABLE();
- }
-
- uint32_t last_reserve_offset_ = 0u;
- MethodReference last_reserve_method_ = kNullMethodRef;
- uint32_t next_reserve_adjustment_ = 0u;
-
- uint32_t last_write_offset_ = 0u;
- uint32_t next_write_alignment_ = 0u;
- uint32_t next_write_call_thunk_ = 0u;
- uint32_t next_write_misc_thunk_ = 0u;
-
- uint32_t last_literal_offset_ = 0u;
- uint32_t last_patch_offset_ = 0u;
- uint32_t last_target_offset_ = 0u;
- };
-
- MultiOatRelativePatcherTest()
- : instruction_set_features_(InstructionSetFeatures::FromCppDefines()),
- patcher_(kRuntimeISA, instruction_set_features_.get()) {
- std::unique_ptr<MockPatcher> mock(new MockPatcher());
- mock_ = mock.get();
- patcher_.relative_patcher_ = std::move(mock);
- }
-
- std::unique_ptr<const InstructionSetFeatures> instruction_set_features_;
- MultiOatRelativePatcher patcher_;
- MockPatcher* mock_;
-};
-
-TEST_F(MultiOatRelativePatcherTest, Offsets) {
- const DexFile* dex_file = reinterpret_cast<const DexFile*>(1);
- MethodReference ref1(dex_file, 1u);
- MethodReference ref2(dex_file, 2u);
- EXPECT_EQ(0u, patcher_.GetOffset(ref1));
- EXPECT_EQ(0u, patcher_.GetOffset(ref2));
-
- uint32_t adjustment1 = 0x1000;
- patcher_.StartOatFile(adjustment1);
- EXPECT_EQ(0u, patcher_.GetOffset(ref1));
- EXPECT_EQ(0u, patcher_.GetOffset(ref2));
-
- uint32_t off1 = 0x1234;
- patcher_.SetOffset(ref1, off1);
- EXPECT_EQ(off1, patcher_.GetOffset(ref1));
- EXPECT_EQ(0u, patcher_.GetOffset(ref2));
-
- uint32_t adjustment2 = 0x30000;
- patcher_.StartOatFile(adjustment2);
- EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1));
- EXPECT_EQ(0u, patcher_.GetOffset(ref2));
-
- uint32_t off2 = 0x4321;
- patcher_.SetOffset(ref2, off2);
- EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1));
- EXPECT_EQ(off2, patcher_.GetOffset(ref2));
-
- uint32_t adjustment3 = 0x78000;
- patcher_.StartOatFile(adjustment3);
- EXPECT_EQ(off1 + adjustment1 - adjustment3, patcher_.GetOffset(ref1));
- EXPECT_EQ(off2 + adjustment2 - adjustment3, patcher_.GetOffset(ref2));
-}
-
-TEST_F(MultiOatRelativePatcherTest, OffsetsInReserve) {
- const DexFile* dex_file = reinterpret_cast<const DexFile*>(1);
- MethodReference ref1(dex_file, 1u);
- MethodReference ref2(dex_file, 2u);
- MethodReference ref3(dex_file, 3u);
- const CompiledMethod* method = reinterpret_cast<const CompiledMethod*>(-1);
-
- uint32_t adjustment1 = 0x1000;
- patcher_.StartOatFile(adjustment1);
-
- uint32_t method1_offset = 0x100;
- uint32_t method1_offset_check = patcher_.ReserveSpace(method1_offset, method, ref1);
- ASSERT_EQ(adjustment1 + method1_offset, mock_->last_reserve_offset_);
- ASSERT_TRUE(ref1 == mock_->last_reserve_method_);
- ASSERT_EQ(method1_offset, method1_offset_check);
-
- uint32_t method2_offset = 0x1230;
- uint32_t method2_reserve_adjustment = 0x10;
- mock_->next_reserve_adjustment_ = method2_reserve_adjustment;
- uint32_t method2_offset_adjusted = patcher_.ReserveSpace(method2_offset, method, ref2);
- ASSERT_EQ(adjustment1 + method2_offset, mock_->last_reserve_offset_);
- ASSERT_TRUE(ref2 == mock_->last_reserve_method_);
- ASSERT_EQ(method2_offset + method2_reserve_adjustment, method2_offset_adjusted);
-
- uint32_t end1_offset = 0x4320;
- uint32_t end1_offset_check = patcher_.ReserveSpaceEnd(end1_offset);
- ASSERT_EQ(adjustment1 + end1_offset, mock_->last_reserve_offset_);
- ASSERT_TRUE(kNullMethodRef == mock_->last_reserve_method_);
- ASSERT_EQ(end1_offset, end1_offset_check);
-
- uint32_t adjustment2 = 0xd000;
- patcher_.StartOatFile(adjustment2);
-
- uint32_t method3_offset = 0xf00;
- uint32_t method3_offset_check = patcher_.ReserveSpace(method3_offset, method, ref3);
- ASSERT_EQ(adjustment2 + method3_offset, mock_->last_reserve_offset_);
- ASSERT_TRUE(ref3 == mock_->last_reserve_method_);
- ASSERT_EQ(method3_offset, method3_offset_check);
-
- uint32_t end2_offset = 0x2400;
- uint32_t end2_reserve_adjustment = 0x20;
- mock_->next_reserve_adjustment_ = end2_reserve_adjustment;
- uint32_t end2_offset_adjusted = patcher_.ReserveSpaceEnd(end2_offset);
- ASSERT_EQ(adjustment2 + end2_offset, mock_->last_reserve_offset_);
- ASSERT_TRUE(kNullMethodRef == mock_->last_reserve_method_);
- ASSERT_EQ(end2_offset + end2_reserve_adjustment, end2_offset_adjusted);
-}
-
-TEST_F(MultiOatRelativePatcherTest, Write) {
- std::vector<uint8_t> output;
- VectorOutputStream vos("output", &output);
-
- uint32_t adjustment1 = 0x1000;
- patcher_.StartOatFile(adjustment1);
-
- uint32_t method1_offset = 0x100;
- uint32_t method1_offset_check = patcher_.WriteThunks(&vos, method1_offset);
- ASSERT_EQ(adjustment1 + method1_offset, mock_->last_write_offset_);
- ASSERT_EQ(method1_offset, method1_offset_check);
- vos.WriteFully("1", 1); // Mark method1.
-
- uint32_t method2_offset = 0x1230;
- uint32_t method2_alignment_size = 1;
- uint32_t method2_call_thunk_size = 2;
- mock_->next_write_alignment_ = method2_alignment_size;
- mock_->next_write_call_thunk_ = method2_call_thunk_size;
- uint32_t method2_offset_adjusted = patcher_.WriteThunks(&vos, method2_offset);
- ASSERT_EQ(adjustment1 + method2_offset, mock_->last_write_offset_);
- ASSERT_EQ(method2_offset + method2_alignment_size + method2_call_thunk_size,
- method2_offset_adjusted);
- vos.WriteFully("2", 1); // Mark method2.
-
- EXPECT_EQ(method2_alignment_size, patcher_.CodeAlignmentSize());
- EXPECT_EQ(method2_call_thunk_size, patcher_.RelativeCallThunksSize());
-
- uint32_t adjustment2 = 0xd000;
- patcher_.StartOatFile(adjustment2);
-
- uint32_t method3_offset = 0xf00;
- uint32_t method3_alignment_size = 2;
- uint32_t method3_misc_thunk_size = 1;
- mock_->next_write_alignment_ = method3_alignment_size;
- mock_->next_write_misc_thunk_ = method3_misc_thunk_size;
- uint32_t method3_offset_adjusted = patcher_.WriteThunks(&vos, method3_offset);
- ASSERT_EQ(adjustment2 + method3_offset, mock_->last_write_offset_);
- ASSERT_EQ(method3_offset + method3_alignment_size + method3_misc_thunk_size,
- method3_offset_adjusted);
- vos.WriteFully("3", 1); // Mark method3.
-
- EXPECT_EQ(method3_alignment_size, patcher_.CodeAlignmentSize());
- EXPECT_EQ(method3_misc_thunk_size, patcher_.MiscThunksSize());
-
- uint8_t expected_output[] = {
- '1',
- 0, 'c', 'c', '2',
- 0, 0, 'm', '3',
- };
- ASSERT_EQ(arraysize(expected_output), output.size());
- for (size_t i = 0; i != arraysize(expected_output); ++i) {
- ASSERT_EQ(expected_output[i], output[i]) << i;
- }
-}
-
-TEST_F(MultiOatRelativePatcherTest, Patch) {
- std::vector<uint8_t> code(16);
-
- uint32_t adjustment1 = 0x1000;
- patcher_.StartOatFile(adjustment1);
-
- uint32_t method1_literal_offset = 4u;
- uint32_t method1_patch_offset = 0x1234u;
- uint32_t method1_target_offset = 0x8888u;
- patcher_.PatchCall(&code, method1_literal_offset, method1_patch_offset, method1_target_offset);
- DCHECK_EQ(method1_literal_offset, mock_->last_literal_offset_);
- DCHECK_EQ(method1_patch_offset + adjustment1, mock_->last_patch_offset_);
- DCHECK_EQ(method1_target_offset + adjustment1, mock_->last_target_offset_);
-
- uint32_t method2_literal_offset = 12u;
- uint32_t method2_patch_offset = 0x7654u;
- uint32_t method2_target_offset = 0xccccu;
- LinkerPatch method2_patch =
- LinkerPatch::StringBssEntryPatch(method2_literal_offset, nullptr, 0u, 1u);
- patcher_.PatchPcRelativeReference(
- &code, method2_patch, method2_patch_offset, method2_target_offset);
- DCHECK_EQ(method2_literal_offset, mock_->last_literal_offset_);
- DCHECK_EQ(method2_patch_offset + adjustment1, mock_->last_patch_offset_);
- DCHECK_EQ(method2_target_offset + adjustment1, mock_->last_target_offset_);
-
- uint32_t adjustment2 = 0xd000;
- patcher_.StartOatFile(adjustment2);
-
- uint32_t method3_literal_offset = 8u;
- uint32_t method3_patch_offset = 0x108u;
- uint32_t method3_target_offset = 0x200u;
- patcher_.PatchCall(&code, method3_literal_offset, method3_patch_offset, method3_target_offset);
- DCHECK_EQ(method3_literal_offset, mock_->last_literal_offset_);
- DCHECK_EQ(method3_patch_offset + adjustment2, mock_->last_patch_offset_);
- DCHECK_EQ(method3_target_offset + adjustment2, mock_->last_target_offset_);
-}
-
-} // namespace linker
-} // namespace art
diff --git a/compiler/linker/output_stream.cc b/compiler/linker/output_stream.cc
index a8b64ca..f5a1913 100644
--- a/compiler/linker/output_stream.cc
+++ b/compiler/linker/output_stream.cc
@@ -17,6 +17,7 @@
#include "output_stream.h"
namespace art {
+namespace linker {
std::ostream& operator<<(std::ostream& os, const Whence& rhs) {
switch (rhs) {
@@ -28,4 +29,5 @@
return os;
}
+} // namespace linker
} // namespace art
diff --git a/compiler/linker/output_stream.h b/compiler/linker/output_stream.h
index 96a5f48..5310e2f 100644
--- a/compiler/linker/output_stream.h
+++ b/compiler/linker/output_stream.h
@@ -23,6 +23,7 @@
#include "base/macros.h"
namespace art {
+namespace linker {
enum Whence {
kSeekSet = SEEK_SET,
@@ -59,6 +60,7 @@
DISALLOW_COPY_AND_ASSIGN(OutputStream);
};
+} // namespace linker
} // namespace art
#endif // ART_COMPILER_LINKER_OUTPUT_STREAM_H_
diff --git a/compiler/linker/output_stream_test.cc b/compiler/linker/output_stream_test.cc
index 87cb100..ad29840 100644
--- a/compiler/linker/output_stream_test.cc
+++ b/compiler/linker/output_stream_test.cc
@@ -23,6 +23,7 @@
#include "common_runtime_test.h"
namespace art {
+namespace linker {
class OutputStreamTest : public CommonRuntimeTest {
protected:
@@ -133,4 +134,5 @@
ASSERT_TRUE(checking_output_stream->flush_called);
}
+} // namespace linker
} // namespace art
diff --git a/compiler/linker/relative_patcher.h b/compiler/linker/relative_patcher.h
index 53a0966..e079946 100644
--- a/compiler/linker/relative_patcher.h
+++ b/compiler/linker/relative_patcher.h
@@ -29,7 +29,6 @@
class CompiledMethod;
class LinkerPatch;
-class OutputStream;
namespace debug {
struct MethodDebugInfo;
@@ -37,6 +36,8 @@
namespace linker {
+class OutputStream;
+
/**
* @class RelativePatcherTargetProvider
* @brief Interface for providing method offsets for relative call targets.
diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h
index ca8743a..f7dbc1e 100644
--- a/compiler/linker/relative_patcher_test.h
+++ b/compiler/linker/relative_patcher_test.h
@@ -252,8 +252,8 @@
}
// Map method reference to assinged offset.
- // Wrap the map in a class implementing linker::RelativePatcherTargetProvider.
- class MethodOffsetMap FINAL : public linker::RelativePatcherTargetProvider {
+ // Wrap the map in a class implementing RelativePatcherTargetProvider.
+ class MethodOffsetMap FINAL : public RelativePatcherTargetProvider {
public:
std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) OVERRIDE {
auto it = map.find(ref);
diff --git a/compiler/linker/vector_output_stream.cc b/compiler/linker/vector_output_stream.cc
index f758005..75f90e5 100644
--- a/compiler/linker/vector_output_stream.cc
+++ b/compiler/linker/vector_output_stream.cc
@@ -19,6 +19,7 @@
#include "base/logging.h"
namespace art {
+namespace linker {
VectorOutputStream::VectorOutputStream(const std::string& location, std::vector<uint8_t>* vector)
: OutputStream(location), offset_(vector->size()), vector_(vector) {}
@@ -45,4 +46,5 @@
return offset_;
}
+} // namespace linker
} // namespace art
diff --git a/compiler/linker/vector_output_stream.h b/compiler/linker/vector_output_stream.h
index a9b93e7..92caf59 100644
--- a/compiler/linker/vector_output_stream.h
+++ b/compiler/linker/vector_output_stream.h
@@ -24,6 +24,7 @@
#include <vector>
namespace art {
+namespace linker {
class VectorOutputStream FINAL : public OutputStream {
public:
@@ -64,6 +65,7 @@
DISALLOW_COPY_AND_ASSIGN(VectorOutputStream);
};
+} // namespace linker
} // namespace art
#endif // ART_COMPILER_LINKER_VECTOR_OUTPUT_STREAM_H_