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" |
| 23 | #include "debug/dwarf/debug_frame_opcode_writer.h" |
| 24 | #include "debug/dwarf/dwarf_constants.h" |
| 25 | #include "debug/dwarf/headers.h" |
| 26 | #include "debug/method_debug_info.h" |
| 27 | #include "elf_builder.h" |
| 28 | |
| 29 | namespace art { |
| 30 | namespace debug { |
| 31 | |
| 32 | static void WriteCIE(InstructionSet isa, |
| 33 | dwarf::CFIFormat format, |
| 34 | std::vector<uint8_t>* buffer) { |
| 35 | using Reg = dwarf::Reg; |
| 36 | // Scratch registers should be marked as undefined. This tells the |
| 37 | // debugger that its value in the previous frame is not recoverable. |
| 38 | bool is64bit = Is64BitInstructionSet(isa); |
| 39 | switch (isa) { |
| 40 | case kArm: |
| 41 | case kThumb2: { |
| 42 | dwarf::DebugFrameOpCodeWriter<> opcodes; |
| 43 | opcodes.DefCFA(Reg::ArmCore(13), 0); // R13(SP). |
| 44 | // core registers. |
| 45 | for (int reg = 0; reg < 13; reg++) { |
| 46 | if (reg < 4 || reg == 12) { |
| 47 | opcodes.Undefined(Reg::ArmCore(reg)); |
| 48 | } else { |
| 49 | opcodes.SameValue(Reg::ArmCore(reg)); |
| 50 | } |
| 51 | } |
| 52 | // fp registers. |
| 53 | for (int reg = 0; reg < 32; reg++) { |
| 54 | if (reg < 16) { |
| 55 | opcodes.Undefined(Reg::ArmFp(reg)); |
| 56 | } else { |
| 57 | opcodes.SameValue(Reg::ArmFp(reg)); |
| 58 | } |
| 59 | } |
| 60 | auto return_reg = Reg::ArmCore(14); // R14(LR). |
| 61 | WriteCIE(is64bit, return_reg, opcodes, format, buffer); |
| 62 | return; |
| 63 | } |
| 64 | case kArm64: { |
| 65 | dwarf::DebugFrameOpCodeWriter<> opcodes; |
| 66 | opcodes.DefCFA(Reg::Arm64Core(31), 0); // R31(SP). |
| 67 | // core registers. |
| 68 | for (int reg = 0; reg < 30; reg++) { |
| 69 | if (reg < 8 || reg == 16 || reg == 17) { |
| 70 | opcodes.Undefined(Reg::Arm64Core(reg)); |
| 71 | } else { |
| 72 | opcodes.SameValue(Reg::Arm64Core(reg)); |
| 73 | } |
| 74 | } |
| 75 | // fp registers. |
| 76 | for (int reg = 0; reg < 32; reg++) { |
| 77 | if (reg < 8 || reg >= 16) { |
| 78 | opcodes.Undefined(Reg::Arm64Fp(reg)); |
| 79 | } else { |
| 80 | opcodes.SameValue(Reg::Arm64Fp(reg)); |
| 81 | } |
| 82 | } |
| 83 | auto return_reg = Reg::Arm64Core(30); // R30(LR). |
| 84 | WriteCIE(is64bit, return_reg, opcodes, format, buffer); |
| 85 | return; |
| 86 | } |
| 87 | case kMips: |
| 88 | case kMips64: { |
| 89 | dwarf::DebugFrameOpCodeWriter<> opcodes; |
| 90 | opcodes.DefCFA(Reg::MipsCore(29), 0); // R29(SP). |
| 91 | // core registers. |
| 92 | for (int reg = 1; reg < 26; reg++) { |
| 93 | if (reg < 16 || reg == 24 || reg == 25) { // AT, V*, A*, T*. |
| 94 | opcodes.Undefined(Reg::MipsCore(reg)); |
| 95 | } else { |
| 96 | opcodes.SameValue(Reg::MipsCore(reg)); |
| 97 | } |
| 98 | } |
| 99 | // fp registers. |
| 100 | for (int reg = 0; reg < 32; reg++) { |
| 101 | if (reg < 24) { |
| 102 | opcodes.Undefined(Reg::Mips64Fp(reg)); |
| 103 | } else { |
| 104 | opcodes.SameValue(Reg::Mips64Fp(reg)); |
| 105 | } |
| 106 | } |
| 107 | auto return_reg = Reg::MipsCore(31); // R31(RA). |
| 108 | WriteCIE(is64bit, return_reg, opcodes, format, buffer); |
| 109 | return; |
| 110 | } |
| 111 | case kX86: { |
| 112 | // FIXME: Add fp registers once libunwind adds support for them. Bug: 20491296 |
| 113 | constexpr bool generate_opcodes_for_x86_fp = false; |
| 114 | dwarf::DebugFrameOpCodeWriter<> opcodes; |
| 115 | opcodes.DefCFA(Reg::X86Core(4), 4); // R4(ESP). |
| 116 | opcodes.Offset(Reg::X86Core(8), -4); // R8(EIP). |
| 117 | // core registers. |
| 118 | for (int reg = 0; reg < 8; reg++) { |
| 119 | if (reg <= 3) { |
| 120 | opcodes.Undefined(Reg::X86Core(reg)); |
| 121 | } else if (reg == 4) { |
| 122 | // Stack pointer. |
| 123 | } else { |
| 124 | opcodes.SameValue(Reg::X86Core(reg)); |
| 125 | } |
| 126 | } |
| 127 | // fp registers. |
| 128 | if (generate_opcodes_for_x86_fp) { |
| 129 | for (int reg = 0; reg < 8; reg++) { |
| 130 | opcodes.Undefined(Reg::X86Fp(reg)); |
| 131 | } |
| 132 | } |
| 133 | auto return_reg = Reg::X86Core(8); // R8(EIP). |
| 134 | WriteCIE(is64bit, return_reg, opcodes, format, buffer); |
| 135 | return; |
| 136 | } |
| 137 | case kX86_64: { |
| 138 | dwarf::DebugFrameOpCodeWriter<> opcodes; |
| 139 | opcodes.DefCFA(Reg::X86_64Core(4), 8); // R4(RSP). |
| 140 | opcodes.Offset(Reg::X86_64Core(16), -8); // R16(RIP). |
| 141 | // core registers. |
| 142 | for (int reg = 0; reg < 16; reg++) { |
| 143 | if (reg == 4) { |
| 144 | // Stack pointer. |
| 145 | } else if (reg < 12 && reg != 3 && reg != 5) { // except EBX and EBP. |
| 146 | opcodes.Undefined(Reg::X86_64Core(reg)); |
| 147 | } else { |
| 148 | opcodes.SameValue(Reg::X86_64Core(reg)); |
| 149 | } |
| 150 | } |
| 151 | // fp registers. |
| 152 | for (int reg = 0; reg < 16; reg++) { |
| 153 | if (reg < 12) { |
| 154 | opcodes.Undefined(Reg::X86_64Fp(reg)); |
| 155 | } else { |
| 156 | opcodes.SameValue(Reg::X86_64Fp(reg)); |
| 157 | } |
| 158 | } |
| 159 | auto return_reg = Reg::X86_64Core(16); // R16(RIP). |
| 160 | WriteCIE(is64bit, return_reg, opcodes, format, buffer); |
| 161 | return; |
| 162 | } |
| 163 | case kNone: |
| 164 | break; |
| 165 | } |
| 166 | LOG(FATAL) << "Cannot write CIE frame for ISA " << isa; |
| 167 | UNREACHABLE(); |
| 168 | } |
| 169 | |
| 170 | template<typename ElfTypes> |
| 171 | void WriteCFISection(ElfBuilder<ElfTypes>* builder, |
| 172 | const ArrayRef<const MethodDebugInfo>& method_infos, |
| 173 | dwarf::CFIFormat format, |
| 174 | bool write_oat_patches) { |
| 175 | CHECK(format == dwarf::DW_DEBUG_FRAME_FORMAT || format == dwarf::DW_EH_FRAME_FORMAT); |
| 176 | typedef typename ElfTypes::Addr Elf_Addr; |
| 177 | |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 178 | // The methods can be written in any order. |
| 179 | // Let's therefore sort them in the lexicographical order of the opcodes. |
| 180 | // This has no effect on its own. However, if the final .debug_frame section is |
| 181 | // compressed it reduces the size since similar opcodes sequences are grouped. |
| 182 | std::vector<const MethodDebugInfo*> sorted_method_infos; |
| 183 | sorted_method_infos.reserve(method_infos.size()); |
| 184 | for (size_t i = 0; i < method_infos.size(); i++) { |
David Srbecky | b14a5ed | 2016-03-11 16:54:22 +0000 | [diff] [blame] | 185 | if (!method_infos[i].cfi.empty() && !method_infos[i].deduped) { |
| 186 | sorted_method_infos.push_back(&method_infos[i]); |
| 187 | } |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 188 | } |
David Srbecky | b14a5ed | 2016-03-11 16:54:22 +0000 | [diff] [blame] | 189 | if (sorted_method_infos.empty()) { |
| 190 | return; |
| 191 | } |
| 192 | std::stable_sort( |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 193 | sorted_method_infos.begin(), |
| 194 | sorted_method_infos.end(), |
| 195 | [](const MethodDebugInfo* lhs, const MethodDebugInfo* rhs) { |
David Srbecky | 197160d | 2016-03-07 17:33:57 +0000 | [diff] [blame] | 196 | ArrayRef<const uint8_t> l = lhs->cfi; |
| 197 | ArrayRef<const uint8_t> r = rhs->cfi; |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 198 | return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); |
| 199 | }); |
| 200 | |
David Srbecky | b14a5ed | 2016-03-11 16:54:22 +0000 | [diff] [blame] | 201 | std::vector<uint32_t> binary_search_table; |
| 202 | std::vector<uintptr_t> patch_locations; |
| 203 | if (format == dwarf::DW_EH_FRAME_FORMAT) { |
| 204 | binary_search_table.reserve(2 * sorted_method_infos.size()); |
| 205 | } else { |
| 206 | patch_locations.reserve(sorted_method_infos.size()); |
| 207 | } |
| 208 | |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 209 | // Write .eh_frame/.debug_frame section. |
| 210 | auto* cfi_section = (format == dwarf::DW_DEBUG_FRAME_FORMAT |
| 211 | ? builder->GetDebugFrame() |
| 212 | : builder->GetEhFrame()); |
| 213 | { |
| 214 | cfi_section->Start(); |
| 215 | const bool is64bit = Is64BitInstructionSet(builder->GetIsa()); |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 216 | const Elf_Addr cfi_address = cfi_section->GetAddress(); |
| 217 | const Elf_Addr cie_address = cfi_address; |
| 218 | Elf_Addr buffer_address = cfi_address; |
| 219 | std::vector<uint8_t> buffer; // Small temporary buffer. |
| 220 | WriteCIE(builder->GetIsa(), format, &buffer); |
| 221 | cfi_section->WriteFully(buffer.data(), buffer.size()); |
| 222 | buffer_address += buffer.size(); |
| 223 | buffer.clear(); |
| 224 | for (const MethodDebugInfo* mi : sorted_method_infos) { |
David Srbecky | b14a5ed | 2016-03-11 16:54:22 +0000 | [diff] [blame] | 225 | DCHECK(!mi->deduped); |
| 226 | DCHECK(!mi->cfi.empty()); |
| 227 | const Elf_Addr code_address = mi->code_address + |
| 228 | (mi->is_code_address_text_relative ? builder->GetText()->GetAddress() : 0); |
| 229 | if (format == dwarf::DW_EH_FRAME_FORMAT) { |
| 230 | binary_search_table.push_back(dchecked_integral_cast<uint32_t>(code_address)); |
| 231 | binary_search_table.push_back(dchecked_integral_cast<uint32_t>(buffer_address)); |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 232 | } |
David Srbecky | b14a5ed | 2016-03-11 16:54:22 +0000 | [diff] [blame] | 233 | WriteFDE(is64bit, cfi_address, cie_address, |
| 234 | code_address, mi->code_size, |
| 235 | mi->cfi, format, buffer_address, &buffer, |
| 236 | &patch_locations); |
| 237 | cfi_section->WriteFully(buffer.data(), buffer.size()); |
| 238 | buffer_address += buffer.size(); |
| 239 | buffer.clear(); |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 240 | } |
| 241 | cfi_section->End(); |
| 242 | } |
| 243 | |
| 244 | if (format == dwarf::DW_EH_FRAME_FORMAT) { |
| 245 | auto* header_section = builder->GetEhFrameHdr(); |
| 246 | header_section->Start(); |
| 247 | uint32_t header_address = dchecked_integral_cast<int32_t>(header_section->GetAddress()); |
| 248 | // Write .eh_frame_hdr section. |
| 249 | std::vector<uint8_t> buffer; |
| 250 | dwarf::Writer<> header(&buffer); |
| 251 | header.PushUint8(1); // Version. |
| 252 | // Encoding of .eh_frame pointer - libunwind does not honor datarel here, |
| 253 | // so we have to use pcrel which means relative to the pointer's location. |
| 254 | header.PushUint8(dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4); |
| 255 | // Encoding of binary search table size. |
| 256 | header.PushUint8(dwarf::DW_EH_PE_udata4); |
| 257 | // Encoding of binary search table addresses - libunwind supports only this |
| 258 | // specific combination, which means relative to the start of .eh_frame_hdr. |
| 259 | header.PushUint8(dwarf::DW_EH_PE_datarel | dwarf::DW_EH_PE_sdata4); |
| 260 | // .eh_frame pointer |
| 261 | header.PushInt32(cfi_section->GetAddress() - (header_address + 4u)); |
| 262 | // Binary search table size (number of entries). |
| 263 | header.PushUint32(dchecked_integral_cast<uint32_t>(binary_search_table.size()/2)); |
| 264 | header_section->WriteFully(buffer.data(), buffer.size()); |
| 265 | // Binary search table. |
| 266 | for (size_t i = 0; i < binary_search_table.size(); i++) { |
| 267 | // Make addresses section-relative since we know the header address now. |
| 268 | binary_search_table[i] -= header_address; |
| 269 | } |
| 270 | header_section->WriteFully(binary_search_table.data(), binary_search_table.size()); |
| 271 | header_section->End(); |
| 272 | } else { |
| 273 | if (write_oat_patches) { |
| 274 | builder->WritePatches(".debug_frame.oat_patches", |
| 275 | ArrayRef<const uintptr_t>(patch_locations)); |
| 276 | } |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | } // namespace debug |
| 281 | } // namespace art |
| 282 | |
| 283 | #endif // ART_COMPILER_DEBUG_ELF_DEBUG_FRAME_WRITER_H_ |
| 284 | |