summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/compiled_method.cc15
-rw-r--r--compiler/compiled_method.h9
-rw-r--r--compiler/compiler_backend.cc21
-rw-r--r--compiler/compiler_backend.h13
-rw-r--r--compiler/dex/quick/codegen_util.cc9
-rw-r--r--compiler/dex/quick/mir_to_lir.h5
-rw-r--r--compiler/dex/quick/x86/call_x86.cc4
-rw-r--r--compiler/dex/quick/x86/codegen_x86.h18
-rw-r--r--compiler/dex/quick/x86/target_x86.cc165
-rw-r--r--compiler/driver/compiler_driver.cc17
-rw-r--r--compiler/driver/compiler_driver.h13
-rw-r--r--compiler/driver/compiler_options.h14
-rw-r--r--compiler/elf_writer_quick.cc328
-rw-r--r--compiler/elf_writer_quick.h10
-rw-r--r--compiler/oat_writer.cc21
-rw-r--r--compiler/oat_writer.h15
-rw-r--r--dex2oat/dex2oat.cc8
-rw-r--r--runtime/elf_file.cc355
-rw-r--r--runtime/elf_file.h13
19 files changed, 1033 insertions, 20 deletions
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc
index f6d724ab56..d884bc0ef8 100644
--- a/compiler/compiled_method.cc
+++ b/compiler/compiled_method.cc
@@ -153,12 +153,14 @@ CompiledMethod::CompiledMethod(CompilerDriver& driver,
const uint32_t fp_spill_mask,
const std::vector<uint8_t>& mapping_table,
const std::vector<uint8_t>& vmap_table,
- const std::vector<uint8_t>& native_gc_map)
+ const std::vector<uint8_t>& native_gc_map,
+ const std::vector<uint8_t>* cfi_info)
: CompiledCode(&driver, instruction_set, quick_code), frame_size_in_bytes_(frame_size_in_bytes),
core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask),
mapping_table_(driver.DeduplicateMappingTable(mapping_table)),
vmap_table_(driver.DeduplicateVMapTable(vmap_table)),
- gc_map_(driver.DeduplicateGCMap(native_gc_map)) {
+ gc_map_(driver.DeduplicateGCMap(native_gc_map)),
+ cfi_info_(driver.DeduplicateCFIInfo(cfi_info)) {
}
CompiledMethod::CompiledMethod(CompilerDriver& driver,
@@ -169,10 +171,11 @@ CompiledMethod::CompiledMethod(CompilerDriver& driver,
const uint32_t fp_spill_mask)
: CompiledCode(&driver, instruction_set, code),
frame_size_in_bytes_(frame_size_in_bytes),
- core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask) {
- mapping_table_ = driver.DeduplicateMappingTable(std::vector<uint8_t>());
- vmap_table_ = driver.DeduplicateVMapTable(std::vector<uint8_t>());
- gc_map_ = driver.DeduplicateGCMap(std::vector<uint8_t>());
+ core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask),
+ mapping_table_(driver.DeduplicateMappingTable(std::vector<uint8_t>())),
+ vmap_table_(driver.DeduplicateVMapTable(std::vector<uint8_t>())),
+ gc_map_(driver.DeduplicateGCMap(std::vector<uint8_t>())),
+ cfi_info_(nullptr) {
}
// Constructs a CompiledMethod for the Portable compiler.
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index 611230509a..90ae6eeae8 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -110,7 +110,8 @@ class CompiledMethod : public CompiledCode {
const uint32_t fp_spill_mask,
const std::vector<uint8_t>& mapping_table,
const std::vector<uint8_t>& vmap_table,
- const std::vector<uint8_t>& native_gc_map);
+ const std::vector<uint8_t>& native_gc_map,
+ const std::vector<uint8_t>* cfi_info);
// Constructs a CompiledMethod for the QuickJniCompiler.
CompiledMethod(CompilerDriver& driver,
@@ -157,6 +158,10 @@ class CompiledMethod : public CompiledCode {
return *gc_map_;
}
+ const std::vector<uint8_t>* GetCFIInfo() const {
+ return cfi_info_;
+ }
+
private:
// For quick code, the size of the activation used by the code.
const size_t frame_size_in_bytes_;
@@ -172,6 +177,8 @@ class CompiledMethod : public CompiledCode {
// For quick code, a map keyed by native PC indices to bitmaps describing what dalvik registers
// are live. For portable code, the key is a dalvik PC.
std::vector<uint8_t>* gc_map_;
+ // For quick code, a FDE entry for the debug_frame section.
+ std::vector<uint8_t>* cfi_info_;
};
} // namespace art
diff --git a/compiler/compiler_backend.cc b/compiler/compiler_backend.cc
index f37ee37634..0afa665eb7 100644
--- a/compiler/compiler_backend.cc
+++ b/compiler/compiler_backend.cc
@@ -83,6 +83,9 @@ static CompiledMethod* TryCompileWithSeaIR(art::CompilerDriver& compiler,
}
+// Hack for CFI CIE initialization
+extern std::vector<uint8_t>* X86CFIInitialization();
+
class QuickBackend : public CompilerBackend {
public:
QuickBackend() : CompilerBackend(100) {}
@@ -166,11 +169,27 @@ class QuickBackend : public CompilerBackend {
bool set_max = cu->mir_graph->SetMaxAvailableNonSpecialCompilerTemps(max_temps);
CHECK(set_max);
}
- return mir_to_lir;;
+ return mir_to_lir;
}
void InitCompilationUnit(CompilationUnit& cu) const {}
+ /*
+ * @brief Generate and return Dwarf CFI initialization, if supported by the
+ * backend.
+ * @param driver CompilerDriver for this compile.
+ * @returns nullptr if not supported by backend or a vector of bytes for CFI DWARF
+ * information.
+ * @note This is used for backtrace information in generated code.
+ */
+ std::vector<uint8_t>* GetCallFrameInformationInitialization(const CompilerDriver& driver) const
+ OVERRIDE {
+ if (driver.GetInstructionSet() == kX86) {
+ return X86CFIInitialization();
+ }
+ return nullptr;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(QuickBackend);
};
diff --git a/compiler/compiler_backend.h b/compiler/compiler_backend.h
index 51fb29afd2..b473806bba 100644
--- a/compiler/compiler_backend.h
+++ b/compiler/compiler_backend.h
@@ -93,6 +93,19 @@ class CompilerBackend {
virtual ~CompilerBackend() {}
+ /*
+ * @brief Generate and return Dwarf CFI initialization, if supported by the
+ * backend.
+ * @param driver CompilerDriver for this compile.
+ * @returns nullptr if not supported by backend or a vector of bytes for CFI DWARF
+ * information.
+ * @note This is used for backtrace information in generated code.
+ */
+ virtual std::vector<uint8_t>* GetCallFrameInformationInitialization(const CompilerDriver& driver)
+ const {
+ return nullptr;
+ }
+
private:
const uint64_t maximum_compilation_time_before_warning_;
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index 31854496ab..b0b8d1ea15 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -1070,10 +1070,12 @@ CompiledMethod* Mir2Lir::GetCompiledMethod() {
DCHECK_EQ(fp_vmap_table_.size(), 0u);
vmap_encoder.PushBackUnsigned(0u); // Size is 0.
}
+
+ UniquePtr<std::vector<uint8_t> > cfi_info(ReturnCallFrameInformation());
CompiledMethod* result =
new CompiledMethod(*cu_->compiler_driver, cu_->instruction_set, code_buffer_, frame_size_,
core_spill_mask_, fp_spill_mask_, encoded_mapping_table_,
- vmap_encoder.GetData(), native_gc_map_);
+ vmap_encoder.GetData(), native_gc_map_, cfi_info.get());
return result;
}
@@ -1216,4 +1218,9 @@ void Mir2Lir::LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic_re
AppendLIR(load_pc_rel);
}
+std::vector<uint8_t>* Mir2Lir::ReturnCallFrameInformation() {
+ // Default case is to do nothing.
+ return nullptr;
+}
+
} // namespace art
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index b74052c117..8f199f8c85 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -1089,6 +1089,11 @@ class Mir2Lir : public Backend {
bool can_assume_type_is_in_dex_cache,
uint32_t type_idx, RegLocation rl_dest,
RegLocation rl_src);
+ /*
+ * @brief Generate the debug_frame FDE information if possible.
+ * @returns pointer to vector containg CFE information, or NULL.
+ */
+ virtual std::vector<uint8_t>* ReturnCallFrameInformation();
/**
* @brief Used to insert marker that can be used to associate MIR with LIR.
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index 0613cdff7a..399001c397 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -198,7 +198,7 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
LockTemp(rX86_ARG2);
/* Build frame, return address already on stack */
- OpRegImm(kOpSub, rX86_SP, frame_size_ - 4);
+ stack_decrement_ = OpRegImm(kOpSub, rX86_SP, frame_size_ - 4);
/*
* We can safely skip the stack overflow check if we're
@@ -246,7 +246,7 @@ void X86Mir2Lir::GenExitSequence() {
NewLIR0(kPseudoMethodExit);
UnSpillCoreRegs();
/* Remove frame except for return address */
- OpRegImm(kOpAdd, rX86_SP, frame_size_ - 4);
+ stack_increment_ = OpRegImm(kOpAdd, rX86_SP, frame_size_ - 4);
NewLIR0(kX86Ret);
}
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index 421d51e4fd..c97d0e6f4d 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -302,6 +302,18 @@ class X86Mir2Lir : public Mir2Lir {
*/
void InstallLiteralPools();
+ /*
+ * @brief Generate the debug_frame CFI information.
+ * @returns pointer to vector containing CFE information
+ */
+ static std::vector<uint8_t>* ReturnCommonCallFrameInformation();
+
+ /*
+ * @brief Generate the debug_frame FDE information.
+ * @returns pointer to vector containing CFE information
+ */
+ std::vector<uint8_t>* ReturnCallFrameInformation();
+
private:
void EmitPrefix(const X86EncodingMap* entry);
void EmitOpcode(const X86EncodingMap* entry);
@@ -549,6 +561,12 @@ class X86Mir2Lir : public Mir2Lir {
// Instructions needing patching with PC relative code addresses.
GrowableArray<LIR*> call_method_insns_;
+
+ // Prologue decrement of stack pointer.
+ LIR* stack_decrement_;
+
+ // Epilogue increment of stack pointer.
+ LIR* stack_increment_;
};
} // namespace art
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index eea7191c3b..7bb866d4aa 100644
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -539,7 +539,8 @@ X86Mir2Lir::X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator*
: Mir2Lir(cu, mir_graph, arena),
method_address_insns_(arena, 100, kGrowableArrayMisc),
class_type_address_insns_(arena, 100, kGrowableArrayMisc),
- call_method_insns_(arena, 100, kGrowableArrayMisc) {
+ call_method_insns_(arena, 100, kGrowableArrayMisc),
+ stack_decrement_(nullptr), stack_increment_(nullptr) {
store_method_addr_used_ = false;
for (int i = 0; i < kX86Last; i++) {
if (X86Mir2Lir::EncodingMap[i].opcode != i) {
@@ -1118,4 +1119,166 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
return true;
}
+/*
+ * @brief Enter a 32 bit quantity into the FDE buffer
+ * @param buf FDE buffer.
+ * @param data Data value.
+ */
+static void PushWord(std::vector<uint8_t>&buf, int data) {
+ buf.push_back(data & 0xff);
+ buf.push_back((data >> 8) & 0xff);
+ buf.push_back((data >> 16) & 0xff);
+ buf.push_back((data >> 24) & 0xff);
+}
+
+/*
+ * @brief Enter an 'advance LOC' into the FDE buffer
+ * @param buf FDE buffer.
+ * @param increment Amount by which to increase the current location.
+ */
+static void AdvanceLoc(std::vector<uint8_t>&buf, uint32_t increment) {
+ if (increment < 64) {
+ // Encoding in opcode.
+ buf.push_back(0x1 << 6 | increment);
+ } else if (increment < 256) {
+ // Single byte delta.
+ buf.push_back(0x02);
+ buf.push_back(increment);
+ } else if (increment < 256 * 256) {
+ // Two byte delta.
+ buf.push_back(0x03);
+ buf.push_back(increment & 0xff);
+ buf.push_back((increment >> 8) & 0xff);
+ } else {
+ // Four byte delta.
+ buf.push_back(0x04);
+ PushWord(buf, increment);
+ }
+}
+
+
+std::vector<uint8_t>* X86CFIInitialization() {
+ return X86Mir2Lir::ReturnCommonCallFrameInformation();
+}
+
+std::vector<uint8_t>* X86Mir2Lir::ReturnCommonCallFrameInformation() {
+ std::vector<uint8_t>*cfi_info = new std::vector<uint8_t>;
+
+ // Length of the CIE (except for this field).
+ PushWord(*cfi_info, 16);
+
+ // CIE id.
+ PushWord(*cfi_info, 0xFFFFFFFFU);
+
+ // Version: 3.
+ cfi_info->push_back(0x03);
+
+ // Augmentation: empty string.
+ cfi_info->push_back(0x0);
+
+ // Code alignment: 1.
+ cfi_info->push_back(0x01);
+
+ // Data alignment: -4.
+ cfi_info->push_back(0x7C);
+
+ // Return address register (R8).
+ cfi_info->push_back(0x08);
+
+ // Initial return PC is 4(ESP): DW_CFA_def_cfa R4 4.
+ cfi_info->push_back(0x0C);
+ cfi_info->push_back(0x04);
+ cfi_info->push_back(0x04);
+
+ // Return address location: 0(SP): DW_CFA_offset R8 1 (* -4);.
+ cfi_info->push_back(0x2 << 6 | 0x08);
+ cfi_info->push_back(0x01);
+
+ // And 2 Noops to align to 4 byte boundary.
+ cfi_info->push_back(0x0);
+ cfi_info->push_back(0x0);
+
+ DCHECK_EQ(cfi_info->size() & 3, 0U);
+ return cfi_info;
+}
+
+static void EncodeUnsignedLeb128(std::vector<uint8_t>& buf, uint32_t value) {
+ uint8_t buffer[12];
+ uint8_t *ptr = EncodeUnsignedLeb128(buffer, value);
+ for (uint8_t *p = buffer; p < ptr; p++) {
+ buf.push_back(*p);
+ }
+}
+
+std::vector<uint8_t>* X86Mir2Lir::ReturnCallFrameInformation() {
+ std::vector<uint8_t>*cfi_info = new std::vector<uint8_t>;
+
+ // Generate the FDE for the method.
+ DCHECK_NE(data_offset_, 0U);
+
+ // Length (will be filled in later in this routine).
+ PushWord(*cfi_info, 0);
+
+ // CIE_pointer (can be filled in by linker); might be left at 0 if there is only
+ // one CIE for the whole debug_frame section.
+ PushWord(*cfi_info, 0);
+
+ // 'initial_location' (filled in by linker).
+ PushWord(*cfi_info, 0);
+
+ // 'address_range' (number of bytes in the method).
+ PushWord(*cfi_info, data_offset_);
+
+ // The instructions in the FDE.
+ if (stack_decrement_ != nullptr) {
+ // Advance LOC to just past the stack decrement.
+ uint32_t pc = NEXT_LIR(stack_decrement_)->offset;
+ AdvanceLoc(*cfi_info, pc);
+
+ // Now update the offset to the call frame: DW_CFA_def_cfa_offset frame_size.
+ cfi_info->push_back(0x0e);
+ EncodeUnsignedLeb128(*cfi_info, frame_size_);
+
+ // We continue with that stack until the epilogue.
+ if (stack_increment_ != nullptr) {
+ uint32_t new_pc = NEXT_LIR(stack_increment_)->offset;
+ AdvanceLoc(*cfi_info, new_pc - pc);
+
+ // We probably have code snippets after the epilogue, so save the
+ // current state: DW_CFA_remember_state.
+ cfi_info->push_back(0x0a);
+
+ // We have now popped the stack: DW_CFA_def_cfa_offset 4. There is only the return
+ // PC on the stack now.
+ cfi_info->push_back(0x0e);
+ EncodeUnsignedLeb128(*cfi_info, 4);
+
+ // Everything after that is the same as before the epilogue.
+ // Stack bump was followed by RET instruction.
+ LIR *post_ret_insn = NEXT_LIR(NEXT_LIR(stack_increment_));
+ if (post_ret_insn != nullptr) {
+ pc = new_pc;
+ new_pc = post_ret_insn->offset;
+ AdvanceLoc(*cfi_info, new_pc - pc);
+ // Restore the state: DW_CFA_restore_state.
+ cfi_info->push_back(0x0b);
+ }
+ }
+ }
+
+ // Padding to a multiple of 4
+ while ((cfi_info->size() & 3) != 0) {
+ // DW_CFA_nop is encoded as 0.
+ cfi_info->push_back(0);
+ }
+
+ // Set the length of the FDE inside the generated bytes.
+ uint32_t length = cfi_info->size() - 4;
+ (*cfi_info)[0] = length;
+ (*cfi_info)[1] = length >> 8;
+ (*cfi_info)[2] = length >> 16;
+ (*cfi_info)[3] = length >> 24;
+ return cfi_info;
+}
+
} // namespace art
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 931055b6b8..708cce6437 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -31,6 +31,7 @@
#include "dex/verification_results.h"
#include "dex/verified_method.h"
#include "dex/quick/dex_file_method_inliner.h"
+#include "driver/compiler_options.h"
#include "jni_internal.h"
#include "object_utils.h"
#include "runtime.h"
@@ -323,10 +324,12 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options,
compiler_enable_auto_elf_loading_(NULL),
compiler_get_method_code_addr_(NULL),
support_boot_image_fixup_(instruction_set != kMips),
+ cfi_info_(nullptr),
dedupe_code_("dedupe code"),
dedupe_mapping_table_("dedupe mapping table"),
dedupe_vmap_table_("dedupe vmap table"),
- dedupe_gc_map_("dedupe gc map") {
+ dedupe_gc_map_("dedupe gc map"),
+ dedupe_cfi_info_("dedupe cfi info") {
DCHECK(compiler_options_ != nullptr);
DCHECK(verification_results_ != nullptr);
DCHECK(method_inliner_map_ != nullptr);
@@ -341,6 +344,11 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options,
if (!image_) {
CHECK(image_classes_.get() == NULL);
}
+
+ // Are we generating CFI information?
+ if (compiler_options->GetGenerateGDBInformation()) {
+ cfi_info_.reset(compiler_backend_->GetCallFrameInformationInitialization(*this));
+ }
}
std::vector<uint8_t>* CompilerDriver::DeduplicateCode(const std::vector<uint8_t>& code) {
@@ -359,6 +367,13 @@ std::vector<uint8_t>* CompilerDriver::DeduplicateGCMap(const std::vector<uint8_t
return dedupe_gc_map_.Add(Thread::Current(), code);
}
+std::vector<uint8_t>* CompilerDriver::DeduplicateCFIInfo(const std::vector<uint8_t>* cfi_info) {
+ if (cfi_info == nullptr) {
+ return nullptr;
+ }
+ return dedupe_cfi_info_.Add(Thread::Current(), *cfi_info);
+}
+
CompilerDriver::~CompilerDriver() {
Thread* self = Thread::Current();
{
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 5a5fc36f21..6ccbf0fdeb 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -493,6 +493,15 @@ class CompilerDriver {
std::vector<uint8_t>* DeduplicateMappingTable(const std::vector<uint8_t>& code);
std::vector<uint8_t>* DeduplicateVMapTable(const std::vector<uint8_t>& code);
std::vector<uint8_t>* DeduplicateGCMap(const std::vector<uint8_t>& code);
+ std::vector<uint8_t>* DeduplicateCFIInfo(const std::vector<uint8_t>* cfi_info);
+
+ /*
+ * @brief return the pointer to the Call Frame Information.
+ * @return pointer to call frame information for this compilation.
+ */
+ std::vector<uint8_t>* GetCallFrameInformation() const {
+ return cfi_info_.get();
+ }
private:
// Compute constant code and method pointers when possible
@@ -625,6 +634,9 @@ class CompilerDriver {
bool support_boot_image_fixup_;
+ // Call Frame Information, which might be generated to help stack tracebacks.
+ UniquePtr<std::vector<uint8_t> > cfi_info_;
+
// DeDuplication data structures, these own the corresponding byte arrays.
class DedupeHashFunc {
public:
@@ -663,6 +675,7 @@ class CompilerDriver {
DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_mapping_table_;
DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_vmap_table_;
DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_gc_map_;
+ DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_cfi_info_;
DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
};
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 9f6745b015..39738ab049 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -43,7 +43,8 @@ class CompilerOptions {
large_method_threshold_(kDefaultLargeMethodThreshold),
small_method_threshold_(kDefaultSmallMethodThreshold),
tiny_method_threshold_(kDefaultTinyMethodThreshold),
- num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold)
+ num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold),
+ generate_gdb_information_(false)
#ifdef ART_SEA_IR_MODE
, sea_ir_mode_(false)
#endif
@@ -54,7 +55,8 @@ class CompilerOptions {
size_t large_method_threshold,
size_t small_method_threshold,
size_t tiny_method_threshold,
- size_t num_dex_methods_threshold
+ size_t num_dex_methods_threshold,
+ bool generate_gdb_information
#ifdef ART_SEA_IR_MODE
, bool sea_ir_mode
#endif
@@ -64,7 +66,8 @@ class CompilerOptions {
large_method_threshold_(large_method_threshold),
small_method_threshold_(small_method_threshold),
tiny_method_threshold_(tiny_method_threshold),
- num_dex_methods_threshold_(num_dex_methods_threshold)
+ num_dex_methods_threshold_(num_dex_methods_threshold),
+ generate_gdb_information_(generate_gdb_information)
#ifdef ART_SEA_IR_MODE
, sea_ir_mode_(sea_ir_mode)
#endif
@@ -118,6 +121,10 @@ class CompilerOptions {
bool GetSeaIrMode();
#endif
+ bool GetGenerateGDBInformation() const {
+ return generate_gdb_information_;
+ }
+
private:
CompilerFilter compiler_filter_;
size_t huge_method_threshold_;
@@ -125,6 +132,7 @@ class CompilerOptions {
size_t small_method_threshold_;
size_t tiny_method_threshold_;
size_t num_dex_methods_threshold_;
+ bool generate_gdb_information_;
#ifdef ART_SEA_IR_MODE
bool sea_ir_mode_;
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 4b416a0d4f..4b823ef5ec 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -98,6 +98,7 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer,
// | .rodata\0 |
// | .text\0 |
// | .shstrtab\0 |
+ // | .debug_frame\0 |
// +-------------------------+
// | Elf32_Shdr NULL |
// | Elf32_Shdr .dynsym |
@@ -107,6 +108,9 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer,
// | Elf32_Shdr .rodata |
// | Elf32_Shdr .dynamic |
// | Elf32_Shdr .shstrtab |
+ // | Elf32_Shdr .debug_info | (Optional)
+ // | Elf32_Shdr .debug_abbrev| (Optional)
+ // | Elf32_Shdr .debug_frame | (Optional)
// +-------------------------+
// phase 1: computing offsets
@@ -259,6 +263,18 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer,
uint32_t shstrtab_shstrtab_offset = shstrtab.size();
shstrtab += ".shstrtab";
shstrtab += '\0';
+ uint32_t shstrtab_debug_info_offset = shstrtab.size();
+ shstrtab += ".debug_info";
+ shstrtab += '\0';
+ uint32_t shstrtab_debug_abbrev_offset = shstrtab.size();
+ shstrtab += ".debug_abbrev";
+ shstrtab += '\0';
+ uint32_t shstrtab_debug_str_offset = shstrtab.size();
+ shstrtab += ".debug_str";
+ shstrtab += '\0';
+ uint32_t shstrtab_debug_frame_offset = shstrtab.size();
+ shstrtab += ".debug_frame";
+ shstrtab += '\0';
uint32_t shstrtab_size = shstrtab.size();
expected_offset += shstrtab_size;
if (debug) {
@@ -266,6 +282,52 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer,
LOG(INFO) << "shstrtab_size=" << shstrtab_size << std::hex << " " << shstrtab_size;
}
+ // Create debug informatin, if we have it.
+ bool generateDebugInformation = compiler_driver_->GetCallFrameInformation() != nullptr;
+ std::vector<uint8_t> dbg_info;
+ std::vector<uint8_t> dbg_abbrev;
+ std::vector<uint8_t> dbg_str;
+ if (generateDebugInformation) {
+ FillInCFIInformation(oat_writer, &dbg_info, &dbg_abbrev, &dbg_str);
+ }
+
+ uint32_t shdbg_info_alignment = 1;
+ uint32_t shdbg_info_offset = expected_offset;
+ uint32_t shdbg_info_size = dbg_info.size();
+ expected_offset += shdbg_info_size;
+ if (debug) {
+ LOG(INFO) << "shdbg_info_offset=" << shdbg_info_offset << std::hex << " " << shdbg_info_offset;
+ LOG(INFO) << "shdbg_info_size=" << shdbg_info_size << std::hex << " " << shdbg_info_size;
+ }
+
+ uint32_t shdbg_abbrev_alignment = 1;
+ uint32_t shdbg_abbrev_offset = expected_offset;
+ uint32_t shdbg_abbrev_size = dbg_abbrev.size();
+ expected_offset += shdbg_abbrev_size;
+ if (debug) {
+ LOG(INFO) << "shdbg_abbrev_offset=" << shdbg_abbrev_offset << std::hex << " " << shdbg_abbrev_offset;
+ LOG(INFO) << "shdbg_abbrev_size=" << shdbg_abbrev_size << std::hex << " " << shdbg_abbrev_size;
+ }
+
+ uint32_t shdbg_frm_alignment = 4;
+ uint32_t shdbg_frm_offset = expected_offset = RoundUp(expected_offset, shdbg_frm_alignment);
+ uint32_t shdbg_frm_size =
+ generateDebugInformation ? compiler_driver_->GetCallFrameInformation()->size() : 0;
+ expected_offset += shdbg_frm_size;
+ if (debug) {
+ LOG(INFO) << "shdbg_frm_offset=" << shdbg_frm_offset << std::hex << " " << shdbg_frm_offset;
+ LOG(INFO) << "shdbg_frm_size=" << shdbg_frm_size << std::hex << " " << shdbg_frm_size;
+ }
+
+ uint32_t shdbg_str_alignment = 1;
+ uint32_t shdbg_str_offset = expected_offset;
+ uint32_t shdbg_str_size = dbg_str.size();
+ expected_offset += shdbg_str_size;
+ if (debug) {
+ LOG(INFO) << "shdbg_str_offset=" << shdbg_str_offset << std::hex << " " << shdbg_str_offset;
+ LOG(INFO) << "shdbg_str_size=" << shdbg_str_size << std::hex << " " << shdbg_str_size;
+ }
+
// section headers (after all sections)
uint32_t shdr_alignment = sizeof(Elf32_Word);
uint32_t shdr_offset = expected_offset = RoundUp(expected_offset, shdr_alignment);
@@ -277,7 +339,11 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer,
const uint8_t SH_TEXT = 5;
const uint8_t SH_DYNAMIC = 6;
const uint8_t SH_SHSTRTAB = 7;
- const uint8_t SH_NUM = 8;
+ const uint8_t SH_DBG_INFO = 8;
+ const uint8_t SH_DBG_ABRV = 9;
+ const uint8_t SH_DBG_FRM = 10;
+ const uint8_t SH_DBG_STR = 11;
+ const uint8_t SH_NUM = generateDebugInformation ? 12 : 8;
uint32_t shdr_size = sizeof(Elf32_Shdr) * SH_NUM;
expected_offset += shdr_size;
if (debug) {
@@ -554,6 +620,52 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer,
section_headers[SH_SHSTRTAB].sh_addralign = shstrtab_alignment;
section_headers[SH_SHSTRTAB].sh_entsize = 0;
+ if (generateDebugInformation) {
+ section_headers[SH_DBG_INFO].sh_name = shstrtab_debug_info_offset;
+ section_headers[SH_DBG_INFO].sh_type = SHT_PROGBITS;
+ section_headers[SH_DBG_INFO].sh_flags = 0;
+ section_headers[SH_DBG_INFO].sh_addr = 0;
+ section_headers[SH_DBG_INFO].sh_offset = shdbg_info_offset;
+ section_headers[SH_DBG_INFO].sh_size = shdbg_info_size;
+ section_headers[SH_DBG_INFO].sh_link = 0;
+ section_headers[SH_DBG_INFO].sh_info = 0;
+ section_headers[SH_DBG_INFO].sh_addralign = shdbg_info_alignment;
+ section_headers[SH_DBG_INFO].sh_entsize = 0;
+
+ section_headers[SH_DBG_ABRV].sh_name = shstrtab_debug_abbrev_offset;
+ section_headers[SH_DBG_ABRV].sh_type = SHT_PROGBITS;
+ section_headers[SH_DBG_ABRV].sh_flags = 0;
+ section_headers[SH_DBG_ABRV].sh_addr = 0;
+ section_headers[SH_DBG_ABRV].sh_offset = shdbg_abbrev_offset;
+ section_headers[SH_DBG_ABRV].sh_size = shdbg_abbrev_size;
+ section_headers[SH_DBG_ABRV].sh_link = 0;
+ section_headers[SH_DBG_ABRV].sh_info = 0;
+ section_headers[SH_DBG_ABRV].sh_addralign = shdbg_abbrev_alignment;
+ section_headers[SH_DBG_ABRV].sh_entsize = 0;
+
+ section_headers[SH_DBG_FRM].sh_name = shstrtab_debug_frame_offset;
+ section_headers[SH_DBG_FRM].sh_type = SHT_PROGBITS;
+ section_headers[SH_DBG_FRM].sh_flags = 0;
+ section_headers[SH_DBG_FRM].sh_addr = 0;
+ section_headers[SH_DBG_FRM].sh_offset = shdbg_frm_offset;
+ section_headers[SH_DBG_FRM].sh_size = shdbg_frm_size;
+ section_headers[SH_DBG_FRM].sh_link = 0;
+ section_headers[SH_DBG_FRM].sh_info = 0;
+ section_headers[SH_DBG_FRM].sh_addralign = shdbg_frm_alignment;
+ section_headers[SH_DBG_FRM].sh_entsize = 0;
+
+ section_headers[SH_DBG_STR].sh_name = shstrtab_debug_str_offset;
+ section_headers[SH_DBG_STR].sh_type = SHT_PROGBITS;
+ section_headers[SH_DBG_STR].sh_flags = 0;
+ section_headers[SH_DBG_STR].sh_addr = 0;
+ section_headers[SH_DBG_STR].sh_offset = shdbg_str_offset;
+ section_headers[SH_DBG_STR].sh_size = shdbg_str_size;
+ section_headers[SH_DBG_STR].sh_link = 0;
+ section_headers[SH_DBG_STR].sh_info = 0;
+ section_headers[SH_DBG_STR].sh_addralign = shdbg_str_alignment;
+ section_headers[SH_DBG_STR].sh_entsize = 0;
+ }
+
// phase 3: writing file
// Elf32_Ehdr
@@ -646,8 +758,62 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer,
return false;
}
+ if (generateDebugInformation) {
+ // .debug_info
+ DCHECK_LE(shstrtab_offset + shstrtab_size, shdbg_info_offset);
+ if (static_cast<off_t>(shdbg_info_offset) != lseek(elf_file_->Fd(), shdbg_info_offset, SEEK_SET)) {
+ PLOG(ERROR) << "Failed to seek to .shdbg_info offset " << shdbg_info_offset
+ << " for " << elf_file_->GetPath();
+ return false;
+ }
+ if (!elf_file_->WriteFully(&dbg_info[0], shdbg_info_size)) {
+ PLOG(ERROR) << "Failed to write .debug_info for " << elf_file_->GetPath();
+ return false;
+ }
+
+ // .debug_abbrev
+ DCHECK_LE(shdbg_info_offset + shdbg_info_size, shdbg_abbrev_offset);
+ if (static_cast<off_t>(shdbg_abbrev_offset) != lseek(elf_file_->Fd(), shdbg_abbrev_offset, SEEK_SET)) {
+ PLOG(ERROR) << "Failed to seek to .shdbg_abbrev offset " << shdbg_abbrev_offset
+ << " for " << elf_file_->GetPath();
+ return false;
+ }
+ if (!elf_file_->WriteFully(&dbg_abbrev[0], shdbg_abbrev_size)) {
+ PLOG(ERROR) << "Failed to write .debug_abbrev for " << elf_file_->GetPath();
+ return false;
+ }
+
+ // .debug_frame
+ DCHECK_LE(shdbg_abbrev_offset + shdbg_abbrev_size, shdbg_frm_offset);
+ if (static_cast<off_t>(shdbg_frm_offset) != lseek(elf_file_->Fd(), shdbg_frm_offset, SEEK_SET)) {
+ PLOG(ERROR) << "Failed to seek to .shdbg_frm offset " << shdbg_frm_offset
+ << " for " << elf_file_->GetPath();
+ return false;
+ }
+ if (!elf_file_->WriteFully(&((*compiler_driver_->GetCallFrameInformation())[0]), shdbg_frm_size)) {
+ PLOG(ERROR) << "Failed to write .debug_frame for " << elf_file_->GetPath();
+ return false;
+ }
+
+ // .debug_str
+ DCHECK_LE(shdbg_frm_offset + shdbg_frm_size, shdbg_str_offset);
+ if (static_cast<off_t>(shdbg_str_offset) != lseek(elf_file_->Fd(), shdbg_str_offset, SEEK_SET)) {
+ PLOG(ERROR) << "Failed to seek to .shdbg_str offset " << shdbg_str_offset
+ << " for " << elf_file_->GetPath();
+ return false;
+ }
+ if (!elf_file_->WriteFully(&dbg_str[0], shdbg_str_size)) {
+ PLOG(ERROR) << "Failed to write .debug_frame for " << elf_file_->GetPath();
+ return false;
+ }
+ }
+
// section headers (after all sections)
- DCHECK_LE(shstrtab_offset + shstrtab_size, shdr_offset);
+ if (generateDebugInformation) {
+ DCHECK_LE(shdbg_str_offset + shdbg_str_size, shdr_offset);
+ } else {
+ DCHECK_LE(shstrtab_offset + shstrtab_size, shdr_offset);
+ }
if (static_cast<off_t>(shdr_offset) != lseek(elf_file_->Fd(), shdr_offset, SEEK_SET)) {
PLOG(ERROR) << "Failed to seek to ELF section headers offset " << shdr_offset
<< " for " << elf_file_->GetPath();
@@ -660,6 +826,164 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer,
VLOG(compiler) << "ELF file written successfully: " << elf_file_->GetPath();
return true;
+} // NOLINT(readability/fn_size)
+
+static void UpdateWord(std::vector<uint8_t>*buf, int offset, int data) {
+ (*buf)[offset+0] = data;
+ (*buf)[offset+1] = data >> 8;
+ (*buf)[offset+2] = data >> 16;
+ (*buf)[offset+3] = data >> 24;
+}
+
+static void PushWord(std::vector<uint8_t>*buf, int data) {
+ buf->push_back(data & 0xff);
+ buf->push_back((data >> 8) & 0xff);
+ buf->push_back((data >> 16) & 0xff);
+ buf->push_back((data >> 24) & 0xff);
+}
+
+static void PushHalf(std::vector<uint8_t>*buf, int data) {
+ buf->push_back(data & 0xff);
+ buf->push_back((data >> 8) & 0xff);
+}
+
+// DWARF constants needed to generate CFI information.
+enum {
+ // Tag encodings.
+ DW_TAG_compile_unit = 0x11,
+ DW_TAG_subprogram = 0X2e,
+
+ // Attribute encodings.
+ DW_AT_name = 0x03,
+ DW_AT_low_pc = 0x11,
+ DW_AT_high_pc = 0x12,
+ DW_AT_language = 0x13,
+
+ // Constant encoding.
+ DW_CHILDREN_no = 0x00,
+ DW_CHILDREN_yes = 0x01,
+
+ // Attribute form encodings.
+ DW_FORM_addr = 0x01,
+ DW_FORM_data1 = 0x0b,
+ DW_FORM_strp = 0x0e,
+
+ // Language encoding.
+ DW_LANG_Java = 0x000b
+};
+
+void ElfWriterQuick::FillInCFIInformation(OatWriter* oat_writer,
+ std::vector<uint8_t>* dbg_info,
+ std::vector<uint8_t>* dbg_abbrev,
+ std::vector<uint8_t>* dbg_str) {
+ // Create the debug_abbrev section with boilerplate information.
+ // We only care about low_pc and high_pc right now for the compilation
+ // unit and methods.
+
+ // Tag 1: Compilation unit: DW_TAG_compile_unit.
+ dbg_abbrev->push_back(1);
+ dbg_abbrev->push_back(DW_TAG_compile_unit);
+
+ // There are children (the methods).
+ dbg_abbrev->push_back(DW_CHILDREN_yes);
+
+ // DW_LANG_Java DW_FORM_data1.
+ dbg_abbrev->push_back(DW_AT_language);
+ dbg_abbrev->push_back(DW_FORM_data1);
+
+ // DW_AT_low_pc DW_FORM_addr.
+ dbg_abbrev->push_back(DW_AT_low_pc);
+ dbg_abbrev->push_back(DW_FORM_addr);
+
+ // DW_AT_high_pc DW_FORM_addr.
+ dbg_abbrev->push_back(DW_AT_high_pc);
+ dbg_abbrev->push_back(DW_FORM_addr);
+
+ // End of DW_TAG_compile_unit.
+ PushHalf(dbg_abbrev, 0);
+
+ // Tag 2: Compilation unit: DW_TAG_subprogram.
+ dbg_abbrev->push_back(2);
+ dbg_abbrev->push_back(DW_TAG_subprogram);
+
+ // There are no children.
+ dbg_abbrev->push_back(DW_CHILDREN_no);
+
+ // Name of the method.
+ dbg_abbrev->push_back(DW_AT_name);
+ dbg_abbrev->push_back(DW_FORM_strp);
+
+ // DW_AT_low_pc DW_FORM_addr.
+ dbg_abbrev->push_back(DW_AT_low_pc);
+ dbg_abbrev->push_back(DW_FORM_addr);
+
+ // DW_AT_high_pc DW_FORM_addr.
+ dbg_abbrev->push_back(DW_AT_high_pc);
+ dbg_abbrev->push_back(DW_FORM_addr);
+
+ // End of DW_TAG_subprogram.
+ PushHalf(dbg_abbrev, 0);
+
+ // Start the debug_info section with the header information
+ // 'unit_length' will be filled in later.
+ PushWord(dbg_info, 0);
+
+ // 'version' - 3.
+ PushHalf(dbg_info, 3);
+
+ // Offset into .debug_abbrev section (always 0).
+ PushWord(dbg_info, 0);
+
+ // Address size: 4.
+ dbg_info->push_back(4);
+
+ // Start the description for the compilation unit.
+ // This uses tag 1.
+ dbg_info->push_back(1);
+
+ // The language is Java.
+ dbg_info->push_back(DW_LANG_Java);
+
+ // Leave space for low_pc and high_pc.
+ int low_pc_offset = dbg_info->size();
+ PushWord(dbg_info, 0);
+ PushWord(dbg_info, 0);
+
+ // Walk through the information in the method table, and enter into dbg_info.
+ const std::vector<OatWriter::DebugInfo>& dbg = oat_writer->GetCFIMethodInfo();
+ uint32_t low_pc = 0xFFFFFFFFU;
+ uint32_t high_pc = 0;
+
+ for (uint32_t i = 0; i < dbg.size(); i++) {
+ const OatWriter::DebugInfo& info = dbg[i];
+ if (info.low_pc_ < low_pc) {
+ low_pc = info.low_pc_;
+ }
+ if (info.high_pc_ > high_pc) {
+ high_pc = info.high_pc_;
+ }
+
+ // Start a new TAG: subroutine (2).
+ dbg_info->push_back(2);
+
+ // Enter the name into the string table (and NUL terminate).
+ uint32_t str_offset = dbg_str->size();
+ dbg_str->insert(dbg_str->end(), info.method_name_.begin(), info.method_name_.end());
+ dbg_str->push_back('\0');
+
+ // Enter name, low_pc, high_pc.
+ PushWord(dbg_info, str_offset);
+ PushWord(dbg_info, info.low_pc_);
+ PushWord(dbg_info, info.high_pc_);
+ }
+
+ // One byte terminator
+ dbg_info->push_back(0);
+
+ // We have now walked all the methods. Fill in lengths and low/high PCs.
+ UpdateWord(dbg_info, 0, dbg_info->size() - 4);
+ UpdateWord(dbg_info, low_pc_offset, low_pc);
+ UpdateWord(dbg_info, low_pc_offset + 4, high_pc);
}
} // namespace art
diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h
index 25b0495c0a..dec75dc83f 100644
--- a/compiler/elf_writer_quick.h
+++ b/compiler/elf_writer_quick.h
@@ -45,6 +45,16 @@ class ElfWriterQuick FINAL : public ElfWriter {
: ElfWriter(driver, elf_file) {}
~ElfWriterQuick() {}
+ /*
+ * @brief Generate the DWARF debug_info and debug_abbrev sections
+ * @param oat_writer The Oat file Writer.
+ * @param dbg_info Compilation unit information.
+ * @param dbg_abbrev Abbreviations used to generate dbg_info.
+ * @param dbg_str Debug strings.
+ */
+ void FillInCFIInformation(OatWriter* oat_writer, std::vector<uint8_t>* dbg_info,
+ std::vector<uint8_t>* dbg_abbrev, std::vector<uint8_t>* dbg_str);
+
DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterQuick);
};
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 970d2e30f5..a400bdde6c 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -378,6 +378,27 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index,
uint32_t thumb_offset = compiled_method->CodeDelta();
quick_code_offset = offset + sizeof(code_size) + thumb_offset;
+ std::vector<uint8_t>* cfi_info = compiler_driver_->GetCallFrameInformation();
+ if (cfi_info != nullptr) {
+ // Copy in the FDE, if present
+ const std::vector<uint8_t>* fde = compiled_method->GetCFIInfo();
+ if (fde != nullptr) {
+ // Copy the information into cfi_info and then fix the address in the new copy.
+ int cur_offset = cfi_info->size();
+ cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
+
+ // Set the 'initial_location' field to address the start of the method.
+ uint32_t new_value = quick_code_offset - oat_header_->GetExecutableOffset();
+ uint32_t offset_to_update = cur_offset + 2*sizeof(uint32_t);
+ (*cfi_info)[offset_to_update+0] = new_value;
+ (*cfi_info)[offset_to_update+1] = new_value >> 8;
+ (*cfi_info)[offset_to_update+2] = new_value >> 16;
+ (*cfi_info)[offset_to_update+3] = new_value >> 24;
+ method_info_.push_back(DebugInfo(PrettyMethod(class_def_method_index, dex_file, false),
+ new_value, new_value + code_size));
+ }
+ }
+
// Deduplicate code arrays
SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator code_iter =
code_offsets_.find(quick_code);
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index f12c84d233..3d4b48ae42 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -82,6 +82,19 @@ class OatWriter {
~OatWriter();
+ struct DebugInfo {
+ DebugInfo(const std::string& method_name, uint32_t low_pc, uint32_t high_pc)
+ : method_name_(method_name), low_pc_(low_pc), high_pc_(high_pc) {
+ }
+ std::string method_name_;
+ uint32_t low_pc_;
+ uint32_t high_pc_;
+ };
+
+ const std::vector<DebugInfo>& GetCFIMethodInfo() const {
+ return method_info_;
+ }
+
private:
size_t InitOatHeader();
size_t InitOatDexFiles(size_t offset);
@@ -205,6 +218,8 @@ class OatWriter {
DISALLOW_COPY_AND_ASSIGN(OatClass);
};
+ std::vector<DebugInfo> method_info_;
+
const CompilerDriver* const compiler_driver_;
// note OatFile does not take ownership of the DexFiles
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 0e54021002..041a66b34e 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -760,6 +760,7 @@ static int dex2oat(int argc, char** argv) {
bool dump_passes = false;
bool dump_slow_timing = kIsDebugBuild;
bool watch_dog_enabled = !kIsTargetBuild;
+ bool generate_gdb_information = kIsDebugBuild;
for (int i = 0; i < argc; i++) {
const StringPiece option(argv[i]);
@@ -797,6 +798,10 @@ static int dex2oat(int argc, char** argv) {
watch_dog_enabled = true;
} else if (option == "--no-watch-dog") {
watch_dog_enabled = false;
+ } else if (option == "--gen-gdb-info") {
+ generate_gdb_information = true;
+ } else if (option == "--no-gen-gdb-info") {
+ generate_gdb_information = false;
} else if (option.starts_with("-j")) {
const char* thread_count_str = option.substr(strlen("-j")).data();
if (!ParseInt(thread_count_str, &thread_count)) {
@@ -1042,7 +1047,8 @@ static int dex2oat(int argc, char** argv) {
large_method_threshold,
small_method_threshold,
tiny_method_threshold,
- num_dex_methods_threshold
+ num_dex_methods_threshold,
+ generate_gdb_information
#ifdef ART_SEA_IR_MODE
, compiler_options.sea_ir_ = true;
#endif
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 3a17e41487..c646b86524 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -22,6 +22,83 @@
namespace art {
+// -------------------------------------------------------------------
+// Binary GDB JIT Interface as described in
+// http://sourceware.org/gdb/onlinedocs/gdb/Declarations.html
+extern "C" {
+ typedef enum {
+ JIT_NOACTION = 0,
+ JIT_REGISTER_FN,
+ JIT_UNREGISTER_FN
+ } JITAction;
+
+ struct JITCodeEntry {
+ JITCodeEntry* next_;
+ JITCodeEntry* prev_;
+ const byte *symfile_addr_;
+ uint64_t symfile_size_;
+ };
+
+ struct JITDescriptor {
+ uint32_t version_;
+ uint32_t action_flag_;
+ JITCodeEntry* relevant_entry_;
+ JITCodeEntry* first_entry_;
+ };
+
+ // GDB will place breakpoint into this function.
+ // To prevent GCC from inlining or removing it we place noinline attribute
+ // and inline assembler statement inside.
+ void __attribute__((noinline)) __jit_debug_register_code() {
+ __asm__("");
+ }
+
+ // GDB will inspect contents of this descriptor.
+ // Static initialization is necessary to prevent GDB from seeing
+ // uninitialized descriptor.
+ JITDescriptor __jit_debug_descriptor = { 1, JIT_NOACTION, nullptr, nullptr };
+}
+
+
+static JITCodeEntry* CreateCodeEntry(const byte *symfile_addr,
+ uintptr_t symfile_size) {
+ JITCodeEntry* entry = new JITCodeEntry;
+ entry->symfile_addr_ = symfile_addr;
+ entry->symfile_size_ = symfile_size;
+ entry->prev_ = nullptr;
+
+ // TODO: Do we need a lock here?
+ entry->next_ = __jit_debug_descriptor.first_entry_;
+ if (entry->next_ != nullptr) {
+ entry->next_->prev_ = entry;
+ }
+ __jit_debug_descriptor.first_entry_ = entry;
+ __jit_debug_descriptor.relevant_entry_ = entry;
+
+ __jit_debug_descriptor.action_flag_ = JIT_REGISTER_FN;
+ __jit_debug_register_code();
+ return entry;
+}
+
+
+static void UnregisterCodeEntry(JITCodeEntry* entry) {
+ // TODO: Do we need a lock here?
+ if (entry->prev_ != nullptr) {
+ entry->prev_->next_ = entry->next_;
+ } else {
+ __jit_debug_descriptor.first_entry_ = entry->next_;
+ }
+
+ if (entry->next_ != nullptr) {
+ entry->next_->prev_ = entry->prev_;
+ }
+
+ __jit_debug_descriptor.relevant_entry_ = entry;
+ __jit_debug_descriptor.action_flag_ = JIT_UNREGISTER_FN;
+ __jit_debug_register_code();
+ delete entry;
+}
+
ElfFile::ElfFile(File* file, bool writable, bool program_header_only)
: file_(file),
writable_(writable),
@@ -38,7 +115,9 @@ ElfFile::ElfFile(File* file, bool writable, bool program_header_only)
dynstr_section_start_(NULL),
hash_section_start_(NULL),
symtab_symbol_table_(NULL),
- dynsym_symbol_table_(NULL) {
+ dynsym_symbol_table_(NULL),
+ jit_elf_image_(NULL),
+ jit_gdb_entry_(NULL) {
CHECK(file != NULL);
}
@@ -172,6 +251,10 @@ ElfFile::~ElfFile() {
STLDeleteElements(&segments_);
delete symtab_symbol_table_;
delete dynsym_symbol_table_;
+ delete jit_elf_image_;
+ if (jit_gdb_entry_) {
+ UnregisterCodeEntry(jit_gdb_entry_);
+ }
}
bool ElfFile::SetMap(MemMap* map, std::string* error_msg) {
@@ -830,6 +913,11 @@ bool ElfFile::Load(bool executable, std::string* error_msg) {
}
}
+ // Use GDB JIT support to do stack backtrace, etc.
+ if (executable) {
+ GdbJITSupport();
+ }
+
return true;
}
@@ -843,4 +931,269 @@ bool ElfFile::ValidPointer(const byte* start) const {
return false;
}
+static bool check_section_name(ElfFile& file, int section_num, const char *name) {
+ Elf32_Shdr& section_header = file.GetSectionHeader(section_num);
+ const char *section_name = file.GetString(SHT_SYMTAB, section_header.sh_name);
+ return strcmp(name, section_name) == 0;
+}
+
+static void IncrementUint32(byte *p, uint32_t increment) {
+ uint32_t *u = reinterpret_cast<uint32_t *>(p);
+ *u += increment;
+}
+
+static void RoundAndClear(byte *image, uint32_t& offset, int pwr2) {
+ uint32_t mask = pwr2 - 1;
+ while (offset & mask) {
+ image[offset++] = 0;
+ }
+}
+
+// Simple macro to bump a point to a section header to the next one.
+#define BUMP_SHENT(sp) \
+ sp = reinterpret_cast<Elf32_Shdr *> (\
+ reinterpret_cast<byte*>(sp) + elf_hdr.e_shentsize);\
+ offset += elf_hdr.e_shentsize
+
+void ElfFile::GdbJITSupport() {
+ // We only get here if we only are mapping the program header.
+ DCHECK(program_header_only_);
+
+ // Well, we need the whole file to do this.
+ std::string error_msg;
+ UniquePtr<ElfFile> ptr(Open(const_cast<File*>(file_), false, false, &error_msg));
+ ElfFile& all = *ptr;
+
+ // Do we have interesting sections?
+ // Is this an OAT file with interesting sections?
+ if (all.GetSectionHeaderNum() != kExpectedSectionsInOATFile) {
+ return;
+ }
+ if (!check_section_name(all, 8, ".debug_info") ||
+ !check_section_name(all, 9, ".debug_abbrev") ||
+ !check_section_name(all, 10, ".debug_frame") ||
+ !check_section_name(all, 11, ".debug_str")) {
+ return;
+ }
+
+ // Okay, we are good enough. Fake up an ELF image and tell GDB about it.
+ // We need some extra space for the debug and string sections, the ELF header, and the
+ // section header.
+ uint32_t needed_size = KB;
+
+ for (Elf32_Word i = 1; i < all.GetSectionHeaderNum(); i++) {
+ Elf32_Shdr& section_header = all.GetSectionHeader(i);
+ if (section_header.sh_addr == 0 && section_header.sh_type != SHT_DYNSYM) {
+ // Debug section: we need it.
+ needed_size += section_header.sh_size;
+ } else if (section_header.sh_type == SHT_STRTAB &&
+ strcmp(".shstrtab",
+ all.GetString(SHT_SYMTAB, section_header.sh_name)) == 0) {
+ // We also need the shared string table.
+ needed_size += section_header.sh_size;
+
+ // We also need the extra strings .symtab\0.strtab\0
+ needed_size += 16;
+ }
+ }
+
+ // Start creating our image.
+ jit_elf_image_ = new byte[needed_size];
+
+ // Create the Elf Header by copying the old one
+ Elf32_Ehdr& elf_hdr =
+ *reinterpret_cast<Elf32_Ehdr*>(jit_elf_image_);
+
+ elf_hdr = all.GetHeader();
+ elf_hdr.e_entry = 0;
+ elf_hdr.e_phoff = 0;
+ elf_hdr.e_phnum = 0;
+ elf_hdr.e_phentsize = 0;
+ elf_hdr.e_type = ET_EXEC;
+
+ uint32_t offset = sizeof(Elf32_Ehdr);
+
+ // Copy the debug sections and string table.
+ uint32_t debug_offsets[kExpectedSectionsInOATFile];
+ memset(debug_offsets, '\0', sizeof debug_offsets);
+ Elf32_Shdr *text_header = nullptr;
+ int extra_shstrtab_entries = -1;
+ int text_section_index = -1;
+ int section_index = 1;
+ for (Elf32_Word i = 1; i < kExpectedSectionsInOATFile; i++) {
+ Elf32_Shdr& section_header = all.GetSectionHeader(i);
+ // Round up to multiple of 4, ensuring zero fill.
+ RoundAndClear(jit_elf_image_, offset, 4);
+ if (section_header.sh_addr == 0 && section_header.sh_type != SHT_DYNSYM) {
+ // Debug section: we need it. Unfortunately, it wasn't mapped in.
+ debug_offsets[i] = offset;
+ // Read it from the file.
+ lseek(file_->Fd(), section_header.sh_offset, SEEK_SET);
+ read(file_->Fd(), jit_elf_image_ + offset, section_header.sh_size);
+ offset += section_header.sh_size;
+ section_index++;
+ offset += 16;
+ } else if (section_header.sh_type == SHT_STRTAB &&
+ strcmp(".shstrtab",
+ all.GetString(SHT_SYMTAB, section_header.sh_name)) == 0) {
+ // We also need the shared string table.
+ debug_offsets[i] = offset;
+ // Read it from the file.
+ lseek(file_->Fd(), section_header.sh_offset, SEEK_SET);
+ read(file_->Fd(), jit_elf_image_ + offset, section_header.sh_size);
+ offset += section_header.sh_size;
+ // We also need the extra strings .symtab\0.strtab\0
+ extra_shstrtab_entries = section_header.sh_size;
+ memcpy(jit_elf_image_+offset, ".symtab\0.strtab\0", 16);
+ offset += 16;
+ section_index++;
+ } else if (section_header.sh_flags & SHF_EXECINSTR) {
+ DCHECK(strcmp(".text", all.GetString(SHT_SYMTAB,
+ section_header.sh_name)) == 0);
+ text_header = &section_header;
+ text_section_index = section_index++;
+ }
+ }
+ DCHECK(text_header != nullptr);
+ DCHECK_NE(extra_shstrtab_entries, -1);
+
+ // We now need to update the addresses for debug_info and debug_frame to get to the
+ // correct offset within the .text section.
+ uint32_t text_start_addr = 0;
+ for (uint32_t i = 0; i < segments_.size(); i++) {
+ if (segments_[i]->GetProtect() & PROT_EXEC) {
+ // We found the .text section.
+ text_start_addr = reinterpret_cast<uint32_t>(segments_[i]->Begin());
+ break;
+ }
+ }
+ DCHECK_NE(text_start_addr, 0U);
+
+ byte *p = jit_elf_image_+debug_offsets[8];
+ byte *end = p + all.GetSectionHeader(8).sh_size;
+
+ // For debug_info; patch compilation using low_pc @ offset 13, high_pc at offset 17.
+ IncrementUint32(p + 13, text_start_addr);
+ IncrementUint32(p + 17, text_start_addr);
+
+ // Now fix the low_pc, high_pc for each method address.
+ // First method starts at offset 0x15, each subsequent method is 1+3*4 bytes further.
+ for (p += 0x15; p < end; p += 1 /* attr# */ + 3 * sizeof(uint32_t) /* addresses */) {
+ IncrementUint32(p + 1 + sizeof(uint32_t), text_start_addr);
+ IncrementUint32(p + 1 + 2 * sizeof(uint32_t), text_start_addr);
+ }
+
+ // Now we have to handle the debug_frame method start addresses
+ p = jit_elf_image_+debug_offsets[10];
+ end = p + all.GetSectionHeader(10).sh_size;
+
+ // Skip past the CIE.
+ p += *reinterpret_cast<uint32_t *>(p) + 4;
+
+ // And walk the FDEs.
+ for (; p < end; p += *reinterpret_cast<uint32_t *>(p) + sizeof(uint32_t)) {
+ IncrementUint32(p + 2 * sizeof(uint32_t), text_start_addr);
+ }
+
+ // Create the data for the symbol table.
+ const int kSymbtabAlignment = 16;
+ RoundAndClear(jit_elf_image_, offset, kSymbtabAlignment);
+ uint32_t symtab_offset = offset;
+
+ // First entry is empty.
+ memset(jit_elf_image_+offset, 0, sizeof(Elf32_Sym));
+ offset += sizeof(Elf32_Sym);
+
+ // Symbol 1 is the real .text section.
+ Elf32_Sym& sym_ent = *reinterpret_cast<Elf32_Sym*>(jit_elf_image_+offset);
+ sym_ent.st_name = 1; /* .text */
+ sym_ent.st_value = text_start_addr;
+ sym_ent.st_size = text_header->sh_size;
+ SetBindingAndType(&sym_ent, STB_LOCAL, STT_SECTION);
+ sym_ent.st_other = 0;
+ sym_ent.st_shndx = text_section_index;
+ offset += sizeof(Elf32_Sym);
+
+ // Create the data for the string table.
+ RoundAndClear(jit_elf_image_, offset, kSymbtabAlignment);
+ const int kTextStringSize = 7;
+ uint32_t strtab_offset = offset;
+ memcpy(jit_elf_image_+offset, "\0.text", kTextStringSize);
+ offset += kTextStringSize;
+
+ // Create the section header table.
+ // Round up to multiple of kSymbtabAlignment, ensuring zero fill.
+ RoundAndClear(jit_elf_image_, offset, kSymbtabAlignment);
+ elf_hdr.e_shoff = offset;
+ Elf32_Shdr *sp =
+ reinterpret_cast<Elf32_Shdr *>(jit_elf_image_ + offset);
+
+ // Copy the first empty index.
+ *sp = all.GetSectionHeader(0);
+ BUMP_SHENT(sp);
+
+ elf_hdr.e_shnum = 1;
+ for (Elf32_Word i = 1; i < kExpectedSectionsInOATFile; i++) {
+ Elf32_Shdr& section_header = all.GetSectionHeader(i);
+ if (section_header.sh_addr == 0 && section_header.sh_type != SHT_DYNSYM) {
+ // Debug section: we need it.
+ *sp = section_header;
+ sp->sh_offset = debug_offsets[i];
+ sp->sh_addr = 0;
+ elf_hdr.e_shnum++;
+ BUMP_SHENT(sp);
+ } else if (section_header.sh_type == SHT_STRTAB &&
+ strcmp(".shstrtab",
+ all.GetString(SHT_SYMTAB, section_header.sh_name)) == 0) {
+ // We also need the shared string table.
+ *sp = section_header;
+ sp->sh_offset = debug_offsets[i];
+ sp->sh_size += 16; /* sizeof ".symtab\0.strtab\0" */
+ sp->sh_addr = 0;
+ elf_hdr.e_shstrndx = elf_hdr.e_shnum;
+ elf_hdr.e_shnum++;
+ BUMP_SHENT(sp);
+ }
+ }
+
+ // Add a .text section for the matching code section.
+ *sp = *text_header;
+ sp->sh_type = SHT_NOBITS;
+ sp->sh_offset = 0;
+ sp->sh_addr = text_start_addr;
+ elf_hdr.e_shnum++;
+ BUMP_SHENT(sp);
+
+ // .symtab section: Need an empty index and the .text entry
+ sp->sh_name = extra_shstrtab_entries;
+ sp->sh_type = SHT_SYMTAB;
+ sp->sh_flags = 0;
+ sp->sh_addr = 0;
+ sp->sh_offset = symtab_offset;
+ sp->sh_size = 2 * sizeof(Elf32_Sym);
+ sp->sh_link = elf_hdr.e_shnum + 1; // Link to .strtab section.
+ sp->sh_info = 0;
+ sp->sh_addralign = 16;
+ sp->sh_entsize = sizeof(Elf32_Sym);
+ elf_hdr.e_shnum++;
+ BUMP_SHENT(sp);
+
+ // .strtab section: Enough for .text\0.
+ sp->sh_name = extra_shstrtab_entries + 8;
+ sp->sh_type = SHT_STRTAB;
+ sp->sh_flags = 0;
+ sp->sh_addr = 0;
+ sp->sh_offset = strtab_offset;
+ sp->sh_size = kTextStringSize;
+ sp->sh_link = 0;
+ sp->sh_info = 0;
+ sp->sh_addralign = 16;
+ sp->sh_entsize = 0;
+ elf_hdr.e_shnum++;
+ BUMP_SHENT(sp);
+
+ // We now have enough information to tell GDB about our file.
+ jit_gdb_entry_ = CreateCodeEntry(jit_elf_image_, offset);
+}
+
} // namespace art
diff --git a/runtime/elf_file.h b/runtime/elf_file.h
index 8a0a5f84c4..d2a044e9cf 100644
--- a/runtime/elf_file.h
+++ b/runtime/elf_file.h
@@ -29,6 +29,12 @@
namespace art {
+// Interface to GDB JIT for backtrace information.
+extern "C" {
+ struct JITCodeEntry;
+}
+
+
// Used for compile time and runtime for ElfFile access. Because of
// the need for use at runtime, cannot directly use LLVM classes such as
// ELFObjectFile.
@@ -171,6 +177,13 @@ class ElfFile {
SymbolTable* symtab_symbol_table_;
SymbolTable* dynsym_symbol_table_;
+
+ // Support for GDB JIT
+ byte* jit_elf_image_;
+ JITCodeEntry* jit_gdb_entry_;
+ void GdbJITSupport();
+ // Is this an OAT file with debug information in it?
+ static constexpr uint32_t kExpectedSectionsInOATFile = 12;
};
} // namespace art