Index: src/gdb-jit.cc |
diff --git a/src/gdb-jit.cc b/src/gdb-jit.cc |
index b1782cbdd0c4b2539d29d79d433e095dc709aafc..1d00f8eb510a7b34b6baa3b725bfe4ccab9d958e 100644 |
--- a/src/gdb-jit.cc |
+++ b/src/gdb-jit.cc |
@@ -201,6 +201,7 @@ class ELFSection : public ZoneObject { |
TYPE_SHLIB = 10, |
TYPE_DYNSYM = 11, |
TYPE_LOPROC = 0x70000000, |
+ TYPE_X86_64_UNWIND = 0x70000001, |
TYPE_HIPROC = 0x7fffffff, |
TYPE_LOUSER = 0x80000000, |
TYPE_HIUSER = 0xffffffff |
@@ -639,26 +640,53 @@ class ELFSymbolTable : public ELFSection { |
class CodeDescription BASE_EMBEDDED { |
public: |
+ |
+#ifdef V8_TARGET_ARCH_X64 |
+ enum StackState { |
+ POST_RBP_PUSH, |
+ POST_RBP_SET, |
+ POST_RBP_POP, |
+ STACK_STATE_MAX |
+ }; |
+#endif |
+ |
CodeDescription(const char* name, |
Code* code, |
Handle<Script> script, |
- GDBJITLineInfo* lineinfo) |
- : name_(name), code_(code), script_(script), lineinfo_(lineinfo) |
+ GDBJITLineInfo* lineinfo, |
+ GDBJITInterface::CodeTag tag) |
+ : name_(name), |
+ code_(code), |
Erik Corry
2011/02/02 13:10:25
Indentation
|
+ script_(script), |
+ lineinfo_(lineinfo), |
+ tag_(tag) |
{ } |
- const char* code_name() const { |
+ const char* name() const { |
return name_; |
} |
- uintptr_t code_size() const { |
- return code_->instruction_end() - code_->instruction_start(); |
+ GDBJITLineInfo* lineinfo() const { |
+ return lineinfo_; |
+ } |
+ |
+ GDBJITInterface::CodeTag tag() const { |
+ return tag_; |
} |
- uintptr_t code_start() const { |
- return (uintptr_t)code_->instruction_start(); |
+ uintptr_t CodeStart() const { |
Erik Corry
2011/02/02 13:10:25
Why can't these return Address?
|
+ return reinterpret_cast<uintptr_t>(code_->instruction_start()); |
} |
- bool is_line_info_available() { |
+ uintptr_t CodeEnd() const { |
+ return reinterpret_cast<uintptr_t>(code_->instruction_end()); |
+ } |
+ |
+ uintptr_t CodeSize() const { |
+ return CodeEnd() - CodeStart(); |
+ } |
+ |
+ bool IsLineInfoAvailable() { |
return !script_.is_null() && |
script_->source()->IsString() && |
script_->HasValidSource() && |
@@ -666,9 +694,19 @@ class CodeDescription BASE_EMBEDDED { |
lineinfo_ != NULL; |
} |
- GDBJITLineInfo* lineinfo() const { return lineinfo_; } |
+#ifdef V8_TARGET_ARCH_X64 |
+ uintptr_t GetStackStateStartAddress(StackState state) const { |
+ ASSERT(state < STACK_STATE_MAX); |
+ return stack_state_start_addresses_[state]; |
+ } |
+ |
+ void SetStackStateStartAddress(StackState state, uintptr_t addr) { |
+ ASSERT(state < STACK_STATE_MAX); |
+ stack_state_start_addresses_[state] = addr; |
+ } |
+#endif |
- SmartPointer<char> filename() { |
+ SmartPointer<char> GetFilename() { |
return String::cast(script_->name())->ToCString(); |
} |
@@ -676,11 +714,16 @@ class CodeDescription BASE_EMBEDDED { |
return GetScriptLineNumberSafe(script_, pos) + 1; |
} |
+ |
private: |
const char* name_; |
Code* code_; |
Handle<Script> script_; |
GDBJITLineInfo* lineinfo_; |
+ GDBJITInterface::CodeTag tag_; |
+#ifdef V8_TARGET_ARCH_X64 |
+ uintptr_t stack_state_start_addresses_[STACK_STATE_MAX]; |
+#endif |
}; |
@@ -701,9 +744,9 @@ static void CreateSymbolsTable(CodeDescription* desc, |
ELFSymbol::TYPE_FILE, |
ELFSection::INDEX_ABSOLUTE)); |
- symtab->Add(ELFSymbol(desc->code_name(), |
+ symtab->Add(ELFSymbol(desc->name(), |
0, |
- desc->code_size(), |
+ desc->CodeSize(), |
ELFSymbol::BIND_GLOBAL, |
ELFSymbol::TYPE_FUNC, |
text_section_index)); |
@@ -723,9 +766,9 @@ class DebugInfoSection : public ELFSection { |
w->Write<uint8_t>(sizeof(intptr_t)); |
w->WriteULEB128(1); // Abbreviation code. |
- w->WriteString(*desc_->filename()); |
- w->Write<intptr_t>(desc_->code_start()); |
- w->Write<intptr_t>(desc_->code_start() + desc_->code_size()); |
+ w->WriteString(*desc_->GetFilename()); |
+ w->Write<intptr_t>(desc_->CodeStart()); |
Erik Corry
2011/02/02 13:10:25
w->Write<Address>(...
|
+ w->Write<intptr_t>(desc_->CodeStart() + desc_->CodeSize()); |
w->Write<uint32_t>(0); |
size.set(static_cast<uint32_t>(w->position() - start)); |
return true; |
@@ -829,7 +872,7 @@ class DebugLineSection : public ELFSection { |
w->Write<uint8_t>(1); // DW_LNS_SET_COLUMN operands count. |
w->Write<uint8_t>(0); // DW_LNS_NEGATE_STMT operands count. |
w->Write<uint8_t>(0); // Empty include_directories sequence. |
- w->WriteString(*desc_->filename()); // File name. |
+ w->WriteString(*desc_->GetFilename()); // File name. |
w->WriteULEB128(0); // Current directory. |
w->WriteULEB128(0); // Unknown modification time. |
w->WriteULEB128(0); // Unknown file size. |
@@ -837,7 +880,7 @@ class DebugLineSection : public ELFSection { |
prologue_length.set(static_cast<uint32_t>(w->position() - prologue_start)); |
WriteExtendedOpcode(w, DW_LNE_SET_ADDRESS, sizeof(intptr_t)); |
- w->Write<intptr_t>(desc_->code_start()); |
+ w->Write<intptr_t>(desc_->CodeStart()); |
intptr_t pc = 0; |
intptr_t line = 1; |
@@ -900,12 +943,235 @@ class DebugLineSection : public ELFSection { |
}; |
+#ifdef V8_TARGET_ARCH_X64 |
+ |
+ |
+class UnwindInfoSection : public ELFSection { |
+ public: |
+ explicit UnwindInfoSection(CodeDescription *desc); |
+ virtual bool WriteBody(Writer *w); |
+ |
+ int WriteCIE(Writer *w); |
+ void WriteFDE(Writer *w, int); |
+ |
+ void WriteFDEStateOnEntry(Writer *w); |
+ void WriteFDEStateAfterRBPPush(Writer *w); |
+ void WriteFDEStateAfterRBPSet(Writer *w); |
+ void WriteFDEStateAfterRBPPop(Writer *w); |
+ |
+ void WriteLength(Writer *w, Writer::Slot<uint32_t> &, int); |
+ |
+ private: |
+ CodeDescription *desc_; |
+ |
+ // DWARF3 Specification, Table 7.23 |
+ enum CFIInstructions { |
+ DW_CFA_ADVANCE_LOC = 0X40, |
Erik Corry
2011/02/02 13:10:25
We use a small x everywhere in V8, so we should do
|
+ DW_CFA_OFFSET = 0X80, |
+ DW_CFA_RESTORE = 0XC0, |
+ DW_CFA_NOP = 0X00, |
+ DW_CFA_SET_LOC = 0X01, |
+ DW_CFA_ADVANCE_LOC1 = 0X02, |
+ DW_CFA_ADVANCE_LOC2 = 0X03, |
+ DW_CFA_ADVANCE_LOC4 = 0X04, |
+ DW_CFA_OFFSET_EXTENDED = 0X05, |
+ DW_CFA_RESTORE_EXTENDED = 0X06, |
+ DW_CFA_UNDEFINED = 0X07, |
+ DW_CFA_SAME_VALUE = 0X08, |
+ DW_CFA_REGISTER = 0X09, |
+ DW_CFA_REMEMBER_STATE = 0X0A, |
+ DW_CFA_RESTORE_STATE = 0X0B, |
+ DW_CFA_DEF_CFA = 0X0C, |
+ DW_CFA_DEF_CFA_REGISTER = 0X0D, |
+ DW_CFA_DEF_CFA_OFFSET = 0X0E, |
+ |
+ DW_CFA_DEF_CFA_EXPRESSION = 0X0F, |
+ DW_CFA_EXPRESSION = 0X10, |
+ DW_CFA_OFFSET_EXTENDED_SF = 0X11, |
+ DW_CFA_DEF_CFA_SF = 0X12, |
+ DW_CFA_DEF_CFA_OFFSET_SF = 0X13, |
+ DW_CFA_VAL_OFFSET = 0X14, |
+ DW_CFA_VAL_OFFSET_SF = 0X15, |
+ DW_CFA_VAL_EXPRESSION = 0X16 |
+ }; |
+ |
+ // System V ABI, AMD64 Supplement, Version 0.99.5, Figure 3.36 |
+ enum RegisterMapping { |
+ // Only the relevant ones have been added to reduce clutter. |
+ AMD64_RBP = 6, |
+ AMD64_RSP = 7, |
+ AMD64_RA = 16 |
+ }; |
+ |
+ enum CFIConstants { |
+ CIE_ID = 0, |
+ CIE_VERSION = 1, |
+ CODE_ALIGN_FACTOR = 1, |
+ DATA_ALIGN_FACTOR = 1, |
+ RETURN_ADDRESS_REGISTER = AMD64_RA |
+ }; |
+}; |
+ |
+ |
+void UnwindInfoSection::WriteLength(Writer *w, |
+ Writer::Slot<uint32_t> &length_slot, |
+ int initial_position) { |
+ uint32_t align = (w->position() - initial_position) % kPointerSize; |
+ |
+ if (align != 0) { |
+ for (uint32_t i = 0; i < (kPointerSize - align); i++) |
Erik Corry
2011/02/02 13:10:25
Missing {} on multiline for statement.
|
+ w->Write<uint8_t>(DW_CFA_NOP); |
+ } |
+ |
+ ASSERT((w->position() - initial_position) % kPointerSize == 0); |
+ length_slot.set(w->position() - initial_position); |
+} |
+ |
+ |
+UnwindInfoSection::UnwindInfoSection(CodeDescription *desc) |
+ : ELFSection(".eh_frame", TYPE_X86_64_UNWIND, 1), desc_(desc) |
+{ } |
+ |
+int UnwindInfoSection::WriteCIE(Writer *w) { |
+ Writer::Slot<uint32_t> cie_length_slot = w->CreateSlotHere<uint32_t>(); |
+ uint32_t cie_position = w->position(); |
+ |
+ // Write out the CIE header. Currently no 'common instructions' are |
+ // emitted onto the CIE; every FDE has its own set of instructions. |
+ |
+ w->Write<uint32_t>(CIE_ID); |
+ w->Write<uint8_t>(CIE_VERSION); |
+ w->Write<uint8_t>(0); // Null augmentation string. |
+ w->WriteSLEB128(CODE_ALIGN_FACTOR); |
+ w->WriteSLEB128(DATA_ALIGN_FACTOR); |
+ w->Write<uint8_t>(RETURN_ADDRESS_REGISTER); |
+ |
+ WriteLength(w, cie_length_slot, cie_position); |
+ |
+ return cie_position; |
+} |
+ |
+ |
+void UnwindInfoSection::WriteFDE(Writer *w, int cie_position) { |
+ // The only FDE for this function. The CFA is the current RBP. |
+ Writer::Slot<uint32_t> fde_length_slot = w->CreateSlotHere<uint32_t>(); |
+ int fde_position = w->position(); |
+ w->Write<int32_t>(fde_position - cie_position + 4); |
+ |
+ w->Write<uintptr_t>(desc_->CodeStart()); |
+ w->Write<uintptr_t>(desc_->CodeSize()); |
+ |
+ WriteFDEStateOnEntry(w); |
+ WriteFDEStateAfterRBPPush(w); |
+ WriteFDEStateAfterRBPSet(w); |
+ WriteFDEStateAfterRBPPop(w); |
+ |
+ WriteLength(w, fde_length_slot, fde_position); |
+} |
+ |
+ |
+void UnwindInfoSection::WriteFDEStateOnEntry(Writer *w) { |
+ // The first state, just after the control has been transferred to the the |
+ // function. |
+ |
+ // RBP for this function will be the value of RSP after pushing the RBP |
+ // for the previous function. The previous RBP has not been pushed yet. |
+ w->Write<uint8_t>(DW_CFA_DEF_CFA_SF); |
+ w->WriteULEB128(AMD64_RSP); |
+ w->WriteSLEB128(-kPointerSize); |
+ |
+ // The RA is stored at location CFA + kCallerPCOffset. This is an invariant, |
+ // and hence omitted from the next states. |
+ w->Write<uint8_t>(DW_CFA_OFFSET_EXTENDED); |
+ w->WriteULEB128(AMD64_RA); |
+ w->WriteSLEB128(StandardFrameConstants::kCallerPCOffset); |
+ |
+ // The RBP of the previous function is still in RBP. |
+ w->Write<uint8_t>(DW_CFA_SAME_VALUE); |
+ w->WriteULEB128(AMD64_RBP); |
+ |
+ // Last location described by this entry. |
+ w->Write<uint8_t>(DW_CFA_SET_LOC); |
+ w->Write<uint64_t>( |
+ desc_->GetStackStateStartAddress(CodeDescription::POST_RBP_PUSH)); |
+} |
+ |
+ |
+void UnwindInfoSection::WriteFDEStateAfterRBPPush(Writer *w) { |
+ // The second state, just after RBP has been pushed. |
+ |
+ // RBP / CFA for this function is now the current RSP, so just set the |
+ // offset from the previous rule (from -8) to 0. |
+ w->Write<uint8_t>(DW_CFA_DEF_CFA_OFFSET); |
+ w->WriteULEB128(0); |
+ |
+ // The previous RBP is stored at CFA + kCallerFPOffset. This is an invariant |
+ // in this and the next state, and hence omitted in the next state. |
+ w->Write<uint8_t>(DW_CFA_OFFSET_EXTENDED); |
+ w->WriteULEB128(AMD64_RBP); |
+ w->WriteSLEB128(StandardFrameConstants::kCallerFPOffset); |
+ |
+ // Last location described by this entry. |
+ w->Write<uint8_t>(DW_CFA_SET_LOC); |
+ w->Write<uint64_t>( |
+ desc_->GetStackStateStartAddress(CodeDescription::POST_RBP_SET)); |
+} |
+ |
+ |
+void UnwindInfoSection::WriteFDEStateAfterRBPSet(Writer *w) { |
+ // The third state, after the RBP has been set. |
+ |
+ // The CFA can now directly be set to RBP. |
+ w->Write<uint8_t>(DW_CFA_DEF_CFA); |
+ w->WriteULEB128(AMD64_RBP); |
+ w->WriteULEB128(0); |
+ |
+ // Last location described by this entry. |
+ w->Write<uint8_t>(DW_CFA_SET_LOC); |
+ w->Write<uint64_t>( |
+ desc_->GetStackStateStartAddress(CodeDescription::POST_RBP_POP)); |
+} |
+ |
+ |
+void UnwindInfoSection::WriteFDEStateAfterRBPPop(Writer *w) { |
+ // The fourth (final) state. The RBP has been popped (just before issuing a |
+ // return). |
+ |
+ // The CFA can is now calculated in the same way as in the first state. |
+ w->Write<uint8_t>(DW_CFA_DEF_CFA_SF); |
+ w->WriteULEB128(AMD64_RSP); |
+ w->WriteSLEB128(-kPointerSize); |
+ |
+ // The RBP |
+ w->Write<uint8_t>(DW_CFA_OFFSET_EXTENDED); |
+ w->WriteULEB128(AMD64_RBP); |
+ w->WriteSLEB128(StandardFrameConstants::kCallerFPOffset); |
+ |
+ // Last location described by this entry. |
+ w->Write<uint8_t>(DW_CFA_SET_LOC); |
+ w->Write<uint64_t>(desc_->CodeEnd()); |
+} |
+ |
+ |
+bool UnwindInfoSection::WriteBody(Writer *w) { |
+ uint32_t cie_position = WriteCIE(w); |
+ WriteFDE(w, cie_position); |
+ return true; |
+} |
+ |
+ |
+#endif // V8_TARGET_ARCH_X64 |
+ |
+ |
static void CreateDWARFSections(CodeDescription* desc, ELF* elf) { |
- if (desc->is_line_info_available()) { |
+ if (desc->IsLineInfoAvailable()) { |
elf->AddSection(new DebugInfoSection(desc)); |
elf->AddSection(new DebugAbbrevSection); |
elf->AddSection(new DebugLineSection(desc)); |
} |
+#ifdef V8_TARGET_ARCH_X64 |
+ elf->AddSection(new UnwindInfoSection(desc)); |
+#endif |
} |
@@ -1005,9 +1271,9 @@ static JITCodeEntry* CreateELFObject(CodeDescription* desc) { |
new FullHeaderELFSection(".text", |
ELFSection::TYPE_NOBITS, |
kCodeAlignment, |
- desc->code_start(), |
+ desc->CodeStart(), |
0, |
- desc->code_size(), |
+ desc->CodeSize(), |
ELFSection::FLAG_ALLOC | ELFSection::FLAG_EXEC)); |
CreateSymbolsTable(desc, &elf, text_section_index); |
@@ -1065,15 +1331,66 @@ void GDBJITInterface::AddCode(Handle<String> name, |
if (!name.is_null()) { |
SmartPointer<char> name_cstring = name->ToCString(DISALLOW_NULLS); |
- AddCode(*name_cstring, *code, *script); |
+ AddCode(*name_cstring, *code, GDBJITInterface::FUNCTION, *script); |
+ } else { |
+ AddCode("", *code, GDBJITInterface::FUNCTION, *script); |
+ } |
+} |
+ |
+static void AddUnwindInfo(CodeDescription *desc) { |
+#ifdef V8_TARGET_ARCH_X64 |
+ if (desc->tag() == GDBJITInterface::FUNCTION) { |
+ // To avoid propagating unwinding information through |
+ // compilation pipeline we rely on constant offsets as |
+ // function prologue and epilogue are the same for all |
Erik Corry
2011/02/02 13:10:25
we rely on ... are the same -> we rely on ... bein
|
+ // code objects generated by the full code generator. |
+ static const int kFramePointerPushOffset = 1; |
+ static const int kFramePointerSetOffset = 4; |
+ static const int kFramePointerPopOffset = -3; |
+ |
+ uintptr_t frame_pointer_push_address = |
+ desc->CodeStart() + kFramePointerPushOffset; |
+ |
+ uintptr_t frame_pointer_set_address = |
+ desc->CodeStart() + kFramePointerSetOffset; |
+ |
+ uintptr_t frame_pointer_pop_address = |
+ desc->CodeEnd() + kFramePointerPopOffset; |
+ |
+#ifdef DEBUG |
+ static const uint8_t kFramePointerPushInstruction = 0x48; // push ebp |
+ static const uint16_t kFramePointerSetInstruction = 0x5756; // mov ebp, esp |
+ static const uint8_t kFramePointerPopInstruction = 0xBE; // pop ebp |
+ |
+ ASSERT(*reinterpret_cast<uint8_t*>(frame_pointer_push_address) == |
+ kFramePointerPushInstruction); |
+ ASSERT(*reinterpret_cast<uint16_t*>(frame_pointer_set_address) == |
+ kFramePointerSetInstruction); |
+ ASSERT(*reinterpret_cast<uint8_t*>(frame_pointer_pop_address) == |
+ kFramePointerPopInstruction); |
+#endif |
+ |
+ desc->SetStackStateStartAddress(CodeDescription::POST_RBP_PUSH, |
+ frame_pointer_push_address); |
+ desc->SetStackStateStartAddress(CodeDescription::POST_RBP_SET, |
+ frame_pointer_set_address); |
+ desc->SetStackStateStartAddress(CodeDescription::POST_RBP_POP, |
+ frame_pointer_pop_address); |
} else { |
- AddCode("", *code, *script); |
+ desc->SetStackStateStartAddress(CodeDescription::POST_RBP_PUSH, |
+ desc->CodeStart()); |
+ desc->SetStackStateStartAddress(CodeDescription::POST_RBP_SET, |
+ desc->CodeStart()); |
+ desc->SetStackStateStartAddress(CodeDescription::POST_RBP_POP, |
+ desc->CodeEnd()); |
} |
+#endif // V8_TARGET_ARCH_X64 |
} |
void GDBJITInterface::AddCode(const char* name, |
Code* code, |
+ GDBJITInterface::CodeTag tag, |
Script* script) { |
if (!FLAG_gdbjit) return; |
AssertNoAllocation no_gc; |
@@ -1086,14 +1403,16 @@ void GDBJITInterface::AddCode(const char* name, |
code, |
script != NULL ? Handle<Script>(script) |
: Handle<Script>(), |
- lineinfo); |
+ lineinfo, |
+ tag); |
- if (!FLAG_gdbjit_full && !code_desc.is_line_info_available()) { |
+ if (!FLAG_gdbjit_full && !code_desc.IsLineInfoAvailable()) { |
delete lineinfo; |
entries.Remove(code, HashForCodeObject(code)); |
return; |
} |
+ AddUnwindInfo(&code_desc); |
JITCodeEntry* entry = CreateELFObject(&code_desc); |
ASSERT(!IsLineInfoTagged(entry)); |
@@ -1120,7 +1439,7 @@ void GDBJITInterface::AddCode(GDBJITInterface::CodeTag tag, |
builder.AddFormatted(": code object %p", static_cast<void*>(code)); |
} |
- AddCode(builder.Finalize(), code); |
+ AddCode(builder.Finalize(), code, tag); |
} |