summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build/Android.oat.mk4
-rw-r--r--compiler/cfi_test.h12
-rw-r--r--compiler/dex/quick/codegen_util.cc4
-rw-r--r--compiler/dex/quick/quick_cfi_test.cc3
-rw-r--r--compiler/dex/quick/x86/quick_assemble_x86_test.cc3
-rw-r--r--compiler/driver/compiler_driver.cc4
-rw-r--r--compiler/driver/compiler_options.cc9
-rw-r--r--compiler/driver/compiler_options.h17
-rw-r--r--compiler/dwarf/dwarf_constants.h8
-rw-r--r--compiler/dwarf/dwarf_test.cc36
-rw-r--r--compiler/dwarf/dwarf_test.h10
-rw-r--r--compiler/dwarf/headers.h43
-rw-r--r--compiler/elf_builder.h31
-rw-r--r--compiler/elf_writer_debug.cc110
-rw-r--r--compiler/elf_writer_debug.h15
-rw-r--r--compiler/elf_writer_quick.cc185
-rw-r--r--compiler/elf_writer_quick.h2
-rw-r--r--compiler/elf_writer_test.cc98
-rw-r--r--compiler/jit/jit_compiler.cc3
-rw-r--r--compiler/jni/quick/jni_compiler.cc2
-rw-r--r--compiler/oat_writer.cc11
-rw-r--r--compiler/oat_writer.h19
-rw-r--r--compiler/optimizing/optimizing_compiler.cc6
-rw-r--r--dex2oat/dex2oat.cc39
-rw-r--r--runtime/class_linker.cc13
-rw-r--r--runtime/elf_file.cc85
-rw-r--r--runtime/elf_file_impl.h9
-rw-r--r--runtime/gc/heap.cc1
-rw-r--r--runtime/gc/space/image_space.cc5
-rw-r--r--runtime/gc/space/image_space.h4
-rw-r--r--runtime/mem_map.cc8
-rw-r--r--runtime/mem_map.h6
-rw-r--r--runtime/native/dalvik_system_ZygoteHooks.cc10
-rw-r--r--runtime/oat_file.cc61
-rw-r--r--runtime/oat_file.h3
-rw-r--r--test/137-cfi/cfi.cc242
-rw-r--r--test/137-cfi/expected.txt0
-rw-r--r--test/137-cfi/info.txt1
-rwxr-xr-xtest/137-cfi/run21
-rw-r--r--test/137-cfi/src/Main.java170
-rw-r--r--test/Android.libarttest.mk3
-rw-r--r--test/Android.run-test.mk52
-rw-r--r--tools/art2
43 files changed, 946 insertions, 424 deletions
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 710b130..728469c 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -113,7 +113,7 @@ $$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency)
--oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \
--base=$$(LIBART_IMG_HOST_BASE_ADDRESS) --instruction-set=$$($(3)ART_HOST_ARCH) \
--instruction-set-features=$$($(3)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES) \
- --host --android-root=$$(HOST_OUT) --include-patch-information \
+ --host --android-root=$$(HOST_OUT) --include-patch-information --generate-debug-info \
$$(PRIVATE_CORE_COMPILE_OPTIONS)
$$(core_oat_name): $$(core_image_name)
@@ -232,7 +232,7 @@ $$(core_image_name): $$(TARGET_CORE_DEX_FILES) $$(core_dex2oat_dependency)
--base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) --instruction-set=$$($(3)TARGET_ARCH) \
--instruction-set-variant=$$($(3)DEX2OAT_TARGET_CPU_VARIANT) \
--instruction-set-features=$$($(3)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
- --android-root=$$(PRODUCT_OUT)/system --include-patch-information \
+ --android-root=$$(PRODUCT_OUT)/system --include-patch-information --generate-debug-info \
$$(PRIVATE_CORE_COMPILE_OPTIONS) || (rm $$(PRIVATE_CORE_OAT_NAME); exit 1)
$$(core_oat_name): $$(core_image_name)
diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h
index f7501d2..5e345db 100644
--- a/compiler/cfi_test.h
+++ b/compiler/cfi_test.h
@@ -30,6 +30,8 @@
namespace art {
+constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT;
+
class CFITest : public dwarf::DwarfTest {
public:
void GenerateExpected(FILE* f, InstructionSet isa, const char* isa_str,
@@ -46,11 +48,11 @@ class CFITest : public dwarf::DwarfTest {
// Pretty-print CFI opcodes.
constexpr bool is64bit = false;
dwarf::DebugFrameOpCodeWriter<> initial_opcodes;
- dwarf::WriteEhFrameCIE(is64bit, dwarf::DW_EH_PE_absptr, dwarf::Reg(8),
- initial_opcodes, &eh_frame_data_);
- std::vector<uintptr_t> eh_frame_patches;
- dwarf::WriteEhFrameFDE(is64bit, 0, 0, actual_asm.size(), &actual_cfi,
- &eh_frame_data_, &eh_frame_patches);
+ dwarf::WriteDebugFrameCIE(is64bit, dwarf::DW_EH_PE_absptr, dwarf::Reg(8),
+ initial_opcodes, kCFIFormat, &debug_frame_data_);
+ std::vector<uintptr_t> debug_frame_patches;
+ dwarf::WriteDebugFrameFDE(is64bit, 0, 0, actual_asm.size(), &actual_cfi,
+ kCFIFormat, &debug_frame_data_, &debug_frame_patches);
ReformatCfi(Objdump(false, "-W"), &lines);
// Pretty-print assembly.
auto* opts = new DisassemblerOptions(false, actual_asm.data(), true);
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index f4bf31f..c803e65 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -670,7 +670,7 @@ bool Mir2Lir::VerifyCatchEntries() {
void Mir2Lir::CreateMappingTables() {
- bool generate_src_map = cu_->compiler_driver->GetCompilerOptions().GetIncludeDebugSymbols();
+ bool generate_src_map = cu_->compiler_driver->GetCompilerOptions().GetGenerateDebugInfo();
uint32_t pc2dex_data_size = 0u;
uint32_t pc2dex_entries = 0u;
@@ -1071,7 +1071,7 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena
pc_rel_temp_(nullptr),
dex_cache_arrays_min_offset_(std::numeric_limits<uint32_t>::max()),
cfi_(&last_lir_insn_,
- cu->compiler_driver->GetCompilerOptions().GetIncludeCFI(),
+ cu->compiler_driver->GetCompilerOptions().GetGenerateDebugInfo(),
arena),
in_to_reg_storage_mapping_(arena) {
switch_tables_.reserve(4);
diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc
index 8694ebc..dd68dd4 100644
--- a/compiler/dex/quick/quick_cfi_test.cc
+++ b/compiler/dex/quick/quick_cfi_test.cc
@@ -59,8 +59,7 @@ class QuickCFITest : public CFITest {
false,
CompilerOptions::kDefaultTopKProfileThreshold,
false,
- true, // include_debug_symbols.
- true, // include_cfi
+ true, // generate_debug_info.
false,
false,
false,
diff --git a/compiler/dex/quick/x86/quick_assemble_x86_test.cc b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
index f58f206..798e23f 100644
--- a/compiler/dex/quick/x86/quick_assemble_x86_test.cc
+++ b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
@@ -42,8 +42,7 @@ class QuickAssembleX86TestBase : public testing::Test {
false,
CompilerOptions::kDefaultTopKProfileThreshold,
false,
- false,
- false,
+ CompilerOptions::kDefaultGenerateDebugInfo,
false,
false,
false,
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 55fabcc..e5fa54e 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -76,8 +76,8 @@ namespace art {
static constexpr bool kTimeCompileMethod = !kIsDebugBuild;
-// Whether to produce 64-bit ELF files for 64-bit targets. Leave this off for now.
-static constexpr bool kProduce64BitELFFiles = false;
+// Whether to produce 64-bit ELF files for 64-bit targets.
+static constexpr bool kProduce64BitELFFiles = true;
// Whether classes-to-compile and methods-to-compile are only applied to the boot image, or, when
// given, too all compilations.
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index c5fc98a..226e6b7 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -30,8 +30,7 @@ CompilerOptions::CompilerOptions()
include_patch_information_(kDefaultIncludePatchInformation),
top_k_profile_threshold_(kDefaultTopKProfileThreshold),
debuggable_(false),
- include_debug_symbols_(kDefaultIncludeDebugSymbols),
- include_cfi_(false),
+ generate_debug_info_(kDefaultGenerateDebugInfo),
implicit_null_checks_(true),
implicit_so_checks_(true),
implicit_suspend_checks_(false),
@@ -56,8 +55,7 @@ CompilerOptions::CompilerOptions(CompilerFilter compiler_filter,
bool include_patch_information,
double top_k_profile_threshold,
bool debuggable,
- bool include_debug_symbols,
- bool include_cfi,
+ bool generate_debug_info,
bool implicit_null_checks,
bool implicit_so_checks,
bool implicit_suspend_checks,
@@ -76,8 +74,7 @@ CompilerOptions::CompilerOptions(CompilerFilter compiler_filter,
include_patch_information_(include_patch_information),
top_k_profile_threshold_(top_k_profile_threshold),
debuggable_(debuggable),
- include_debug_symbols_(include_debug_symbols),
- include_cfi_(include_cfi),
+ generate_debug_info_(generate_debug_info),
implicit_null_checks_(implicit_null_checks),
implicit_so_checks_(implicit_so_checks),
implicit_suspend_checks_(implicit_suspend_checks),
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index bf3f8ec..356663b 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -49,7 +49,7 @@ class CompilerOptions FINAL {
static const size_t kDefaultTinyMethodThreshold = 20;
static const size_t kDefaultNumDexMethodsThreshold = 900;
static constexpr double kDefaultTopKProfileThreshold = 90.0;
- static const bool kDefaultIncludeDebugSymbols = kIsDebugBuild;
+ static const bool kDefaultGenerateDebugInfo = kIsDebugBuild;
static const bool kDefaultIncludePatchInformation = false;
CompilerOptions();
@@ -64,8 +64,7 @@ class CompilerOptions FINAL {
bool include_patch_information,
double top_k_profile_threshold,
bool debuggable,
- bool include_debug_symbols,
- bool include_cfi,
+ bool generate_debug_info,
bool implicit_null_checks,
bool implicit_so_checks,
bool implicit_suspend_checks,
@@ -146,13 +145,8 @@ class CompilerOptions FINAL {
return debuggable_;
}
- bool GetIncludeDebugSymbols() const {
- return include_debug_symbols_;
- }
-
- bool GetIncludeCFI() const {
- // include-debug-symbols implies include-cfi.
- return include_cfi_ || include_debug_symbols_;
+ bool GetGenerateDebugInfo() const {
+ return generate_debug_info_;
}
bool GetImplicitNullChecks() const {
@@ -212,8 +206,7 @@ class CompilerOptions FINAL {
// When using a profile file only the top K% of the profiled samples will be compiled.
const double top_k_profile_threshold_;
const bool debuggable_;
- const bool include_debug_symbols_;
- const bool include_cfi_;
+ const bool generate_debug_info_;
const bool implicit_null_checks_;
const bool implicit_so_checks_;
const bool implicit_suspend_checks_;
diff --git a/compiler/dwarf/dwarf_constants.h b/compiler/dwarf/dwarf_constants.h
index 61a44cd..3b570e5 100644
--- a/compiler/dwarf/dwarf_constants.h
+++ b/compiler/dwarf/dwarf_constants.h
@@ -680,6 +680,14 @@ enum ExceptionHeaderValueApplication : uint8_t {
DW_EH_PE_aligned = 0x50,
};
+enum CFIFormat : uint8_t {
+ // This is the original format as defined by the specification.
+ // It is used for the .debug_frame section.
+ DW_DEBUG_FRAME_FORMAT,
+ // Slightly modified format used for the .eh_frame section.
+ DW_EH_FRAME_FORMAT
+};
+
} // namespace dwarf
} // namespace art
diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc
index edba00a..4d423d0 100644
--- a/compiler/dwarf/dwarf_test.cc
+++ b/compiler/dwarf/dwarf_test.cc
@@ -29,6 +29,8 @@ namespace dwarf {
// Run the tests only on host since we need objdump.
#ifndef HAVE_ANDROID_OS
+constexpr CFIFormat kCFIFormat = DW_DEBUG_FRAME_FORMAT;
+
TEST_F(DwarfTest, DebugFrame) {
const bool is64bit = false;
@@ -120,30 +122,30 @@ TEST_F(DwarfTest, DebugFrame) {
DW_CHECK_NEXT("DW_CFA_restore: r5 (ebp)");
DebugFrameOpCodeWriter<> initial_opcodes;
- WriteEhFrameCIE(is64bit, DW_EH_PE_absptr, Reg(is64bit ? 16 : 8),
- initial_opcodes, &eh_frame_data_);
- std::vector<uintptr_t> eh_frame_patches;
+ WriteDebugFrameCIE(is64bit, DW_EH_PE_absptr, Reg(is64bit ? 16 : 8),
+ initial_opcodes, kCFIFormat, &debug_frame_data_);
+ std::vector<uintptr_t> debug_frame_patches;
std::vector<uintptr_t> expected_patches { 28 }; // NOLINT
- WriteEhFrameFDE(is64bit, 0, 0x01000000, 0x01000000, opcodes.data(),
- &eh_frame_data_, &eh_frame_patches);
+ WriteDebugFrameFDE(is64bit, 0, 0x01000000, 0x01000000, opcodes.data(),
+ kCFIFormat, &debug_frame_data_, &debug_frame_patches);
- EXPECT_EQ(expected_patches, eh_frame_patches);
+ EXPECT_EQ(expected_patches, debug_frame_patches);
CheckObjdumpOutput(is64bit, "-W");
}
TEST_F(DwarfTest, DebugFrame64) {
constexpr bool is64bit = true;
DebugFrameOpCodeWriter<> initial_opcodes;
- WriteEhFrameCIE(is64bit, DW_EH_PE_absptr, Reg(16),
- initial_opcodes, &eh_frame_data_);
+ WriteDebugFrameCIE(is64bit, DW_EH_PE_absptr, Reg(16),
+ initial_opcodes, kCFIFormat, &debug_frame_data_);
DebugFrameOpCodeWriter<> opcodes;
- std::vector<uintptr_t> eh_frame_patches;
+ std::vector<uintptr_t> debug_frame_patches;
std::vector<uintptr_t> expected_patches { 32 }; // NOLINT
- WriteEhFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000,
- opcodes.data(), &eh_frame_data_, &eh_frame_patches);
+ WriteDebugFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000,
+ opcodes.data(), kCFIFormat, &debug_frame_data_, &debug_frame_patches);
DW_CHECK("FDE cie=00000000 pc=100000000000000..300000000000000");
- EXPECT_EQ(expected_patches, eh_frame_patches);
+ EXPECT_EQ(expected_patches, debug_frame_patches);
CheckObjdumpOutput(is64bit, "-W");
}
@@ -173,11 +175,11 @@ TEST_F(DwarfTest, x86_64_RegisterMapping) {
DW_CHECK_NEXT("DW_CFA_offset: r14 (r14)");
DW_CHECK_NEXT("DW_CFA_offset: r15 (r15)");
DebugFrameOpCodeWriter<> initial_opcodes;
- WriteEhFrameCIE(is64bit, DW_EH_PE_absptr, Reg(16),
- initial_opcodes, &eh_frame_data_);
- std::vector<uintptr_t> eh_frame_patches;
- WriteEhFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000,
- opcodes.data(), &eh_frame_data_, &eh_frame_patches);
+ WriteDebugFrameCIE(is64bit, DW_EH_PE_absptr, Reg(16),
+ initial_opcodes, kCFIFormat, &debug_frame_data_);
+ std::vector<uintptr_t> debug_frame_patches;
+ WriteDebugFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000,
+ opcodes.data(), kCFIFormat, &debug_frame_data_, &debug_frame_patches);
CheckObjdumpOutput(is64bit, "-W");
}
diff --git a/compiler/dwarf/dwarf_test.h b/compiler/dwarf/dwarf_test.h
index 370c744..f819c49 100644
--- a/compiler/dwarf/dwarf_test.h
+++ b/compiler/dwarf/dwarf_test.h
@@ -68,7 +68,7 @@ class DwarfTest : public CommonRuntimeTest {
RawSection debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
RawSection debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
RawSection debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
- RawSection eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0);
+ RawSection debug_frame(".debug_frame", SHT_PROGBITS, 0, nullptr, 0, 8, 0);
if (!debug_info_data_.empty()) {
debug_info.SetBuffer(debug_info_data_);
builder.RegisterSection(&debug_info);
@@ -85,9 +85,9 @@ class DwarfTest : public CommonRuntimeTest {
debug_line.SetBuffer(debug_line_data_);
builder.RegisterSection(&debug_line);
}
- if (!eh_frame_data_.empty()) {
- eh_frame.SetBuffer(eh_frame_data_);
- builder.RegisterSection(&eh_frame);
+ if (!debug_frame_data_.empty()) {
+ debug_frame.SetBuffer(debug_frame_data_);
+ builder.RegisterSection(&debug_frame);
}
ScratchFile file;
builder.Write(file.GetFile());
@@ -166,7 +166,7 @@ class DwarfTest : public CommonRuntimeTest {
}
// Buffers which are going to assembled into ELF file and passed to objdump.
- std::vector<uint8_t> eh_frame_data_;
+ std::vector<uint8_t> debug_frame_data_;
std::vector<uint8_t> debug_info_data_;
std::vector<uint8_t> debug_abbrev_data_;
std::vector<uint8_t> debug_str_data_;
diff --git a/compiler/dwarf/headers.h b/compiler/dwarf/headers.h
index 9f64766..ad315ee 100644
--- a/compiler/dwarf/headers.h
+++ b/compiler/dwarf/headers.h
@@ -35,17 +35,18 @@ namespace dwarf {
// and compilers are expected *not* to use it by default.
// In particular, it is not related to machine architecture.
-// Write common information entry (CIE) to .eh_frame section.
+// Write common information entry (CIE) to .debug_frame or .eh_frame section.
template<typename Allocator>
-void WriteEhFrameCIE(bool is64bit,
- ExceptionHeaderValueApplication address_type,
- Reg return_address_register,
- const DebugFrameOpCodeWriter<Allocator>& opcodes,
- std::vector<uint8_t>* eh_frame) {
- Writer<> writer(eh_frame);
+void WriteDebugFrameCIE(bool is64bit,
+ ExceptionHeaderValueApplication address_type,
+ Reg return_address_register,
+ const DebugFrameOpCodeWriter<Allocator>& opcodes,
+ CFIFormat format,
+ std::vector<uint8_t>* debug_frame) {
+ Writer<> writer(debug_frame);
size_t cie_header_start_ = writer.data()->size();
writer.PushUint32(0); // Length placeholder.
- writer.PushUint32(0); // CIE id.
+ writer.PushUint32((format == DW_EH_FRAME_FORMAT) ? 0 : 0xFFFFFFFF); // CIE id.
writer.PushUint8(1); // Version.
writer.PushString("zR");
writer.PushUleb128(DebugFrameOpCodeWriter<Allocator>::kCodeAlignmentFactor);
@@ -62,20 +63,26 @@ void WriteEhFrameCIE(bool is64bit,
writer.UpdateUint32(cie_header_start_, writer.data()->size() - cie_header_start_ - 4);
}
-// Write frame description entry (FDE) to .eh_frame section.
+// Write frame description entry (FDE) to .debug_frame or .eh_frame section.
template<typename Allocator>
-void WriteEhFrameFDE(bool is64bit, size_t cie_offset,
- uint64_t initial_address, uint64_t address_range,
- const std::vector<uint8_t, Allocator>* opcodes,
- std::vector<uint8_t>* eh_frame,
- std::vector<uintptr_t>* eh_frame_patches) {
- Writer<> writer(eh_frame);
+void WriteDebugFrameFDE(bool is64bit, size_t cie_offset,
+ uint64_t initial_address, uint64_t address_range,
+ const std::vector<uint8_t, Allocator>* opcodes,
+ CFIFormat format,
+ std::vector<uint8_t>* debug_frame,
+ std::vector<uintptr_t>* debug_frame_patches) {
+ Writer<> writer(debug_frame);
size_t fde_header_start = writer.data()->size();
writer.PushUint32(0); // Length placeholder.
- uint32_t cie_pointer = writer.data()->size() - cie_offset;
- writer.PushUint32(cie_pointer);
+ if (format == DW_EH_FRAME_FORMAT) {
+ uint32_t cie_pointer = writer.data()->size() - cie_offset;
+ writer.PushUint32(cie_pointer);
+ } else {
+ uint32_t cie_pointer = cie_offset;
+ writer.PushUint32(cie_pointer);
+ }
// Relocate initial_address, but not address_range (it is size).
- eh_frame_patches->push_back(writer.data()->size());
+ debug_frame_patches->push_back(writer.data()->size());
if (is64bit) {
writer.PushUint64(initial_address);
writer.PushUint64(address_range);
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index 2c68bb8..bbd962f 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -166,6 +166,10 @@ class ElfBuilder FINAL {
patched_(false), patch_(patch), patch_base_section_(patch_base_section) {
}
+ RawSection(const std::string& name, Elf_Word type)
+ : RawSection(name, type, 0, nullptr, 0, 1, 0, nullptr, nullptr) {
+ }
+
Elf_Word GetSize() const OVERRIDE {
return buffer_.size();
}
@@ -263,7 +267,7 @@ class ElfBuilder FINAL {
class StrtabSection FINAL : public Section {
public:
StrtabSection(const std::string& name, Elf_Word flags)
- : Section(name, SHT_STRTAB, flags, nullptr, 0, 1, 1) {
+ : Section(name, SHT_STRTAB, flags, nullptr, 0, 1, 0) {
buffer_.reserve(4 * KB);
// The first entry of strtab must be empty string.
buffer_ += '\0';
@@ -306,7 +310,7 @@ class ElfBuilder FINAL {
SymtabSection(const std::string& name, Elf_Word type, Elf_Word flags,
StrtabSection* strtab)
- : Section(name, type, flags, strtab, 0, sizeof(Elf_Word), sizeof(Elf_Sym)),
+ : Section(name, type, flags, strtab, 0, sizeof(Elf_Off), sizeof(Elf_Sym)),
strtab_(strtab) {
}
@@ -499,7 +503,7 @@ class ElfBuilder FINAL {
text_(".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR,
nullptr, 0, kPageSize, 0, text_size, text_writer),
bss_(".bss", bss_size),
- dynamic_(".dynamic", &dynsym_),
+ dynamic_(".dynamic", &dynstr_),
strtab_(".strtab", 0),
symtab_(".symtab", SHT_SYMTAB, 0, &strtab_),
shstrtab_(".shstrtab", 0) {
@@ -641,11 +645,10 @@ class ElfBuilder FINAL {
// It is easiest to just reserve a fixed amount of space for them.
constexpr size_t kMaxProgramHeaders = 8;
constexpr size_t kProgramHeadersOffset = sizeof(Elf_Ehdr);
- constexpr size_t kProgramHeadersSize = sizeof(Elf_Phdr) * kMaxProgramHeaders;
// Layout of all sections - determine the final file offsets and addresses.
// This must be done after we have built all sections and know their size.
- Elf_Off file_offset = kProgramHeadersOffset + kProgramHeadersSize;
+ Elf_Off file_offset = kProgramHeadersOffset + sizeof(Elf_Phdr) * kMaxProgramHeaders;
Elf_Addr load_address = file_offset;
std::vector<Elf_Shdr> section_headers;
section_headers.reserve(1u + sections.size());
@@ -674,7 +677,7 @@ class ElfBuilder FINAL {
// Collect section headers into continuous array for convenience.
section_headers.push_back(*header);
}
- Elf_Off section_headers_offset = RoundUp(file_offset, sizeof(Elf_Word));
+ Elf_Off section_headers_offset = RoundUp(file_offset, sizeof(Elf_Off));
// Create program headers now that we know the layout of the whole file.
// Each segment contains one or more sections which are mapped together.
@@ -682,8 +685,7 @@ class ElfBuilder FINAL {
// PT_LOAD does the mapping. Other PT_* types allow the program to locate
// interesting parts of memory and their addresses overlap with PT_LOAD.
std::vector<Elf_Phdr> program_headers;
- program_headers.push_back(MakeProgramHeader(PT_PHDR, PF_R,
- kProgramHeadersOffset, kProgramHeadersSize, sizeof(Elf_Word)));
+ program_headers.push_back(Elf_Phdr()); // Placeholder for PT_PHDR.
// Create the main LOAD R segment which spans all sections up to .rodata.
const Elf_Shdr* rodata = rodata_.GetHeader();
program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R,
@@ -709,6 +711,9 @@ class ElfBuilder FINAL {
program_headers.push_back(MakeProgramHeader(PT_GNU_EH_FRAME, PF_R, *eh_frame_hdr));
}
}
+ DCHECK_EQ(program_headers[0].p_type, 0u); // Check placeholder.
+ program_headers[0] = MakeProgramHeader(PT_PHDR, PF_R,
+ kProgramHeadersOffset, program_headers.size() * sizeof(Elf_Phdr), sizeof(Elf_Off));
CHECK_LE(program_headers.size(), kMaxProgramHeaders);
// Create the main ELF header.
@@ -777,10 +782,12 @@ class ElfBuilder FINAL {
template<typename T>
static bool WriteArray(File* elf_file, const T* data, size_t count) {
- DCHECK(data != nullptr);
- if (!elf_file->WriteFully(data, count * sizeof(T))) {
- PLOG(ERROR) << "Failed to write to file " << elf_file->GetPath();
- return false;
+ if (count != 0) {
+ DCHECK(data != nullptr);
+ if (!elf_file->WriteFully(data, count * sizeof(T))) {
+ PLOG(ERROR) << "Failed to write to file " << elf_file->GetPath();
+ return false;
+ }
}
return true;
}
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index dbbe82e..c68bbc0 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -30,9 +30,10 @@
namespace art {
namespace dwarf {
-static void WriteEhFrameCIE(InstructionSet isa,
- ExceptionHeaderValueApplication addr_type,
- std::vector<uint8_t>* eh_frame) {
+static void WriteDebugFrameCIE(InstructionSet isa,
+ ExceptionHeaderValueApplication addr_type,
+ CFIFormat format,
+ std::vector<uint8_t>* eh_frame) {
// Scratch registers should be marked as undefined. This tells the
// debugger that its value in the previous frame is not recoverable.
bool is64bit = Is64BitInstructionSet(isa);
@@ -58,7 +59,8 @@ static void WriteEhFrameCIE(InstructionSet isa,
}
}
auto return_reg = Reg::ArmCore(14); // R14(LR).
- WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame);
+ WriteDebugFrameCIE(is64bit, addr_type, return_reg,
+ opcodes, format, eh_frame);
return;
}
case kArm64: {
@@ -81,7 +83,8 @@ static void WriteEhFrameCIE(InstructionSet isa,
}
}
auto return_reg = Reg::Arm64Core(30); // R30(LR).
- WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame);
+ WriteDebugFrameCIE(is64bit, addr_type, return_reg,
+ opcodes, format, eh_frame);
return;
}
case kMips:
@@ -97,7 +100,8 @@ static void WriteEhFrameCIE(InstructionSet isa,
}
}
auto return_reg = Reg::MipsCore(31); // R31(RA).
- WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame);
+ WriteDebugFrameCIE(is64bit, addr_type, return_reg,
+ opcodes, format, eh_frame);
return;
}
case kX86: {
@@ -123,7 +127,8 @@ static void WriteEhFrameCIE(InstructionSet isa,
}
}
auto return_reg = Reg::X86Core(8); // R8(EIP).
- WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame);
+ WriteDebugFrameCIE(is64bit, addr_type, return_reg,
+ opcodes, format, eh_frame);
return;
}
case kX86_64: {
@@ -149,7 +154,8 @@ static void WriteEhFrameCIE(InstructionSet isa,
}
}
auto return_reg = Reg::X86_64Core(16); // R16(RIP).
- WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame);
+ WriteDebugFrameCIE(is64bit, addr_type, return_reg,
+ opcodes, format, eh_frame);
return;
}
case kNone:
@@ -159,58 +165,61 @@ static void WriteEhFrameCIE(InstructionSet isa,
UNREACHABLE();
}
-void WriteEhFrame(const CompilerDriver* compiler,
- const OatWriter* oat_writer,
- ExceptionHeaderValueApplication address_type,
- std::vector<uint8_t>* eh_frame,
- std::vector<uintptr_t>* eh_frame_patches,
- std::vector<uint8_t>* eh_frame_hdr,
- std::vector<uintptr_t>* eh_frame_hdr_patches) {
+void WriteCFISection(const CompilerDriver* compiler,
+ const OatWriter* oat_writer,
+ ExceptionHeaderValueApplication address_type,
+ CFIFormat format,
+ std::vector<uint8_t>* debug_frame,
+ std::vector<uintptr_t>* debug_frame_patches,
+ std::vector<uint8_t>* eh_frame_hdr,
+ std::vector<uintptr_t>* eh_frame_hdr_patches) {
const auto& method_infos = oat_writer->GetMethodDebugInfo();
const InstructionSet isa = compiler->GetInstructionSet();
- // Write .eh_frame section.
+ // Write .eh_frame/.debug_frame section.
std::map<uint32_t, size_t> address_to_fde_offset_map;
- size_t cie_offset = eh_frame->size();
- WriteEhFrameCIE(isa, address_type, eh_frame);
+ size_t cie_offset = debug_frame->size();
+ WriteDebugFrameCIE(isa, address_type, format, debug_frame);
for (const OatWriter::DebugInfo& mi : method_infos) {
if (!mi.deduped_) { // Only one FDE per unique address.
const SwapVector<uint8_t>* opcodes = mi.compiled_method_->GetCFIInfo();
if (opcodes != nullptr) {
- address_to_fde_offset_map.emplace(mi.low_pc_, eh_frame->size());
- WriteEhFrameFDE(Is64BitInstructionSet(isa), cie_offset,
- mi.low_pc_, mi.high_pc_ - mi.low_pc_,
- opcodes, eh_frame, eh_frame_patches);
+ address_to_fde_offset_map.emplace(mi.low_pc_, debug_frame->size());
+ WriteDebugFrameFDE(Is64BitInstructionSet(isa), cie_offset,
+ mi.low_pc_, mi.high_pc_ - mi.low_pc_,
+ opcodes, format, debug_frame, debug_frame_patches);
}
}
}
- // Write .eh_frame_hdr section.
- Writer<> header(eh_frame_hdr);
- header.PushUint8(1); // Version.
- // Encoding of .eh_frame pointer - libunwind does not honor datarel here,
- // so we have to use pcrel which means relative to the pointer's location.
- header.PushUint8(DW_EH_PE_pcrel | DW_EH_PE_sdata4);
- // Encoding of binary search table size.
- header.PushUint8(DW_EH_PE_udata4);
- // Encoding of binary search table addresses - libunwind supports only this
- // specific combination, which means relative to the start of .eh_frame_hdr.
- header.PushUint8(DW_EH_PE_datarel | DW_EH_PE_sdata4);
- // .eh_frame pointer - .eh_frame_hdr section is after .eh_frame section
- const int32_t relative_eh_frame_begin = -static_cast<int32_t>(eh_frame->size());
- header.PushInt32(relative_eh_frame_begin - 4U);
- // Binary search table size (number of entries).
- header.PushUint32(dchecked_integral_cast<uint32_t>(address_to_fde_offset_map.size()));
- // Binary search table.
- for (const auto& address_to_fde_offset : address_to_fde_offset_map) {
- u_int32_t code_address = address_to_fde_offset.first;
- int32_t fde_address = dchecked_integral_cast<int32_t>(address_to_fde_offset.second);
- eh_frame_hdr_patches->push_back(header.data()->size());
- header.PushUint32(code_address);
- // We know the exact layout (eh_frame is immediately before eh_frame_hdr)
- // and the data is relative to the start of the eh_frame_hdr,
- // so patching isn't necessary (in contrast to the code address above).
- header.PushInt32(relative_eh_frame_begin + fde_address);
+ if (format == DW_EH_FRAME_FORMAT) {
+ // Write .eh_frame_hdr section.
+ Writer<> header(eh_frame_hdr);
+ header.PushUint8(1); // Version.
+ // Encoding of .eh_frame pointer - libunwind does not honor datarel here,
+ // so we have to use pcrel which means relative to the pointer's location.
+ header.PushUint8(DW_EH_PE_pcrel | DW_EH_PE_sdata4);
+ // Encoding of binary search table size.
+ header.PushUint8(DW_EH_PE_udata4);
+ // Encoding of binary search table addresses - libunwind supports only this
+ // specific combination, which means relative to the start of .eh_frame_hdr.
+ header.PushUint8(DW_EH_PE_datarel | DW_EH_PE_sdata4);
+ // .eh_frame pointer - .eh_frame_hdr section is after .eh_frame section
+ const int32_t relative_eh_frame_begin = -static_cast<int32_t>(debug_frame->size());
+ header.PushInt32(relative_eh_frame_begin - 4U);
+ // Binary search table size (number of entries).
+ header.PushUint32(dchecked_integral_cast<uint32_t>(address_to_fde_offset_map.size()));
+ // Binary search table.
+ for (const auto& address_to_fde_offset : address_to_fde_offset_map) {
+ u_int32_t code_address = address_to_fde_offset.first;
+ int32_t fde_address = dchecked_integral_cast<int32_t>(address_to_fde_offset.second);
+ eh_frame_hdr_patches->push_back(header.data()->size());
+ header.PushUint32(code_address);
+ // We know the exact layout (eh_frame is immediately before eh_frame_hdr)
+ // and the data is relative to the start of the eh_frame_hdr,
+ // so patching isn't necessary (in contrast to the code address above).
+ header.PushInt32(relative_eh_frame_begin + fde_address);
+ }
}
}
@@ -235,6 +244,7 @@ void WriteDebugSections(const CompilerDriver* compiler,
std::vector<uintptr_t>* debug_line_patches) {
const std::vector<OatWriter::DebugInfo>& method_infos = oat_writer->GetMethodDebugInfo();
const InstructionSet isa = compiler->GetInstructionSet();
+ const bool is64bit = Is64BitInstructionSet(isa);
// Find all addresses (low_pc) which contain deduped methods.
// The first instance of method is not marked deduped_, but the rest is.
@@ -272,7 +282,7 @@ void WriteDebugSections(const CompilerDriver* compiler,
}
size_t debug_abbrev_offset = debug_abbrev->size();
- DebugInfoEntryWriter<> info(false /* 32 bit */, debug_abbrev);
+ DebugInfoEntryWriter<> info(is64bit, debug_abbrev);
info.StartTag(DW_TAG_compile_unit, DW_CHILDREN_yes);
info.WriteStrp(DW_AT_producer, "Android dex2oat", debug_str);
info.WriteData1(DW_AT_language, DW_LANG_Java);
@@ -317,7 +327,7 @@ void WriteDebugSections(const CompilerDriver* compiler,
case kX86_64:
break;
}
- DebugLineOpCodeWriter<> opcodes(false /* 32bit */, code_factor_bits_);
+ DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits_);
opcodes.SetAddress(cunit_low_pc);
if (dwarf_isa != -1) {
opcodes.SetISA(dwarf_isa);
diff --git a/compiler/elf_writer_debug.h b/compiler/elf_writer_debug.h
index 28d0e2c..69f7e0d 100644
--- a/compiler/elf_writer_debug.h
+++ b/compiler/elf_writer_debug.h
@@ -25,13 +25,14 @@
namespace art {
namespace dwarf {
-void WriteEhFrame(const CompilerDriver* compiler,
- const OatWriter* oat_writer,
- ExceptionHeaderValueApplication address_type,
- std::vector<uint8_t>* eh_frame,
- std::vector<uintptr_t>* eh_frame_patches,
- std::vector<uint8_t>* eh_frame_hdr,
- std::vector<uintptr_t>* eh_frame_hdr_patches);
+void WriteCFISection(const CompilerDriver* compiler,
+ const OatWriter* oat_writer,
+ ExceptionHeaderValueApplication address_type,
+ CFIFormat format,
+ std::vector<uint8_t>* debug_frame,
+ std::vector<uintptr_t>* debug_frame_patches,
+ std::vector<uint8_t>* eh_frame_hdr,
+ std::vector<uintptr_t>* eh_frame_hdr_patches);
void WriteDebugSections(const CompilerDriver* compiler,
const OatWriter* oat_writer,
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 79f9955..ddee3ba 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -19,6 +19,7 @@
#include <unordered_map>
#include <unordered_set>
+#include "base/casts.h"
#include "base/logging.h"
#include "base/unix_file/fd_file.h"
#include "compiled_method.h"
@@ -37,6 +38,23 @@
namespace art {
+// .eh_frame and .debug_frame are almost identical.
+// Except for some minor formatting differences, the main difference
+// is that .eh_frame is allocated within the running program because
+// it is used by C++ exception handling (which we do not use so we
+// can choose either). C++ compilers generally tend to use .eh_frame
+// because if they need it sometimes, they might as well always use it.
+constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_EH_FRAME_FORMAT;
+
+// The ARM specification defines three special mapping symbols
+// $a, $t and $d which mark ARM, Thumb and data ranges respectively.
+// These symbols can be used by tools, for example, to pretty
+// print instructions correctly. Objdump will use them if they
+// exist, but it will still work well without them.
+// However, these extra symbols take space, so let's just generate
+// one symbol which marks the whole .text section as code.
+constexpr bool kGenerateSingleArmMappingSymbol = true;
+
template <typename ElfTypes>
bool ElfWriterQuick<ElfTypes>::Create(File* elf_file,
OatWriter* oat_writer,
@@ -51,36 +69,17 @@ bool ElfWriterQuick<ElfTypes>::Create(File* elf_file,
template <typename ElfTypes>
static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, OatWriter* oat_writer);
-// Encode patch locations in .oat_patches format.
+// Encode patch locations as LEB128 list of deltas between consecutive addresses.
template <typename ElfTypes>
-void ElfWriterQuick<ElfTypes>::EncodeOatPatches(
- const OatWriter::PatchLocationsMap& sections,
- std::vector<uint8_t>* buffer) {
- for (const auto& section : sections) {
- const std::string& name = section.first;
- std::vector<uintptr_t>* locations = section.second.get();
- DCHECK(!name.empty());
- std::sort(locations->begin(), locations->end());
- // Reserve buffer space - guess 2 bytes per ULEB128.
- buffer->reserve(buffer->size() + name.size() + locations->size() * 2);
- // Write null-terminated section name.
- const uint8_t* name_data = reinterpret_cast<const uint8_t*>(name.c_str());
- buffer->insert(buffer->end(), name_data, name_data + name.size() + 1);
- // Write placeholder for data length.
- size_t length_pos = buffer->size();
- EncodeUnsignedLeb128(buffer, UINT32_MAX);
- // Write LEB128 encoded list of advances (deltas between consequtive addresses).
- size_t data_pos = buffer->size();
- uintptr_t address = 0; // relative to start of section.
- for (uintptr_t location : *locations) {
- DCHECK_LT(location - address, UINT32_MAX) << "Large gap between patch locations";
- EncodeUnsignedLeb128(buffer, location - address);
- address = location;
- }
- // Update length.
- UpdateUnsignedLeb128(buffer->data() + length_pos, buffer->size() - data_pos);
+void ElfWriterQuick<ElfTypes>::EncodeOatPatches(const std::vector<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;
}
- buffer->push_back(0); // End of sections.
}
class RodataWriter FINAL : public CodeOutput {
@@ -156,61 +155,95 @@ bool ElfWriterQuick<ElfTypes>::Write(
isa, rodata_size, &rodata_writer, text_size, &text_writer, bss_size));
// Add debug sections.
- // They are stack allocated here (in the same scope as the builder),
- // but they are registred with the builder only if they are used.
+ // They are allocated here (in the same scope as the builder),
+ // but they are registered with the builder only if they are used.
using RawSection = typename ElfBuilder<ElfTypes>::RawSection;
const auto* text = builder->GetText();
const bool is64bit = Is64BitInstructionSet(isa);
- RawSection eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0,
- is64bit ? Patch<Elf_Addr, uint64_t, kPointerRelativeAddress> :
- Patch<Elf_Addr, uint32_t, kPointerRelativeAddress>,
- text);
- RawSection eh_frame_hdr(".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0,
- Patch<Elf_Addr, uint32_t, kSectionRelativeAddress>, text);
- RawSection debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0,
- Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text);
- RawSection debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
- RawSection debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
- RawSection debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0,
- Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text);
+ const int pointer_size = GetInstructionSetPointerSize(isa);
+ std::unique_ptr<RawSection> eh_frame(new RawSection(
+ ".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0,
+ is64bit ? Patch<Elf_Addr, uint64_t, kPointerRelativeAddress> :
+ Patch<Elf_Addr, uint32_t, kPointerRelativeAddress>,
+ text));
+ std::unique_ptr<RawSection> eh_frame_hdr(new RawSection(
+ ".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0,
+ Patch<Elf_Addr, uint32_t, kSectionRelativeAddress>, text));
+ std::unique_ptr<RawSection> debug_frame(new RawSection(
+ ".debug_frame", SHT_PROGBITS, 0, nullptr, 0, pointer_size, 0,
+ is64bit ? Patch<Elf_Addr, uint64_t, kAbsoluteAddress> :
+ Patch<Elf_Addr, uint32_t, kAbsoluteAddress>,
+ text));
+ std::unique_ptr<RawSection> debug_frame_oat_patches(new RawSection(
+ ".debug_frame.oat_patches", SHT_OAT_PATCH));
+ std::unique_ptr<RawSection> debug_info(new RawSection(
+ ".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0,
+ Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text));
+ std::unique_ptr<RawSection> debug_info_oat_patches(new RawSection(
+ ".debug_info.oat_patches", SHT_OAT_PATCH));
+ std::unique_ptr<RawSection> debug_abbrev(new RawSection(
+ ".debug_abbrev", SHT_PROGBITS));
+ std::unique_ptr<RawSection> debug_str(new RawSection(
+ ".debug_str", SHT_PROGBITS));
+ std::unique_ptr<RawSection> debug_line(new RawSection(
+ ".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0,
+ Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text));
+ std::unique_ptr<RawSection> debug_line_oat_patches(new RawSection(
+ ".debug_line.oat_patches", SHT_OAT_PATCH));
if (!oat_writer->GetMethodDebugInfo().empty()) {
- if (compiler_driver_->GetCompilerOptions().GetIncludeCFI()) {
- dwarf::WriteEhFrame(
- compiler_driver_, oat_writer, dwarf::DW_EH_PE_pcrel,
- eh_frame.GetBuffer(), eh_frame.GetPatchLocations(),
- eh_frame_hdr.GetBuffer(), eh_frame_hdr.GetPatchLocations());
- builder->RegisterSection(&eh_frame);
- builder->RegisterSection(&eh_frame_hdr);
- }
- if (compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
+ if (compiler_driver_->GetCompilerOptions().GetGenerateDebugInfo()) {
+ // Generate CFI (stack unwinding information).
+ if (kCFIFormat == dwarf::DW_EH_FRAME_FORMAT) {
+ dwarf::WriteCFISection(
+ compiler_driver_, oat_writer,
+ dwarf::DW_EH_PE_pcrel, kCFIFormat,
+ eh_frame->GetBuffer(), eh_frame->GetPatchLocations(),
+ eh_frame_hdr->GetBuffer(), eh_frame_hdr->GetPatchLocations());
+ builder->RegisterSection(eh_frame.get());
+ builder->RegisterSection(eh_frame_hdr.get());
+ } else {
+ DCHECK(kCFIFormat == dwarf::DW_DEBUG_FRAME_FORMAT);
+ dwarf::WriteCFISection(
+ compiler_driver_, oat_writer,
+ dwarf::DW_EH_PE_absptr, kCFIFormat,
+ debug_frame->GetBuffer(), debug_frame->GetPatchLocations(),
+ nullptr, nullptr);
+ builder->RegisterSection(debug_frame.get());
+ EncodeOatPatches(*debug_frame->GetPatchLocations(),
+ debug_frame_oat_patches->GetBuffer());
+ builder->RegisterSection(debug_frame_oat_patches.get());
+ }
// Add methods to .symtab.
WriteDebugSymbols(builder.get(), oat_writer);
// Generate DWARF .debug_* sections.
dwarf::WriteDebugSections(
compiler_driver_, oat_writer,
- debug_info.GetBuffer(), debug_info.GetPatchLocations(),
- debug_abbrev.GetBuffer(),
- debug_str.GetBuffer(),
- debug_line.GetBuffer(), debug_line.GetPatchLocations());
- builder->RegisterSection(&debug_info);
- builder->RegisterSection(&debug_abbrev);
- builder->RegisterSection(&debug_str);
- builder->RegisterSection(&debug_line);
- *oat_writer->GetAbsolutePatchLocationsFor(".debug_info") =
- *debug_info.GetPatchLocations();
- *oat_writer->GetAbsolutePatchLocationsFor(".debug_line") =
- *debug_line.GetPatchLocations();
+ debug_info->GetBuffer(), debug_info->GetPatchLocations(),
+ debug_abbrev->GetBuffer(),
+ debug_str->GetBuffer(),
+ debug_line->GetBuffer(), debug_line->GetPatchLocations());
+ builder->RegisterSection(debug_info.get());
+ EncodeOatPatches(*debug_info->GetPatchLocations(),
+ debug_info_oat_patches->GetBuffer());
+ builder->RegisterSection(debug_info_oat_patches.get());
+ builder->RegisterSection(debug_abbrev.get());
+ builder->RegisterSection(debug_str.get());
+ builder->RegisterSection(debug_line.get());
+ EncodeOatPatches(*debug_line->GetPatchLocations(),
+ debug_line_oat_patches->GetBuffer());
+ builder->RegisterSection(debug_line_oat_patches.get());
}
}
- // Add relocation section.
- RawSection oat_patches(".oat_patches", SHT_OAT_PATCH, 0, nullptr, 0, 1, 0);
- if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation() ||
- // ElfWriter::Fixup will be called regardless and it needs to be able
- // to patch debug sections so we have to include patches for them.
- compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
- EncodeOatPatches(oat_writer->GetAbsolutePatchLocations(), oat_patches.GetBuffer());
- builder->RegisterSection(&oat_patches);
+ // Add relocation section for .text.
+ std::unique_ptr<RawSection> text_oat_patches(new RawSection(
+ ".text.oat_patches", SHT_OAT_PATCH));
+ if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation()) {
+ // Note that ElfWriter::Fixup will be called regardless and therefore
+ // we need to include oat_patches for debug sections unconditionally.
+ EncodeOatPatches(oat_writer->GetAbsolutePatchLocations(),
+ text_oat_patches->GetBuffer());
+ builder->RegisterSection(text_oat_patches.get());
}
return builder->Write(elf_file_);
@@ -219,6 +252,7 @@ bool ElfWriterQuick<ElfTypes>::Write(
template <typename ElfTypes>
static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, OatWriter* oat_writer) {
const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetMethodDebugInfo();
+ bool generated_mapping_symbol = false;
// Find all addresses (low_pc) which contain deduped methods.
// The first instance of method is not marked deduped_, but the rest is.
@@ -247,9 +281,14 @@ static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, OatWriter* oat_writ
// Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2
// instructions, so that disassembler tools can correctly disassemble.
+ // Note that even if we generate just a single mapping symbol, ARM's Streamline
+ // requires it to match function symbol. Just address 0 does not work.
if (it->compiled_method_->GetInstructionSet() == kThumb2) {
- symtab->AddSymbol("$t", builder->GetText(), it->low_pc_ & ~1, true,
- 0, STB_LOCAL, STT_NOTYPE);
+ if (!generated_mapping_symbol || !kGenerateSingleArmMappingSymbol) {
+ symtab->AddSymbol("$t", builder->GetText(), it->low_pc_ & ~1, true,
+ 0, STB_LOCAL, STT_NOTYPE);
+ generated_mapping_symbol = true;
+ }
}
}
}
diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h
index 955b568..fd202ee 100644
--- a/compiler/elf_writer_quick.h
+++ b/compiler/elf_writer_quick.h
@@ -35,7 +35,7 @@ class ElfWriterQuick FINAL : public ElfWriter {
const CompilerDriver& driver)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void EncodeOatPatches(const OatWriter::PatchLocationsMap& sections,
+ static void EncodeOatPatches(const std::vector<uintptr_t>& locations,
std::vector<uint8_t>* buffer);
protected:
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
index 08523d8..ccf34b8 100644
--- a/compiler/elf_writer_test.cc
+++ b/compiler/elf_writer_test.cc
@@ -88,73 +88,41 @@ TEST_F(ElfWriterTest, dlsym) {
}
}
-// Run only on host since we do unaligned memory accesses.
-#ifndef HAVE_ANDROID_OS
-
-static void PatchSection(const std::vector<uintptr_t>& patch_locations,
- std::vector<uint8_t>* section, int32_t delta) {
- for (uintptr_t location : patch_locations) {
- *reinterpret_cast<int32_t*>(section->data() + location) += delta;
- }
-}
-
TEST_F(ElfWriterTest, EncodeDecodeOatPatches) {
- std::vector<uint8_t> oat_patches; // Encoded patches.
-
- // Encode patch locations for a few sections.
- OatWriter::PatchLocationsMap sections;
- std::vector<uintptr_t> patches0 { 0, 4, 8, 15, 128, 200 }; // NOLINT
- sections.emplace(".section0", std::unique_ptr<std::vector<uintptr_t>>(
- new std::vector<uintptr_t> { patches0 }));
- std::vector<uintptr_t> patches1 { 8, 127 }; // NOLINT
- sections.emplace(".section1", std::unique_ptr<std::vector<uintptr_t>>(
- new std::vector<uintptr_t> { patches1 }));
- std::vector<uintptr_t> patches2 { }; // NOLINT
- sections.emplace(".section2", std::unique_ptr<std::vector<uintptr_t>>(
- new std::vector<uintptr_t> { patches2 }));
- ElfWriterQuick32::EncodeOatPatches(sections, &oat_patches);
-
- // Create buffers to be patched.
- std::vector<uint8_t> initial_data(256);
- for (size_t i = 0; i < initial_data.size(); i++) {
- initial_data[i] = i;
+ const std::vector<std::vector<uintptr_t>> test_data {
+ { 0, 4, 8, 15, 128, 200 },
+ { 8, 8 + 127 },
+ { 8, 8 + 128 },
+ { },
+ };
+ for (const auto& patch_locations : test_data) {
+ constexpr int32_t delta = 0x11235813;
+
+ // Encode patch locations.
+ std::vector<uint8_t> oat_patches;
+ ElfWriterQuick32::EncodeOatPatches(patch_locations, &oat_patches);
+
+ // Create buffer to be patched.
+ std::vector<uint8_t> initial_data(256);
+ for (size_t i = 0; i < initial_data.size(); i++) {
+ initial_data[i] = i;
+ }
+
+ // Patch manually.
+ std::vector<uint8_t> expected = initial_data;
+ for (uintptr_t location : patch_locations) {
+ typedef __attribute__((__aligned__(1))) uint32_t UnalignedAddress;
+ *reinterpret_cast<UnalignedAddress*>(expected.data() + location) += delta;
+ }
+
+ // Decode and apply patch locations.
+ std::vector<uint8_t> actual = initial_data;
+ ElfFileImpl32::ApplyOatPatches(
+ oat_patches.data(), oat_patches.data() + oat_patches.size(), delta,
+ actual.data(), actual.data() + actual.size());
+
+ EXPECT_EQ(expected, actual);
}
- std::vector<uint8_t> section0_expected = initial_data;
- std::vector<uint8_t> section1_expected = initial_data;
- std::vector<uint8_t> section2_expected = initial_data;
- std::vector<uint8_t> section0_actual = initial_data;
- std::vector<uint8_t> section1_actual = initial_data;
- std::vector<uint8_t> section2_actual = initial_data;
-
- // Patch manually.
- constexpr int32_t delta = 0x11235813;
- PatchSection(patches0, &section0_expected, delta);
- PatchSection(patches1, &section1_expected, delta);
- PatchSection(patches2, &section2_expected, delta);
-
- // Decode and apply patch locations.
- bool section0_successful = ElfFileImpl32::ApplyOatPatches(
- oat_patches.data(), oat_patches.data() + oat_patches.size(),
- ".section0", delta,
- section0_actual.data(), section0_actual.data() + section0_actual.size());
- EXPECT_TRUE(section0_successful);
- EXPECT_EQ(section0_expected, section0_actual);
-
- bool section1_successful = ElfFileImpl32::ApplyOatPatches(
- oat_patches.data(), oat_patches.data() + oat_patches.size(),
- ".section1", delta,
- section1_actual.data(), section1_actual.data() + section1_actual.size());
- EXPECT_TRUE(section1_successful);
- EXPECT_EQ(section1_expected, section1_actual);
-
- bool section2_successful = ElfFileImpl32::ApplyOatPatches(
- oat_patches.data(), oat_patches.data() + oat_patches.size(),
- ".section2", delta,
- section2_actual.data(), section2_actual.data() + section2_actual.size());
- EXPECT_TRUE(section2_successful);
- EXPECT_EQ(section2_expected, section2_actual);
}
-#endif
-
} // namespace art
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index ce277cd..a1d8226 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -74,8 +74,7 @@ JitCompiler::JitCompiler() : total_time_(0) {
false,
CompilerOptions::kDefaultTopKProfileThreshold,
false, // TODO: Think about debuggability of JIT-compiled code.
- false,
- false,
+ CompilerOptions::kDefaultGenerateDebugInfo,
false,
false,
false,
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 0347c5e..4d7d86c 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -94,7 +94,7 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver,
// Assembler that holds generated instructions
std::unique_ptr<Assembler> jni_asm(Assembler::Create(instruction_set));
- jni_asm->cfi().SetEnabled(driver->GetCompilerOptions().GetIncludeCFI());
+ jni_asm->cfi().SetEnabled(driver->GetCompilerOptions().GetGenerateDebugInfo());
// Offsets into data structures
// TODO: if cross compiling these offsets are for the host not the target
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 633bf64..a98a304 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -351,9 +351,8 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor {
public:
InitCodeMethodVisitor(OatWriter* writer, size_t offset)
: OatDexMethodVisitor(writer, offset),
- text_absolute_patch_locations_(writer->GetAbsolutePatchLocationsFor(".text")),
debuggable_(writer->GetCompilerDriver()->GetCompilerOptions().GetDebuggable()) {
- text_absolute_patch_locations_->reserve(
+ writer_->absolute_patch_locations_.reserve(
writer_->compiler_driver_->GetNonRelativeLinkerPatchCount());
}
@@ -444,14 +443,13 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor {
uintptr_t base_loc = offset_ - code_size - writer_->oat_header_->GetExecutableOffset();
for (const LinkerPatch& patch : compiled_method->GetPatches()) {
if (!patch.IsPcRelative()) {
- text_absolute_patch_locations_->push_back(base_loc + patch.LiteralOffset());
+ writer_->absolute_patch_locations_.push_back(base_loc + patch.LiteralOffset());
}
}
}
}
- if (writer_->compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols() ||
- writer_->compiler_driver_->GetCompilerOptions().GetIncludeCFI()) {
+ if (writer_->compiler_driver_->GetCompilerOptions().GetGenerateDebugInfo()) {
// Record debug information for this function if we are doing that.
const uint32_t quick_code_start = quick_code_offset -
writer_->oat_header_->GetExecutableOffset() - thumb_offset;
@@ -547,9 +545,6 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor {
// so we can simply compare the pointers to find out if things are duplicated.
SafeMap<const CompiledMethod*, uint32_t, CodeOffsetsKeyComparator> dedupe_map_;
- // Patch locations for the .text section.
- std::vector<uintptr_t>* const text_absolute_patch_locations_;
-
// Cache of compiler's --debuggable option.
const bool debuggable_;
};
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 6f1b4ec..82b9377 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -19,7 +19,6 @@
#include <stdint.h>
#include <cstddef>
-#include <map>
#include <memory>
#include "linker/relative_patcher.h" // For linker::RelativePatcherTargetProvider.
@@ -82,8 +81,6 @@ class TimingLogger;
//
class OatWriter {
public:
- typedef std::map<std::string, std::unique_ptr<std::vector<uintptr_t>>> PatchLocationsMap;
-
OatWriter(const std::vector<const DexFile*>& dex_files,
uint32_t image_file_location_oat_checksum,
uintptr_t image_file_location_oat_begin,
@@ -105,19 +102,10 @@ class OatWriter {
return bss_size_;
}
- const PatchLocationsMap& GetAbsolutePatchLocations() const {
+ const std::vector<uintptr_t>& GetAbsolutePatchLocations() const {
return absolute_patch_locations_;
}
- std::vector<uintptr_t>* GetAbsolutePatchLocationsFor(const char* section_name) {
- auto it = absolute_patch_locations_.emplace(
- std::string(section_name), std::unique_ptr<std::vector<uintptr_t>>());
- if (it.second) { // Inserted new item.
- it.first->second.reset(new std::vector<uintptr_t>());
- }
- return it.first->second.get();
- }
-
bool WriteRodata(OutputStream* out);
bool WriteCode(OutputStream* out);
@@ -339,9 +327,8 @@ class OatWriter {
std::unique_ptr<linker::RelativePatcher> relative_patcher_;
- // The locations of absolute patches relative to the start of section.
- // The map's key is the ELF's section name (including the dot).
- PatchLocationsMap absolute_patch_locations_;
+ // The locations of absolute patches relative to the start of the executable section.
+ std::vector<uintptr_t> absolute_patch_locations_;
// Map method reference to assigned offset.
// Wrap the map in a class implementing linker::RelativePatcherTargetProvider.
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index c7b2c67..5632434 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -404,7 +404,7 @@ CompiledMethod* OptimizingCompiler::CompileOptimized(HGraph* graph,
codegen->CompileOptimized(&allocator);
DefaultSrcMap src_mapping_table;
- if (compiler_driver->GetCompilerOptions().GetIncludeDebugSymbols()) {
+ if (compiler_driver->GetCompilerOptions().GetGenerateDebugInfo()) {
codegen->BuildSourceMap(&src_mapping_table);
}
@@ -441,7 +441,7 @@ CompiledMethod* OptimizingCompiler::CompileBaseline(
std::vector<uint8_t> mapping_table;
codegen->BuildMappingTable(&mapping_table);
DefaultSrcMap src_mapping_table;
- if (compiler_driver->GetCompilerOptions().GetIncludeDebugSymbols()) {
+ if (compiler_driver->GetCompilerOptions().GetGenerateDebugInfo()) {
codegen->BuildSourceMap(&src_mapping_table);
}
std::vector<uint8_t> vmap_table;
@@ -533,7 +533,7 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite
return nullptr;
}
codegen->GetAssembler()->cfi().SetEnabled(
- compiler_driver->GetCompilerOptions().GetIncludeCFI());
+ compiler_driver->GetCompilerOptions().GetGenerateDebugInfo());
PassInfoPrinter pass_info_printer(graph,
method_name.c_str(),
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 7fa4328..d45ab1b 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -230,14 +230,14 @@ NO_RETURN static void Usage(const char* fmt, ...) {
UsageError("");
UsageError(" --no-include-patch-information: Do not include patching information.");
UsageError("");
- UsageError(" --include-debug-symbols: Include ELF symbols in this oat file");
+ UsageError(" -g");
+ UsageError(" --generate-debug-info: Generate debug information for native debugging,");
+ UsageError(" such as stack unwinding information, ELF symbols and DWARF sections.");
+ UsageError(" This generates all the available information. Unneeded parts can be");
+ UsageError(" stripped using standard command line tools such as strip or objcopy.");
+ UsageError(" (enabled by default in debug builds, disabled by default otherwise)");
UsageError("");
- UsageError(" --no-include-debug-symbols: Do not include ELF symbols in this oat file");
- UsageError("");
- UsageError(" --include-cfi: Include call frame information in the .eh_frame section.");
- UsageError(" The --include-debug-symbols option implies --include-cfi.");
- UsageError("");
- UsageError(" --no-include-cfi: Do not include call frame information in the .eh_frame section.");
+ UsageError(" --no-generate-debug-info: Do not generate debug information for native debugging.");
UsageError("");
UsageError(" --runtime-arg <argument>: used to specify various arguments for the runtime,");
UsageError(" such as initial heap size, maximum heap size, and verbose output.");
@@ -499,8 +499,7 @@ class Dex2Oat FINAL {
bool debuggable = false;
bool include_patch_information = CompilerOptions::kDefaultIncludePatchInformation;
- bool include_debug_symbols = kIsDebugBuild;
- bool include_cfi = kIsDebugBuild;
+ bool generate_debug_info = kIsDebugBuild;
bool watch_dog_enabled = true;
bool abort_on_hard_verifier_error = false;
bool requested_specific_compiler = false;
@@ -682,18 +681,13 @@ class Dex2Oat FINAL {
dump_cfg_file_name_ = option.substr(strlen("--dump-cfg=")).data();
} else if (option == "--dump-stats") {
dump_stats_ = true;
- } else if (option == "--include-debug-symbols" || option == "--no-strip-symbols") {
- include_debug_symbols = true;
- } else if (option == "--no-include-debug-symbols" || option == "--strip-symbols") {
- include_debug_symbols = false;
- } else if (option == "--include-cfi") {
- include_cfi = true;
- } else if (option == "--no-include-cfi") {
- include_cfi = false;
+ } else if (option == "--generate-debug-info" || option == "-g") {
+ generate_debug_info = true;
+ } else if (option == "--no-generate-debug-info") {
+ generate_debug_info = false;
} else if (option == "--debuggable") {
debuggable = true;
- include_debug_symbols = true;
- include_cfi = true;
+ generate_debug_info = true;
} else if (option.starts_with("--profile-file=")) {
profile_file_ = option.substr(strlen("--profile-file=")).data();
VLOG(compiler) << "dex2oat: profile file is " << profile_file_;
@@ -933,10 +927,6 @@ class Dex2Oat FINAL {
break;
}
- if (debuggable) {
- // TODO: Consider adding CFI info and symbols here.
- }
-
compiler_options_.reset(new CompilerOptions(compiler_filter,
huge_method_threshold,
large_method_threshold,
@@ -946,8 +936,7 @@ class Dex2Oat FINAL {
include_patch_information,
top_k_profile_threshold,
debuggable,
- include_debug_symbols,
- include_cfi,
+ generate_debug_info,
implicit_null_checks,
implicit_so_checks,
implicit_suspend_checks,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 31749bf..d2805cd 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -808,18 +808,11 @@ static void FreeDexFilesInHeap(std::priority_queue<DexFileAndClassPair>* heap) {
}
const OatFile* ClassLinker::GetBootOatFile() {
- // To grab the boot oat, look at the dex files in the boot classpath. Any of those is fine, as
- // they were all compiled into the same oat file. So grab the first one, which is guaranteed to
- // exist if the boot class-path isn't empty.
- if (boot_class_path_.empty()) {
+ gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
+ if (image_space == nullptr) {
return nullptr;
}
- const DexFile* boot_dex_file = boot_class_path_[0];
- // Is it from an oat file?
- if (boot_dex_file->GetOatDexFile() != nullptr) {
- return boot_dex_file->GetOatDexFile()->GetOatFile();
- }
- return nullptr;
+ return image_space->GetOatFile();
}
const OatFile* ClassLinker::GetPrimaryOatFile() {
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 0c5210d..9fd8c87 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -1401,88 +1401,53 @@ typename ElfTypes::Shdr* ElfFileImpl<ElfTypes>::FindSectionByName(
}
template <typename ElfTypes>
-bool ElfFileImpl<ElfTypes>::FixupDebugSections(typename std::make_signed<Elf_Off>::type base_address_delta) {
- const Elf_Shdr* debug_info = FindSectionByName(".debug_info");
- const Elf_Shdr* debug_abbrev = FindSectionByName(".debug_abbrev");
- const Elf_Shdr* debug_str = FindSectionByName(".debug_str");
- const Elf_Shdr* strtab_sec = FindSectionByName(".strtab");
- const Elf_Shdr* symtab_sec = FindSectionByName(".symtab");
-
- if (debug_info == nullptr || debug_abbrev == nullptr ||
- debug_str == nullptr || strtab_sec == nullptr || symtab_sec == nullptr) {
- // Release version of ART does not generate debug info.
- return true;
- }
+bool ElfFileImpl<ElfTypes>::FixupDebugSections(Elf_Addr base_address_delta) {
if (base_address_delta == 0) {
return true;
}
- if (!ApplyOatPatchesTo(".debug_info", base_address_delta)) {
- return false;
- }
- if (!ApplyOatPatchesTo(".debug_line", base_address_delta)) {
- return false;
- }
- return true;
+ return ApplyOatPatchesTo(".debug_frame", base_address_delta) &&
+ ApplyOatPatchesTo(".debug_info", base_address_delta) &&
+ ApplyOatPatchesTo(".debug_line", base_address_delta);
}
template <typename ElfTypes>
bool ElfFileImpl<ElfTypes>::ApplyOatPatchesTo(
- const char* target_section_name,
- typename std::make_signed<Elf_Off>::type delta) {
- auto patches_section = FindSectionByName(".oat_patches");
+ const char* target_section_name, Elf_Addr delta) {
+ auto target_section = FindSectionByName(target_section_name);
+ if (target_section == nullptr) {
+ return true;
+ }
+ std::string patches_name = target_section_name + std::string(".oat_patches");
+ auto patches_section = FindSectionByName(patches_name.c_str());
if (patches_section == nullptr) {
- LOG(ERROR) << ".oat_patches section not found.";
+ LOG(ERROR) << patches_name << " section not found.";
return false;
}
if (patches_section->sh_type != SHT_OAT_PATCH) {
- LOG(ERROR) << "Unexpected type of .oat_patches.";
- return false;
- }
- auto target_section = FindSectionByName(target_section_name);
- if (target_section == nullptr) {
- LOG(ERROR) << target_section_name << " section not found.";
+ LOG(ERROR) << "Unexpected type of " << patches_name;
return false;
}
- if (!ApplyOatPatches(
+ ApplyOatPatches(
Begin() + patches_section->sh_offset,
Begin() + patches_section->sh_offset + patches_section->sh_size,
- target_section_name, delta,
+ delta,
Begin() + target_section->sh_offset,
- Begin() + target_section->sh_offset + target_section->sh_size)) {
- LOG(ERROR) << target_section_name << " section not found in .oat_patches.";
- }
+ Begin() + target_section->sh_offset + target_section->sh_size);
return true;
}
-// Apply .oat_patches to given section.
+// Apply LEB128 encoded patches to given section.
template <typename ElfTypes>
-bool ElfFileImpl<ElfTypes>::ApplyOatPatches(
- const uint8_t* patches, const uint8_t* patches_end,
- const char* target_section_name,
- typename std::make_signed<Elf_Off>::type delta,
+void ElfFileImpl<ElfTypes>::ApplyOatPatches(
+ const uint8_t* patches, const uint8_t* patches_end, Elf_Addr delta,
uint8_t* to_patch, const uint8_t* to_patch_end) {
- // Read null-terminated section name.
- const char* section_name;
- while ((section_name = reinterpret_cast<const char*>(patches))[0] != '\0') {
- patches += strlen(section_name) + 1;
- uint32_t length = DecodeUnsignedLeb128(&patches);
- const uint8_t* next_section = patches + length;
- // Is it the section we want to patch?
- if (strcmp(section_name, target_section_name) == 0) {
- // Read LEB128 encoded list of advances.
- while (patches < next_section) {
- DCHECK_LT(patches, patches_end) << "Unexpected end of .oat_patches.";
- to_patch += DecodeUnsignedLeb128(&patches);
- DCHECK_LT(to_patch, to_patch_end) << "Patch past the end of " << section_name;
- // TODO: 32-bit vs 64-bit. What is the right type to use here?
- auto* patch_loc = reinterpret_cast<typename std::make_signed<Elf_Off>::type*>(to_patch);
- *patch_loc += delta;
- }
- return true;
- }
- patches = next_section;
+ typedef __attribute__((__aligned__(1))) Elf_Addr UnalignedAddress;
+ while (patches < patches_end) {
+ to_patch += DecodeUnsignedLeb128(&patches);
+ DCHECK_LE(patches, patches_end) << "Unexpected end of patch list.";
+ DCHECK_LT(to_patch, to_patch_end) << "Patch past the end of section.";
+ *reinterpret_cast<UnalignedAddress*>(to_patch) += delta;
}
- return false;
}
template <typename ElfTypes>
diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h
index 3ad096f..0f466bd 100644
--- a/runtime/elf_file_impl.h
+++ b/runtime/elf_file_impl.h
@@ -119,12 +119,9 @@ class ElfFileImpl {
bool FixupProgramHeaders(Elf_Addr base_address);
bool FixupSymbols(Elf_Addr base_address, bool dynamic);
bool FixupRelocations(Elf_Addr base_address);
- bool FixupDebugSections(typename std::make_signed<Elf_Off>::type base_address_delta);
- bool ApplyOatPatchesTo(const char* target_section_name,
- typename std::make_signed<Elf_Off>::type base_address_delta);
- static bool ApplyOatPatches(const uint8_t* patches, const uint8_t* patches_end,
- const char* target_section_name,
- typename std::make_signed<Elf_Off>::type delta,
+ bool FixupDebugSections(Elf_Addr base_address_delta);
+ bool ApplyOatPatchesTo(const char* target_section_name, Elf_Addr base_address_delta);
+ static void ApplyOatPatches(const uint8_t* patches, const uint8_t* patches_end, Elf_Addr delta,
uint8_t* to_patch, const uint8_t* to_patch_end);
bool Strip(std::string* error_msg);
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index eabbbec..9a70d69 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -502,6 +502,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
bool no_gap = MemMap::CheckNoGaps(GetImageSpace()->GetMemMap(),
non_moving_space_->GetMemMap());
if (!no_gap) {
+ PrintFileToLog("/proc/self/maps", LogSeverity::ERROR);
MemMap::DumpMaps(LOG(ERROR), true);
LOG(FATAL) << "There's a gap between the image space and the non-moving space";
}
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index f7ceb84..1923d24 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -755,6 +755,7 @@ ImageSpace* ImageSpace::Init(const char* image_filename, const char* image_locat
DCHECK(!error_msg->empty());
return nullptr;
}
+ space->oat_file_non_owned_ = space->oat_file_.get();
if (validate_oat_file && !space->ValidateOatFile(error_msg)) {
DCHECK(!error_msg->empty());
@@ -838,10 +839,12 @@ bool ImageSpace::ValidateOatFile(std::string* error_msg) const {
return true;
}
+
const OatFile* ImageSpace::GetOatFile() const {
- return oat_file_.get();
+ return oat_file_non_owned_;
}
+
OatFile* ImageSpace::ReleaseOatFile() {
CHECK(oat_file_.get() != nullptr);
return oat_file_.release();
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 54dc7a6..93ff8aa 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -152,6 +152,10 @@ class ImageSpace : public MemMapSpace {
// the ClassLinker during it's initialization.
std::unique_ptr<OatFile> oat_file_;
+ // There are times when we need to find the boot image oat file. As
+ // we release ownership during startup, keep a non-owned reference.
+ const OatFile* oat_file_non_owned_;
+
const std::string image_location_;
DISALLOW_COPY_AND_ASSIGN(ImageSpace);
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index d6d71f2..6566060 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -439,6 +439,14 @@ MemMap* MemMap::MapAnonymous(const char* name, uint8_t* expected_ptr, size_t byt
page_aligned_byte_count, prot, false);
}
+MemMap* MemMap::MapDummy(const char* name, uint8_t* addr, size_t byte_count) {
+ if (byte_count == 0) {
+ return new MemMap(name, nullptr, 0, nullptr, 0, 0, false);
+ }
+ const size_t page_aligned_byte_count = RoundUp(byte_count, kPageSize);
+ return new MemMap(name, addr, byte_count, addr, page_aligned_byte_count, 0, true /* reuse */);
+}
+
MemMap* MemMap::MapFileAtAddress(uint8_t* expected_ptr, size_t byte_count, int prot, int flags,
int fd, off_t start, bool reuse, const char* filename,
std::string* error_msg) {
diff --git a/runtime/mem_map.h b/runtime/mem_map.h
index 6023a70..14387ee 100644
--- a/runtime/mem_map.h
+++ b/runtime/mem_map.h
@@ -64,6 +64,12 @@ class MemMap {
static MemMap* MapAnonymous(const char* ashmem_name, uint8_t* addr, size_t byte_count, int prot,
bool low_4gb, bool reuse, std::string* error_msg);
+ // Create placeholder for a region allocated by direct call to mmap.
+ // This is useful when we do not have control over the code calling mmap,
+ // but when we still want to keep track of it in the list.
+ // The region is not considered to be owned and will not be unmmaped.
+ static MemMap* MapDummy(const char* name, uint8_t* addr, size_t byte_count);
+
// Map part of a file, taking care of non-page aligned offsets. The
// "start" offset is absolute, not relative.
//
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 1a7a3e5..1d06706 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -65,7 +65,7 @@ static void EnableDebugFeatures(uint32_t debug_flags) {
DEBUG_ENABLE_SAFEMODE = 1 << 3,
DEBUG_ENABLE_JNI_LOGGING = 1 << 4,
DEBUG_ENABLE_JIT = 1 << 5,
- DEBUG_GENERATE_CFI = 1 << 6,
+ DEBUG_GENERATE_DEBUG_INFO = 1 << 6,
};
Runtime* const runtime = Runtime::Current();
@@ -112,10 +112,10 @@ static void EnableDebugFeatures(uint32_t debug_flags) {
}
runtime->GetJITOptions()->SetUseJIT(use_jit);
- const bool generate_cfi = (debug_flags & DEBUG_GENERATE_CFI) != 0;
- if (generate_cfi) {
- runtime->AddCompilerOption("--include-cfi");
- debug_flags &= ~DEBUG_GENERATE_CFI;
+ const bool generate_debug_info = (debug_flags & DEBUG_GENERATE_DEBUG_INFO) != 0;
+ if (generate_debug_info) {
+ runtime->AddCompilerOption("--generate-debug-info");
+ debug_flags &= ~DEBUG_GENERATE_DEBUG_INFO;
}
// This is for backwards compatibility with Dalvik.
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 6fda790..ad5741e 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -21,6 +21,9 @@
#include <unistd.h>
#include <cstdlib>
+#ifndef __APPLE__
+#include <link.h> // for dl_iterate_phdr.
+#endif
#include <sstream>
// dlopen_ext support from bionic.
@@ -35,6 +38,7 @@
#include "elf_file.h"
#include "elf_utils.h"
#include "oat.h"
+#include "mem_map.h"
#include "mirror/class.h"
#include "mirror/object-inl.h"
#include "os.h"
@@ -45,13 +49,13 @@
namespace art {
// Whether OatFile::Open will try DlOpen() first. Fallback is our own ELF loader.
-static constexpr bool kUseDlopen = false;
+static constexpr bool kUseDlopen = true;
// Whether OatFile::Open will try DlOpen() on the host. On the host we're not linking against
// bionic, so cannot take advantage of the support for changed semantics (loading the same soname
// multiple times). However, if/when we switch the above, we likely want to switch this, too,
// to get test coverage of the code paths.
-static constexpr bool kUseDlopenOnHost = false;
+static constexpr bool kUseDlopenOnHost = true;
// For debugging, Open will print DlOpen error message if set to true.
static constexpr bool kPrintDlOpenErrorMessage = false;
@@ -210,6 +214,15 @@ OatFile::~OatFile() {
bool OatFile::Dlopen(const std::string& elf_filename, uint8_t* requested_base,
const char* abs_dex_location, std::string* error_msg) {
+#ifdef __APPLE__
+ // The dl_iterate_phdr syscall is missing. There is similar API on OSX,
+ // but let's fallback to the custom loading code for the time being.
+ UNUSED(elf_filename);
+ UNUSED(requested_base);
+ UNUSED(abs_dex_location);
+ UNUSED(error_msg);
+ return false;
+#else
std::unique_ptr<char> absolute_path(realpath(elf_filename.c_str(), nullptr));
if (absolute_path == nullptr) {
*error_msg = StringPrintf("Failed to find absolute path for '%s'", elf_filename.c_str());
@@ -217,7 +230,7 @@ bool OatFile::Dlopen(const std::string& elf_filename, uint8_t* requested_base,
}
#ifdef HAVE_ANDROID_OS
android_dlextinfo extinfo;
- extinfo.flags = ANDROID_DLEXT_FORCE_LOAD;
+ extinfo.flags = ANDROID_DLEXT_FORCE_LOAD | ANDROID_DLEXT_FORCE_FIXED_VADDR;
dlopen_handle_ = android_dlopen_ext(absolute_path.get(), RTLD_NOW, &extinfo);
#else
dlopen_handle_ = dlopen(absolute_path.get(), RTLD_NOW);
@@ -264,7 +277,49 @@ bool OatFile::Dlopen(const std::string& elf_filename, uint8_t* requested_base,
bss_end_ += sizeof(uint32_t);
}
+ // Ask the linker where it mmaped the file and notify our mmap wrapper of the regions.
+ struct dl_iterate_context {
+ static int callback(struct dl_phdr_info *info, size_t /* size */, void *data) {
+ auto* context = reinterpret_cast<dl_iterate_context*>(data);
+ // See whether this callback corresponds to the file which we have just loaded.
+ bool contains_begin = false;
+ for (int i = 0; i < info->dlpi_phnum; i++) {
+ if (info->dlpi_phdr[i].p_type == PT_LOAD) {
+ uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr +
+ info->dlpi_phdr[i].p_vaddr);
+ size_t memsz = info->dlpi_phdr[i].p_memsz;
+ if (vaddr <= context->begin_ && context->begin_ < vaddr + memsz) {
+ contains_begin = true;
+ break;
+ }
+ }
+ }
+ // Add dummy mmaps for this file.
+ if (contains_begin) {
+ for (int i = 0; i < info->dlpi_phnum; i++) {
+ if (info->dlpi_phdr[i].p_type == PT_LOAD) {
+ uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr +
+ info->dlpi_phdr[i].p_vaddr);
+ size_t memsz = info->dlpi_phdr[i].p_memsz;
+ MemMap* mmap = MemMap::MapDummy(info->dlpi_name, vaddr, memsz);
+ context->dlopen_mmaps_->push_back(std::unique_ptr<MemMap>(mmap));
+ }
+ }
+ return 1; // Stop iteration and return 1 from dl_iterate_phdr.
+ }
+ return 0; // Continue iteration and return 0 from dl_iterate_phdr when finished.
+ }
+ const uint8_t* const begin_;
+ std::vector<std::unique_ptr<MemMap>>* const dlopen_mmaps_;
+ } context = { begin_, &dlopen_mmaps_ };
+
+ if (dl_iterate_phdr(dl_iterate_context::callback, &context) == 0) {
+ PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
+ LOG(ERROR) << "File " << elf_filename << " loaded with dlopen but can not find its mmaps.";
+ }
+
return Setup(abs_dex_location, error_msg);
+#endif // __APPLE__
}
bool OatFile::ElfFileOpen(File* file, uint8_t* requested_base, uint8_t* oat_file_begin,
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index c58b029..1a782de 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -321,6 +321,9 @@ class OatFile FINAL {
// dlopen handle during runtime.
void* dlopen_handle_;
+ // Dummy memory map objects corresponding to the regions mapped by dlopen.
+ std::vector<std::unique_ptr<MemMap>> dlopen_mmaps_;
+
// Owning storage for the OatDexFile objects.
std::vector<const OatDexFile*> oat_dex_files_storage_;
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
new file mode 100644
index 0000000..59722ad
--- /dev/null
+++ b/test/137-cfi/cfi.cc
@@ -0,0 +1,242 @@
+/*
+ * 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.
+ */
+
+#if __linux__
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#endif
+
+#include "jni.h"
+
+#include <backtrace/Backtrace.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "gc/heap.h"
+#include "gc/space/image_space.h"
+#include "oat_file.h"
+#include "utils.h"
+
+namespace art {
+
+// For testing debuggerd. We do not have expected-death tests, so can't test this by default.
+// Code for this is copied from SignalTest.
+static constexpr bool kCauseSegfault = false;
+char* go_away_compiler_cfi = nullptr;
+
+static void CauseSegfault() {
+#if defined(__arm__) || defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
+ // On supported architectures we cause a real SEGV.
+ *go_away_compiler_cfi = 'a';
+#else
+ // On other architectures we simulate SEGV.
+ kill(getpid(), SIGSEGV);
+#endif
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_sleep(JNIEnv*, jobject, jint, jboolean, jdouble) {
+ // Keep pausing.
+ for (;;) {
+ pause();
+ }
+}
+
+// Helper to look for a sequence in the stack trace.
+#if __linux__
+static bool CheckStack(Backtrace* bt, const std::vector<std::string>& seq) {
+ size_t cur_search_index = 0; // The currently active index in seq.
+ CHECK_GT(seq.size(), 0U);
+
+ for (Backtrace::const_iterator it = bt->begin(); it != bt->end(); ++it) {
+ if (BacktraceMap::IsValid(it->map)) {
+ LOG(INFO) << "Got " << it->func_name << ", looking for " << seq[cur_search_index];
+ if (it->func_name == seq[cur_search_index]) {
+ cur_search_index++;
+ if (cur_search_index == seq.size()) {
+ return true;
+ }
+ }
+ }
+ }
+
+ printf("Can not find %s in backtrace:\n", seq[cur_search_index].c_str());
+ for (Backtrace::const_iterator it = bt->begin(); it != bt->end(); ++it) {
+ if (BacktraceMap::IsValid(it->map)) {
+ printf(" %s\n", it->func_name.c_str());
+ }
+ }
+
+ return false;
+}
+#endif
+
+// Currently we have to fall back to our own loader for the boot image when it's compiled PIC
+// because its base is zero. Thus in-process unwinding through it won't work. This is a helper
+// detecting this.
+#if __linux__
+static bool IsPicImage() {
+ gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
+ CHECK(image_space != nullptr); // We should be running with an image.
+ const OatFile* oat_file = image_space->GetOatFile();
+ CHECK(oat_file != nullptr); // We should have an oat file to go with the image.
+ return oat_file->IsPic();
+}
+#endif
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(JNIEnv*, jobject, jint, jboolean) {
+#if __linux__
+ if (IsPicImage()) {
+ LOG(INFO) << "Image is pic, in-process unwinding check bypassed.";
+ return JNI_TRUE;
+ }
+
+ // TODO: What to do on Valgrind?
+
+ std::unique_ptr<Backtrace> bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid()));
+ if (!bt->Unwind(0, nullptr)) {
+ printf("Can not unwind in process.\n");
+ return JNI_FALSE;
+ } else if (bt->NumFrames() == 0) {
+ printf("No frames for unwind in process.\n");
+ return JNI_FALSE;
+ }
+
+ // We cannot really parse an exact stack, as the optimizing compiler may inline some functions.
+ // This is also risky, as deduping might play a trick on us, so the test needs to make sure that
+ // only unique functions are being expected.
+ std::vector<std::string> seq = {
+ "Java_Main_unwindInProcess", // This function.
+ "boolean Main.unwindInProcess(int, boolean)", // The corresponding Java native method frame.
+ "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method.
+ "void Main.main(java.lang.String[])" // The Java entry method.
+ };
+
+ bool result = CheckStack(bt.get(), seq);
+ if (!kCauseSegfault) {
+ return result ? JNI_TRUE : JNI_FALSE;
+ } else {
+ LOG(INFO) << "Result of check-stack: " << result;
+ }
+#endif
+
+ if (kCauseSegfault) {
+ CauseSegfault();
+ }
+
+ return JNI_FALSE;
+}
+
+#if __linux__
+static constexpr int kSleepTimeMicroseconds = 50000; // 0.05 seconds
+static constexpr int kMaxTotalSleepTimeMicroseconds = 1000000; // 1 second
+
+// Wait for a sigstop. This code is copied from libbacktrace.
+int wait_for_sigstop(pid_t tid, int* total_sleep_time_usec, bool* detach_failed ATTRIBUTE_UNUSED) {
+ for (;;) {
+ int status;
+ pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL | WNOHANG));
+ if (n == -1) {
+ PLOG(WARNING) << "waitpid failed: tid " << tid;
+ break;
+ } else if (n == tid) {
+ if (WIFSTOPPED(status)) {
+ return WSTOPSIG(status);
+ } else {
+ PLOG(ERROR) << "unexpected waitpid response: n=" << n << ", status=" << std::hex << status;
+ break;
+ }
+ }
+
+ if (*total_sleep_time_usec > kMaxTotalSleepTimeMicroseconds) {
+ PLOG(WARNING) << "timed out waiting for stop signal: tid=" << tid;
+ break;
+ }
+
+ usleep(kSleepTimeMicroseconds);
+ *total_sleep_time_usec += kSleepTimeMicroseconds;
+ }
+
+ return -1;
+}
+#endif
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(JNIEnv*, jobject, jint pid_int) {
+#if __linux__
+ // TODO: What to do on Valgrind?
+ pid_t pid = static_cast<pid_t>(pid_int);
+
+ // OK, this is painful. debuggerd uses ptrace to unwind other processes.
+
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0)) {
+ // Were not able to attach, bad.
+ printf("Failed to attach to other process.\n");
+ PLOG(ERROR) << "Failed to attach.";
+ kill(pid, SIGKILL);
+ return JNI_FALSE;
+ }
+
+ kill(pid, SIGSTOP);
+
+ bool detach_failed = false;
+ int total_sleep_time_usec = 0;
+ int signal = wait_for_sigstop(pid, &total_sleep_time_usec, &detach_failed);
+ if (signal == -1) {
+ LOG(WARNING) << "wait_for_sigstop failed.";
+ }
+
+ std::unique_ptr<Backtrace> bt(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
+ bool result = true;
+ if (!bt->Unwind(0, nullptr)) {
+ printf("Can not unwind other process.\n");
+ result = false;
+ } else if (bt->NumFrames() == 0) {
+ printf("No frames for unwind of other process.\n");
+ result = false;
+ }
+
+ if (result) {
+ // See comment in unwindInProcess for non-exact stack matching.
+ std::vector<std::string> seq = {
+ // "Java_Main_sleep", // The sleep function being executed in the
+ // other runtime.
+ // Note: For some reason, the name isn't
+ // resolved, so don't look for it right now.
+ "boolean Main.sleep(int, boolean, double)", // The corresponding Java native method frame.
+ "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method.
+ "void Main.main(java.lang.String[])" // The Java entry method.
+ };
+
+ result = CheckStack(bt.get(), seq);
+ }
+
+ if (ptrace(PTRACE_DETACH, pid, 0, 0) != 0) {
+ PLOG(ERROR) << "Detach failed";
+ }
+
+ // Kill the other process once we are done with it.
+ kill(pid, SIGKILL);
+
+ return result ? JNI_TRUE : JNI_FALSE;
+#else
+ return JNI_FALSE;
+#endif
+}
+
+} // namespace art
diff --git a/test/137-cfi/expected.txt b/test/137-cfi/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/137-cfi/expected.txt
diff --git a/test/137-cfi/info.txt b/test/137-cfi/info.txt
new file mode 100644
index 0000000..7d59605
--- /dev/null
+++ b/test/137-cfi/info.txt
@@ -0,0 +1 @@
+Test that unwinding with CFI info works.
diff --git a/test/137-cfi/run b/test/137-cfi/run
new file mode 100755
index 0000000..78cf2aa
--- /dev/null
+++ b/test/137-cfi/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 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.
+
+# Temporarily disable address space layout randomization (ASLR).
+# This is need on host so that the linker loads core.oat at fixed address.
+export LD_USE_LOAD_BIAS=1
+
+exec ${RUN} "$@"
diff --git a/test/137-cfi/src/Main.java b/test/137-cfi/src/Main.java
new file mode 100644
index 0000000..6cd187a
--- /dev/null
+++ b/test/137-cfi/src/Main.java
@@ -0,0 +1,170 @@
+/*
+ * 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.
+ */
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class Main implements Comparator<Main> {
+ // Whether to test local unwinding. Libunwind uses linker info to find executables. As we do
+ // not dlopen at the moment, this doesn't work, so keep it off for now.
+ public final static boolean TEST_LOCAL_UNWINDING = true;
+
+ // Unwinding another process, modelling debuggerd. This doesn't use the linker, so should work
+ // no matter whether we're using dlopen or not.
+ public final static boolean TEST_REMOTE_UNWINDING = true;
+
+ private boolean secondary;
+
+ private boolean passed;
+
+ public Main(boolean secondary) {
+ this.secondary = secondary;
+ }
+
+ public static void main(String[] args) throws Exception {
+ boolean secondary = false;
+ if (args.length > 0 && args[args.length - 1].equals("--secondary")) {
+ secondary = true;
+ }
+ new Main(secondary).run();
+ }
+
+ static {
+ System.loadLibrary("arttest");
+ }
+
+ private void run() {
+ if (secondary) {
+ if (!TEST_REMOTE_UNWINDING) {
+ throw new RuntimeException("Should not be running secondary!");
+ }
+ runSecondary();
+ } else {
+ runPrimary();
+ }
+ }
+
+ private void runSecondary() {
+ foo();
+ throw new RuntimeException("Didn't expect to get back...");
+ }
+
+ private void runPrimary() {
+ // First do the in-process unwinding.
+ if (TEST_LOCAL_UNWINDING && !foo()) {
+ System.out.println("Unwinding self failed.");
+ }
+
+ if (!TEST_REMOTE_UNWINDING) {
+ // Skip the remote step.
+ return;
+ }
+
+ // Fork the secondary.
+ String[] cmdline = getCmdLine();
+ String[] secCmdLine = new String[cmdline.length + 1];
+ System.arraycopy(cmdline, 0, secCmdLine, 0, cmdline.length);
+ secCmdLine[secCmdLine.length - 1] = "--secondary";
+ Process p = exec(secCmdLine);
+
+ try {
+ int pid = getPid(p);
+ if (pid <= 0) {
+ throw new RuntimeException("Couldn't parse process");
+ }
+
+ // Wait a bit, so the forked process has time to run until its sleep phase.
+ try {
+ Thread.sleep(5000);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ if (!unwindOtherProcess(pid)) {
+ System.out.println("Unwinding other process failed.");
+ }
+ } finally {
+ // Kill the forked process if it is not already dead.
+ p.destroy();
+ }
+ }
+
+ private static Process exec(String[] args) {
+ try {
+ return Runtime.getRuntime().exec(args);
+ } catch (Exception exc) {
+ throw new RuntimeException(exc);
+ }
+ }
+
+ private static int getPid(Process p) {
+ // Could do reflection for the private pid field, but String parsing is easier.
+ String s = p.toString();
+ if (s.startsWith("Process[pid=")) {
+ return Integer.parseInt(s.substring("Process[pid=".length(), s.length() - 1));
+ } else {
+ return -1;
+ }
+ }
+
+ // Read /proc/self/cmdline to find the invocation command line (so we can fork another runtime).
+ private static String[] getCmdLine() {
+ try {
+ BufferedReader in = new BufferedReader(new FileReader("/proc/self/cmdline"));
+ String s = in.readLine();
+ in.close();
+ return s.split("\0");
+ } catch (Exception exc) {
+ throw new RuntimeException(exc);
+ }
+ }
+
+ public boolean foo() {
+ // Call bar via Arrays.binarySearch.
+ // This tests that we can unwind from framework code.
+ Main[] array = { this, this, this };
+ Arrays.binarySearch(array, 0, 3, this /* value */, this /* comparator */);
+ return passed;
+ }
+
+ public int compare(Main lhs, Main rhs) {
+ passed = bar(secondary);
+ // Returning "equal" ensures that we terminate search
+ // after first item and thus call bar() only once.
+ return 0;
+ }
+
+ public boolean bar(boolean b) {
+ if (b) {
+ return sleep(2, b, 1.0);
+ } else {
+ return unwindInProcess(1, b);
+ }
+ }
+
+ // Native functions. Note: to avoid deduping, they must all have different signatures.
+
+ public native boolean sleep(int i, boolean b, double dummy);
+
+ public native boolean unwindInProcess(int i, boolean b);
+ public native boolean unwindOtherProcess(int pid);
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 6ad8787..57d06c4 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -28,6 +28,7 @@ LIBARTTEST_COMMON_SRC_FILES := \
116-nodex2oat/nodex2oat.cc \
117-nopatchoat/nopatchoat.cc \
118-noimage-dex2oat/noimage-dex2oat.cc \
+ 137-cfi/cfi.cc \
139-register-natives/regnative.cc \
454-get-vreg/get_vreg_jni.cc \
455-set-vreg/set_vreg_jni.cc \
@@ -57,7 +58,7 @@ define build-libarttest
LOCAL_MODULE_TAGS := tests
endif
LOCAL_SRC_FILES := $(LIBARTTEST_COMMON_SRC_FILES)
- LOCAL_SHARED_LIBRARIES += libartd
+ LOCAL_SHARED_LIBRARIES += libartd libbacktrace
LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.libarttest.mk
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index c9e512e..5eda539 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -269,10 +269,12 @@ TEST_ART_BROKEN_FALLBACK_RUN_TESTS := \
117-nopatchoat \
118-noimage-dex2oat \
119-noimage-patchoat \
+ 137-cfi \
138-duplicate-classes-check2
# This test fails without an image.
TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \
+ 137-cfi \
138-duplicate-classes-check
ifneq (,$(filter no-dex2oat,$(PREBUILD_TYPES)))
@@ -299,9 +301,13 @@ endif
TEST_ART_BROKEN_FALLBACK_RUN_TESTS :=
+# 137:
+# This test unrolls and expects managed frames, but tracing means we run the interpreter.
+# 802:
# This test dynamically enables tracing to force a deoptimization. This makes the test meaningless
# when already tracing, and writes an error message that we do not want to check for.
TEST_ART_BROKEN_TRACING_RUN_TESTS := \
+ 137-cfi \
802-deoptimization
ifneq (,$(filter trace,$(TRACE_TYPES)))
@@ -328,6 +334,7 @@ TEST_ART_BROKEN_NDEBUG_TESTS := \
118-noimage-dex2oat \
119-noimage-patchoat \
131-structural-change \
+ 137-cfi \
139-register-natives \
454-get-vreg \
455-set-vreg \
@@ -343,6 +350,33 @@ endif
TEST_ART_BROKEN_NDEBUG_TESTS :=
+# Known broken tests for the interpreter.
+# CFI unwinding expects managed frames.
+TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := \
+ 137-cfi
+
+ifneq (,$(filter interpreter,$(COMPILER_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ interpreter,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_INTERPRETER_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_INTERPRETER_RUN_TESTS :=
+
+# Known broken tests for the JIT.
+# CFI unwinding expects managed frames, and the test does not iterate enough to even compile. JIT
+# also uses Generic JNI instead of the JNI compiler.
+TEST_ART_BROKEN_JIT_RUN_TESTS := \
+ 137-cfi
+
+ifneq (,$(filter jit,$(COMPILER_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ jit,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_JIT_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_JIT_RUN_TESTS :=
+
# Known broken tests for the default compiler (Quick).
TEST_ART_BROKEN_DEFAULT_RUN_TESTS := \
457-regs
@@ -434,8 +468,11 @@ endif
TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS :=
# Tests that should fail in the read barrier configuration.
+# 098: b/20720510
+# 137: Read barrier forces interpreter. Cannot run this with the interpreter.
TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS := \
- 098-ddmc # b/20720510
+ 098-ddmc \
+ 137-cfi \
ifeq ($(ART_USE_READ_BARRIER),true)
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -445,6 +482,19 @@ endif
TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS :=
+# Tests that should fail in the heap poisoning configuration.
+# 137: Heap poisoning forces interpreter. Cannot run this with the interpreter.
+TEST_ART_BROKEN_HEAP_POISONING_RUN_TESTS := \
+ 137-cfi
+
+ifeq ($(ART_HEAP_POISONING),true)
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_HEAP_POISONING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_HEAP_POISONING_RUN_TESTS :=
+
# Clear variables ahead of appending to them when defining tests.
$(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=))
$(foreach target, $(TARGET_TYPES), \
diff --git a/tools/art b/tools/art
index f167a73..2ee8940 100644
--- a/tools/art
+++ b/tools/art
@@ -97,7 +97,7 @@ ANDROID_DATA=$ANDROID_DATA \
-XXlib:$LIBART \
-Xnorelocate \
-Ximage:$ANDROID_ROOT/framework/core.art \
- -Xcompiler-option --include-debug-symbols \
+ -Xcompiler-option --generate-debug-info \
"$@"
EXIT_STATUS=$?