Fix elf writer to properly handle empty text sections
Previously, empty text sections caused an underflow in the oat file
size.
Added regression test.
Bug: 22858531
Change-Id: I9391a3666ab04e32472a9776d5217fe1743417a9
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index ff41736..0afec2d 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -74,6 +74,7 @@
ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
ART_GTEST_oat_file_assistant_test_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex
+ART_GTEST_oat_test_DEX_DEPS := Main
ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
ART_GTEST_proxy_test_DEX_DEPS := Interfaces
ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index 4e2b15d..e977798 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -599,6 +599,7 @@
// This is the order in which they will be written.
std::vector<Section*> sections;
sections.push_back(&rodata_);
+ // Need to write text to update checksum of header even if it is empty.
sections.push_back(&text_);
if (bss_.GetSize() != 0u) {
sections.push_back(&bss_);
@@ -682,7 +683,9 @@
const Elf_Shdr* rodata = rodata_.GetHeader();
program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R,
0, rodata->sh_offset + rodata->sh_size, rodata->sh_addralign));
- program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R | PF_X, text_));
+ if (text_.GetHeader()->sh_size != 0u) {
+ program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R | PF_X, text_));
+ }
if (bss_.GetHeader()->sh_size != 0u) {
program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R | PF_W, bss_));
}
@@ -909,10 +912,16 @@
void BuildDynsymSection() {
dynsym_.AddSymbol("oatdata", &rodata_, 0, true,
rodata_.GetSize(), STB_GLOBAL, STT_OBJECT);
- dynsym_.AddSymbol("oatexec", &text_, 0, true,
- text_.GetSize(), STB_GLOBAL, STT_OBJECT);
- dynsym_.AddSymbol("oatlastword", &text_, text_.GetSize() - 4,
- true, 4, STB_GLOBAL, STT_OBJECT);
+ if (text_.GetSize() != 0u) {
+ dynsym_.AddSymbol("oatexec", &text_, 0, true,
+ text_.GetSize(), STB_GLOBAL, STT_OBJECT);
+ dynsym_.AddSymbol("oatlastword", &text_, text_.GetSize() - 4,
+ true, 4, STB_GLOBAL, STT_OBJECT);
+ } else if (rodata_.GetSize() != 0) {
+ // rodata_ be size 0 for dwarf_test.
+ dynsym_.AddSymbol("oatlastword", &rodata_, rodata_.GetSize() - 4,
+ true, 4, STB_GLOBAL, STT_OBJECT);
+ }
if (bss_.GetSize() != 0u) {
dynsym_.AddSymbol("oatbss", &bss_, 0, true,
bss_.GetSize(), STB_GLOBAL, STT_OBJECT);
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index ea3cb66..fb69f8e 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -16,6 +16,7 @@
#include "arch/instruction_set_features.h"
#include "art_method-inl.h"
+#include "base/unix_file/fd_file.h"
#include "class_linker.h"
#include "common_compiler_test.h"
#include "compiled_method.h"
@@ -37,6 +38,16 @@
namespace art {
+NO_RETURN static void Usage(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string error;
+ StringAppendV(&error, fmt, ap);
+ LOG(FATAL) << error;
+ va_end(ap);
+ UNREACHABLE();
+}
+
class OatTest : public CommonCompilerTest {
protected:
static const bool kCompile = false; // DISABLED_ due to the time to compile libcore
@@ -71,6 +82,66 @@
CHECK_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size));
}
}
+
+ void SetupCompiler(Compiler::Kind compiler_kind,
+ InstructionSet insn_set,
+ const std::vector<std::string>& compiler_options,
+ /*out*/std::string* error_msg) {
+ ASSERT_TRUE(error_msg != nullptr);
+ insn_features_.reset(InstructionSetFeatures::FromVariant(insn_set, "default", error_msg));
+ ASSERT_TRUE(insn_features_ != nullptr) << error_msg;
+ compiler_options_.reset(new CompilerOptions);
+ for (const std::string& option : compiler_options) {
+ compiler_options_->ParseCompilerOption(option, Usage);
+ }
+ verification_results_.reset(new VerificationResults(compiler_options_.get()));
+ method_inliner_map_.reset(new DexFileToMethodInlinerMap);
+ callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(),
+ method_inliner_map_.get(),
+ CompilerCallbacks::CallbackMode::kCompileApp));
+ Runtime::Current()->SetCompilerCallbacks(callbacks_.get());
+ timer_.reset(new CumulativeLogger("Compilation times"));
+ compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
+ verification_results_.get(),
+ method_inliner_map_.get(),
+ compiler_kind,
+ insn_set,
+ insn_features_.get(),
+ false,
+ nullptr,
+ nullptr,
+ nullptr,
+ 2,
+ true,
+ true,
+ "",
+ false,
+ timer_.get(),
+ -1,
+ ""));
+ }
+
+ bool WriteElf(File* file,
+ const std::vector<const DexFile*>& dex_files,
+ SafeMap<std::string, std::string>& key_value_store) {
+ TimingLogger timings("WriteElf", false, false);
+ OatWriter oat_writer(dex_files,
+ 42U,
+ 4096U,
+ 0,
+ compiler_driver_.get(),
+ nullptr,
+ &timings,
+ &key_value_store);
+ return compiler_driver_->WriteElf(GetTestAndroidRoot(),
+ !kIsTargetBuild,
+ dex_files,
+ &oat_writer,
+ file);
+ }
+
+ std::unique_ptr<const InstructionSetFeatures> insn_features_;
+ std::unique_ptr<QuickCompilerCallbacks> callbacks_;
};
TEST_F(OatTest, WriteRead) {
@@ -80,21 +151,9 @@
// TODO: make selectable.
Compiler::Kind compiler_kind = Compiler::kQuick;
InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86;
-
std::string error_msg;
- std::unique_ptr<const InstructionSetFeatures> insn_features(
- InstructionSetFeatures::FromVariant(insn_set, "default", &error_msg));
- ASSERT_TRUE(insn_features.get() != nullptr) << error_msg;
- compiler_options_.reset(new CompilerOptions);
- verification_results_.reset(new VerificationResults(compiler_options_.get()));
- method_inliner_map_.reset(new DexFileToMethodInlinerMap);
- timer_.reset(new CumulativeLogger("Compilation times"));
- compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
- verification_results_.get(),
- method_inliner_map_.get(),
- compiler_kind, insn_set,
- insn_features.get(), false, nullptr, nullptr, nullptr,
- 2, true, true, "", false, timer_.get(), -1, ""));
+ SetupCompiler(compiler_kind, insn_set, std::vector<std::string>(), /*out*/ &error_msg);
+
jobject class_loader = nullptr;
if (kCompile) {
TimingLogger timings2("OatTest::WriteRead", false, false);
@@ -105,19 +164,7 @@
ScratchFile tmp;
SafeMap<std::string, std::string> key_value_store;
key_value_store.Put(OatHeader::kImageLocationKey, "lue.art");
- OatWriter oat_writer(class_linker->GetBootClassPath(),
- 42U,
- 4096U,
- 0,
- compiler_driver_.get(),
- nullptr,
- &timings,
- &key_value_store);
- bool success = compiler_driver_->WriteElf(GetTestAndroidRoot(),
- !kIsTargetBuild,
- class_linker->GetBootClassPath(),
- &oat_writer,
- tmp.GetFile());
+ bool success = WriteElf(tmp.GetFile(), class_linker->GetBootClassPath(), key_value_store);
ASSERT_TRUE(success);
if (kCompile) { // OatWriter strips the code, regenerate to compare
@@ -212,4 +259,53 @@
ASSERT_FALSE(oat_header->IsValid());
}
+TEST_F(OatTest, EmptyTextSection) {
+ TimingLogger timings("OatTest::EmptyTextSection", false, false);
+
+ // TODO: make selectable.
+ Compiler::Kind compiler_kind = Compiler::kQuick;
+ InstructionSet insn_set = kRuntimeISA;
+ if (insn_set == kArm) insn_set = kThumb2;
+ std::string error_msg;
+ std::vector<std::string> compiler_options;
+ compiler_options.push_back("--compiler-filter=verify-at-runtime");
+ SetupCompiler(compiler_kind, insn_set, compiler_options, /*out*/ &error_msg);
+
+ jobject class_loader;
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ class_loader = LoadDex("Main");
+ }
+ ASSERT_TRUE(class_loader != nullptr);
+ std::vector<const DexFile*> dex_files = GetDexFiles(class_loader);
+ ASSERT_TRUE(!dex_files.empty());
+
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ for (const DexFile* dex_file : dex_files) {
+ ScopedObjectAccess soa(Thread::Current());
+ class_linker->RegisterDexFile(
+ *dex_file,
+ class_linker->GetOrCreateAllocatorForClassLoader(
+ soa.Decode<mirror::ClassLoader*>(class_loader)));
+ }
+ compiler_driver_->SetDexFilesForOatFile(dex_files);
+ compiler_driver_->CompileAll(class_loader, dex_files, &timings);
+
+ ScratchFile tmp;
+ SafeMap<std::string, std::string> key_value_store;
+ key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
+ bool success = WriteElf(tmp.GetFile(), dex_files, key_value_store);
+ ASSERT_TRUE(success);
+
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(),
+ tmp.GetFilename(),
+ nullptr,
+ nullptr,
+ false,
+ nullptr,
+ &error_msg));
+ ASSERT_TRUE(oat_file != nullptr);
+ EXPECT_LT(static_cast<size_t>(oat_file->Size()), static_cast<size_t>(tmp.GetFile()->GetLength()));
+}
+
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 7b1fdb2..a8ba19b 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -151,6 +151,11 @@
return compiler_callbacks_;
}
+ void SetCompilerCallbacks(CompilerCallbacks* callbacks) {
+ CHECK(callbacks != nullptr);
+ compiler_callbacks_ = callbacks;
+ }
+
bool IsZygote() const {
return is_zygote_;
}