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