| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #ifndef ART_COMPILER_DEBUG_ELF_DEBUG_FRAME_WRITER_H_ |
| 18 | #define ART_COMPILER_DEBUG_ELF_DEBUG_FRAME_WRITER_H_ |
| 19 | |
| 20 | #include <vector> |
| 21 | |
| 22 | #include "arch/instruction_set.h" |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 23 | #include "debug/method_debug_info.h" |
| David Srbecky | 2faab00 | 2019-02-12 16:35:48 +0000 | [diff] [blame] | 24 | #include "dwarf/debug_frame_opcode_writer.h" |
| 25 | #include "dwarf/dwarf_constants.h" |
| 26 | #include "dwarf/headers.h" |
| 27 | #include "elf/elf_builder.h" |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 28 | |
| 29 | namespace art { |
| 30 | namespace debug { |
| 31 | |
| David Srbecky | d4f8afb | 2019-05-10 16:44:38 +0000 | [diff] [blame] | 32 | static constexpr bool kWriteDebugFrameHdr = false; |
| 33 | |
| David Srbecky | 76b9c69 | 2019-04-01 19:36:33 +0100 | [diff] [blame] | 34 | // Binary search table is not useful if the number of entries is small. |
| 35 | // In particular, this avoids it for the in-memory JIT mini-debug-info. |
| 36 | static constexpr size_t kMinDebugFrameHdrEntries = 100; |
| 37 | |
| David Srbecky | 91b2900 | 2019-02-08 15:51:31 +0000 | [diff] [blame] | 38 | static void WriteCIE(InstructionSet isa, /*inout*/ std::vector<uint8_t>* buffer) { |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 39 | using Reg = dwarf::Reg; |
| 40 | // Scratch registers should be marked as undefined. This tells the |
| 41 | // debugger that its value in the previous frame is not recoverable. |
| 42 | bool is64bit = Is64BitInstructionSet(isa); |
| 43 | switch (isa) { |
| Vladimir Marko | 33bff25 | 2017-11-01 14:35:42 +0000 | [diff] [blame] | 44 | case InstructionSet::kArm: |
| 45 | case InstructionSet::kThumb2: { |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 46 | dwarf::DebugFrameOpCodeWriter<> opcodes; |
| 47 | opcodes.DefCFA(Reg::ArmCore(13), 0); // R13(SP). |
| 48 | // core registers. |
| 49 | for (int reg = 0; reg < 13; reg++) { |
| 50 | if (reg < 4 || reg == 12) { |
| 51 | opcodes.Undefined(Reg::ArmCore(reg)); |
| 52 | } else { |
| 53 | opcodes.SameValue(Reg::ArmCore(reg)); |
| 54 | } |
| 55 | } |
| 56 | // fp registers. |
| 57 | for (int reg = 0; reg < 32; reg++) { |
| 58 | if (reg < 16) { |
| 59 | opcodes.Undefined(Reg::ArmFp(reg)); |
| 60 | } else { |
| 61 | opcodes.SameValue(Reg::ArmFp(reg)); |
| 62 | } |
| 63 | } |
| 64 | auto return_reg = Reg::ArmCore(14); // R14(LR). |
| David Srbecky | 91b2900 | 2019-02-08 15:51:31 +0000 | [diff] [blame] | 65 | WriteCIE(is64bit, return_reg, opcodes, buffer); |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 66 | return; |
| 67 | } |
| Vladimir Marko | 33bff25 | 2017-11-01 14:35:42 +0000 | [diff] [blame] | 68 | case InstructionSet::kArm64: { |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 69 | dwarf::DebugFrameOpCodeWriter<> opcodes; |
| 70 | opcodes.DefCFA(Reg::Arm64Core(31), 0); // R31(SP). |
| 71 | // core registers. |
| 72 | for (int reg = 0; reg < 30; reg++) { |
| 73 | if (reg < 8 || reg == 16 || reg == 17) { |
| 74 | opcodes.Undefined(Reg::Arm64Core(reg)); |
| 75 | } else { |
| 76 | opcodes.SameValue(Reg::Arm64Core(reg)); |
| 77 | } |
| 78 | } |
| 79 | // fp registers. |
| 80 | for (int reg = 0; reg < 32; reg++) { |
| 81 | if (reg < 8 || reg >= 16) { |
| 82 | opcodes.Undefined(Reg::Arm64Fp(reg)); |
| 83 | } else { |
| 84 | opcodes.SameValue(Reg::Arm64Fp(reg)); |
| 85 | } |
| 86 | } |
| 87 | auto return_reg = Reg::Arm64Core(30); // R30(LR). |
| David Srbecky | 91b2900 | 2019-02-08 15:51:31 +0000 | [diff] [blame] | 88 | WriteCIE(is64bit, return_reg, opcodes, buffer); |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 89 | return; |
| 90 | } |
| Vladimir Marko | 33bff25 | 2017-11-01 14:35:42 +0000 | [diff] [blame] | 91 | case InstructionSet::kMips: |
| 92 | case InstructionSet::kMips64: { |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 93 | dwarf::DebugFrameOpCodeWriter<> opcodes; |
| 94 | opcodes.DefCFA(Reg::MipsCore(29), 0); // R29(SP). |
| 95 | // core registers. |
| 96 | for (int reg = 1; reg < 26; reg++) { |
| 97 | if (reg < 16 || reg == 24 || reg == 25) { // AT, V*, A*, T*. |
| 98 | opcodes.Undefined(Reg::MipsCore(reg)); |
| 99 | } else { |
| 100 | opcodes.SameValue(Reg::MipsCore(reg)); |
| 101 | } |
| 102 | } |
| 103 | // fp registers. |
| 104 | for (int reg = 0; reg < 32; reg++) { |
| 105 | if (reg < 24) { |
| 106 | opcodes.Undefined(Reg::Mips64Fp(reg)); |
| 107 | } else { |
| 108 | opcodes.SameValue(Reg::Mips64Fp(reg)); |
| 109 | } |
| 110 | } |
| 111 | auto return_reg = Reg::MipsCore(31); // R31(RA). |
| David Srbecky | 91b2900 | 2019-02-08 15:51:31 +0000 | [diff] [blame] | 112 | WriteCIE(is64bit, return_reg, opcodes, buffer); |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 113 | return; |
| 114 | } |
| Vladimir Marko | 33bff25 | 2017-11-01 14:35:42 +0000 | [diff] [blame] | 115 | case InstructionSet::kX86: { |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 116 | // FIXME: Add fp registers once libunwind adds support for them. Bug: 20491296 |
| 117 | constexpr bool generate_opcodes_for_x86_fp = false; |
| 118 | dwarf::DebugFrameOpCodeWriter<> opcodes; |
| 119 | opcodes.DefCFA(Reg::X86Core(4), 4); // R4(ESP). |
| 120 | opcodes.Offset(Reg::X86Core(8), -4); // R8(EIP). |
| 121 | // core registers. |
| 122 | for (int reg = 0; reg < 8; reg++) { |
| 123 | if (reg <= 3) { |
| 124 | opcodes.Undefined(Reg::X86Core(reg)); |
| 125 | } else if (reg == 4) { |
| 126 | // Stack pointer. |
| 127 | } else { |
| 128 | opcodes.SameValue(Reg::X86Core(reg)); |
| 129 | } |
| 130 | } |
| 131 | // fp registers. |
| 132 | if (generate_opcodes_for_x86_fp) { |
| 133 | for (int reg = 0; reg < 8; reg++) { |
| 134 | opcodes.Undefined(Reg::X86Fp(reg)); |
| 135 | } |
| 136 | } |
| 137 | auto return_reg = Reg::X86Core(8); // R8(EIP). |
| David Srbecky | 91b2900 | 2019-02-08 15:51:31 +0000 | [diff] [blame] | 138 | WriteCIE(is64bit, return_reg, opcodes, buffer); |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 139 | return; |
| 140 | } |
| Vladimir Marko | 33bff25 | 2017-11-01 14:35:42 +0000 | [diff] [blame] | 141 | case InstructionSet::kX86_64: { |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 142 | dwarf::DebugFrameOpCodeWriter<> opcodes; |
| 143 | opcodes.DefCFA(Reg::X86_64Core(4), 8); // R4(RSP). |
| 144 | opcodes.Offset(Reg::X86_64Core(16), -8); // R16(RIP). |
| 145 | // core registers. |
| 146 | for (int reg = 0; reg < 16; reg++) { |
| 147 | if (reg == 4) { |
| 148 | // Stack pointer. |
| 149 | } else if (reg < 12 && reg != 3 && reg != 5) { // except EBX and EBP. |
| 150 | opcodes.Undefined(Reg::X86_64Core(reg)); |
| 151 | } else { |
| 152 | opcodes.SameValue(Reg::X86_64Core(reg)); |
| 153 | } |
| 154 | } |
| 155 | // fp registers. |
| 156 | for (int reg = 0; reg < 16; reg++) { |
| 157 | if (reg < 12) { |
| 158 | opcodes.Undefined(Reg::X86_64Fp(reg)); |
| 159 | } else { |
| 160 | opcodes.SameValue(Reg::X86_64Fp(reg)); |
| 161 | } |
| 162 | } |
| 163 | auto return_reg = Reg::X86_64Core(16); // R16(RIP). |
| David Srbecky | 91b2900 | 2019-02-08 15:51:31 +0000 | [diff] [blame] | 164 | WriteCIE(is64bit, return_reg, opcodes, buffer); |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 165 | return; |
| 166 | } |
| Vladimir Marko | 33bff25 | 2017-11-01 14:35:42 +0000 | [diff] [blame] | 167 | case InstructionSet::kNone: |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 168 | break; |
| 169 | } |
| 170 | LOG(FATAL) << "Cannot write CIE frame for ISA " << isa; |
| 171 | UNREACHABLE(); |
| 172 | } |
| 173 | |
| 174 | template<typename ElfTypes> |
| David Srbecky | 2faab00 | 2019-02-12 16:35:48 +0000 | [diff] [blame] | 175 | void WriteCFISection(ElfBuilder<ElfTypes>* builder, |
| David Srbecky | 7370d92 | 2019-02-12 14:00:30 +0000 | [diff] [blame] | 176 | const ArrayRef<const MethodDebugInfo>& method_infos) { |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 177 | typedef typename ElfTypes::Addr Elf_Addr; |
| 178 | |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 179 | // The methods can be written in any order. |
| 180 | // Let's therefore sort them in the lexicographical order of the opcodes. |
| 181 | // This has no effect on its own. However, if the final .debug_frame section is |
| 182 | // compressed it reduces the size since similar opcodes sequences are grouped. |
| 183 | std::vector<const MethodDebugInfo*> sorted_method_infos; |
| 184 | sorted_method_infos.reserve(method_infos.size()); |
| 185 | for (size_t i = 0; i < method_infos.size(); i++) { |
| David Srbecky | 51bc752 | 2019-01-05 15:41:06 +0000 | [diff] [blame] | 186 | if (!method_infos[i].cfi.empty() && !method_infos[i].deduped) { |
| David Srbecky | b14a5ed | 2016-03-11 16:54:22 +0000 | [diff] [blame] | 187 | sorted_method_infos.push_back(&method_infos[i]); |
| 188 | } |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 189 | } |
| David Srbecky | b14a5ed | 2016-03-11 16:54:22 +0000 | [diff] [blame] | 190 | if (sorted_method_infos.empty()) { |
| 191 | return; |
| 192 | } |
| 193 | std::stable_sort( |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 194 | sorted_method_infos.begin(), |
| 195 | sorted_method_infos.end(), |
| 196 | [](const MethodDebugInfo* lhs, const MethodDebugInfo* rhs) { |
| David Srbecky | 197160d | 2016-03-07 17:33:57 +0000 | [diff] [blame] | 197 | ArrayRef<const uint8_t> l = lhs->cfi; |
| 198 | ArrayRef<const uint8_t> r = rhs->cfi; |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 199 | return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); |
| 200 | }); |
| 201 | |
| David Srbecky | b14a5ed | 2016-03-11 16:54:22 +0000 | [diff] [blame] | 202 | std::vector<uint32_t> binary_search_table; |
| David Srbecky | d4f8afb | 2019-05-10 16:44:38 +0000 | [diff] [blame] | 203 | if (kWriteDebugFrameHdr) { |
| 204 | binary_search_table.reserve(2 * sorted_method_infos.size()); |
| 205 | } |
| David Srbecky | b14a5ed | 2016-03-11 16:54:22 +0000 | [diff] [blame] | 206 | |
| David Srbecky | 91b2900 | 2019-02-08 15:51:31 +0000 | [diff] [blame] | 207 | // Write .debug_frame section. |
| 208 | auto* cfi_section = builder->GetDebugFrame(); |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 209 | { |
| 210 | cfi_section->Start(); |
| 211 | const bool is64bit = Is64BitInstructionSet(builder->GetIsa()); |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 212 | std::vector<uint8_t> buffer; // Small temporary buffer. |
| David Srbecky | 91b2900 | 2019-02-08 15:51:31 +0000 | [diff] [blame] | 213 | WriteCIE(builder->GetIsa(), &buffer); |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 214 | cfi_section->WriteFully(buffer.data(), buffer.size()); |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 215 | buffer.clear(); |
| 216 | for (const MethodDebugInfo* mi : sorted_method_infos) { |
| David Srbecky | b14a5ed | 2016-03-11 16:54:22 +0000 | [diff] [blame] | 217 | DCHECK(!mi->deduped); |
| David Srbecky | 51bc752 | 2019-01-05 15:41:06 +0000 | [diff] [blame] | 218 | DCHECK(!mi->cfi.empty()); |
| David Srbecky | b14a5ed | 2016-03-11 16:54:22 +0000 | [diff] [blame] | 219 | const Elf_Addr code_address = mi->code_address + |
| 220 | (mi->is_code_address_text_relative ? builder->GetText()->GetAddress() : 0); |
| David Srbecky | d4f8afb | 2019-05-10 16:44:38 +0000 | [diff] [blame] | 221 | if (kWriteDebugFrameHdr) { |
| 222 | binary_search_table.push_back(dchecked_integral_cast<uint32_t>(code_address)); |
| 223 | binary_search_table.push_back(cfi_section->GetPosition()); |
| 224 | } |
| David Srbecky | 91b2900 | 2019-02-08 15:51:31 +0000 | [diff] [blame] | 225 | dwarf::WriteFDE(is64bit, |
| David Srbecky | 7370d92 | 2019-02-12 14:00:30 +0000 | [diff] [blame] | 226 | /* cie_pointer= */ 0, |
| David Srbecky | 91b2900 | 2019-02-08 15:51:31 +0000 | [diff] [blame] | 227 | code_address, |
| 228 | mi->code_size, |
| 229 | mi->cfi, |
| David Srbecky | 7370d92 | 2019-02-12 14:00:30 +0000 | [diff] [blame] | 230 | &buffer); |
| David Srbecky | b14a5ed | 2016-03-11 16:54:22 +0000 | [diff] [blame] | 231 | cfi_section->WriteFully(buffer.data(), buffer.size()); |
| David Srbecky | b14a5ed | 2016-03-11 16:54:22 +0000 | [diff] [blame] | 232 | buffer.clear(); |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 233 | } |
| 234 | cfi_section->End(); |
| 235 | } |
| 236 | |
| David Srbecky | d4f8afb | 2019-05-10 16:44:38 +0000 | [diff] [blame] | 237 | if (kWriteDebugFrameHdr && method_infos.size() > kMinDebugFrameHdrEntries) { |
| David Srbecky | 5f1465f | 2019-02-12 17:34:39 +0000 | [diff] [blame] | 238 | std::sort(binary_search_table.begin(), binary_search_table.end()); |
| 239 | |
| 240 | // Custom Android section. It is very similar to the official .eh_frame_hdr format. |
| David Srbecky | 91b2900 | 2019-02-08 15:51:31 +0000 | [diff] [blame] | 241 | std::vector<uint8_t> header_buffer; |
| 242 | dwarf::Writer<> header(&header_buffer); |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 243 | header.PushUint8(1); // Version. |
| David Srbecky | 91b2900 | 2019-02-08 15:51:31 +0000 | [diff] [blame] | 244 | header.PushUint8(dwarf::DW_EH_PE_omit); // Encoding of .eh_frame pointer - none. |
| 245 | header.PushUint8(dwarf::DW_EH_PE_udata4); // Encoding of binary search table size. |
| 246 | header.PushUint8(dwarf::DW_EH_PE_udata4); // Encoding of binary search table data. |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 247 | header.PushUint32(dchecked_integral_cast<uint32_t>(binary_search_table.size()/2)); |
| David Srbecky | 91b2900 | 2019-02-08 15:51:31 +0000 | [diff] [blame] | 248 | |
| 249 | auto* header_section = builder->GetDebugFrameHdr(); |
| 250 | header_section->Start(); |
| 251 | header_section->WriteFully(header_buffer.data(), header_buffer.size()); |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 252 | header_section->WriteFully(binary_search_table.data(), binary_search_table.size()); |
| 253 | header_section->End(); |
| David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 254 | } |
| 255 | } |
| 256 | |
| 257 | } // namespace debug |
| 258 | } // namespace art |
| 259 | |
| 260 | #endif // ART_COMPILER_DEBUG_ELF_DEBUG_FRAME_WRITER_H_ |
| 261 | |