You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
284 lines
10 KiB
284 lines
10 KiB
/* |
|
* Copyright (C) 2016 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. |
|
*/ |
|
|
|
#ifndef ART_COMPILER_DEBUG_ELF_DEBUG_FRAME_WRITER_H_ |
|
#define ART_COMPILER_DEBUG_ELF_DEBUG_FRAME_WRITER_H_ |
|
|
|
#include <vector> |
|
|
|
#include "arch/instruction_set.h" |
|
#include "debug/dwarf/debug_frame_opcode_writer.h" |
|
#include "debug/dwarf/dwarf_constants.h" |
|
#include "debug/dwarf/headers.h" |
|
#include "debug/method_debug_info.h" |
|
#include "elf_builder.h" |
|
|
|
namespace art { |
|
namespace debug { |
|
|
|
static void WriteCIE(InstructionSet isa, |
|
dwarf::CFIFormat format, |
|
std::vector<uint8_t>* buffer) { |
|
using Reg = dwarf::Reg; |
|
// 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); |
|
switch (isa) { |
|
case kArm: |
|
case kThumb2: { |
|
dwarf::DebugFrameOpCodeWriter<> opcodes; |
|
opcodes.DefCFA(Reg::ArmCore(13), 0); // R13(SP). |
|
// core registers. |
|
for (int reg = 0; reg < 13; reg++) { |
|
if (reg < 4 || reg == 12) { |
|
opcodes.Undefined(Reg::ArmCore(reg)); |
|
} else { |
|
opcodes.SameValue(Reg::ArmCore(reg)); |
|
} |
|
} |
|
// fp registers. |
|
for (int reg = 0; reg < 32; reg++) { |
|
if (reg < 16) { |
|
opcodes.Undefined(Reg::ArmFp(reg)); |
|
} else { |
|
opcodes.SameValue(Reg::ArmFp(reg)); |
|
} |
|
} |
|
auto return_reg = Reg::ArmCore(14); // R14(LR). |
|
WriteCIE(is64bit, return_reg, opcodes, format, buffer); |
|
return; |
|
} |
|
case kArm64: { |
|
dwarf::DebugFrameOpCodeWriter<> opcodes; |
|
opcodes.DefCFA(Reg::Arm64Core(31), 0); // R31(SP). |
|
// core registers. |
|
for (int reg = 0; reg < 30; reg++) { |
|
if (reg < 8 || reg == 16 || reg == 17) { |
|
opcodes.Undefined(Reg::Arm64Core(reg)); |
|
} else { |
|
opcodes.SameValue(Reg::Arm64Core(reg)); |
|
} |
|
} |
|
// fp registers. |
|
for (int reg = 0; reg < 32; reg++) { |
|
if (reg < 8 || reg >= 16) { |
|
opcodes.Undefined(Reg::Arm64Fp(reg)); |
|
} else { |
|
opcodes.SameValue(Reg::Arm64Fp(reg)); |
|
} |
|
} |
|
auto return_reg = Reg::Arm64Core(30); // R30(LR). |
|
WriteCIE(is64bit, return_reg, opcodes, format, buffer); |
|
return; |
|
} |
|
case kMips: |
|
case kMips64: { |
|
dwarf::DebugFrameOpCodeWriter<> opcodes; |
|
opcodes.DefCFA(Reg::MipsCore(29), 0); // R29(SP). |
|
// core registers. |
|
for (int reg = 1; reg < 26; reg++) { |
|
if (reg < 16 || reg == 24 || reg == 25) { // AT, V*, A*, T*. |
|
opcodes.Undefined(Reg::MipsCore(reg)); |
|
} else { |
|
opcodes.SameValue(Reg::MipsCore(reg)); |
|
} |
|
} |
|
// fp registers. |
|
for (int reg = 0; reg < 32; reg++) { |
|
if (reg < 24) { |
|
opcodes.Undefined(Reg::Mips64Fp(reg)); |
|
} else { |
|
opcodes.SameValue(Reg::Mips64Fp(reg)); |
|
} |
|
} |
|
auto return_reg = Reg::MipsCore(31); // R31(RA). |
|
WriteCIE(is64bit, return_reg, opcodes, format, buffer); |
|
return; |
|
} |
|
case kX86: { |
|
// FIXME: Add fp registers once libunwind adds support for them. Bug: 20491296 |
|
constexpr bool generate_opcodes_for_x86_fp = false; |
|
dwarf::DebugFrameOpCodeWriter<> opcodes; |
|
opcodes.DefCFA(Reg::X86Core(4), 4); // R4(ESP). |
|
opcodes.Offset(Reg::X86Core(8), -4); // R8(EIP). |
|
// core registers. |
|
for (int reg = 0; reg < 8; reg++) { |
|
if (reg <= 3) { |
|
opcodes.Undefined(Reg::X86Core(reg)); |
|
} else if (reg == 4) { |
|
// Stack pointer. |
|
} else { |
|
opcodes.SameValue(Reg::X86Core(reg)); |
|
} |
|
} |
|
// fp registers. |
|
if (generate_opcodes_for_x86_fp) { |
|
for (int reg = 0; reg < 8; reg++) { |
|
opcodes.Undefined(Reg::X86Fp(reg)); |
|
} |
|
} |
|
auto return_reg = Reg::X86Core(8); // R8(EIP). |
|
WriteCIE(is64bit, return_reg, opcodes, format, buffer); |
|
return; |
|
} |
|
case kX86_64: { |
|
dwarf::DebugFrameOpCodeWriter<> opcodes; |
|
opcodes.DefCFA(Reg::X86_64Core(4), 8); // R4(RSP). |
|
opcodes.Offset(Reg::X86_64Core(16), -8); // R16(RIP). |
|
// core registers. |
|
for (int reg = 0; reg < 16; reg++) { |
|
if (reg == 4) { |
|
// Stack pointer. |
|
} else if (reg < 12 && reg != 3 && reg != 5) { // except EBX and EBP. |
|
opcodes.Undefined(Reg::X86_64Core(reg)); |
|
} else { |
|
opcodes.SameValue(Reg::X86_64Core(reg)); |
|
} |
|
} |
|
// fp registers. |
|
for (int reg = 0; reg < 16; reg++) { |
|
if (reg < 12) { |
|
opcodes.Undefined(Reg::X86_64Fp(reg)); |
|
} else { |
|
opcodes.SameValue(Reg::X86_64Fp(reg)); |
|
} |
|
} |
|
auto return_reg = Reg::X86_64Core(16); // R16(RIP). |
|
WriteCIE(is64bit, return_reg, opcodes, format, buffer); |
|
return; |
|
} |
|
case kNone: |
|
break; |
|
} |
|
LOG(FATAL) << "Cannot write CIE frame for ISA " << isa; |
|
UNREACHABLE(); |
|
} |
|
|
|
template<typename ElfTypes> |
|
void WriteCFISection(ElfBuilder<ElfTypes>* builder, |
|
const ArrayRef<const MethodDebugInfo>& method_infos, |
|
dwarf::CFIFormat format, |
|
bool write_oat_patches) { |
|
CHECK(format == dwarf::DW_DEBUG_FRAME_FORMAT || format == dwarf::DW_EH_FRAME_FORMAT); |
|
typedef typename ElfTypes::Addr Elf_Addr; |
|
|
|
// The methods can be written in any order. |
|
// Let's therefore sort them in the lexicographical order of the opcodes. |
|
// This has no effect on its own. However, if the final .debug_frame section is |
|
// compressed it reduces the size since similar opcodes sequences are grouped. |
|
std::vector<const MethodDebugInfo*> sorted_method_infos; |
|
sorted_method_infos.reserve(method_infos.size()); |
|
for (size_t i = 0; i < method_infos.size(); i++) { |
|
if (!method_infos[i].cfi.empty() && !method_infos[i].deduped) { |
|
sorted_method_infos.push_back(&method_infos[i]); |
|
} |
|
} |
|
if (sorted_method_infos.empty()) { |
|
return; |
|
} |
|
std::stable_sort( |
|
sorted_method_infos.begin(), |
|
sorted_method_infos.end(), |
|
[](const MethodDebugInfo* lhs, const MethodDebugInfo* rhs) { |
|
ArrayRef<const uint8_t> l = lhs->cfi; |
|
ArrayRef<const uint8_t> r = rhs->cfi; |
|
return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); |
|
}); |
|
|
|
std::vector<uint32_t> binary_search_table; |
|
std::vector<uintptr_t> patch_locations; |
|
if (format == dwarf::DW_EH_FRAME_FORMAT) { |
|
binary_search_table.reserve(2 * sorted_method_infos.size()); |
|
} else { |
|
patch_locations.reserve(sorted_method_infos.size()); |
|
} |
|
|
|
// Write .eh_frame/.debug_frame section. |
|
auto* cfi_section = (format == dwarf::DW_DEBUG_FRAME_FORMAT |
|
? builder->GetDebugFrame() |
|
: builder->GetEhFrame()); |
|
{ |
|
cfi_section->Start(); |
|
const bool is64bit = Is64BitInstructionSet(builder->GetIsa()); |
|
const Elf_Addr cfi_address = cfi_section->GetAddress(); |
|
const Elf_Addr cie_address = cfi_address; |
|
Elf_Addr buffer_address = cfi_address; |
|
std::vector<uint8_t> buffer; // Small temporary buffer. |
|
WriteCIE(builder->GetIsa(), format, &buffer); |
|
cfi_section->WriteFully(buffer.data(), buffer.size()); |
|
buffer_address += buffer.size(); |
|
buffer.clear(); |
|
for (const MethodDebugInfo* mi : sorted_method_infos) { |
|
DCHECK(!mi->deduped); |
|
DCHECK(!mi->cfi.empty()); |
|
const Elf_Addr code_address = mi->code_address + |
|
(mi->is_code_address_text_relative ? builder->GetText()->GetAddress() : 0); |
|
if (format == dwarf::DW_EH_FRAME_FORMAT) { |
|
binary_search_table.push_back(dchecked_integral_cast<uint32_t>(code_address)); |
|
binary_search_table.push_back(dchecked_integral_cast<uint32_t>(buffer_address)); |
|
} |
|
WriteFDE(is64bit, cfi_address, cie_address, |
|
code_address, mi->code_size, |
|
mi->cfi, format, buffer_address, &buffer, |
|
&patch_locations); |
|
cfi_section->WriteFully(buffer.data(), buffer.size()); |
|
buffer_address += buffer.size(); |
|
buffer.clear(); |
|
} |
|
cfi_section->End(); |
|
} |
|
|
|
if (format == dwarf::DW_EH_FRAME_FORMAT) { |
|
auto* header_section = builder->GetEhFrameHdr(); |
|
header_section->Start(); |
|
uint32_t header_address = dchecked_integral_cast<int32_t>(header_section->GetAddress()); |
|
// Write .eh_frame_hdr section. |
|
std::vector<uint8_t> buffer; |
|
dwarf::Writer<> header(&buffer); |
|
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(dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4); |
|
// Encoding of binary search table size. |
|
header.PushUint8(dwarf::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(dwarf::DW_EH_PE_datarel | dwarf::DW_EH_PE_sdata4); |
|
// .eh_frame pointer |
|
header.PushInt32(cfi_section->GetAddress() - (header_address + 4u)); |
|
// Binary search table size (number of entries). |
|
header.PushUint32(dchecked_integral_cast<uint32_t>(binary_search_table.size()/2)); |
|
header_section->WriteFully(buffer.data(), buffer.size()); |
|
// Binary search table. |
|
for (size_t i = 0; i < binary_search_table.size(); i++) { |
|
// Make addresses section-relative since we know the header address now. |
|
binary_search_table[i] -= header_address; |
|
} |
|
header_section->WriteFully(binary_search_table.data(), binary_search_table.size()); |
|
header_section->End(); |
|
} else { |
|
if (write_oat_patches) { |
|
builder->WritePatches(".debug_frame.oat_patches", |
|
ArrayRef<const uintptr_t>(patch_locations)); |
|
} |
|
} |
|
} |
|
|
|
} // namespace debug |
|
} // namespace art |
|
|
|
#endif // ART_COMPILER_DEBUG_ELF_DEBUG_FRAME_WRITER_H_ |
|
|
|
|