const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
if (oat_file == nullptr) {
CHECK(Runtime::Current()->IsCompiler());
- oat_file = OatFile::Open(oat_location, oat_location, NULL, false, error_msg);
+ oat_file = OatFile::Open(oat_location, oat_location, nullptr, nullptr, false, error_msg);
if (oat_file == nullptr) {
*error_msg = StringPrintf("Unable to find or open oat file at '%s': %s", oat_location.c_str(),
error_msg->c_str());
{
ImageWriter writer(*compiler_driver_.get());
bool success_image = writer.Write(image_file.GetFilename(), requested_image_base,
- dup_oat->GetPath(), dup_oat->GetPath());
+ dup_oat->GetPath(), dup_oat->GetPath(), /*compile_pic*/false);
ASSERT_TRUE(success_image);
bool success_fixup = ElfFixup::Fixup(dup_oat.get(), writer.GetOatDataBegin());
ASSERT_TRUE(success_fixup);
oat_file_begin,
oat_data_begin,
oat_data_end,
- oat_file_end);
+ oat_file_end,
+ /*compile_pic*/false);
ASSERT_TRUE(image_header.IsValid());
char* magic = const_cast<char*>(image_header.GetMagic());
bool ImageWriter::Write(const std::string& image_filename,
uintptr_t image_begin,
const std::string& oat_filename,
- const std::string& oat_location) {
+ const std::string& oat_location,
+ bool compile_pic) {
CHECK(!image_filename.empty());
CHECK_NE(image_begin, 0U);
image_begin_ = reinterpret_cast<byte*>(image_begin);
+ compile_pic_ = compile_pic;
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
PointerToLowMemUInt32(oat_file_begin),
PointerToLowMemUInt32(oat_data_begin_),
PointerToLowMemUInt32(oat_data_end),
- PointerToLowMemUInt32(oat_file_end));
+ PointerToLowMemUInt32(oat_file_end),
+ compile_pic_);
memcpy(image_->Begin(), &image_header, sizeof(image_header));
// Note that image_end_ is left at end of used space
#include <set>
#include <string>
+#include "base/macros.h"
#include "driver/compiler_driver.h"
#include "mem_map.h"
#include "oat_file.h"
namespace art {
// Write a Space built during compilation for use during execution.
-class ImageWriter {
+class ImageWriter FINAL {
public:
explicit ImageWriter(const CompilerDriver& compiler_driver)
: compiler_driver_(compiler_driver), oat_file_(NULL), image_end_(0), image_begin_(NULL),
oat_data_begin_(NULL), interpreter_to_interpreter_bridge_offset_(0),
interpreter_to_compiled_code_bridge_offset_(0), portable_imt_conflict_trampoline_offset_(0),
portable_resolution_trampoline_offset_(0), quick_generic_jni_trampoline_offset_(0),
- quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0) {}
+ quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0),
+ compile_pic_(false) {}
~ImageWriter() {}
bool Write(const std::string& image_filename,
uintptr_t image_begin,
const std::string& oat_filename,
- const std::string& oat_location)
+ const std::string& oat_location,
+ bool compile_pic)
LOCKS_EXCLUDED(Locks::mutator_lock_);
uintptr_t GetOatDataBegin() {
// different .o ELF objects.
DCHECK_LT(offset, oat_file_->Size());
#endif
- if (offset == 0) {
- return NULL;
+ if (offset == 0u) {
+ return nullptr;
}
return oat_data_begin_ + offset;
}
uint32_t quick_imt_conflict_trampoline_offset_;
uint32_t quick_resolution_trampoline_offset_;
uint32_t quick_to_interpreter_bridge_offset_;
+ bool compile_pic_;
friend class FixupVisitor;
friend class FixupClassVisitor;
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
}
std::string error_msg;
- std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), NULL, false,
- &error_msg));
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), nullptr,
+ nullptr, false, &error_msg));
ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
const OatHeader& oat_header = oat_file->GetOatHeader();
ASSERT_TRUE(oat_header.IsValid());
UsageError(" Example: --instruction-set-features=div");
UsageError(" Default: default");
UsageError("");
+ UsageError(" --compile-pic: Force indirect use of code, methods, and classes");
+ UsageError(" Default: disabled");
+ UsageError("");
UsageError(" --compiler-backend=(Quick|Optimizing|Portable): select compiler backend");
UsageError(" set.");
UsageError(" Example: --compiler-backend=Portable");
{
// ImageWriter is scoped so it can free memory before doing FixupElf
ImageWriter image_writer(compiler);
- if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location)) {
+ if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location,
+ compiler_options_->GetCompilePic())) {
LOG(ERROR) << "Failed to create image file " << image_filename;
return false;
}
PLOG(ERROR) << "Failed to open ELF file: " << oat_filename;
return false;
}
- if (!ElfFixup::Fixup(oat_file.get(), oat_data_begin)) {
- LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
- return false;
+ // Do not fix up the ELF file if we are --compile-pic
+ if (!compiler_options_->GetCompilePic()) {
+ if (!ElfFixup::Fixup(oat_file.get(), oat_data_begin)) {
+ LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
+ return false;
+ }
}
+
return true;
}
os << "PATCH DELTA:" << image_header_.GetPatchDelta() << "\n\n";
+ os << "COMPILE PIC: " << (image_header_.CompilePic() ? "yes" : "no") << "\n\n";
+
{
os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
Indenter indent1_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
std::string error_msg;
const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
if (oat_file == nullptr) {
- oat_file = OatFile::Open(oat_location, oat_location, nullptr, false, &error_msg);
+ oat_file = OatFile::Open(oat_location, oat_location, nullptr, nullptr, false, &error_msg);
if (oat_file == nullptr) {
os << "NOT FOUND: " << error_msg << "\n";
return false;
if (oat_filename != nullptr) {
std::string error_msg;
OatFile* oat_file =
- OatFile::Open(oat_filename, oat_filename, nullptr, false, &error_msg);
+ OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false, &error_msg);
if (oat_file == nullptr) {
fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
return EXIT_FAILURE;
bool has_system = false;
bool has_cache = false;
// image_location = /system/framework/boot.art
- // system_image_location = /system/framework/<image_isa>/boot.art
+ // system_image_filename = /system/framework/<image_isa>/boot.art
std::string system_filename(GetSystemImageFilename(location.c_str(), isa));
if (OS::FileExists(system_filename.c_str())) {
has_system = true;
<< " for location " << image_location;
return false;
}
+
int64_t image_len = input_image->GetLength();
if (image_len < 0) {
LOG(ERROR) << "Error while getting image length";
return false;
}
+ /*bool is_image_pic = */IsImagePic(image_header, input_image->GetPath());
+ // Nothing special to do right now since the image always needs to get patched.
+ // Perhaps in some far-off future we may have images with relative addresses that are true-PIC.
+
// Set up the runtime
RuntimeOptions options;
NoopCompilerCallbacks callbacks;
return true;
}
-bool PatchOat::Patch(const File* input_oat, const std::string& image_location, off_t delta,
+bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t delta,
File* output_oat, File* output_image, InstructionSet isa,
- TimingLogger* timings) {
+ TimingLogger* timings,
+ bool output_oat_opened_from_fd,
+ bool new_oat_out) {
CHECK(Runtime::Current() == nullptr);
CHECK(output_image != nullptr);
CHECK_GE(output_image->Fd(), 0);
LOG(ERROR) << "Unable to read image header from image file " << input_image->GetPath();
}
+ /*bool is_image_pic = */IsImagePic(image_header, input_image->GetPath());
+ // Nothing special to do right now since the image always needs to get patched.
+ // Perhaps in some far-off future we may have images with relative addresses that are true-PIC.
+
// Set up the runtime
RuntimeOptions options;
NoopCompilerCallbacks callbacks;
}
gc::space::ImageSpace* ispc = Runtime::Current()->GetHeap()->GetImageSpace();
- std::unique_ptr<ElfFile> elf(ElfFile::Open(const_cast<File*>(input_oat),
+ std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat,
PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg));
if (elf.get() == nullptr) {
LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
return false;
}
+ bool skip_patching_oat = false;
+ MaybePic is_oat_pic = IsOatPic(elf.get());
+ if (is_oat_pic >= ERROR_FIRST) {
+ // Error logged by IsOatPic
+ return false;
+ } else if (is_oat_pic == PIC) {
+ // Do not need to do ELF-file patching. Create a symlink and skip the ELF patching.
+ if (!ReplaceOatFileWithSymlink(input_oat->GetPath(),
+ output_oat->GetPath(),
+ output_oat_opened_from_fd,
+ new_oat_out)) {
+ // Errors already logged by above call.
+ return false;
+ }
+ // Don't patch the OAT, since we just symlinked it. Image still needs patching.
+ skip_patching_oat = true;
+ } else {
+ CHECK(is_oat_pic == NOT_PIC);
+ }
+
PatchOat p(elf.release(), image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(),
delta, timings);
t.NewTiming("Patching files");
- if (!p.PatchElf()) {
+ if (!skip_patching_oat && !p.PatchElf()) {
LOG(ERROR) << "Failed to patch oat file " << input_oat->GetPath();
return false;
}
}
t.NewTiming("Writing files");
- if (!p.WriteElf(output_oat)) {
+ if (!skip_patching_oat && !p.WriteElf(output_oat)) {
+ LOG(ERROR) << "Failed to write oat file " << input_oat->GetPath();
return false;
}
if (!p.WriteImage(output_image)) {
+ LOG(ERROR) << "Failed to write image file " << input_image->GetPath();
return false;
}
return true;
}
}
+bool PatchOat::IsImagePic(const ImageHeader& image_header, const std::string& image_path) {
+ if (!image_header.CompilePic()) {
+ if (kIsDebugBuild) {
+ LOG(INFO) << "image at location " << image_path << " was *not* compiled pic";
+ }
+ return false;
+ }
+
+ if (kIsDebugBuild) {
+ LOG(INFO) << "image at location " << image_path << " was compiled PIC";
+ }
+
+ return true;
+}
+
+PatchOat::MaybePic PatchOat::IsOatPic(const ElfFile* oat_in) {
+ if (oat_in == nullptr) {
+ LOG(ERROR) << "No ELF input oat fie available";
+ return ERROR_OAT_FILE;
+ }
+
+ const std::string& file_path = oat_in->GetFile().GetPath();
+
+ const OatHeader* oat_header = GetOatHeader(oat_in);
+ if (oat_header == nullptr) {
+ LOG(ERROR) << "Failed to find oat header in oat file " << file_path;
+ return ERROR_OAT_FILE;
+ }
+
+ if (!oat_header->IsValid()) {
+ LOG(ERROR) << "Elf file " << file_path << " has an invalid oat header";
+ return ERROR_OAT_FILE;
+ }
+
+ bool is_pic = oat_header->IsPic();
+ if (kIsDebugBuild) {
+ LOG(INFO) << "Oat file at " << file_path << " is " << (is_pic ? "PIC" : "not pic");
+ }
+
+ return is_pic ? PIC : NOT_PIC;
+}
+
+bool PatchOat::ReplaceOatFileWithSymlink(const std::string& input_oat_filename,
+ const std::string& output_oat_filename,
+ bool output_oat_opened_from_fd,
+ bool new_oat_out) {
+ // Need a file when we are PIC, since we symlink over it. Refusing to symlink into FD.
+ if (output_oat_opened_from_fd) {
+ // TODO: installd uses --output-oat-fd. Should we change class linking logic for PIC?
+ LOG(ERROR) << "No output oat filename specified, needs filename for when we are PIC";
+ return false;
+ }
+
+ // Image was PIC. Create symlink where the oat is supposed to go.
+ if (!new_oat_out) {
+ LOG(ERROR) << "Oat file " << output_oat_filename << " already exists, refusing to overwrite";
+ return false;
+ }
+
+ // Delete the original file, since we won't need it.
+ TEMP_FAILURE_RETRY(unlink(output_oat_filename.c_str()));
+
+ // Create a symlink from the old oat to the new oat
+ if (symlink(input_oat_filename.c_str(), output_oat_filename.c_str()) < 0) {
+ int err = errno;
+ LOG(ERROR) << "Failed to create symlink at " << output_oat_filename
+ << " error(" << err << "): " << strerror(err);
+ return false;
+ }
+
+ if (kIsDebugBuild) {
+ LOG(INFO) << "Created symlink " << output_oat_filename << " -> " << input_oat_filename;
+ }
+
+ return true;
+}
+
bool PatchOat::PatchImage() {
ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
CHECK_GT(image_->Size(), sizeof(ImageHeader));
}
}
+const OatHeader* PatchOat::GetOatHeader(const ElfFile* elf_file) {
+ auto rodata_sec = elf_file->FindSectionByName(".rodata");
+ if (rodata_sec == nullptr) {
+ return nullptr;
+ }
+
+ OatHeader* oat_header = reinterpret_cast<OatHeader*>(elf_file->Begin() + rodata_sec->sh_offset);
+ return oat_header;
+}
+
// Called by BitmapCallback
void PatchOat::VisitObject(mirror::Object* object) {
mirror::Object* copy = RelocatedCopyOf(object);
}
}
-bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogger* timings) {
+bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogger* timings,
+ bool output_oat_opened_from_fd, bool new_oat_out) {
CHECK(input_oat != nullptr);
CHECK(output_oat != nullptr);
CHECK_GE(input_oat->Fd(), 0);
TimingLogger::ScopedTiming t("Setup Oat File Patching", timings);
std::string error_msg;
- std::unique_ptr<ElfFile> elf(ElfFile::Open(const_cast<File*>(input_oat),
+ std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat,
PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg));
if (elf.get() == nullptr) {
LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
return false;
}
+ MaybePic is_oat_pic = IsOatPic(elf.get());
+ if (is_oat_pic >= ERROR_FIRST) {
+ // Error logged by IsOatPic
+ return false;
+ } else if (is_oat_pic == PIC) {
+ // Do not need to do ELF-file patching. Create a symlink and skip the rest.
+ // Any errors will be logged by the function call.
+ return ReplaceOatFileWithSymlink(input_oat->GetPath(),
+ output_oat->GetPath(),
+ output_oat_opened_from_fd,
+ new_oat_out);
+ } else {
+ CHECK(is_oat_pic == NOT_PIC);
+ }
+
PatchOat p(elf.release(), delta, timings);
t.NewTiming("Patch Oat file");
if (!p.PatchElf()) {
input_oat_filename = "input-oat-file";
}
input_oat.reset(new File(input_oat_fd, input_oat_filename));
+ if (input_oat == nullptr) {
+ // Unlikely, but ensure exhaustive logging in non-0 exit code case
+ LOG(ERROR) << "Failed to open input oat file by its FD" << input_oat_fd;
+ }
} else {
CHECK(!input_oat_filename.empty());
input_oat.reset(OS::OpenFileForReading(input_oat_filename.c_str()));
- if (input_oat.get() == nullptr) {
- LOG(ERROR) << "Could not open input oat file: " << strerror(errno);
+ if (input_oat == nullptr) {
+ int err = errno;
+ LOG(ERROR) << "Failed to open input oat file " << input_oat_filename
+ << ": " << strerror(err) << "(" << err << ")";
}
}
output_oat_filename = "output-oat-file";
}
output_oat.reset(new File(output_oat_fd, output_oat_filename));
+ if (output_oat == nullptr) {
+ // Unlikely, but ensure exhaustive logging in non-0 exit code case
+ LOG(ERROR) << "Failed to open output oat file by its FD" << output_oat_fd;
+ }
} else {
CHECK(!output_oat_filename.empty());
output_oat.reset(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out));
+ if (output_oat == nullptr) {
+ int err = errno;
+ LOG(ERROR) << "Failed to open output oat file " << output_oat_filename
+ << ": " << strerror(err) << "(" << err << ")";
+ }
}
}
+ // TODO: get rid of this.
auto cleanup = [&output_image_filename, &output_oat_filename,
&new_oat_out, &new_image_out, &timings, &dump_timings](bool success) {
timings.EndTiming();
if (dump_timings) {
LOG(INFO) << Dumpable<TimingLogger>(timings);
}
+
+ if (kIsDebugBuild) {
+ LOG(INFO) << "Cleaning up.. success? " << success;
+ }
};
- if ((have_oat_files && (input_oat.get() == nullptr || output_oat.get() == nullptr)) ||
- (have_image_files && output_image.get() == nullptr)) {
+ if (have_oat_files && (input_oat.get() == nullptr || output_oat.get() == nullptr)) {
+ LOG(ERROR) << "Failed to open input/output oat files";
cleanup(false);
return EXIT_FAILURE;
+ } else if (have_image_files && output_image.get() == nullptr) {
+ LOG(ERROR) << "Failed to open output image file";
+ cleanup(false);
+ return EXIT_FAILURE;
+ }
+
+ if (debug) {
+ LOG(INFO) << "moving offset by " << base_delta
+ << " (0x" << std::hex << base_delta << ") bytes or "
+ << std::dec << (base_delta/kPageSize) << " pages.";
}
+ // TODO: is it going to be promatic to unlink a file that was flock-ed?
ScopedFlock output_oat_lock;
if (lock_output) {
std::string error_msg;
}
}
- if (debug) {
- LOG(INFO) << "moving offset by " << base_delta
- << " (0x" << std::hex << base_delta << ") bytes or "
- << std::dec << (base_delta/kPageSize) << " pages.";
- }
-
bool ret;
if (have_image_files && have_oat_files) {
TimingLogger::ScopedTiming pt("patch image and oat", &timings);
ret = PatchOat::Patch(input_oat.get(), input_image_location, base_delta,
- output_oat.get(), output_image.get(), isa, &timings);
+ output_oat.get(), output_image.get(), isa, &timings,
+ output_oat_fd >= 0, // was it opened from FD?
+ new_oat_out);
} else if (have_oat_files) {
TimingLogger::ScopedTiming pt("patch oat", &timings);
- ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings);
- } else {
+ ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings,
+ output_oat_fd >= 0, // was it opened from FD?
+ new_oat_out);
+ } else if (have_image_files) {
TimingLogger::ScopedTiming pt("patch image", &timings);
- CHECK(have_image_files);
ret = PatchOat::Patch(input_image_location, base_delta, output_image.get(), isa, &timings);
+ } else {
+ CHECK(false);
+ ret = true;
+ }
+
+ if (kIsDebugBuild) {
+ LOG(INFO) << "Exiting with return ... " << ret;
}
cleanup(ret);
return (ret) ? EXIT_SUCCESS : EXIT_FAILURE;
namespace art {
class ImageHeader;
+class OatHeader;
namespace mirror {
class Object;
class PatchOat {
public:
- static bool Patch(File* oat_in, off_t delta, File* oat_out, TimingLogger* timings);
+ // Patch only the oat file
+ static bool Patch(File* oat_in, off_t delta, File* oat_out, TimingLogger* timings,
+ bool output_oat_opened_from_fd, // Was this using --oatput-oat-fd ?
+ bool new_oat_out); // Output oat was a new file created by us?
+ // Patch only the image (art file)
static bool Patch(const std::string& art_location, off_t delta, File* art_out, InstructionSet isa,
TimingLogger* timings);
- static bool Patch(const File* oat_in, const std::string& art_location,
+ // Patch both the image and the oat file
+ static bool Patch(File* oat_in, const std::string& art_location,
off_t delta, File* oat_out, File* art_out, InstructionSet isa,
- TimingLogger* timings);
+ TimingLogger* timings,
+ bool output_oat_opened_from_fd, // Was this using --oatput-oat-fd ?
+ bool new_oat_out); // Output oat was a new file created by us?
private:
// Takes ownership only of the ElfFile. All other pointers are only borrowed.
delta_(delta), timings_(timings) {}
~PatchOat() {}
+ // Was the .art image at image_path made with --compile-pic ?
+ static bool IsImagePic(const ImageHeader& image_header, const std::string& image_path);
+
+ enum MaybePic {
+ NOT_PIC, // Code not pic. Patch as usual.
+ PIC, // Code was pic. Create symlink; skip OAT patching.
+ ERROR_OAT_FILE, // Failed to symlink oat file
+ ERROR_FIRST = ERROR_OAT_FILE,
+ };
+
+ // Was the .oat image at oat_in made with --compile-pic ?
+ static MaybePic IsOatPic(const ElfFile* oat_in);
+
+ // Attempt to replace the file with a symlink
+ // Returns false if it fails
+ static bool ReplaceOatFileWithSymlink(const std::string& input_oat_filename,
+ const std::string& output_oat_filename,
+ bool output_oat_opened_from_fd,
+ bool new_oat_out); // Output oat was newly created?
+
static void BitmapCallback(mirror::Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
reinterpret_cast<PatchOat*>(arg)->VisitObject(obj);
mirror::Object* RelocatedCopyOf(mirror::Object*);
mirror::Object* RelocatedAddressOf(mirror::Object* obj);
+ // Look up the oat header from any elf file.
+ static const OatHeader* GetOatHeader(const ElfFile* elf_file);
+
// Walks through the old image and patches the mmap'd copy of it to the new offset. It does not
// change the heap.
class PatchVisitor {
uint32_t dex_location_checksum,
const char* oat_location,
std::string* error_msg) {
- std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr,
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
!Runtime::Current()->IsCompiler(),
error_msg));
if (oat_file.get() == nullptr) {
error_msgs->push_back(error_msg);
return nullptr;
}
- std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr,
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
!Runtime::Current()->IsCompiler(),
&error_msg));
if (oat_file.get() == nullptr) {
// There is a high probability that both these oat files map similar/the same address
// spaces so we must scope them like this so they each gets its turn.
std::unique_ptr<OatFile> odex_oat_file(OatFile::Open(odex_filename, odex_filename, nullptr,
+ nullptr,
executable, &odex_error_msg));
if (odex_oat_file.get() != nullptr && CheckOatFile(runtime, odex_oat_file.get(), isa,
&odex_checksum_verified,
bool cache_checksum_verified = false;
if (have_dalvik_cache) {
std::unique_ptr<OatFile> cache_oat_file(OatFile::Open(cache_filename, cache_filename, nullptr,
+ nullptr,
executable, &cache_error_msg));
if (cache_oat_file.get() != nullptr && CheckOatFile(runtime, cache_oat_file.get(), isa,
&cache_checksum_verified,
InstructionSet isa,
std::string* error_msg) {
// We open it non-executable
- std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, nullptr, false, error_msg));
+ std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, nullptr, nullptr, false, error_msg));
if (output.get() == nullptr) {
return nullptr;
}
LOG(INFO) << "Relocate Oat File: " << command_line;
bool success = Exec(argv, error_msg);
if (success) {
- std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr,
+ std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr, nullptr,
!runtime->IsCompiler(), error_msg));
bool checksum_verified = false;
if (output.get() != nullptr && CheckOatFile(runtime, output.get(), isa, &checksum_verified,
return oat_file;
}
- return OatFile::Open(oat_location, oat_location, nullptr, !Runtime::Current()->IsCompiler(),
+ return OatFile::Open(oat_location, oat_location, nullptr, nullptr, !Runtime::Current()->IsCompiler(),
error_msg);
}
delete entry;
}
-ElfFile::ElfFile(File* file, bool writable, bool program_header_only)
+ElfFile::ElfFile(File* file, bool writable, bool program_header_only, uint8_t* requested_base)
: file_(file),
writable_(writable),
program_header_only_(program_header_only),
symtab_symbol_table_(nullptr),
dynsym_symbol_table_(nullptr),
jit_elf_image_(nullptr),
- jit_gdb_entry_(nullptr) {
+ jit_gdb_entry_(nullptr),
+ requested_base_(requested_base) {
CHECK(file != nullptr);
}
ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only,
- std::string* error_msg) {
- std::unique_ptr<ElfFile> elf_file(new ElfFile(file, writable, program_header_only));
+ std::string* error_msg, uint8_t* requested_base) {
+ std::unique_ptr<ElfFile> elf_file(new ElfFile(file, writable, program_header_only,
+ requested_base));
int prot;
int flags;
if (writable) {
}
ElfFile* ElfFile::Open(File* file, int prot, int flags, std::string* error_msg) {
- std::unique_ptr<ElfFile> elf_file(new ElfFile(file, (prot & PROT_WRITE) == PROT_WRITE, false));
+ std::unique_ptr<ElfFile> elf_file(new ElfFile(file, (prot & PROT_WRITE) == PROT_WRITE, false,
+ /*requested_base*/nullptr));
if (!elf_file->Setup(prot, flags, error_msg)) {
return nullptr;
}
}
const Elf32_Sym* sym = FindDynamicSymbol(symbol_name);
if (sym != nullptr) {
+ // TODO: we need to change this to calculate base_address_ in ::Open,
+ // otherwise it will be wrongly 0 if ::Load has not yet been called.
return base_address_ + sym->st_value;
} else {
return nullptr;
}
size_t file_length = static_cast<size_t>(temp_file_length);
if (!reserved) {
- byte* reserve_base = ((program_header->p_vaddr != 0) ?
- reinterpret_cast<byte*>(program_header->p_vaddr) : nullptr);
+ uint8_t* reserve_base = reinterpret_cast<uint8_t*>(program_header->p_vaddr);
+ uint8_t* reserve_base_override = reserve_base;
+ // Override the base (e.g. when compiling with --compile-pic)
+ if (requested_base_ != nullptr) {
+ reserve_base_override = requested_base_;
+ }
std::string reservation_name("ElfFile reservation for ");
reservation_name += file_->GetPath();
std::unique_ptr<MemMap> reserve(MemMap::MapAnonymous(reservation_name.c_str(),
- reserve_base,
+ reserve_base_override,
GetLoadedSize(), PROT_NONE, false,
error_msg));
if (reserve.get() == nullptr) {
return false;
}
reserved = true;
- if (reserve_base == nullptr) {
- base_address_ = reserve->Begin();
- }
+
+ // Base address is the difference of actual mapped location and the p_vaddr
+ base_address_ = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(reserve->Begin())
+ - reinterpret_cast<uintptr_t>(reserve_base));
+ // By adding the p_vaddr of a section/symbol to base_address_ we will always get the
+ // dynamic memory address of where that object is actually mapped
+ //
+ // TODO: base_address_ needs to be calculated in ::Open, otherwise
+ // FindDynamicSymbolAddress returns the wrong values until Load is called.
segments_.push_back(reserve.release());
}
// empty segment, nothing to map
// ELFObjectFile.
class ElfFile {
public:
- static ElfFile* Open(File* file, bool writable, bool program_header_only, std::string* error_msg);
+ static ElfFile* Open(File* file, bool writable, bool program_header_only, std::string* error_msg,
+ uint8_t* requested_base = nullptr); // TODO: move arg to before error_msg.
// Open with specific mmap flags, Always maps in the whole file, not just the
// program header sections.
static ElfFile* Open(File* file, int mmap_prot, int mmap_flags, std::string* error_msg);
return *file_;
}
+ // The start of the memory map address range for this ELF file.
byte* Begin() const {
return map_->Begin();
}
+ // The end of the memory map address range for this ELF file.
byte* End() const {
return map_->End();
}
bool Load(bool executable, std::string* error_msg);
private:
- ElfFile(File* file, bool writable, bool program_header_only);
+ ElfFile(File* file, bool writable, bool program_header_only, uint8_t* requested_base);
bool Setup(int prot, int flags, std::string* error_msg);
JITCodeEntry* jit_gdb_entry_;
std::unique_ptr<ElfFile> gdb_file_mapping_;
void GdbJITSupport();
+
+ // When not-null, override the base vaddr we memory map LOAD segments into.
+ uint8_t* requested_base_;
};
} // namespace art
const ImageHeader& image_header = GetImageHeader();
std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(image_path);
+ CHECK(image_header.GetOatDataBegin() != nullptr);
+
OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, image_header.GetOatDataBegin(),
+ image_header.GetOatFileBegin(),
!Runtime::Current()->IsCompiler(), error_msg);
if (oat_file == NULL) {
*error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
}
int32_t image_patch_delta = image_header.GetPatchDelta();
int32_t oat_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();
- if (oat_patch_delta != image_patch_delta) {
+ if (oat_patch_delta != image_patch_delta && !image_header.CompilePic()) {
// We should have already relocated by this point. Bail out.
*error_msg = StringPrintf("Failed to match oat file patch delta %d to expected patch delta %d "
"in image %s", oat_patch_delta, image_patch_delta, GetName());
uint32_t oat_file_begin,
uint32_t oat_data_begin,
uint32_t oat_data_end,
- uint32_t oat_file_end)
+ uint32_t oat_file_end,
+ bool compile_pic)
: image_begin_(image_begin),
image_size_(image_size),
image_bitmap_offset_(image_bitmap_offset),
oat_data_end_(oat_data_end),
oat_file_end_(oat_file_end),
patch_delta_(0),
- image_roots_(image_roots) {
+ image_roots_(image_roots),
+ compile_pic_(compile_pic) {
CHECK_EQ(image_begin, RoundUp(image_begin, kPageSize));
CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kPageSize));
CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, kPageSize));
// header of image files written by ImageWriter, read and validated by Space.
class PACKED(4) ImageHeader {
public:
- ImageHeader() {}
+ ImageHeader() : compile_pic_(0) {}
ImageHeader(uint32_t image_begin,
uint32_t image_size_,
uint32_t oat_file_begin,
uint32_t oat_data_begin,
uint32_t oat_data_end,
- uint32_t oat_file_end);
+ uint32_t oat_file_end,
+ bool compile_pic_);
bool IsValid() const;
const char* GetMagic() const;
void RelocateImage(off_t delta);
+ bool CompilePic() const {
+ return compile_pic_ != 0;
+ }
+
private:
static const byte kImageMagic[4];
static const byte kImageVersion[4];
// Absolute address of an Object[] of objects needed to reinitialize from an image.
uint32_t image_roots_;
+ // Boolean (0 or 1) to denote if the image was compiled with --compile-pic option
+ const uint32_t compile_pic_;
+
friend class ImageWriter;
};
template <const bool kVerboseLogging, const bool kReasonLogging>
static jbyte IsDexOptNeededForFile(const std::string& oat_filename, const char* filename,
- InstructionSet target_instruction_set) {
+ InstructionSet target_instruction_set,
+ bool* oat_is_pic) {
std::string error_msg;
std::unique_ptr<const OatFile> oat_file(OatFile::Open(oat_filename, oat_filename, nullptr,
+ nullptr,
false, &error_msg));
if (oat_file.get() == nullptr) {
if (kReasonLogging) {
error_msg.clear();
return kDexoptNeeded;
}
+
+ // Pass-up the information about if this is PIC.
+ // TODO: Refactor this function to be less complicated.
+ *oat_is_pic = oat_file->IsPic();
+
bool should_relocate_if_possible = Runtime::Current()->ShouldRelocate();
uint32_t location_checksum = 0;
const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename, nullptr,
// Lets try the cache first (since we want to load from there since thats where the relocated
// versions will be).
if (have_cache_filename && !force_system_only) {
+ bool oat_is_pic;
// We can use the dalvik-cache if we find a good file.
dalvik_cache_decision =
IsDexOptNeededForFile<kVerboseLogging, kReasonLogging>(cache_filename, filename,
- target_instruction_set);
+ target_instruction_set, &oat_is_pic);
+
+ // Apps that are compiled with --compile-pic never need to be patchoat-d
+ if (oat_is_pic && dalvik_cache_decision == kPatchoatNeeded) {
+ dalvik_cache_decision = kUpToDate;
+ }
// We will only return DexOptNeeded if both the cache and system return it.
if (dalvik_cache_decision != kDexoptNeeded && !require_system_version) {
CHECK(!(dalvik_cache_decision == kPatchoatNeeded && !should_relocate_if_possible))
// We couldn't find one thats easy. We should now try the system.
}
+ bool oat_is_pic;
jbyte system_decision =
IsDexOptNeededForFile<kVerboseLogging, kReasonLogging>(odex_filename, filename,
- target_instruction_set);
+ target_instruction_set, &oat_is_pic);
CHECK(!(system_decision == kPatchoatNeeded && !should_relocate_if_possible))
<< "May not return PatchoatNeeded when patching is disabled.";
+ // Apps that are compiled with --compile-pic never need to be patchoat-d
+ if (oat_is_pic && system_decision == kPatchoatNeeded) {
+ system_decision = kUpToDate;
+ }
+
if (require_system_version && system_decision == kPatchoatNeeded
&& dalvik_cache_decision == kUpToDate) {
// We have a version from system relocated to the cache. Return it.
return sizeof(OatHeader) + key_value_store_size_;
}
+bool OatHeader::IsPic() const {
+ const char* pic_string = GetStoreValueByKey(OatHeader::kPicKey);
+ static const char kTrue[] = "true";
+ return (pic_string != nullptr && strncmp(pic_string, kTrue, sizeof(kTrue)) == 0);
+}
+
void OatHeader::Flatten(const SafeMap<std::string, std::string>* key_value_store) {
char* data_ptr = reinterpret_cast<char*>(&key_value_store_);
if (key_value_store != nullptr) {
bool GetStoreKeyValuePairByIndex(size_t index, const char** key, const char** value) const;
size_t GetHeaderSize() const;
+ bool IsPic() const;
private:
OatHeader(InstructionSet instruction_set,
OatFile* OatFile::Open(const std::string& filename,
const std::string& location,
byte* requested_base,
+ uint8_t* oat_file_begin,
bool executable,
std::string* error_msg) {
CHECK(!filename.empty()) << location;
*error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno));
return nullptr;
}
- ret.reset(OpenElfFile(file.get(), location, requested_base, false, executable, error_msg));
+ ret.reset(OpenElfFile(file.get(), location, requested_base, oat_file_begin, false, executable,
+ error_msg));
// It would be nice to unlink here. But we might have opened the file created by the
// ScopedLock, which we better not delete to avoid races. TODO: Investigate how to fix the API
OatFile* OatFile::OpenWritable(File* file, const std::string& location, std::string* error_msg) {
CheckLocation(location);
- return OpenElfFile(file, location, NULL, true, false, error_msg);
+ return OpenElfFile(file, location, nullptr, nullptr, true, false, error_msg);
}
OatFile* OatFile::OpenReadable(File* file, const std::string& location, std::string* error_msg) {
CheckLocation(location);
- return OpenElfFile(file, location, NULL, false, false, error_msg);
+ return OpenElfFile(file, location, nullptr, nullptr, false, false, error_msg);
}
OatFile* OatFile::OpenDlopen(const std::string& elf_filename,
OatFile* OatFile::OpenElfFile(File* file,
const std::string& location,
byte* requested_base,
+ uint8_t* oat_file_begin,
bool writable,
bool executable,
std::string* error_msg) {
std::unique_ptr<OatFile> oat_file(new OatFile(location, executable));
- bool success = oat_file->ElfFileOpen(file, requested_base, writable, executable, error_msg);
+ bool success = oat_file->ElfFileOpen(file, requested_base, oat_file_begin, writable, executable,
+ error_msg);
if (!success) {
CHECK(!error_msg->empty());
return nullptr;
return Setup(error_msg);
}
-bool OatFile::ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable,
+bool OatFile::ElfFileOpen(File* file, byte* requested_base, uint8_t* oat_file_begin,
+ bool writable, bool executable,
std::string* error_msg) {
- elf_file_.reset(ElfFile::Open(file, writable, true, error_msg));
+ // TODO: rename requested_base to oat_data_begin
+ elf_file_.reset(ElfFile::Open(file, writable, /*program_header_only*/true, error_msg,
+ oat_file_begin));
if (elf_file_.get() == nullptr) {
DCHECK(!error_msg->empty());
return false;
}
bool OatFile::IsPic() const {
- const char* pic_string = GetOatHeader().GetStoreValueByKey(OatHeader::kPicKey);
- return (pic_string != nullptr && strncmp(pic_string, "true", 5) == 0);
+ return GetOatHeader().IsPic();
// TODO: Check against oat_patches. b/18144996
}
static OatFile* Open(const std::string& filename,
const std::string& location,
byte* requested_base,
+ uint8_t* oat_file_begin,
bool executable,
std::string* error_msg);
static OatFile* OpenElfFile(File* file,
const std::string& location,
byte* requested_base,
+ uint8_t* oat_file_begin, // Override base if not null
bool writable,
bool executable,
std::string* error_msg);
explicit OatFile(const std::string& filename, bool executable);
bool Dlopen(const std::string& elf_filename, byte* requested_base, std::string* error_msg);
- bool ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable,
+ bool ElfFileOpen(File* file, byte* requested_base,
+ uint8_t* oat_file_begin, // Override where the file is loaded to if not null
+ bool writable, bool executable,
std::string* error_msg);
bool Setup(std::string* error_msg);