Add patchoat tool to Art.
Add a new executable called patchoat to art. This tool takes already
compiled images and oat files and changes their base address, acting as
a cheap form of relocation.
Add a --include-patch-information flag to dex2oat and code to add
required patch information to oat files created with the quick compiler.
Bug: 15358152
Change-Id: Ie0c580db45bb14ec180deb84930def6c3628d97d
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index bb33978..e5402e1 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -724,14 +724,23 @@
return *(GetDynamicSectionStart() + i);
}
-Elf32_Word ElfFile::FindDynamicValueByType(Elf32_Sword type) const {
+Elf32_Dyn* ElfFile::FindDynamicByType(Elf32_Sword type) const {
for (Elf32_Word i = 0; i < GetDynamicNum(); i++) {
- Elf32_Dyn& elf_dyn = GetDynamic(i);
- if (elf_dyn.d_tag == type) {
- return elf_dyn.d_un.d_val;
+ Elf32_Dyn* dyn = &GetDynamic(i);
+ if (dyn->d_tag == type) {
+ return dyn;
}
}
- return 0;
+ return NULL;
+}
+
+Elf32_Word ElfFile::FindDynamicValueByType(Elf32_Sword type) const {
+ Elf32_Dyn* dyn = FindDynamicByType(type);
+ if (dyn == NULL) {
+ return 0;
+ } else {
+ return dyn->d_un.d_val;
+ }
}
Elf32_Rel* ElfFile::GetRelSectionStart(Elf32_Shdr& section_header) const {
diff --git a/runtime/elf_file.h b/runtime/elf_file.h
index 496690b..a966bd9 100644
--- a/runtime/elf_file.h
+++ b/runtime/elf_file.h
@@ -112,6 +112,7 @@
Elf32_Word GetDynamicNum() const;
Elf32_Dyn& GetDynamic(Elf32_Word) const;
+ Elf32_Dyn* FindDynamicByType(Elf32_Sword type) const;
Elf32_Word FindDynamicValueByType(Elf32_Sword type) const;
Elf32_Word GetRelNum(Elf32_Shdr&) const;
diff --git a/runtime/elf_utils.h b/runtime/elf_utils.h
index 2c50047..5966d05 100644
--- a/runtime/elf_utils.h
+++ b/runtime/elf_utils.h
@@ -22,6 +22,8 @@
// Explicitly include our own elf.h to avoid Linux and other dependencies.
#include "./elf.h"
+#include "base/logging.h"
+
// Architecture dependent flags for the ELF header.
#define EF_ARM_EABI_VER5 0x05000000
#define EF_MIPS_ABI_O32 0x00001000
@@ -62,8 +64,103 @@
#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */
#define DT_MIPS_RLD_MAP 0x70000016 /* Address of debug map pointer */
+// Patching section type
+#define SHT_OAT_PATCH SHT_LOUSER
+
inline void SetBindingAndType(Elf32_Sym* sym, unsigned char b, unsigned char t) {
sym->st_info = (b << 4) + (t & 0x0f);
}
+inline bool IsDynamicSectionPointer(Elf32_Word d_tag, Elf32_Word e_machine) {
+ switch (d_tag) {
+ // case 1: well known d_tag values that imply Elf32_Dyn.d_un contains an address in d_ptr
+ case DT_PLTGOT:
+ case DT_HASH:
+ case DT_STRTAB:
+ case DT_SYMTAB:
+ case DT_RELA:
+ case DT_INIT:
+ case DT_FINI:
+ case DT_REL:
+ case DT_DEBUG:
+ case DT_JMPREL: {
+ return true;
+ }
+ // d_val or ignored values
+ case DT_NULL:
+ case DT_NEEDED:
+ case DT_PLTRELSZ:
+ case DT_RELASZ:
+ case DT_RELAENT:
+ case DT_STRSZ:
+ case DT_SYMENT:
+ case DT_SONAME:
+ case DT_RPATH:
+ case DT_SYMBOLIC:
+ case DT_RELSZ:
+ case DT_RELENT:
+ case DT_PLTREL:
+ case DT_TEXTREL:
+ case DT_BIND_NOW:
+ case DT_INIT_ARRAYSZ:
+ case DT_FINI_ARRAYSZ:
+ case DT_RUNPATH:
+ case DT_FLAGS: {
+ return false;
+ }
+ // boundary values that should not be used
+ case DT_ENCODING:
+ case DT_LOOS:
+ case DT_HIOS:
+ case DT_LOPROC:
+ case DT_HIPROC: {
+ LOG(FATAL) << "Illegal d_tag value 0x" << std::hex << d_tag;
+ return false;
+ }
+ default: {
+ // case 2: "regular" DT_* ranges where even d_tag values imply an address in d_ptr
+ if ((DT_ENCODING < d_tag && d_tag < DT_LOOS)
+ || (DT_LOOS < d_tag && d_tag < DT_HIOS)
+ || (DT_LOPROC < d_tag && d_tag < DT_HIPROC)) {
+ // Special case for MIPS which breaks the regular rules between DT_LOPROC and DT_HIPROC
+ if (e_machine == EM_MIPS) {
+ switch (d_tag) {
+ case DT_MIPS_RLD_VERSION:
+ case DT_MIPS_TIME_STAMP:
+ case DT_MIPS_ICHECKSUM:
+ case DT_MIPS_IVERSION:
+ case DT_MIPS_FLAGS:
+ case DT_MIPS_LOCAL_GOTNO:
+ case DT_MIPS_CONFLICTNO:
+ case DT_MIPS_LIBLISTNO:
+ case DT_MIPS_SYMTABNO:
+ case DT_MIPS_UNREFEXTNO:
+ case DT_MIPS_GOTSYM:
+ case DT_MIPS_HIPAGENO: {
+ return false;
+ }
+ case DT_MIPS_BASE_ADDRESS:
+ case DT_MIPS_CONFLICT:
+ case DT_MIPS_LIBLIST:
+ case DT_MIPS_RLD_MAP: {
+ return true;
+ }
+ default: {
+ LOG(FATAL) << "Unknown MIPS d_tag value 0x" << std::hex << d_tag;
+ return false;
+ }
+ }
+ } else if ((d_tag % 2) == 0) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ LOG(FATAL) << "Unknown d_tag value 0x" << std::hex << d_tag;
+ return false;
+ }
+ }
+ }
+}
+
#endif // ART_RUNTIME_ELF_UTILS_H_
diff --git a/runtime/image.cc b/runtime/image.cc
index 528bfc6..93ec27d 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -24,7 +24,7 @@
namespace art {
const byte ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const byte ImageHeader::kImageVersion[] = { '0', '0', '7', '\0' };
+const byte ImageHeader::kImageVersion[] = { '0', '0', '8', '\0' };
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
@@ -45,6 +45,7 @@
oat_data_begin_(oat_data_begin),
oat_data_end_(oat_data_end),
oat_file_end_(oat_file_end),
+ patch_delta_(0),
image_roots_(image_roots) {
CHECK_EQ(image_begin, RoundUp(image_begin, kPageSize));
CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kPageSize));
@@ -58,6 +59,17 @@
memcpy(version_, kImageVersion, sizeof(kImageVersion));
}
+void ImageHeader::RelocateImage(off_t delta) {
+ CHECK_ALIGNED(delta, kPageSize) << " patch delta must be page aligned";
+ image_begin_ += delta;
+ oat_file_begin_ += delta;
+ oat_data_begin_ += delta;
+ oat_data_end_ += delta;
+ oat_file_end_ += delta;
+ image_roots_ += delta;
+ patch_delta_ += delta;
+}
+
bool ImageHeader::IsValid() const {
if (memcmp(magic_, kImageMagic, sizeof(kImageMagic)) != 0) {
return false;
@@ -65,6 +77,25 @@
if (memcmp(version_, kImageVersion, sizeof(kImageVersion)) != 0) {
return false;
}
+ // Unsigned so wraparound is well defined.
+ if (image_begin_ >= image_begin_ + image_size_) {
+ return false;
+ }
+ if (oat_file_begin_ > oat_file_end_) {
+ return false;
+ }
+ if (oat_data_begin_ > oat_data_end_) {
+ return false;
+ }
+ if (oat_file_begin_ >= oat_data_begin_) {
+ return false;
+ }
+ if (image_roots_ <= image_begin_ || oat_file_begin_ <= image_roots_) {
+ return false;
+ }
+ if (!IsAligned<kPageSize>(patch_delta_)) {
+ return false;
+ }
return true;
}
diff --git a/runtime/image.h b/runtime/image.h
index abe1ad8..424a40b 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -84,6 +84,10 @@
return reinterpret_cast<byte*>(oat_file_end_);
}
+ off_t GetPatchDelta() const {
+ return patch_delta_;
+ }
+
size_t GetBitmapOffset() const {
return RoundUp(image_size_, kPageSize);
}
@@ -112,10 +116,11 @@
mirror::Object* GetImageRoot(ImageRoot image_root) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- private:
mirror::ObjectArray<mirror::Object>* GetImageRoots() const;
+ void RelocateImage(off_t delta);
+
+ private:
static const byte kImageMagic[4];
static const byte kImageVersion[4];
@@ -150,11 +155,13 @@
// .so files. Used for positioning a following alloc spaces.
uint32_t oat_file_end_;
+ // The total delta that this image has been patched.
+ int32_t patch_delta_;
+
// Absolute address of an Object[] of objects needed to reinitialize from an image.
uint32_t image_roots_;
friend class ImageWriter;
- friend class ImageDumper; // For GetImageRoots()
};
} // namespace art
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index eae0418..70253af 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -60,6 +60,12 @@
~OatFile();
+ ElfFile* GetElfFile() const {
+ CHECK_NE(reinterpret_cast<uintptr_t>(elf_file_.get()), reinterpret_cast<uintptr_t>(nullptr))
+ << "Cannot get an elf file from " << GetLocation();
+ return elf_file_.get();
+ }
+
const std::string& GetLocation() const {
return location_;
}
@@ -227,6 +233,9 @@
return End() - Begin();
}
+ const byte* Begin() const;
+ const byte* End() const;
+
private:
static void CheckLocation(const std::string& location);
@@ -248,9 +257,6 @@
std::string* error_msg);
bool Setup(std::string* error_msg);
- const byte* Begin() const;
- const byte* End() const;
-
// The oat file name.
//
// The image will embed this to link its associated oat file.
diff --git a/runtime/utils.h b/runtime/utils.h
index eb79968..448c591 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -19,6 +19,7 @@
#include <pthread.h>
+#include <limits>
#include <string>
#include <vector>
@@ -50,6 +51,34 @@
kTimeUnitSecond,
};
+template <typename T>
+bool ParseUint(const char *in, T* out) {
+ char* end;
+ unsigned long long int result = strtoull(in, &end, 0); // NOLINT(runtime/int)
+ if (in == end || *end != '\0') {
+ return false;
+ }
+ if (std::numeric_limits<T>::max() < result) {
+ return false;
+ }
+ *out = static_cast<T>(result);
+ return true;
+}
+
+template <typename T>
+bool ParseInt(const char* in, T* out) {
+ char* end;
+ long long int result = strtoll(in, &end, 0); // NOLINT(runtime/int)
+ if (in == end || *end != '\0') {
+ return false;
+ }
+ if (result < std::numeric_limits<T>::min() || std::numeric_limits<T>::max() < result) {
+ return false;
+ }
+ *out = static_cast<T>(result);
+ return true;
+}
+
template<typename T>
static constexpr bool IsPowerOfTwo(T x) {
return (x & (x - 1)) == 0;