diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB')
20 files changed, 8416 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.cpp new file mode 100644 index 000000000000..3c10d4aa314b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.cpp @@ -0,0 +1,748 @@ +//===-- CodeViewRegisterMapping.cpp ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "CodeViewRegisterMapping.h" + +#include "lldb/lldb-defines.h" + +#include "Plugins/Process/Utility/lldb-arm64-register-enums.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +using namespace lldb_private; + +static const uint32_t g_code_view_to_lldb_registers_arm64[] = { + LLDB_INVALID_REGNUM, // NONE + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_w0_arm64, // ARM64_W0, 10) + gpr_w1_arm64, // ARM64_W1, 11) + gpr_w2_arm64, // ARM64_W2, 12) + gpr_w3_arm64, // ARM64_W3, 13) + gpr_w4_arm64, // ARM64_W4, 14) + gpr_w5_arm64, // ARM64_W5, 15) + gpr_w6_arm64, // ARM64_W6, 16) + gpr_w7_arm64, // ARM64_W7, 17) + gpr_w8_arm64, // ARM64_W8, 18) + gpr_w9_arm64, // ARM64_W9, 19) + gpr_w10_arm64, // ARM64_W10, 20) + gpr_w11_arm64, // ARM64_W11, 21) + gpr_w12_arm64, // ARM64_W12, 22) + gpr_w13_arm64, // ARM64_W13, 23) + gpr_w14_arm64, // ARM64_W14, 24) + gpr_w15_arm64, // ARM64_W15, 25) + gpr_w16_arm64, // ARM64_W16, 26) + gpr_w17_arm64, // ARM64_W17, 27) + gpr_w18_arm64, // ARM64_W18, 28) + gpr_w19_arm64, // ARM64_W19, 29) + gpr_w20_arm64, // ARM64_W20, 30) + gpr_w21_arm64, // ARM64_W21, 31) + gpr_w22_arm64, // ARM64_W22, 32) + gpr_w23_arm64, // ARM64_W23, 33) + gpr_w24_arm64, // ARM64_W24, 34) + gpr_w25_arm64, // ARM64_W25, 35) + gpr_w26_arm64, // ARM64_W26, 36) + gpr_w27_arm64, // ARM64_W27, 37) + gpr_w28_arm64, // ARM64_W28, 38) + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_x0_arm64, // ARM64_X0, 50) + gpr_x1_arm64, // ARM64_X1, 51) + gpr_x2_arm64, // ARM64_X2, 52) + gpr_x3_arm64, // ARM64_X3, 53) + gpr_x4_arm64, // ARM64_X4, 54) + gpr_x5_arm64, // ARM64_X5, 55) + gpr_x6_arm64, // ARM64_X6, 56) + gpr_x7_arm64, // ARM64_X7, 57) + gpr_x8_arm64, // ARM64_X8, 58) + gpr_x9_arm64, // ARM64_X9, 59) + gpr_x10_arm64, // ARM64_X10, 60) + gpr_x11_arm64, // ARM64_X11, 61) + gpr_x12_arm64, // ARM64_X12, 62) + gpr_x13_arm64, // ARM64_X13, 63) + gpr_x14_arm64, // ARM64_X14, 64) + gpr_x15_arm64, // ARM64_X15, 65) + gpr_x16_arm64, // ARM64_X16, 66) + gpr_x17_arm64, // ARM64_X17, 67) + gpr_x18_arm64, // ARM64_X18, 68) + gpr_x19_arm64, // ARM64_X19, 69) + gpr_x20_arm64, // ARM64_X20, 70) + gpr_x21_arm64, // ARM64_X21, 71) + gpr_x22_arm64, // ARM64_X22, 72) + gpr_x23_arm64, // ARM64_X23, 73) + gpr_x24_arm64, // ARM64_X24, 74) + gpr_x25_arm64, // ARM64_X25, 75) + gpr_x26_arm64, // ARM64_X26, 76) + gpr_x27_arm64, // ARM64_X27, 77) + gpr_x28_arm64, // ARM64_X28, 78) + gpr_fp_arm64, // ARM64_FP, 79) + gpr_lr_arm64, // ARM64_LR, 80) + gpr_sp_arm64, // ARM64_SP, 81) + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_cpsr_arm64, // ARM64_NZCV, 90) + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s0_arm64, // (ARM64_S0, 100) + fpu_s1_arm64, // (ARM64_S1, 101) + fpu_s2_arm64, // (ARM64_S2, 102) + fpu_s3_arm64, // (ARM64_S3, 103) + fpu_s4_arm64, // (ARM64_S4, 104) + fpu_s5_arm64, // (ARM64_S5, 105) + fpu_s6_arm64, // (ARM64_S6, 106) + fpu_s7_arm64, // (ARM64_S7, 107) + fpu_s8_arm64, // (ARM64_S8, 108) + fpu_s9_arm64, // (ARM64_S9, 109) + fpu_s10_arm64, // (ARM64_S10, 110) + fpu_s11_arm64, // (ARM64_S11, 111) + fpu_s12_arm64, // (ARM64_S12, 112) + fpu_s13_arm64, // (ARM64_S13, 113) + fpu_s14_arm64, // (ARM64_S14, 114) + fpu_s15_arm64, // (ARM64_S15, 115) + fpu_s16_arm64, // (ARM64_S16, 116) + fpu_s17_arm64, // (ARM64_S17, 117) + fpu_s18_arm64, // (ARM64_S18, 118) + fpu_s19_arm64, // (ARM64_S19, 119) + fpu_s20_arm64, // (ARM64_S20, 120) + fpu_s21_arm64, // (ARM64_S21, 121) + fpu_s22_arm64, // (ARM64_S22, 122) + fpu_s23_arm64, // (ARM64_S23, 123) + fpu_s24_arm64, // (ARM64_S24, 124) + fpu_s25_arm64, // (ARM64_S25, 125) + fpu_s26_arm64, // (ARM64_S26, 126) + fpu_s27_arm64, // (ARM64_S27, 127) + fpu_s28_arm64, // (ARM64_S28, 128) + fpu_s29_arm64, // (ARM64_S29, 129) + fpu_s30_arm64, // (ARM64_S30, 130) + fpu_s31_arm64, // (ARM64_S31, 131) + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_d0_arm64, // (ARM64_D0, 140) + fpu_d1_arm64, // (ARM64_D1, 141) + fpu_d2_arm64, // (ARM64_D2, 142) + fpu_d3_arm64, // (ARM64_D3, 143) + fpu_d4_arm64, // (ARM64_D4, 144) + fpu_d5_arm64, // (ARM64_D5, 145) + fpu_d6_arm64, // (ARM64_D6, 146) + fpu_d7_arm64, // (ARM64_D7, 147) + fpu_d8_arm64, // (ARM64_D8, 148) + fpu_d9_arm64, // (ARM64_D9, 149) + fpu_d10_arm64, // (ARM64_D10, 150) + fpu_d11_arm64, // (ARM64_D11, 151) + fpu_d12_arm64, // (ARM64_D12, 152) + fpu_d13_arm64, // (ARM64_D13, 153) + fpu_d14_arm64, // (ARM64_D14, 154) + fpu_d15_arm64, // (ARM64_D15, 155) + fpu_d16_arm64, // (ARM64_D16, 156) + fpu_d17_arm64, // (ARM64_D17, 157) + fpu_d18_arm64, // (ARM64_D18, 158) + fpu_d19_arm64, // (ARM64_D19, 159) + fpu_d20_arm64, // (ARM64_D20, 160) + fpu_d21_arm64, // (ARM64_D21, 161) + fpu_d22_arm64, // (ARM64_D22, 162) + fpu_d23_arm64, // (ARM64_D23, 163) + fpu_d24_arm64, // (ARM64_D24, 164) + fpu_d25_arm64, // (ARM64_D25, 165) + fpu_d26_arm64, // (ARM64_D26, 166) + fpu_d27_arm64, // (ARM64_D27, 167) + fpu_d28_arm64, // (ARM64_D28, 168) + fpu_d29_arm64, // (ARM64_D29, 169) + fpu_d30_arm64, // (ARM64_D30, 170) + fpu_d31_arm64, // (ARM64_D31, 171) + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_v0_arm64, // (ARM64_Q0, 180) + fpu_v1_arm64, // (ARM64_Q1, 181) + fpu_v2_arm64, // (ARM64_Q2, 182) + fpu_v3_arm64, // (ARM64_Q3, 183) + fpu_v4_arm64, // (ARM64_Q4, 184) + fpu_v5_arm64, // (ARM64_Q5, 185) + fpu_v6_arm64, // (ARM64_Q6, 186) + fpu_v7_arm64, // (ARM64_Q7, 187) + fpu_v8_arm64, // (ARM64_Q8, 188) + fpu_v9_arm64, // (ARM64_Q9, 189) + fpu_v10_arm64, // (ARM64_Q10, 190) + fpu_v11_arm64, // (ARM64_Q11, 191) + fpu_v12_arm64, // (ARM64_Q12, 192) + fpu_v13_arm64, // (ARM64_Q13, 193) + fpu_v14_arm64, // (ARM64_Q14, 194) + fpu_v15_arm64, // (ARM64_Q15, 195) + fpu_v16_arm64, // (ARM64_Q16, 196) + fpu_v17_arm64, // (ARM64_Q17, 197) + fpu_v18_arm64, // (ARM64_Q18, 198) + fpu_v19_arm64, // (ARM64_Q19, 199) + fpu_v20_arm64, // (ARM64_Q20, 200) + fpu_v21_arm64, // (ARM64_Q21, 201) + fpu_v22_arm64, // (ARM64_Q22, 202) + fpu_v23_arm64, // (ARM64_Q23, 203) + fpu_v24_arm64, // (ARM64_Q24, 204) + fpu_v25_arm64, // (ARM64_Q25, 205) + fpu_v26_arm64, // (ARM64_Q26, 206) + fpu_v27_arm64, // (ARM64_Q27, 207) + fpu_v28_arm64, // (ARM64_Q28, 208) + fpu_v29_arm64, // (ARM64_Q29, 209) + fpu_v30_arm64, // (ARM64_Q30, 210) + fpu_v31_arm64, // (ARM64_Q31, 211) + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_fpsr_arm64 // ARM64_FPSR, 220) +}; + +static const uint32_t g_code_view_to_lldb_registers_x86[] = { + LLDB_INVALID_REGNUM, // NONE + lldb_al_i386, // AL + lldb_cl_i386, // CL + lldb_dl_i386, // DL + lldb_bl_i386, // BL + lldb_ah_i386, // AH + lldb_ch_i386, // CH + lldb_dh_i386, // DH + lldb_bh_i386, // BH + lldb_ax_i386, // AX + lldb_cx_i386, // CX + lldb_dx_i386, // DX + lldb_bx_i386, // BX + lldb_sp_i386, // SP + lldb_bp_i386, // BP + lldb_si_i386, // SI + lldb_di_i386, // DI + lldb_eax_i386, // EAX + lldb_ecx_i386, // ECX + lldb_edx_i386, // EDX + lldb_ebx_i386, // EBX + lldb_esp_i386, // ESP + lldb_ebp_i386, // EBP + lldb_esi_i386, // ESI + lldb_edi_i386, // EDI + lldb_es_i386, // ES + lldb_cs_i386, // CS + lldb_ss_i386, // SS + lldb_ds_i386, // DS + lldb_fs_i386, // FS + lldb_gs_i386, // GS + LLDB_INVALID_REGNUM, // IP + LLDB_INVALID_REGNUM, // FLAGS + lldb_eip_i386, // EIP + lldb_eflags_i386, // EFLAGS + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, // TEMP + LLDB_INVALID_REGNUM, // TEMPH + LLDB_INVALID_REGNUM, // QUOTE + LLDB_INVALID_REGNUM, // PCDR3 + LLDB_INVALID_REGNUM, // PCDR4 + LLDB_INVALID_REGNUM, // PCDR5 + LLDB_INVALID_REGNUM, // PCDR6 + LLDB_INVALID_REGNUM, // PCDR7 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, // CR0 + LLDB_INVALID_REGNUM, // CR1 + LLDB_INVALID_REGNUM, // CR2 + LLDB_INVALID_REGNUM, // CR3 + LLDB_INVALID_REGNUM, // CR4 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + lldb_dr0_i386, // DR0 + lldb_dr1_i386, // DR1 + lldb_dr2_i386, // DR2 + lldb_dr3_i386, // DR3 + lldb_dr4_i386, // DR4 + lldb_dr5_i386, // DR5 + lldb_dr6_i386, // DR6 + lldb_dr7_i386, // DR7 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, // GDTR + LLDB_INVALID_REGNUM, // GDTL + LLDB_INVALID_REGNUM, // IDTR + LLDB_INVALID_REGNUM, // IDTL + LLDB_INVALID_REGNUM, // LDTR + LLDB_INVALID_REGNUM, // TR + LLDB_INVALID_REGNUM, // PSEUDO1 + LLDB_INVALID_REGNUM, // PSEUDO2 + LLDB_INVALID_REGNUM, // PSEUDO3 + LLDB_INVALID_REGNUM, // PSEUDO4 + LLDB_INVALID_REGNUM, // PSEUDO5 + LLDB_INVALID_REGNUM, // PSEUDO6 + LLDB_INVALID_REGNUM, // PSEUDO7 + LLDB_INVALID_REGNUM, // PSEUDO8 + LLDB_INVALID_REGNUM, // PSEUDO9 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + lldb_st0_i386, // ST0 + lldb_st1_i386, // ST1 + lldb_st2_i386, // ST2 + lldb_st3_i386, // ST3 + lldb_st4_i386, // ST4 + lldb_st5_i386, // ST5 + lldb_st6_i386, // ST6 + lldb_st7_i386, // ST7 + LLDB_INVALID_REGNUM, // CTRL + LLDB_INVALID_REGNUM, // STAT + LLDB_INVALID_REGNUM, // TAG + LLDB_INVALID_REGNUM, // FPIP + LLDB_INVALID_REGNUM, // FPCS + LLDB_INVALID_REGNUM, // FPDO + LLDB_INVALID_REGNUM, // FPDS + LLDB_INVALID_REGNUM, // ISEM + LLDB_INVALID_REGNUM, // FPEIP + LLDB_INVALID_REGNUM, // FPEDO + lldb_mm0_i386, // MM0 + lldb_mm1_i386, // MM1 + lldb_mm2_i386, // MM2 + lldb_mm3_i386, // MM3 + lldb_mm4_i386, // MM4 + lldb_mm5_i386, // MM5 + lldb_mm6_i386, // MM6 + lldb_mm7_i386, // MM7 + lldb_xmm0_i386, // XMM0 + lldb_xmm1_i386, // XMM1 + lldb_xmm2_i386, // XMM2 + lldb_xmm3_i386, // XMM3 + lldb_xmm4_i386, // XMM4 + lldb_xmm5_i386, // XMM5 + lldb_xmm6_i386, // XMM6 + lldb_xmm7_i386 // XMM7 +}; + +static const uint32_t g_code_view_to_lldb_registers_x86_64[] = { + LLDB_INVALID_REGNUM, // NONE + lldb_al_x86_64, // AL + lldb_cl_x86_64, // CL + lldb_dl_x86_64, // DL + lldb_bl_x86_64, // BL + lldb_ah_x86_64, // AH + lldb_ch_x86_64, // CH + lldb_dh_x86_64, // DH + lldb_bh_x86_64, // BH + lldb_ax_x86_64, // AX + lldb_cx_x86_64, // CX + lldb_dx_x86_64, // DX + lldb_bx_x86_64, // BX + lldb_sp_x86_64, // SP + lldb_bp_x86_64, // BP + lldb_si_x86_64, // SI + lldb_di_x86_64, // DI + lldb_eax_x86_64, // EAX + lldb_ecx_x86_64, // ECX + lldb_edx_x86_64, // EDX + lldb_ebx_x86_64, // EBX + lldb_esp_x86_64, // ESP + lldb_ebp_x86_64, // EBP + lldb_esi_x86_64, // ESI + lldb_edi_x86_64, // EDI + lldb_es_x86_64, // ES + lldb_cs_x86_64, // CS + lldb_ss_x86_64, // SS + lldb_ds_x86_64, // DS + lldb_fs_x86_64, // FS + lldb_gs_x86_64, // GS + LLDB_INVALID_REGNUM, // IP + LLDB_INVALID_REGNUM, // FLAGS + LLDB_INVALID_REGNUM, // EIP + LLDB_INVALID_REGNUM, // EFLAGS + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, // TEMP + LLDB_INVALID_REGNUM, // TEMPH + LLDB_INVALID_REGNUM, // QUOTE + LLDB_INVALID_REGNUM, // PCDR3 + LLDB_INVALID_REGNUM, // PCDR4 + LLDB_INVALID_REGNUM, // PCDR5 + LLDB_INVALID_REGNUM, // PCDR6 + LLDB_INVALID_REGNUM, // PCDR7 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, // CR0 + LLDB_INVALID_REGNUM, // CR1 + LLDB_INVALID_REGNUM, // CR2 + LLDB_INVALID_REGNUM, // CR3 + LLDB_INVALID_REGNUM, // CR4 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + lldb_dr0_x86_64, // DR0 + lldb_dr1_x86_64, // DR1 + lldb_dr2_x86_64, // DR2 + lldb_dr3_x86_64, // DR3 + lldb_dr4_x86_64, // DR4 + lldb_dr5_x86_64, // DR5 + lldb_dr6_x86_64, // DR6 + lldb_dr7_x86_64, // DR7 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, // GDTR + LLDB_INVALID_REGNUM, // GDTL + LLDB_INVALID_REGNUM, // IDTR + LLDB_INVALID_REGNUM, // IDTL + LLDB_INVALID_REGNUM, // LDTR + LLDB_INVALID_REGNUM, // TR + LLDB_INVALID_REGNUM, // PSEUDO1 + LLDB_INVALID_REGNUM, // PSEUDO2 + LLDB_INVALID_REGNUM, // PSEUDO3 + LLDB_INVALID_REGNUM, // PSEUDO4 + LLDB_INVALID_REGNUM, // PSEUDO5 + LLDB_INVALID_REGNUM, // PSEUDO6 + LLDB_INVALID_REGNUM, // PSEUDO7 + LLDB_INVALID_REGNUM, // PSEUDO8 + LLDB_INVALID_REGNUM, // PSEUDO9 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + lldb_st0_x86_64, // ST0 + lldb_st1_x86_64, // ST1 + lldb_st2_x86_64, // ST2 + lldb_st3_x86_64, // ST3 + lldb_st4_x86_64, // ST4 + lldb_st5_x86_64, // ST5 + lldb_st6_x86_64, // ST6 + lldb_st7_x86_64, // ST7 + LLDB_INVALID_REGNUM, // CTRL + LLDB_INVALID_REGNUM, // STAT + LLDB_INVALID_REGNUM, // TAG + LLDB_INVALID_REGNUM, // FPIP + LLDB_INVALID_REGNUM, // FPCS + LLDB_INVALID_REGNUM, // FPDO + LLDB_INVALID_REGNUM, // FPDS + LLDB_INVALID_REGNUM, // ISEM + LLDB_INVALID_REGNUM, // FPEIP + LLDB_INVALID_REGNUM, // FPEDO + lldb_mm0_x86_64, // MM0 + lldb_mm1_x86_64, // MM1 + lldb_mm2_x86_64, // MM2 + lldb_mm3_x86_64, // MM3 + lldb_mm4_x86_64, // MM4 + lldb_mm5_x86_64, // MM5 + lldb_mm6_x86_64, // MM6 + lldb_mm7_x86_64, // MM7 + lldb_xmm0_x86_64, // XMM0 + lldb_xmm1_x86_64, // XMM1 + lldb_xmm2_x86_64, // XMM2 + lldb_xmm3_x86_64, // XMM3 + lldb_xmm4_x86_64, // XMM4 + lldb_xmm5_x86_64, // XMM5 + lldb_xmm6_x86_64, // XMM6 + lldb_xmm7_x86_64, // XMM7 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, + lldb_mxcsr_x86_64, // MXCSR + LLDB_INVALID_REGNUM, // EDXEAX + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, // EMM0L + LLDB_INVALID_REGNUM, // EMM1L + LLDB_INVALID_REGNUM, // EMM2L + LLDB_INVALID_REGNUM, // EMM3L + LLDB_INVALID_REGNUM, // EMM4L + LLDB_INVALID_REGNUM, // EMM5L + LLDB_INVALID_REGNUM, // EMM6L + LLDB_INVALID_REGNUM, // EMM7L + LLDB_INVALID_REGNUM, // EMM0H + LLDB_INVALID_REGNUM, // EMM1H + LLDB_INVALID_REGNUM, // EMM2H + LLDB_INVALID_REGNUM, // EMM3H + LLDB_INVALID_REGNUM, // EMM4H + LLDB_INVALID_REGNUM, // EMM5H + LLDB_INVALID_REGNUM, // EMM6H + LLDB_INVALID_REGNUM, // EMM7H + LLDB_INVALID_REGNUM, // MM00 + LLDB_INVALID_REGNUM, // MM01 + LLDB_INVALID_REGNUM, // MM10 + LLDB_INVALID_REGNUM, // MM11 + LLDB_INVALID_REGNUM, // MM20 + LLDB_INVALID_REGNUM, // MM21 + LLDB_INVALID_REGNUM, // MM30 + LLDB_INVALID_REGNUM, // MM31 + LLDB_INVALID_REGNUM, // MM40 + LLDB_INVALID_REGNUM, // MM41 + LLDB_INVALID_REGNUM, // MM50 + LLDB_INVALID_REGNUM, // MM51 + LLDB_INVALID_REGNUM, // MM60 + LLDB_INVALID_REGNUM, // MM61 + LLDB_INVALID_REGNUM, // MM70 + LLDB_INVALID_REGNUM, // MM71 + lldb_xmm8_x86_64, // XMM8 + lldb_xmm9_x86_64, // XMM9 + lldb_xmm10_x86_64, // XMM10 + lldb_xmm11_x86_64, // XMM11 + lldb_xmm12_x86_64, // XMM12 + lldb_xmm13_x86_64, // XMM13 + lldb_xmm14_x86_64, // XMM14 + lldb_xmm15_x86_64, // XMM15 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, + lldb_sil_x86_64, // SIL + lldb_dil_x86_64, // DIL + lldb_bpl_x86_64, // BPL + lldb_spl_x86_64, // SPL + lldb_rax_x86_64, // RAX + lldb_rbx_x86_64, // RBX + lldb_rcx_x86_64, // RCX + lldb_rdx_x86_64, // RDX + lldb_rsi_x86_64, // RSI + lldb_rdi_x86_64, // RDI + lldb_rbp_x86_64, // RBP + lldb_rsp_x86_64, // RSP + lldb_r8_x86_64, // R8 + lldb_r9_x86_64, // R9 + lldb_r10_x86_64, // R10 + lldb_r11_x86_64, // R11 + lldb_r12_x86_64, // R12 + lldb_r13_x86_64, // R13 + lldb_r14_x86_64, // R14 + lldb_r15_x86_64, // R15 + lldb_r8l_x86_64, // R8B + lldb_r9l_x86_64, // R9B + lldb_r10l_x86_64, // R10B + lldb_r11l_x86_64, // R11B + lldb_r12l_x86_64, // R12B + lldb_r13l_x86_64, // R13B + lldb_r14l_x86_64, // R14B + lldb_r15l_x86_64, // R15B + lldb_r8w_x86_64, // R8W + lldb_r9w_x86_64, // R9W + lldb_r10w_x86_64, // R10W + lldb_r11w_x86_64, // R11W + lldb_r12w_x86_64, // R12W + lldb_r13w_x86_64, // R13W + lldb_r14w_x86_64, // R14W + lldb_r15w_x86_64, // R15W + lldb_r8d_x86_64, // R8D + lldb_r9d_x86_64, // R9D + lldb_r10d_x86_64, // R10D + lldb_r11d_x86_64, // R11D + lldb_r12d_x86_64, // R12D + lldb_r13d_x86_64, // R13D + lldb_r14d_x86_64, // R14D + lldb_r15d_x86_64, // R15D + lldb_ymm0_x86_64, // AMD64_YMM0 + lldb_ymm1_x86_64, // AMD64_YMM1 + lldb_ymm2_x86_64, // AMD64_YMM2 + lldb_ymm3_x86_64, // AMD64_YMM3 + lldb_ymm4_x86_64, // AMD64_YMM4 + lldb_ymm5_x86_64, // AMD64_YMM5 + lldb_ymm6_x86_64, // AMD64_YMM6 + lldb_ymm7_x86_64, // AMD64_YMM7 + lldb_ymm8_x86_64, // AMD64_YMM8 + lldb_ymm9_x86_64, // AMD64_YMM9 + lldb_ymm10_x86_64, // AMD64_YMM10 + lldb_ymm11_x86_64, // AMD64_YMM11 + lldb_ymm12_x86_64, // AMD64_YMM12 + lldb_ymm13_x86_64, // AMD64_YMM13 + lldb_ymm14_x86_64, // AMD64_YMM14 + lldb_ymm15_x86_64, // AMD64_YMM15 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + lldb_bnd0_x86_64, // BND0 + lldb_bnd1_x86_64, // BND1 + lldb_bnd2_x86_64 // BND2 +}; + +uint32_t lldb_private::npdb::GetLLDBRegisterNumber( + llvm::Triple::ArchType arch_type, llvm::codeview::RegisterId register_id) { + switch (arch_type) { + case llvm::Triple::aarch64: + if (static_cast<uint16_t>(register_id) < + sizeof(g_code_view_to_lldb_registers_arm64) / + sizeof(g_code_view_to_lldb_registers_arm64[0])) + return g_code_view_to_lldb_registers_arm64[static_cast<uint16_t>( + register_id)]; + + return LLDB_INVALID_REGNUM; + case llvm::Triple::x86: + if (static_cast<uint16_t>(register_id) < + sizeof(g_code_view_to_lldb_registers_x86) / + sizeof(g_code_view_to_lldb_registers_x86[0])) + return g_code_view_to_lldb_registers_x86[static_cast<uint16_t>( + register_id)]; + + switch (register_id) { + case llvm::codeview::RegisterId::MXCSR: + return lldb_mxcsr_i386; + case llvm::codeview::RegisterId::BND0: + return lldb_bnd0_i386; + case llvm::codeview::RegisterId::BND1: + return lldb_bnd1_i386; + case llvm::codeview::RegisterId::BND2: + return lldb_bnd2_i386; + default: + return LLDB_INVALID_REGNUM; + } + case llvm::Triple::x86_64: + if (static_cast<uint16_t>(register_id) < + sizeof(g_code_view_to_lldb_registers_x86_64) / + sizeof(g_code_view_to_lldb_registers_x86_64[0])) + return g_code_view_to_lldb_registers_x86_64[static_cast<uint16_t>( + register_id)]; + + return LLDB_INVALID_REGNUM; + default: + return LLDB_INVALID_REGNUM; + } +} + +uint32_t +lldb_private::npdb::GetRegisterSize(llvm::codeview::RegisterId register_id) { + switch(register_id) { + case llvm::codeview::RegisterId::AL: + case llvm::codeview::RegisterId::BL: + case llvm::codeview::RegisterId::CL: + case llvm::codeview::RegisterId::DL: + case llvm::codeview::RegisterId::AH: + case llvm::codeview::RegisterId::BH: + case llvm::codeview::RegisterId::CH: + case llvm::codeview::RegisterId::DH: + case llvm::codeview::RegisterId::SIL: + case llvm::codeview::RegisterId::DIL: + case llvm::codeview::RegisterId::BPL: + case llvm::codeview::RegisterId::SPL: + case llvm::codeview::RegisterId::R8B: + case llvm::codeview::RegisterId::R9B: + case llvm::codeview::RegisterId::R10B: + case llvm::codeview::RegisterId::R11B: + case llvm::codeview::RegisterId::R12B: + case llvm::codeview::RegisterId::R13B: + case llvm::codeview::RegisterId::R14B: + case llvm::codeview::RegisterId::R15B: + return 1; + case llvm::codeview::RegisterId::AX: + case llvm::codeview::RegisterId::BX: + case llvm::codeview::RegisterId::CX: + case llvm::codeview::RegisterId::DX: + case llvm::codeview::RegisterId::SP: + case llvm::codeview::RegisterId::BP: + case llvm::codeview::RegisterId::SI: + case llvm::codeview::RegisterId::DI: + case llvm::codeview::RegisterId::R8W: + case llvm::codeview::RegisterId::R9W: + case llvm::codeview::RegisterId::R10W: + case llvm::codeview::RegisterId::R11W: + case llvm::codeview::RegisterId::R12W: + case llvm::codeview::RegisterId::R13W: + case llvm::codeview::RegisterId::R14W: + case llvm::codeview::RegisterId::R15W: + return 2; + case llvm::codeview::RegisterId::EAX: + case llvm::codeview::RegisterId::EBX: + case llvm::codeview::RegisterId::ECX: + case llvm::codeview::RegisterId::EDX: + case llvm::codeview::RegisterId::ESP: + case llvm::codeview::RegisterId::EBP: + case llvm::codeview::RegisterId::ESI: + case llvm::codeview::RegisterId::EDI: + case llvm::codeview::RegisterId::R8D: + case llvm::codeview::RegisterId::R9D: + case llvm::codeview::RegisterId::R10D: + case llvm::codeview::RegisterId::R11D: + case llvm::codeview::RegisterId::R12D: + case llvm::codeview::RegisterId::R13D: + case llvm::codeview::RegisterId::R14D: + case llvm::codeview::RegisterId::R15D: + return 4; + case llvm::codeview::RegisterId::RAX: + case llvm::codeview::RegisterId::RBX: + case llvm::codeview::RegisterId::RCX: + case llvm::codeview::RegisterId::RDX: + case llvm::codeview::RegisterId::RSI: + case llvm::codeview::RegisterId::RDI: + case llvm::codeview::RegisterId::RBP: + case llvm::codeview::RegisterId::RSP: + case llvm::codeview::RegisterId::R8: + case llvm::codeview::RegisterId::R9: + case llvm::codeview::RegisterId::R10: + case llvm::codeview::RegisterId::R11: + case llvm::codeview::RegisterId::R12: + case llvm::codeview::RegisterId::R13: + case llvm::codeview::RegisterId::R14: + case llvm::codeview::RegisterId::R15: + return 8; + case llvm::codeview::RegisterId::XMM0: + case llvm::codeview::RegisterId::XMM1: + case llvm::codeview::RegisterId::XMM2: + case llvm::codeview::RegisterId::XMM3: + case llvm::codeview::RegisterId::XMM4: + case llvm::codeview::RegisterId::XMM5: + case llvm::codeview::RegisterId::XMM6: + case llvm::codeview::RegisterId::XMM7: + case llvm::codeview::RegisterId::XMM8: + case llvm::codeview::RegisterId::XMM9: + case llvm::codeview::RegisterId::XMM10: + case llvm::codeview::RegisterId::XMM11: + case llvm::codeview::RegisterId::XMM12: + case llvm::codeview::RegisterId::XMM13: + case llvm::codeview::RegisterId::XMM14: + case llvm::codeview::RegisterId::XMM15: + return 16; + default: + return 0; + } +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h new file mode 100644 index 000000000000..4717fdcf11e3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h @@ -0,0 +1,25 @@ +//===-- CodeViewRegisterMapping.h -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_CODEVIEWREGISTERMAPPING_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_CODEVIEWREGISTERMAPPING_H + +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/TargetParser/Triple.h" + +namespace lldb_private { +namespace npdb { + +uint32_t GetLLDBRegisterNumber(llvm::Triple::ArchType arch_type, + llvm::codeview::RegisterId register_id); +uint32_t GetRegisterSize(llvm::codeview::RegisterId register_id); + +} // namespace npdb +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp new file mode 100644 index 000000000000..25d04f999ad6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp @@ -0,0 +1,248 @@ +//===-- CompileUnitIndex.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "CompileUnitIndex.h" + +#include "PdbIndex.h" +#include "PdbUtil.h" + +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/Support/Path.h" + +#include "lldb/Utility/LLDBAssert.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::npdb; +using namespace llvm::codeview; +using namespace llvm::pdb; + +static bool IsMainFile(llvm::StringRef main, llvm::StringRef other) { + if (main == other) + return true; + + // If the files refer to the local file system, we can just ask the file + // system if they're equivalent. But if the source isn't present on disk + // then we still want to try. + if (llvm::sys::fs::equivalent(main, other)) + return true; + + llvm::SmallString<64> normalized(other); + llvm::sys::path::native(normalized); + return main.equals_insensitive(normalized); +} + +static void ParseCompile3(const CVSymbol &sym, CompilandIndexItem &cci) { + cci.m_compile_opts.emplace(); + llvm::cantFail( + SymbolDeserializer::deserializeAs<Compile3Sym>(sym, *cci.m_compile_opts)); +} + +static void ParseObjname(const CVSymbol &sym, CompilandIndexItem &cci) { + cci.m_obj_name.emplace(); + llvm::cantFail( + SymbolDeserializer::deserializeAs<ObjNameSym>(sym, *cci.m_obj_name)); +} + +static void ParseBuildInfo(PdbIndex &index, const CVSymbol &sym, + CompilandIndexItem &cci) { + BuildInfoSym bis(SymbolRecordKind::BuildInfoSym); + llvm::cantFail(SymbolDeserializer::deserializeAs<BuildInfoSym>(sym, bis)); + + // S_BUILDINFO just points to an LF_BUILDINFO in the IPI stream. Let's do + // a little extra work to pull out the LF_BUILDINFO. + LazyRandomTypeCollection &types = index.ipi().typeCollection(); + std::optional<CVType> cvt = types.tryGetType(bis.BuildId); + + if (!cvt || cvt->kind() != LF_BUILDINFO) + return; + + BuildInfoRecord bir; + llvm::cantFail(TypeDeserializer::deserializeAs<BuildInfoRecord>(*cvt, bir)); + cci.m_build_info.assign(bir.ArgIndices.begin(), bir.ArgIndices.end()); +} + +static void ParseExtendedInfo(PdbIndex &index, CompilandIndexItem &item) { + const CVSymbolArray &syms = item.m_debug_stream.getSymbolArray(); + + // This is a private function, it shouldn't be called if the information + // has already been parsed. + lldbassert(!item.m_obj_name); + lldbassert(!item.m_compile_opts); + lldbassert(item.m_build_info.empty()); + + // We're looking for 3 things. S_COMPILE3, S_OBJNAME, and S_BUILDINFO. + int found = 0; + for (const CVSymbol &sym : syms) { + switch (sym.kind()) { + case S_COMPILE3: + ParseCompile3(sym, item); + break; + case S_OBJNAME: + ParseObjname(sym, item); + break; + case S_BUILDINFO: + ParseBuildInfo(index, sym, item); + break; + default: + continue; + } + if (++found >= 3) + break; + } +} + +static void ParseInlineeLineTableForCompileUnit(CompilandIndexItem &item) { + for (const auto &ss : item.m_debug_stream.getSubsectionsArray()) { + if (ss.kind() != DebugSubsectionKind::InlineeLines) + continue; + + DebugInlineeLinesSubsectionRef inlinee_lines; + llvm::BinaryStreamReader reader(ss.getRecordData()); + if (llvm::Error error = inlinee_lines.initialize(reader)) { + consumeError(std::move(error)); + continue; + } + + for (const InlineeSourceLine &Line : inlinee_lines) { + item.m_inline_map[Line.Header->Inlinee] = Line; + } + } +} + +CompilandIndexItem::CompilandIndexItem( + PdbCompilandId id, llvm::pdb::ModuleDebugStreamRef debug_stream, + llvm::pdb::DbiModuleDescriptor descriptor) + : m_id(id), m_debug_stream(std::move(debug_stream)), + m_module_descriptor(std::move(descriptor)) {} + +CompilandIndexItem &CompileUnitIndex::GetOrCreateCompiland(uint16_t modi) { + auto result = m_comp_units.try_emplace(modi, nullptr); + if (!result.second) + return *result.first->second; + + // Find the module list and load its debug information stream and cache it + // since we need to use it for almost all interesting operations. + const DbiModuleList &modules = m_index.dbi().modules(); + llvm::pdb::DbiModuleDescriptor descriptor = modules.getModuleDescriptor(modi); + uint16_t stream = descriptor.getModuleStreamIndex(); + std::unique_ptr<llvm::msf::MappedBlockStream> stream_data = + m_index.pdb().createIndexedStream(stream); + + + std::unique_ptr<CompilandIndexItem>& cci = result.first->second; + + if (!stream_data) { + llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor, nullptr); + cci = std::make_unique<CompilandIndexItem>(PdbCompilandId{ modi }, debug_stream, std::move(descriptor)); + return *cci; + } + + llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor, + std::move(stream_data)); + + cantFail(debug_stream.reload()); + + cci = std::make_unique<CompilandIndexItem>( + PdbCompilandId{modi}, std::move(debug_stream), std::move(descriptor)); + ParseExtendedInfo(m_index, *cci); + ParseInlineeLineTableForCompileUnit(*cci); + + auto strings = m_index.pdb().getStringTable(); + if (strings) { + cci->m_strings.initialize(cci->m_debug_stream.getSubsectionsArray()); + cci->m_strings.setStrings(strings->getStringTable()); + } else { + consumeError(strings.takeError()); + } + + // We want the main source file to always comes first. Note that we can't + // just push_back the main file onto the front because `GetMainSourceFile` + // computes it in such a way that it doesn't own the resulting memory. So we + // have to iterate the module file list comparing each one to the main file + // name until we find it, and we can cache that one since the memory is backed + // by a contiguous chunk inside the mapped PDB. + llvm::SmallString<64> main_file = GetMainSourceFile(*cci); + std::string s = std::string(main_file.str()); + llvm::sys::path::native(main_file); + + uint32_t file_count = modules.getSourceFileCount(modi); + cci->m_file_list.reserve(file_count); + bool found_main_file = false; + for (llvm::StringRef file : modules.source_files(modi)) { + if (!found_main_file && IsMainFile(main_file, file)) { + cci->m_file_list.insert(cci->m_file_list.begin(), file); + found_main_file = true; + continue; + } + cci->m_file_list.push_back(file); + } + + return *cci; +} + +const CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) const { + auto iter = m_comp_units.find(modi); + if (iter == m_comp_units.end()) + return nullptr; + return iter->second.get(); +} + +CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) { + auto iter = m_comp_units.find(modi); + if (iter == m_comp_units.end()) + return nullptr; + return iter->second.get(); +} + +llvm::SmallString<64> +CompileUnitIndex::GetMainSourceFile(const CompilandIndexItem &item) const { + // LF_BUILDINFO contains a list of arg indices which point to LF_STRING_ID + // records in the IPI stream. The order of the arg indices is as follows: + // [0] - working directory where compiler was invoked. + // [1] - absolute path to compiler binary + // [2] - source file name + // [3] - path to compiler generated PDB (the /Zi PDB, although this entry gets + // added even when using /Z7) + // [4] - full command line invocation. + // + // We need to form the path [0]\[2] to generate the full path to the main + // file.source + if (item.m_build_info.size() < 3) + return {""}; + + LazyRandomTypeCollection &types = m_index.ipi().typeCollection(); + + StringIdRecord working_dir; + StringIdRecord file_name; + CVType dir_cvt = types.getType(item.m_build_info[0]); + CVType file_cvt = types.getType(item.m_build_info[2]); + llvm::cantFail( + TypeDeserializer::deserializeAs<StringIdRecord>(dir_cvt, working_dir)); + llvm::cantFail( + TypeDeserializer::deserializeAs<StringIdRecord>(file_cvt, file_name)); + + llvm::sys::path::Style style = working_dir.String.starts_with("/") + ? llvm::sys::path::Style::posix + : llvm::sys::path::Style::windows; + if (llvm::sys::path::is_absolute(file_name.String, style)) + return file_name.String; + + llvm::SmallString<64> absolute_path = working_dir.String; + llvm::sys::path::append(absolute_path, file_name.String); + return absolute_path; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h new file mode 100644 index 000000000000..3e6766a204dd --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h @@ -0,0 +1,108 @@ +//===-- CompileUnitIndex.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_COMPILEUNITINDEX_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_COMPILEUNITINDEX_H + +#include "lldb/Utility/RangeMap.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/IntervalMap.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +#include "PdbSymUid.h" + +#include <map> +#include <memory> +#include <optional> + +namespace lldb_private { + +namespace npdb { +class PdbIndex; + +/// Represents a single compile unit. This class is useful for collecting the +/// important accessors and information about a compile unit from disparate +/// parts of the PDB into a single place, simplifying acess to compile unit +/// information for the callers. +struct CompilandIndexItem { + CompilandIndexItem(PdbCompilandId m_id, + llvm::pdb::ModuleDebugStreamRef debug_stream, + llvm::pdb::DbiModuleDescriptor descriptor); + + // index of this compile unit. + PdbCompilandId m_id; + + // debug stream. + llvm::pdb::ModuleDebugStreamRef m_debug_stream; + + // dbi module descriptor. + llvm::pdb::DbiModuleDescriptor m_module_descriptor; + + llvm::codeview::StringsAndChecksumsRef m_strings; + + // List of files which contribute to this compiland. + std::vector<llvm::StringRef> m_file_list; + + // Maps virtual address to global symbol id, which can then be used to + // locate the exact compile unit and offset of the symbol. Note that this + // is intentionally an ordered map so that we can find all symbols up to a + // given starting address. + std::map<lldb::addr_t, PdbSymUid> m_symbols_by_va; + + // S_COMPILE3 sym describing compilation settings for the module. + std::optional<llvm::codeview::Compile3Sym> m_compile_opts; + + // S_OBJNAME sym describing object name. + std::optional<llvm::codeview::ObjNameSym> m_obj_name; + + // LF_BUILDINFO sym describing source file name, working directory, + // command line, etc. This usually contains exactly 5 items which + // are references to other strings. + llvm::SmallVector<llvm::codeview::TypeIndex, 5> m_build_info; + + // Inlinee lines table in this compile unit. + std::map<llvm::codeview::TypeIndex, llvm::codeview::InlineeSourceLine> + m_inline_map; + + // It's the line table parsed from DEBUG_S_LINES sections, mapping the file + // address range to file index and source line number. + using GlobalLineTable = + lldb_private::RangeDataVector<lldb::addr_t, uint32_t, + std::pair<uint32_t, uint32_t>>; + GlobalLineTable m_global_line_table; +}; + +/// Indexes information about all compile units. This is really just a map of +/// global compile unit index to |CompilandIndexItem| structures. +class CompileUnitIndex { + PdbIndex &m_index; + llvm::DenseMap<uint16_t, std::unique_ptr<CompilandIndexItem>> m_comp_units; + +public: + explicit CompileUnitIndex(PdbIndex &index) : m_index(index) {} + + CompilandIndexItem &GetOrCreateCompiland(uint16_t modi); + + const CompilandIndexItem *GetCompiland(uint16_t modi) const; + + CompilandIndexItem *GetCompiland(uint16_t modi); + + llvm::SmallString<64> GetMainSourceFile(const CompilandIndexItem &item) const; +}; +} // namespace npdb +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp new file mode 100644 index 000000000000..75adf7302f00 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp @@ -0,0 +1,300 @@ +//===-- DWARFLocationExpression.cpp ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DWARFLocationExpression.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/StreamBuffer.h" + +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/Support/Endian.h" + +#include "PdbUtil.h" +#include "CodeViewRegisterMapping.h" +#include "PdbFPOProgramToDWARFExpression.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::npdb; +using namespace llvm::codeview; +using namespace llvm::pdb; + +uint32_t GetGenericRegisterNumber(llvm::codeview::RegisterId register_id) { + if (register_id == llvm::codeview::RegisterId::VFRAME) + return LLDB_REGNUM_GENERIC_FP; + + return LLDB_INVALID_REGNUM; +} + +static uint32_t GetRegisterNumber(llvm::Triple::ArchType arch_type, + llvm::codeview::RegisterId register_id, + RegisterKind ®ister_kind) { + register_kind = eRegisterKindLLDB; + uint32_t reg_num = GetLLDBRegisterNumber(arch_type, register_id); + if (reg_num != LLDB_INVALID_REGNUM) + return reg_num; + + register_kind = eRegisterKindGeneric; + return GetGenericRegisterNumber(register_id); +} + +static bool IsSimpleTypeSignedInteger(SimpleTypeKind kind) { + switch (kind) { + case SimpleTypeKind::Int128: + case SimpleTypeKind::Int64: + case SimpleTypeKind::Int64Quad: + case SimpleTypeKind::Int32: + case SimpleTypeKind::Int32Long: + case SimpleTypeKind::Int16: + case SimpleTypeKind::Int16Short: + case SimpleTypeKind::Float128: + case SimpleTypeKind::Float80: + case SimpleTypeKind::Float64: + case SimpleTypeKind::Float32: + case SimpleTypeKind::Float16: + case SimpleTypeKind::NarrowCharacter: + case SimpleTypeKind::SignedCharacter: + case SimpleTypeKind::SByte: + return true; + default: + return false; + } +} + +static std::pair<size_t, bool> GetIntegralTypeInfo(TypeIndex ti, + TpiStream &tpi) { + if (ti.isSimple()) { + SimpleTypeKind stk = ti.getSimpleKind(); + return {GetTypeSizeForSimpleKind(stk), IsSimpleTypeSignedInteger(stk)}; + } + + CVType cvt = tpi.getType(ti); + switch (cvt.kind()) { + case LF_MODIFIER: { + ModifierRecord mfr; + llvm::cantFail(TypeDeserializer::deserializeAs<ModifierRecord>(cvt, mfr)); + return GetIntegralTypeInfo(mfr.ModifiedType, tpi); + } + case LF_POINTER: { + PointerRecord pr; + llvm::cantFail(TypeDeserializer::deserializeAs<PointerRecord>(cvt, pr)); + return GetIntegralTypeInfo(pr.ReferentType, tpi); + } + case LF_ENUM: { + EnumRecord er; + llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er)); + return GetIntegralTypeInfo(er.UnderlyingType, tpi); + } + default: + assert(false && "Type is not integral!"); + return {0, false}; + } +} + +template <typename StreamWriter> +static DWARFExpression MakeLocationExpressionInternal(lldb::ModuleSP module, + StreamWriter &&writer) { + const ArchSpec &architecture = module->GetArchitecture(); + ByteOrder byte_order = architecture.GetByteOrder(); + uint32_t address_size = architecture.GetAddressByteSize(); + uint32_t byte_size = architecture.GetDataByteSize(); + if (byte_order == eByteOrderInvalid || address_size == 0) + return DWARFExpression(); + + RegisterKind register_kind = eRegisterKindDWARF; + StreamBuffer<32> stream(Stream::eBinary, address_size, byte_order); + + if (!writer(stream, register_kind)) + return DWARFExpression(); + + DataBufferSP buffer = + std::make_shared<DataBufferHeap>(stream.GetData(), stream.GetSize()); + DataExtractor extractor(buffer, byte_order, address_size, byte_size); + DWARFExpression result(extractor); + result.SetRegisterKind(register_kind); + + return result; +} + +static bool MakeRegisterBasedLocationExpressionInternal( + Stream &stream, llvm::codeview::RegisterId reg, RegisterKind ®ister_kind, + std::optional<int32_t> relative_offset, lldb::ModuleSP module) { + uint32_t reg_num = GetRegisterNumber(module->GetArchitecture().GetMachine(), + reg, register_kind); + if (reg_num == LLDB_INVALID_REGNUM) + return false; + + if (reg_num > 31) { + llvm::dwarf::LocationAtom base = + relative_offset ? llvm::dwarf::DW_OP_bregx : llvm::dwarf::DW_OP_regx; + stream.PutHex8(base); + stream.PutULEB128(reg_num); + } else { + llvm::dwarf::LocationAtom base = + relative_offset ? llvm::dwarf::DW_OP_breg0 : llvm::dwarf::DW_OP_reg0; + stream.PutHex8(base + reg_num); + } + + if (relative_offset) + stream.PutSLEB128(*relative_offset); + + return true; +} + +static DWARFExpression MakeRegisterBasedLocationExpressionInternal( + llvm::codeview::RegisterId reg, std::optional<int32_t> relative_offset, + lldb::ModuleSP module) { + return MakeLocationExpressionInternal( + module, [&](Stream &stream, RegisterKind ®ister_kind) -> bool { + return MakeRegisterBasedLocationExpressionInternal( + stream, reg, register_kind, relative_offset, module); + }); +} + +DWARFExpression lldb_private::npdb::MakeEnregisteredLocationExpression( + llvm::codeview::RegisterId reg, lldb::ModuleSP module) { + return MakeRegisterBasedLocationExpressionInternal(reg, std::nullopt, module); +} + +DWARFExpression lldb_private::npdb::MakeRegRelLocationExpression( + llvm::codeview::RegisterId reg, int32_t offset, lldb::ModuleSP module) { + return MakeRegisterBasedLocationExpressionInternal(reg, offset, module); +} + +static bool EmitVFrameEvaluationDWARFExpression( + llvm::StringRef program, llvm::Triple::ArchType arch_type, Stream &stream) { + // VFrame value always stored in $TO pseudo-register + return TranslateFPOProgramToDWARFExpression(program, "$T0", arch_type, + stream); +} + +DWARFExpression lldb_private::npdb::MakeVFrameRelLocationExpression( + llvm::StringRef fpo_program, int32_t offset, lldb::ModuleSP module) { + return MakeLocationExpressionInternal( + module, [&](Stream &stream, RegisterKind ®ister_kind) -> bool { + const ArchSpec &architecture = module->GetArchitecture(); + + if (!EmitVFrameEvaluationDWARFExpression(fpo_program, architecture.GetMachine(), + stream)) + return false; + + stream.PutHex8(llvm::dwarf::DW_OP_consts); + stream.PutSLEB128(offset); + stream.PutHex8(llvm::dwarf::DW_OP_plus); + + register_kind = eRegisterKindLLDB; + + return true; + }); +} + +DWARFExpression lldb_private::npdb::MakeGlobalLocationExpression( + uint16_t section, uint32_t offset, ModuleSP module) { + assert(section > 0); + assert(module); + + return MakeLocationExpressionInternal( + module, [&](Stream &stream, RegisterKind ®ister_kind) -> bool { + stream.PutHex8(llvm::dwarf::DW_OP_addr); + + SectionList *section_list = module->GetSectionList(); + assert(section_list); + + auto section_ptr = section_list->FindSectionByID(section); + if (!section_ptr) + return false; + + stream.PutMaxHex64(section_ptr->GetFileAddress() + offset, + stream.GetAddressByteSize(), stream.GetByteOrder()); + + return true; + }); +} + +DWARFExpression lldb_private::npdb::MakeConstantLocationExpression( + TypeIndex underlying_ti, TpiStream &tpi, const llvm::APSInt &constant, + ModuleSP module) { + const ArchSpec &architecture = module->GetArchitecture(); + uint32_t address_size = architecture.GetAddressByteSize(); + + size_t size = 0; + bool is_signed = false; + std::tie(size, is_signed) = GetIntegralTypeInfo(underlying_ti, tpi); + + union { + llvm::support::little64_t I; + llvm::support::ulittle64_t U; + } Value; + + std::shared_ptr<DataBufferHeap> buffer = std::make_shared<DataBufferHeap>(); + buffer->SetByteSize(size); + + llvm::ArrayRef<uint8_t> bytes; + if (is_signed) { + Value.I = constant.getSExtValue(); + } else { + Value.U = constant.getZExtValue(); + } + + bytes = llvm::ArrayRef(reinterpret_cast<const uint8_t *>(&Value), 8) + .take_front(size); + buffer->CopyData(bytes.data(), size); + DataExtractor extractor(buffer, lldb::eByteOrderLittle, address_size); + DWARFExpression result(extractor); + return result; +} + +DWARFExpression +lldb_private::npdb::MakeEnregisteredLocationExpressionForComposite( + const std::map<uint64_t, MemberValLocation> &offset_to_location, + std::map<uint64_t, size_t> &offset_to_size, size_t total_size, + lldb::ModuleSP module) { + return MakeLocationExpressionInternal( + module, [&](Stream &stream, RegisterKind ®ister_kind) -> bool { + size_t cur_offset = 0; + bool is_simple_type = offset_to_size.empty(); + // Iterate through offset_to_location because offset_to_size might be + // empty if the variable is a simple type. + for (const auto &offset_loc : offset_to_location) { + if (cur_offset < offset_loc.first) { + stream.PutHex8(llvm::dwarf::DW_OP_piece); + stream.PutULEB128(offset_loc.first - cur_offset); + cur_offset = offset_loc.first; + } + MemberValLocation loc = offset_loc.second; + std::optional<int32_t> offset = + loc.is_at_reg ? std::nullopt + : std::optional<int32_t>(loc.reg_offset); + if (!MakeRegisterBasedLocationExpressionInternal( + stream, (RegisterId)loc.reg_id, register_kind, offset, + module)) + return false; + if (!is_simple_type) { + stream.PutHex8(llvm::dwarf::DW_OP_piece); + stream.PutULEB128(offset_to_size[offset_loc.first]); + cur_offset = offset_loc.first + offset_to_size[offset_loc.first]; + } + } + // For simple type, it specifies the byte size of the value described by + // the previous dwarf expr. For udt, it's the remaining byte size at end + // of a struct. + if (total_size > cur_offset) { + stream.PutHex8(llvm::dwarf::DW_OP_piece); + stream.PutULEB128(total_size - cur_offset); + } + return true; + }); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h new file mode 100644 index 000000000000..2f12d8bf0dd7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h @@ -0,0 +1,57 @@ +//===-- DWARFLocationExpression.h -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_DWARFLOCATIONEXPRESSION_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_DWARFLOCATIONEXPRESSION_H + +#include "lldb/lldb-forward.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include <map> + +namespace llvm { +class APSInt; +class StringRef; +namespace codeview { +class TypeIndex; +} +namespace pdb { +class TpiStream; +} +} // namespace llvm +namespace lldb_private { +namespace npdb { +struct MemberValLocation { + uint16_t reg_id; + uint16_t reg_offset; + bool is_at_reg = true; +}; + +DWARFExpression +MakeEnregisteredLocationExpression(llvm::codeview::RegisterId reg, + lldb::ModuleSP module); + +DWARFExpression MakeRegRelLocationExpression(llvm::codeview::RegisterId reg, + int32_t offset, + lldb::ModuleSP module); +DWARFExpression MakeVFrameRelLocationExpression(llvm::StringRef fpo_program, + int32_t offset, + lldb::ModuleSP module); +DWARFExpression MakeGlobalLocationExpression(uint16_t section, uint32_t offset, + lldb::ModuleSP module); +DWARFExpression MakeConstantLocationExpression( + llvm::codeview::TypeIndex underlying_ti, llvm::pdb::TpiStream &tpi, + const llvm::APSInt &constant, lldb::ModuleSP module); +DWARFExpression MakeEnregisteredLocationExpressionForComposite( + const std::map<uint64_t, MemberValLocation> &offset_to_location, + std::map<uint64_t, size_t> &offset_to_size, size_t total_size, + lldb::ModuleSP module); +} // namespace npdb +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp new file mode 100644 index 000000000000..b79d3e63f72b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp @@ -0,0 +1,1454 @@ +#include "PdbAstBuilder.h" + +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/RecordName.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/Demangle/MicrosoftDemangle.h" + +#include "PdbUtil.h" +#include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h" +#include "Plugins/ExpressionParser/Clang/ClangUtil.h" +#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "SymbolFileNativePDB.h" +#include "UdtRecordCompleter.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/LLDBAssert.h" +#include <optional> +#include <string_view> + +using namespace lldb_private; +using namespace lldb_private::npdb; +using namespace llvm::codeview; +using namespace llvm::pdb; + +namespace { +struct CreateMethodDecl : public TypeVisitorCallbacks { + CreateMethodDecl(PdbIndex &m_index, TypeSystemClang &m_clang, + TypeIndex func_type_index, + clang::FunctionDecl *&function_decl, + lldb::opaque_compiler_type_t parent_ty, + llvm::StringRef proc_name, CompilerType func_ct) + : m_index(m_index), m_clang(m_clang), func_type_index(func_type_index), + function_decl(function_decl), parent_ty(parent_ty), + proc_name(proc_name), func_ct(func_ct) {} + PdbIndex &m_index; + TypeSystemClang &m_clang; + TypeIndex func_type_index; + clang::FunctionDecl *&function_decl; + lldb::opaque_compiler_type_t parent_ty; + llvm::StringRef proc_name; + CompilerType func_ct; + + llvm::Error visitKnownMember(CVMemberRecord &cvr, + OverloadedMethodRecord &overloaded) override { + TypeIndex method_list_idx = overloaded.MethodList; + + CVType method_list_type = m_index.tpi().getType(method_list_idx); + assert(method_list_type.kind() == LF_METHODLIST); + + MethodOverloadListRecord method_list; + llvm::cantFail(TypeDeserializer::deserializeAs<MethodOverloadListRecord>( + method_list_type, method_list)); + + for (const OneMethodRecord &method : method_list.Methods) { + if (method.getType().getIndex() == func_type_index.getIndex()) + AddMethod(overloaded.Name, method.getAccess(), method.getOptions(), + method.Attrs); + } + + return llvm::Error::success(); + } + + llvm::Error visitKnownMember(CVMemberRecord &cvr, + OneMethodRecord &record) override { + AddMethod(record.getName(), record.getAccess(), record.getOptions(), + record.Attrs); + return llvm::Error::success(); + } + + void AddMethod(llvm::StringRef name, MemberAccess access, + MethodOptions options, MemberAttributes attrs) { + if (name != proc_name || function_decl) + return; + lldb::AccessType access_type = TranslateMemberAccess(access); + bool is_virtual = attrs.isVirtual(); + bool is_static = attrs.isStatic(); + bool is_artificial = (options & MethodOptions::CompilerGenerated) == + MethodOptions::CompilerGenerated; + function_decl = m_clang.AddMethodToCXXRecordType( + parent_ty, proc_name, + /*mangled_name=*/nullptr, func_ct, /*access=*/access_type, + /*is_virtual=*/is_virtual, /*is_static=*/is_static, + /*is_inline=*/false, /*is_explicit=*/false, + /*is_attr_used=*/false, /*is_artificial=*/is_artificial); + } +}; +} // namespace + +static clang::TagTypeKind TranslateUdtKind(const TagRecord &cr) { + switch (cr.Kind) { + case TypeRecordKind::Class: + return clang::TagTypeKind::Class; + case TypeRecordKind::Struct: + return clang::TagTypeKind::Struct; + case TypeRecordKind::Union: + return clang::TagTypeKind::Union; + case TypeRecordKind::Interface: + return clang::TagTypeKind::Interface; + case TypeRecordKind::Enum: + return clang::TagTypeKind::Enum; + default: + lldbassert(false && "Invalid tag record kind!"); + return clang::TagTypeKind::Struct; + } +} + +static bool IsCVarArgsFunction(llvm::ArrayRef<TypeIndex> args) { + if (args.empty()) + return false; + return args.back() == TypeIndex::None(); +} + +static bool +AnyScopesHaveTemplateParams(llvm::ArrayRef<llvm::ms_demangle::Node *> scopes) { + for (llvm::ms_demangle::Node *n : scopes) { + auto *idn = static_cast<llvm::ms_demangle::IdentifierNode *>(n); + if (idn->TemplateParams) + return true; + } + return false; +} + +static std::optional<clang::CallingConv> +TranslateCallingConvention(llvm::codeview::CallingConvention conv) { + using CC = llvm::codeview::CallingConvention; + switch (conv) { + + case CC::NearC: + case CC::FarC: + return clang::CallingConv::CC_C; + case CC::NearPascal: + case CC::FarPascal: + return clang::CallingConv::CC_X86Pascal; + case CC::NearFast: + case CC::FarFast: + return clang::CallingConv::CC_X86FastCall; + case CC::NearStdCall: + case CC::FarStdCall: + return clang::CallingConv::CC_X86StdCall; + case CC::ThisCall: + return clang::CallingConv::CC_X86ThisCall; + case CC::NearVector: + return clang::CallingConv::CC_X86VectorCall; + default: + return std::nullopt; + } +} + +static bool IsAnonymousNamespaceName(llvm::StringRef name) { + return name == "`anonymous namespace'" || name == "`anonymous-namespace'"; +} + +PdbAstBuilder::PdbAstBuilder(TypeSystemClang &clang) : m_clang(clang) {} + +lldb_private::CompilerDeclContext PdbAstBuilder::GetTranslationUnitDecl() { + return ToCompilerDeclContext(*m_clang.GetTranslationUnitDecl()); +} + +std::pair<clang::DeclContext *, std::string> +PdbAstBuilder::CreateDeclInfoForType(const TagRecord &record, TypeIndex ti) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + // FIXME: Move this to GetDeclContextContainingUID. + if (!record.hasUniqueName()) + return CreateDeclInfoForUndecoratedName(record.Name); + + llvm::ms_demangle::Demangler demangler; + std::string_view sv(record.UniqueName.begin(), record.UniqueName.size()); + llvm::ms_demangle::TagTypeNode *ttn = demangler.parseTagUniqueName(sv); + if (demangler.Error) + return {m_clang.GetTranslationUnitDecl(), std::string(record.UniqueName)}; + + llvm::ms_demangle::IdentifierNode *idn = + ttn->QualifiedName->getUnqualifiedIdentifier(); + std::string uname = idn->toString(llvm::ms_demangle::OF_NoTagSpecifier); + + llvm::ms_demangle::NodeArrayNode *name_components = + ttn->QualifiedName->Components; + llvm::ArrayRef<llvm::ms_demangle::Node *> scopes(name_components->Nodes, + name_components->Count - 1); + + clang::DeclContext *context = m_clang.GetTranslationUnitDecl(); + + // If this type doesn't have a parent type in the debug info, then the best we + // can do is to say that it's either a series of namespaces (if the scope is + // non-empty), or the translation unit (if the scope is empty). + std::optional<TypeIndex> parent_index = pdb->GetParentType(ti); + if (!parent_index) { + if (scopes.empty()) + return {context, uname}; + + // If there is no parent in the debug info, but some of the scopes have + // template params, then this is a case of bad debug info. See, for + // example, llvm.org/pr39607. We don't want to create an ambiguity between + // a NamespaceDecl and a CXXRecordDecl, so instead we create a class at + // global scope with the fully qualified name. + if (AnyScopesHaveTemplateParams(scopes)) + return {context, std::string(record.Name)}; + + for (llvm::ms_demangle::Node *scope : scopes) { + auto *nii = static_cast<llvm::ms_demangle::NamedIdentifierNode *>(scope); + std::string str = nii->toString(); + context = GetOrCreateNamespaceDecl(str.c_str(), *context); + } + return {context, uname}; + } + + // Otherwise, all we need to do is get the parent type of this type and + // recurse into our lazy type creation / AST reconstruction logic to get an + // LLDB TypeSP for the parent. This will cause the AST to automatically get + // the right DeclContext created for any parent. + clang::QualType parent_qt = GetOrCreateType(*parent_index); + if (parent_qt.isNull()) + return {nullptr, ""}; + + context = clang::TagDecl::castToDeclContext(parent_qt->getAsTagDecl()); + return {context, uname}; +} + +static bool isLocalVariableType(SymbolKind K) { + switch (K) { + case S_REGISTER: + case S_REGREL32: + case S_LOCAL: + return true; + default: + break; + } + return false; +} + +clang::Decl *PdbAstBuilder::GetOrCreateSymbolForId(PdbCompilandSymId id) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVSymbol cvs = index.ReadSymbolRecord(id); + + if (isLocalVariableType(cvs.kind())) { + clang::DeclContext *scope = GetParentDeclContext(id); + if (!scope) + return nullptr; + clang::Decl *scope_decl = clang::Decl::castFromDeclContext(scope); + PdbCompilandSymId scope_id = + PdbSymUid(m_decl_to_status[scope_decl].uid).asCompilandSym(); + return GetOrCreateVariableDecl(scope_id, id); + } + + switch (cvs.kind()) { + case S_GPROC32: + case S_LPROC32: + return GetOrCreateFunctionDecl(id); + case S_GDATA32: + case S_LDATA32: + case S_GTHREAD32: + case S_CONSTANT: + // global variable + return nullptr; + case S_BLOCK32: + return GetOrCreateBlockDecl(id); + case S_INLINESITE: + return GetOrCreateInlinedFunctionDecl(id); + default: + return nullptr; + } +} + +std::optional<CompilerDecl> +PdbAstBuilder::GetOrCreateDeclForUid(PdbSymUid uid) { + if (clang::Decl *result = TryGetDecl(uid)) + return ToCompilerDecl(*result); + + clang::Decl *result = nullptr; + switch (uid.kind()) { + case PdbSymUidKind::CompilandSym: + result = GetOrCreateSymbolForId(uid.asCompilandSym()); + break; + case PdbSymUidKind::Type: { + clang::QualType qt = GetOrCreateType(uid.asTypeSym()); + if (qt.isNull()) + return std::nullopt; + if (auto *tag = qt->getAsTagDecl()) { + result = tag; + break; + } + return std::nullopt; + } + default: + return std::nullopt; + } + + if (!result) + return std::nullopt; + m_uid_to_decl[toOpaqueUid(uid)] = result; + return ToCompilerDecl(*result); +} + +clang::DeclContext *PdbAstBuilder::GetOrCreateDeclContextForUid(PdbSymUid uid) { + if (uid.kind() == PdbSymUidKind::CompilandSym) { + if (uid.asCompilandSym().offset == 0) + return FromCompilerDeclContext(GetTranslationUnitDecl()); + } + auto option = GetOrCreateDeclForUid(uid); + if (!option) + return nullptr; + clang::Decl *decl = FromCompilerDecl(*option); + if (!decl) + return nullptr; + + return clang::Decl::castToDeclContext(decl); +} + +std::pair<clang::DeclContext *, std::string> +PdbAstBuilder::CreateDeclInfoForUndecoratedName(llvm::StringRef name) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + MSVCUndecoratedNameParser parser(name); + llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers(); + + auto *context = FromCompilerDeclContext(GetTranslationUnitDecl()); + + llvm::StringRef uname = specs.back().GetBaseName(); + specs = specs.drop_back(); + if (specs.empty()) + return {context, std::string(name)}; + + llvm::StringRef scope_name = specs.back().GetFullName(); + + // It might be a class name, try that first. + std::vector<TypeIndex> types = index.tpi().findRecordsByName(scope_name); + while (!types.empty()) { + clang::QualType qt = GetOrCreateType(types.back()); + if (qt.isNull()) + continue; + clang::TagDecl *tag = qt->getAsTagDecl(); + if (tag) + return {clang::TagDecl::castToDeclContext(tag), std::string(uname)}; + types.pop_back(); + } + + // If that fails, treat it as a series of namespaces. + for (const MSVCUndecoratedNameSpecifier &spec : specs) { + std::string ns_name = spec.GetBaseName().str(); + context = GetOrCreateNamespaceDecl(ns_name.c_str(), *context); + } + return {context, std::string(uname)}; +} + +clang::DeclContext *PdbAstBuilder::GetParentDeclContext(PdbSymUid uid) { + // We must do this *without* calling GetOrCreate on the current uid, as + // that would be an infinite recursion. + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex& index = pdb->GetIndex(); + switch (uid.kind()) { + case PdbSymUidKind::CompilandSym: { + std::optional<PdbCompilandSymId> scope = + pdb->FindSymbolScope(uid.asCompilandSym()); + if (scope) + return GetOrCreateDeclContextForUid(*scope); + + CVSymbol sym = index.ReadSymbolRecord(uid.asCompilandSym()); + return CreateDeclInfoForUndecoratedName(getSymbolName(sym)).first; + } + case PdbSymUidKind::Type: { + // It could be a namespace, class, or global. We don't support nested + // functions yet. Anyway, we just need to consult the parent type map. + PdbTypeSymId type_id = uid.asTypeSym(); + std::optional<TypeIndex> parent_index = pdb->GetParentType(type_id.index); + if (!parent_index) + return FromCompilerDeclContext(GetTranslationUnitDecl()); + return GetOrCreateDeclContextForUid(PdbTypeSymId(*parent_index)); + } + case PdbSymUidKind::FieldListMember: + // In this case the parent DeclContext is the one for the class that this + // member is inside of. + break; + case PdbSymUidKind::GlobalSym: { + // If this refers to a compiland symbol, just recurse in with that symbol. + // The only other possibilities are S_CONSTANT and S_UDT, in which case we + // need to parse the undecorated name to figure out the scope, then look + // that up in the TPI stream. If it's found, it's a type, othewrise it's + // a series of namespaces. + // FIXME: do this. + CVSymbol global = index.ReadSymbolRecord(uid.asGlobalSym()); + switch (global.kind()) { + case SymbolKind::S_GDATA32: + case SymbolKind::S_LDATA32: + return CreateDeclInfoForUndecoratedName(getSymbolName(global)).first;; + case SymbolKind::S_PROCREF: + case SymbolKind::S_LPROCREF: { + ProcRefSym ref{global.kind()}; + llvm::cantFail( + SymbolDeserializer::deserializeAs<ProcRefSym>(global, ref)); + PdbCompilandSymId cu_sym_id{ref.modi(), ref.SymOffset}; + return GetParentDeclContext(cu_sym_id); + } + case SymbolKind::S_CONSTANT: + case SymbolKind::S_UDT: + return CreateDeclInfoForUndecoratedName(getSymbolName(global)).first; + default: + break; + } + break; + } + default: + break; + } + return FromCompilerDeclContext(GetTranslationUnitDecl()); +} + +bool PdbAstBuilder::CompleteType(clang::QualType qt) { + if (qt.isNull()) + return false; + clang::TagDecl *tag = qt->getAsTagDecl(); + if (qt->isArrayType()) { + const clang::Type *element_type = qt->getArrayElementTypeNoTypeQual(); + tag = element_type->getAsTagDecl(); + } + if (!tag) + return false; + + return CompleteTagDecl(*tag); +} + +bool PdbAstBuilder::CompleteTagDecl(clang::TagDecl &tag) { + // If this is not in our map, it's an error. + auto status_iter = m_decl_to_status.find(&tag); + lldbassert(status_iter != m_decl_to_status.end()); + + // If it's already complete, just return. + DeclStatus &status = status_iter->second; + if (status.resolved) + return true; + + PdbTypeSymId type_id = PdbSymUid(status.uid).asTypeSym(); + PdbIndex &index = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()) + ->GetIndex(); + lldbassert(IsTagRecord(type_id, index.tpi())); + + clang::QualType tag_qt = m_clang.getASTContext().getTypeDeclType(&tag); + TypeSystemClang::SetHasExternalStorage(tag_qt.getAsOpaquePtr(), false); + + TypeIndex tag_ti = type_id.index; + CVType cvt = index.tpi().getType(tag_ti); + if (cvt.kind() == LF_MODIFIER) + tag_ti = LookThroughModifierRecord(cvt); + + PdbTypeSymId best_ti = GetBestPossibleDecl(tag_ti, index.tpi()); + cvt = index.tpi().getType(best_ti.index); + lldbassert(IsTagRecord(cvt)); + + if (IsForwardRefUdt(cvt)) { + // If we can't find a full decl for this forward ref anywhere in the debug + // info, then we have no way to complete it. + return false; + } + + TypeIndex field_list_ti = GetFieldListIndex(cvt); + CVType field_list_cvt = index.tpi().getType(field_list_ti); + if (field_list_cvt.kind() != LF_FIELDLIST) + return false; + FieldListRecord field_list; + if (llvm::Error error = TypeDeserializer::deserializeAs<FieldListRecord>( + field_list_cvt, field_list)) + llvm::consumeError(std::move(error)); + + // Visit all members of this class, then perform any finalization necessary + // to complete the class. + CompilerType ct = ToCompilerType(tag_qt); + UdtRecordCompleter completer(best_ti, ct, tag, *this, index, m_decl_to_status, + m_cxx_record_map); + llvm::Error error = + llvm::codeview::visitMemberRecordStream(field_list.Data, completer); + completer.complete(); + + m_decl_to_status[&tag].resolved = true; + if (error) { + llvm::consumeError(std::move(error)); + return false; + } + return true; +} + +clang::QualType PdbAstBuilder::CreateSimpleType(TypeIndex ti) { + if (ti == TypeIndex::NullptrT()) + return GetBasicType(lldb::eBasicTypeNullPtr); + + if (ti.getSimpleMode() != SimpleTypeMode::Direct) { + clang::QualType direct_type = GetOrCreateType(ti.makeDirect()); + if (direct_type.isNull()) + return {}; + return m_clang.getASTContext().getPointerType(direct_type); + } + + if (ti.getSimpleKind() == SimpleTypeKind::NotTranslated) + return {}; + + lldb::BasicType bt = GetCompilerTypeForSimpleKind(ti.getSimpleKind()); + if (bt == lldb::eBasicTypeInvalid) + return {}; + + return GetBasicType(bt); +} + +clang::QualType PdbAstBuilder::CreatePointerType(const PointerRecord &pointer) { + clang::QualType pointee_type = GetOrCreateType(pointer.ReferentType); + + // This can happen for pointers to LF_VTSHAPE records, which we shouldn't + // create in the AST. + if (pointee_type.isNull()) + return {}; + + if (pointer.isPointerToMember()) { + MemberPointerInfo mpi = pointer.getMemberInfo(); + clang::QualType class_type = GetOrCreateType(mpi.ContainingType); + if (class_type.isNull()) + return {}; + if (clang::TagDecl *tag = class_type->getAsTagDecl()) { + clang::MSInheritanceAttr::Spelling spelling; + switch (mpi.Representation) { + case llvm::codeview::PointerToMemberRepresentation::SingleInheritanceData: + case llvm::codeview::PointerToMemberRepresentation:: + SingleInheritanceFunction: + spelling = + clang::MSInheritanceAttr::Spelling::Keyword_single_inheritance; + break; + case llvm::codeview::PointerToMemberRepresentation:: + MultipleInheritanceData: + case llvm::codeview::PointerToMemberRepresentation:: + MultipleInheritanceFunction: + spelling = + clang::MSInheritanceAttr::Spelling::Keyword_multiple_inheritance; + break; + case llvm::codeview::PointerToMemberRepresentation:: + VirtualInheritanceData: + case llvm::codeview::PointerToMemberRepresentation:: + VirtualInheritanceFunction: + spelling = + clang::MSInheritanceAttr::Spelling::Keyword_virtual_inheritance; + break; + case llvm::codeview::PointerToMemberRepresentation::Unknown: + spelling = + clang::MSInheritanceAttr::Spelling::Keyword_unspecified_inheritance; + break; + default: + spelling = clang::MSInheritanceAttr::Spelling::SpellingNotCalculated; + break; + } + tag->addAttr(clang::MSInheritanceAttr::CreateImplicit( + m_clang.getASTContext(), spelling)); + } + return m_clang.getASTContext().getMemberPointerType( + pointee_type, class_type.getTypePtr()); + } + + clang::QualType pointer_type; + if (pointer.getMode() == PointerMode::LValueReference) + pointer_type = m_clang.getASTContext().getLValueReferenceType(pointee_type); + else if (pointer.getMode() == PointerMode::RValueReference) + pointer_type = m_clang.getASTContext().getRValueReferenceType(pointee_type); + else + pointer_type = m_clang.getASTContext().getPointerType(pointee_type); + + if ((pointer.getOptions() & PointerOptions::Const) != PointerOptions::None) + pointer_type.addConst(); + + if ((pointer.getOptions() & PointerOptions::Volatile) != PointerOptions::None) + pointer_type.addVolatile(); + + if ((pointer.getOptions() & PointerOptions::Restrict) != PointerOptions::None) + pointer_type.addRestrict(); + + return pointer_type; +} + +clang::QualType +PdbAstBuilder::CreateModifierType(const ModifierRecord &modifier) { + clang::QualType unmodified_type = GetOrCreateType(modifier.ModifiedType); + if (unmodified_type.isNull()) + return {}; + + if ((modifier.Modifiers & ModifierOptions::Const) != ModifierOptions::None) + unmodified_type.addConst(); + if ((modifier.Modifiers & ModifierOptions::Volatile) != ModifierOptions::None) + unmodified_type.addVolatile(); + + return unmodified_type; +} + +clang::QualType PdbAstBuilder::CreateRecordType(PdbTypeSymId id, + const TagRecord &record) { + clang::DeclContext *context = nullptr; + std::string uname; + std::tie(context, uname) = CreateDeclInfoForType(record, id.index); + if (!context) + return {}; + + clang::TagTypeKind ttk = TranslateUdtKind(record); + lldb::AccessType access = (ttk == clang::TagTypeKind::Class) + ? lldb::eAccessPrivate + : lldb::eAccessPublic; + + ClangASTMetadata metadata; + metadata.SetUserID(toOpaqueUid(id)); + metadata.SetIsDynamicCXXType(false); + + CompilerType ct = m_clang.CreateRecordType( + context, OptionalClangModuleID(), access, uname, llvm::to_underlying(ttk), + lldb::eLanguageTypeC_plus_plus, &metadata); + + lldbassert(ct.IsValid()); + + TypeSystemClang::StartTagDeclarationDefinition(ct); + + // Even if it's possible, don't complete it at this point. Just mark it + // forward resolved, and if/when LLDB needs the full definition, it can + // ask us. + clang::QualType result = + clang::QualType::getFromOpaquePtr(ct.GetOpaqueQualType()); + + TypeSystemClang::SetHasExternalStorage(result.getAsOpaquePtr(), true); + return result; +} + +clang::Decl *PdbAstBuilder::TryGetDecl(PdbSymUid uid) const { + auto iter = m_uid_to_decl.find(toOpaqueUid(uid)); + if (iter != m_uid_to_decl.end()) + return iter->second; + return nullptr; +} + +clang::NamespaceDecl * +PdbAstBuilder::GetOrCreateNamespaceDecl(const char *name, + clang::DeclContext &context) { + return m_clang.GetUniqueNamespaceDeclaration( + IsAnonymousNamespaceName(name) ? nullptr : name, &context, + OptionalClangModuleID()); +} + +clang::BlockDecl * +PdbAstBuilder::GetOrCreateBlockDecl(PdbCompilandSymId block_id) { + if (clang::Decl *decl = TryGetDecl(block_id)) + return llvm::dyn_cast<clang::BlockDecl>(decl); + + clang::DeclContext *scope = GetParentDeclContext(block_id); + + clang::BlockDecl *block_decl = + m_clang.CreateBlockDeclaration(scope, OptionalClangModuleID()); + m_uid_to_decl.insert({toOpaqueUid(block_id), block_decl}); + + DeclStatus status; + status.resolved = true; + status.uid = toOpaqueUid(block_id); + m_decl_to_status.insert({block_decl, status}); + + return block_decl; +} + +clang::VarDecl *PdbAstBuilder::CreateVariableDecl(PdbSymUid uid, CVSymbol sym, + clang::DeclContext &scope) { + VariableInfo var_info = GetVariableNameInfo(sym); + clang::QualType qt = GetOrCreateType(var_info.type); + if (qt.isNull()) + return nullptr; + + clang::VarDecl *var_decl = m_clang.CreateVariableDeclaration( + &scope, OptionalClangModuleID(), var_info.name.str().c_str(), qt); + + m_uid_to_decl[toOpaqueUid(uid)] = var_decl; + DeclStatus status; + status.resolved = true; + status.uid = toOpaqueUid(uid); + m_decl_to_status.insert({var_decl, status}); + return var_decl; +} + +clang::VarDecl * +PdbAstBuilder::GetOrCreateVariableDecl(PdbCompilandSymId scope_id, + PdbCompilandSymId var_id) { + if (clang::Decl *decl = TryGetDecl(var_id)) + return llvm::dyn_cast<clang::VarDecl>(decl); + + clang::DeclContext *scope = GetOrCreateDeclContextForUid(scope_id); + if (!scope) + return nullptr; + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVSymbol sym = index.ReadSymbolRecord(var_id); + return CreateVariableDecl(PdbSymUid(var_id), sym, *scope); +} + +clang::VarDecl *PdbAstBuilder::GetOrCreateVariableDecl(PdbGlobalSymId var_id) { + if (clang::Decl *decl = TryGetDecl(var_id)) + return llvm::dyn_cast<clang::VarDecl>(decl); + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVSymbol sym = index.ReadSymbolRecord(var_id); + auto context = FromCompilerDeclContext(GetTranslationUnitDecl()); + return CreateVariableDecl(PdbSymUid(var_id), sym, *context); +} + +clang::TypedefNameDecl * +PdbAstBuilder::GetOrCreateTypedefDecl(PdbGlobalSymId id) { + if (clang::Decl *decl = TryGetDecl(id)) + return llvm::dyn_cast<clang::TypedefNameDecl>(decl); + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVSymbol sym = index.ReadSymbolRecord(id); + lldbassert(sym.kind() == S_UDT); + UDTSym udt = llvm::cantFail(SymbolDeserializer::deserializeAs<UDTSym>(sym)); + + clang::DeclContext *scope = GetParentDeclContext(id); + + PdbTypeSymId real_type_id{udt.Type, false}; + clang::QualType qt = GetOrCreateType(real_type_id); + if (qt.isNull() || !scope) + return nullptr; + + std::string uname = std::string(DropNameScope(udt.Name)); + + CompilerType ct = ToCompilerType(qt).CreateTypedef( + uname.c_str(), ToCompilerDeclContext(*scope), 0); + clang::TypedefNameDecl *tnd = m_clang.GetAsTypedefDecl(ct); + DeclStatus status; + status.resolved = true; + status.uid = toOpaqueUid(id); + m_decl_to_status.insert({tnd, status}); + return tnd; +} + +clang::QualType PdbAstBuilder::GetBasicType(lldb::BasicType type) { + CompilerType ct = m_clang.GetBasicType(type); + return clang::QualType::getFromOpaquePtr(ct.GetOpaqueQualType()); +} + +clang::QualType PdbAstBuilder::CreateType(PdbTypeSymId type) { + if (type.index.isSimple()) + return CreateSimpleType(type.index); + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVType cvt = index.tpi().getType(type.index); + + if (cvt.kind() == LF_MODIFIER) { + ModifierRecord modifier; + llvm::cantFail( + TypeDeserializer::deserializeAs<ModifierRecord>(cvt, modifier)); + return CreateModifierType(modifier); + } + + if (cvt.kind() == LF_POINTER) { + PointerRecord pointer; + llvm::cantFail( + TypeDeserializer::deserializeAs<PointerRecord>(cvt, pointer)); + return CreatePointerType(pointer); + } + + if (IsTagRecord(cvt)) { + CVTagRecord tag = CVTagRecord::create(cvt); + if (tag.kind() == CVTagRecord::Union) + return CreateRecordType(type.index, tag.asUnion()); + if (tag.kind() == CVTagRecord::Enum) + return CreateEnumType(type.index, tag.asEnum()); + return CreateRecordType(type.index, tag.asClass()); + } + + if (cvt.kind() == LF_ARRAY) { + ArrayRecord ar; + llvm::cantFail(TypeDeserializer::deserializeAs<ArrayRecord>(cvt, ar)); + return CreateArrayType(ar); + } + + if (cvt.kind() == LF_PROCEDURE) { + ProcedureRecord pr; + llvm::cantFail(TypeDeserializer::deserializeAs<ProcedureRecord>(cvt, pr)); + return CreateFunctionType(pr.ArgumentList, pr.ReturnType, pr.CallConv); + } + + if (cvt.kind() == LF_MFUNCTION) { + MemberFunctionRecord mfr; + llvm::cantFail( + TypeDeserializer::deserializeAs<MemberFunctionRecord>(cvt, mfr)); + return CreateFunctionType(mfr.ArgumentList, mfr.ReturnType, mfr.CallConv); + } + + return {}; +} + +clang::QualType PdbAstBuilder::GetOrCreateType(PdbTypeSymId type) { + if (type.index.isNoneType()) + return {}; + + lldb::user_id_t uid = toOpaqueUid(type); + auto iter = m_uid_to_type.find(uid); + if (iter != m_uid_to_type.end()) + return iter->second; + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + PdbTypeSymId best_type = GetBestPossibleDecl(type, index.tpi()); + + clang::QualType qt; + if (best_type.index != type.index) { + // This is a forward decl. Call GetOrCreate on the full decl, then map the + // forward decl id to the full decl QualType. + clang::QualType qt = GetOrCreateType(best_type); + if (qt.isNull()) + return {}; + m_uid_to_type[toOpaqueUid(type)] = qt; + return qt; + } + + // This is either a full decl, or a forward decl with no matching full decl + // in the debug info. + qt = CreateType(type); + if (qt.isNull()) + return {}; + + m_uid_to_type[toOpaqueUid(type)] = qt; + if (IsTagRecord(type, index.tpi())) { + clang::TagDecl *tag = qt->getAsTagDecl(); + lldbassert(m_decl_to_status.count(tag) == 0); + + DeclStatus &status = m_decl_to_status[tag]; + status.uid = uid; + status.resolved = false; + } + return qt; +} + +clang::FunctionDecl * +PdbAstBuilder::CreateFunctionDecl(PdbCompilandSymId func_id, + llvm::StringRef func_name, TypeIndex func_ti, + CompilerType func_ct, uint32_t param_count, + clang::StorageClass func_storage, + bool is_inline, clang::DeclContext *parent) { + clang::FunctionDecl *function_decl = nullptr; + if (parent->isRecord()) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + clang::QualType parent_qt = llvm::cast<clang::TypeDecl>(parent) + ->getTypeForDecl() + ->getCanonicalTypeInternal(); + lldb::opaque_compiler_type_t parent_opaque_ty = + ToCompilerType(parent_qt).GetOpaqueQualType(); + // FIXME: Remove this workaround. + auto iter = m_cxx_record_map.find(parent_opaque_ty); + if (iter != m_cxx_record_map.end()) { + if (iter->getSecond().contains({func_name, func_ct})) { + return nullptr; + } + } + + CVType cvt = index.tpi().getType(func_ti); + MemberFunctionRecord func_record(static_cast<TypeRecordKind>(cvt.kind())); + llvm::cantFail(TypeDeserializer::deserializeAs<MemberFunctionRecord>( + cvt, func_record)); + TypeIndex class_index = func_record.getClassType(); + + CVType parent_cvt = index.tpi().getType(class_index); + TagRecord tag_record = CVTagRecord::create(parent_cvt).asTag(); + // If it's a forward reference, try to get the real TypeIndex. + if (tag_record.isForwardRef()) { + llvm::Expected<TypeIndex> eti = + index.tpi().findFullDeclForForwardRef(class_index); + if (eti) { + tag_record = CVTagRecord::create(index.tpi().getType(*eti)).asTag(); + } + } + if (!tag_record.FieldList.isSimple()) { + CVType field_list_cvt = index.tpi().getType(tag_record.FieldList); + FieldListRecord field_list; + if (llvm::Error error = TypeDeserializer::deserializeAs<FieldListRecord>( + field_list_cvt, field_list)) + llvm::consumeError(std::move(error)); + CreateMethodDecl process(index, m_clang, func_ti, function_decl, + parent_opaque_ty, func_name, func_ct); + if (llvm::Error err = visitMemberRecordStream(field_list.Data, process)) + llvm::consumeError(std::move(err)); + } + + if (!function_decl) { + function_decl = m_clang.AddMethodToCXXRecordType( + parent_opaque_ty, func_name, + /*mangled_name=*/nullptr, func_ct, + /*access=*/lldb::AccessType::eAccessPublic, + /*is_virtual=*/false, /*is_static=*/false, + /*is_inline=*/false, /*is_explicit=*/false, + /*is_attr_used=*/false, /*is_artificial=*/false); + } + m_cxx_record_map[parent_opaque_ty].insert({func_name, func_ct}); + } else { + function_decl = m_clang.CreateFunctionDeclaration( + parent, OptionalClangModuleID(), func_name, func_ct, func_storage, + is_inline); + CreateFunctionParameters(func_id, *function_decl, param_count); + } + return function_decl; +} + +clang::FunctionDecl * +PdbAstBuilder::GetOrCreateInlinedFunctionDecl(PdbCompilandSymId inlinesite_id) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CompilandIndexItem *cii = + index.compilands().GetCompiland(inlinesite_id.modi); + CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(inlinesite_id.offset); + InlineSiteSym inline_site(static_cast<SymbolRecordKind>(sym.kind())); + cantFail(SymbolDeserializer::deserializeAs<InlineSiteSym>(sym, inline_site)); + + // Inlinee is the id index to the function id record that is inlined. + PdbTypeSymId func_id(inline_site.Inlinee, true); + // Look up the function decl by the id index to see if we have created a + // function decl for a different inlinesite that refers the same function. + if (clang::Decl *decl = TryGetDecl(func_id)) + return llvm::dyn_cast<clang::FunctionDecl>(decl); + clang::FunctionDecl *function_decl = + CreateFunctionDeclFromId(func_id, inlinesite_id); + if (function_decl == nullptr) + return nullptr; + + // Use inline site id in m_decl_to_status because it's expected to be a + // PdbCompilandSymId so that we can parse local variables info after it. + uint64_t inlinesite_uid = toOpaqueUid(inlinesite_id); + DeclStatus status; + status.resolved = true; + status.uid = inlinesite_uid; + m_decl_to_status.insert({function_decl, status}); + // Use the index in IPI stream as uid in m_uid_to_decl, because index in IPI + // stream are unique and there could be multiple inline sites (different ids) + // referring the same inline function. This avoid creating multiple same + // inline function delcs. + uint64_t func_uid = toOpaqueUid(func_id); + lldbassert(m_uid_to_decl.count(func_uid) == 0); + m_uid_to_decl[func_uid] = function_decl; + return function_decl; +} + +clang::FunctionDecl * +PdbAstBuilder::CreateFunctionDeclFromId(PdbTypeSymId func_tid, + PdbCompilandSymId func_sid) { + lldbassert(func_tid.is_ipi); + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVType func_cvt = index.ipi().getType(func_tid.index); + llvm::StringRef func_name; + TypeIndex func_ti; + clang::DeclContext *parent = nullptr; + switch (func_cvt.kind()) { + case LF_MFUNC_ID: { + MemberFuncIdRecord mfr; + cantFail( + TypeDeserializer::deserializeAs<MemberFuncIdRecord>(func_cvt, mfr)); + func_name = mfr.getName(); + func_ti = mfr.getFunctionType(); + PdbTypeSymId class_type_id(mfr.ClassType, false); + parent = GetOrCreateDeclContextForUid(class_type_id); + break; + } + case LF_FUNC_ID: { + FuncIdRecord fir; + cantFail(TypeDeserializer::deserializeAs<FuncIdRecord>(func_cvt, fir)); + func_name = fir.getName(); + func_ti = fir.getFunctionType(); + parent = FromCompilerDeclContext(GetTranslationUnitDecl()); + if (!fir.ParentScope.isNoneType()) { + CVType parent_cvt = index.ipi().getType(fir.ParentScope); + if (parent_cvt.kind() == LF_STRING_ID) { + StringIdRecord sir; + cantFail( + TypeDeserializer::deserializeAs<StringIdRecord>(parent_cvt, sir)); + parent = GetOrCreateNamespaceDecl(sir.String.data(), *parent); + } + } + break; + } + default: + lldbassert(false && "Invalid function id type!"); + } + clang::QualType func_qt = GetOrCreateType(func_ti); + if (func_qt.isNull() || !parent) + return nullptr; + CompilerType func_ct = ToCompilerType(func_qt); + uint32_t param_count = + llvm::cast<clang::FunctionProtoType>(func_qt)->getNumParams(); + return CreateFunctionDecl(func_sid, func_name, func_ti, func_ct, param_count, + clang::SC_None, true, parent); +} + +clang::FunctionDecl * +PdbAstBuilder::GetOrCreateFunctionDecl(PdbCompilandSymId func_id) { + if (clang::Decl *decl = TryGetDecl(func_id)) + return llvm::dyn_cast<clang::FunctionDecl>(decl); + + clang::DeclContext *parent = GetParentDeclContext(PdbSymUid(func_id)); + if (!parent) + return nullptr; + std::string context_name; + if (clang::NamespaceDecl *ns = llvm::dyn_cast<clang::NamespaceDecl>(parent)) { + context_name = ns->getQualifiedNameAsString(); + } else if (clang::TagDecl *tag = llvm::dyn_cast<clang::TagDecl>(parent)) { + context_name = tag->getQualifiedNameAsString(); + } + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVSymbol cvs = index.ReadSymbolRecord(func_id); + ProcSym proc(static_cast<SymbolRecordKind>(cvs.kind())); + llvm::cantFail(SymbolDeserializer::deserializeAs<ProcSym>(cvs, proc)); + + PdbTypeSymId type_id(proc.FunctionType); + clang::QualType qt = GetOrCreateType(type_id); + if (qt.isNull()) + return nullptr; + + clang::StorageClass storage = clang::SC_None; + if (proc.Kind == SymbolRecordKind::ProcSym) + storage = clang::SC_Static; + + const clang::FunctionProtoType *func_type = + llvm::dyn_cast<clang::FunctionProtoType>(qt); + + CompilerType func_ct = ToCompilerType(qt); + + llvm::StringRef proc_name = proc.Name; + proc_name.consume_front(context_name); + proc_name.consume_front("::"); + clang::FunctionDecl *function_decl = + CreateFunctionDecl(func_id, proc_name, proc.FunctionType, func_ct, + func_type->getNumParams(), storage, false, parent); + if (function_decl == nullptr) + return nullptr; + + lldbassert(m_uid_to_decl.count(toOpaqueUid(func_id)) == 0); + m_uid_to_decl[toOpaqueUid(func_id)] = function_decl; + DeclStatus status; + status.resolved = true; + status.uid = toOpaqueUid(func_id); + m_decl_to_status.insert({function_decl, status}); + + return function_decl; +} + +void PdbAstBuilder::CreateFunctionParameters(PdbCompilandSymId func_id, + clang::FunctionDecl &function_decl, + uint32_t param_count) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CompilandIndexItem *cii = index.compilands().GetCompiland(func_id.modi); + CVSymbolArray scope = + cii->m_debug_stream.getSymbolArrayForScope(func_id.offset); + + scope.drop_front(); + auto begin = scope.begin(); + auto end = scope.end(); + std::vector<clang::ParmVarDecl *> params; + for (uint32_t i = 0; i < param_count && begin != end;) { + uint32_t record_offset = begin.offset(); + CVSymbol sym = *begin++; + + TypeIndex param_type; + llvm::StringRef param_name; + switch (sym.kind()) { + case S_REGREL32: { + RegRelativeSym reg(SymbolRecordKind::RegRelativeSym); + cantFail(SymbolDeserializer::deserializeAs<RegRelativeSym>(sym, reg)); + param_type = reg.Type; + param_name = reg.Name; + break; + } + case S_REGISTER: { + RegisterSym reg(SymbolRecordKind::RegisterSym); + cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg)); + param_type = reg.Index; + param_name = reg.Name; + break; + } + case S_LOCAL: { + LocalSym local(SymbolRecordKind::LocalSym); + cantFail(SymbolDeserializer::deserializeAs<LocalSym>(sym, local)); + if ((local.Flags & LocalSymFlags::IsParameter) == LocalSymFlags::None) + continue; + param_type = local.Type; + param_name = local.Name; + break; + } + case S_BLOCK32: + case S_INLINESITE: + case S_INLINESITE2: + // All parameters should come before the first block/inlinesite. If that + // isn't the case, then perhaps this is bad debug info that doesn't + // contain information about all parameters. + return; + default: + continue; + } + + PdbCompilandSymId param_uid(func_id.modi, record_offset); + clang::QualType qt = GetOrCreateType(param_type); + if (qt.isNull()) + return; + + CompilerType param_type_ct = m_clang.GetType(qt); + clang::ParmVarDecl *param = m_clang.CreateParameterDeclaration( + &function_decl, OptionalClangModuleID(), param_name.str().c_str(), + param_type_ct, clang::SC_None, true); + lldbassert(m_uid_to_decl.count(toOpaqueUid(param_uid)) == 0); + + m_uid_to_decl[toOpaqueUid(param_uid)] = param; + params.push_back(param); + ++i; + } + + if (!params.empty() && params.size() == param_count) + m_clang.SetFunctionParameters(&function_decl, params); +} + +clang::QualType PdbAstBuilder::CreateEnumType(PdbTypeSymId id, + const EnumRecord &er) { + clang::DeclContext *decl_context = nullptr; + std::string uname; + std::tie(decl_context, uname) = CreateDeclInfoForType(er, id.index); + if (!decl_context) + return {}; + + clang::QualType underlying_type = GetOrCreateType(er.UnderlyingType); + if (underlying_type.isNull()) + return {}; + + Declaration declaration; + CompilerType enum_ct = m_clang.CreateEnumerationType( + uname, decl_context, OptionalClangModuleID(), declaration, + ToCompilerType(underlying_type), er.isScoped()); + + TypeSystemClang::StartTagDeclarationDefinition(enum_ct); + TypeSystemClang::SetHasExternalStorage(enum_ct.GetOpaqueQualType(), true); + + return clang::QualType::getFromOpaquePtr(enum_ct.GetOpaqueQualType()); +} + +clang::QualType PdbAstBuilder::CreateArrayType(const ArrayRecord &ar) { + clang::QualType element_type = GetOrCreateType(ar.ElementType); + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + uint64_t element_size = GetSizeOfType({ar.ElementType}, index.tpi()); + if (element_type.isNull() || element_size == 0) + return {}; + uint64_t element_count = ar.Size / element_size; + + CompilerType array_ct = m_clang.CreateArrayType(ToCompilerType(element_type), + element_count, false); + return clang::QualType::getFromOpaquePtr(array_ct.GetOpaqueQualType()); +} + +clang::QualType PdbAstBuilder::CreateFunctionType( + TypeIndex args_type_idx, TypeIndex return_type_idx, + llvm::codeview::CallingConvention calling_convention) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + TpiStream &stream = index.tpi(); + CVType args_cvt = stream.getType(args_type_idx); + ArgListRecord args; + llvm::cantFail( + TypeDeserializer::deserializeAs<ArgListRecord>(args_cvt, args)); + + llvm::ArrayRef<TypeIndex> arg_indices = llvm::ArrayRef(args.ArgIndices); + bool is_variadic = IsCVarArgsFunction(arg_indices); + if (is_variadic) + arg_indices = arg_indices.drop_back(); + + std::vector<CompilerType> arg_types; + arg_types.reserve(arg_indices.size()); + + for (TypeIndex arg_index : arg_indices) { + clang::QualType arg_type = GetOrCreateType(arg_index); + if (arg_type.isNull()) + continue; + arg_types.push_back(ToCompilerType(arg_type)); + } + + clang::QualType return_type = GetOrCreateType(return_type_idx); + if (return_type.isNull()) + return {}; + + std::optional<clang::CallingConv> cc = + TranslateCallingConvention(calling_convention); + if (!cc) + return {}; + + CompilerType return_ct = ToCompilerType(return_type); + CompilerType func_sig_ast_type = m_clang.CreateFunctionType( + return_ct, arg_types.data(), arg_types.size(), is_variadic, 0, *cc); + + return clang::QualType::getFromOpaquePtr( + func_sig_ast_type.GetOpaqueQualType()); +} + +static bool isTagDecl(clang::DeclContext &context) { + return llvm::isa<clang::TagDecl>(&context); +} + +static bool isFunctionDecl(clang::DeclContext &context) { + return llvm::isa<clang::FunctionDecl>(&context); +} + +static bool isBlockDecl(clang::DeclContext &context) { + return llvm::isa<clang::BlockDecl>(&context); +} + +void PdbAstBuilder::ParseNamespace(clang::DeclContext &context) { + clang::NamespaceDecl *ns = llvm::dyn_cast<clang::NamespaceDecl>(&context); + if (m_parsed_namespaces.contains(ns)) + return; + std::string qname = ns->getQualifiedNameAsString(); + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + TypeIndex ti{index.tpi().TypeIndexBegin()}; + for (const CVType &cvt : index.tpi().typeArray()) { + PdbTypeSymId tid{ti}; + ++ti; + + if (!IsTagRecord(cvt)) + continue; + + CVTagRecord tag = CVTagRecord::create(cvt); + + // Call CreateDeclInfoForType unconditionally so that the namespace info + // gets created. But only call CreateRecordType if the namespace name + // matches. + clang::DeclContext *context = nullptr; + std::string uname; + std::tie(context, uname) = CreateDeclInfoForType(tag.asTag(), tid.index); + if (!context || !context->isNamespace()) + continue; + + clang::NamespaceDecl *ns = llvm::cast<clang::NamespaceDecl>(context); + llvm::StringRef ns_name = ns->getName(); + if (ns_name.starts_with(qname)) { + ns_name = ns_name.drop_front(qname.size()); + if (ns_name.starts_with("::")) + GetOrCreateType(tid); + } + } + ParseAllFunctionsAndNonLocalVars(); + m_parsed_namespaces.insert(ns); +} + +void PdbAstBuilder::ParseAllTypes() { + llvm::call_once(m_parse_all_types, [this]() { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + TypeIndex ti{index.tpi().TypeIndexBegin()}; + for (const CVType &cvt : index.tpi().typeArray()) { + PdbTypeSymId tid{ti}; + ++ti; + + if (!IsTagRecord(cvt)) + continue; + + GetOrCreateType(tid); + } + }); +} + +void PdbAstBuilder::ParseAllFunctionsAndNonLocalVars() { + llvm::call_once(m_parse_functions_and_non_local_vars, [this]() { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + uint32_t module_count = index.dbi().modules().getModuleCount(); + for (uint16_t modi = 0; modi < module_count; ++modi) { + CompilandIndexItem &cii = index.compilands().GetOrCreateCompiland(modi); + const CVSymbolArray &symbols = cii.m_debug_stream.getSymbolArray(); + auto iter = symbols.begin(); + while (iter != symbols.end()) { + PdbCompilandSymId sym_id{modi, iter.offset()}; + + switch (iter->kind()) { + case S_GPROC32: + case S_LPROC32: + GetOrCreateFunctionDecl(sym_id); + iter = symbols.at(getScopeEndOffset(*iter)); + break; + case S_GDATA32: + case S_GTHREAD32: + case S_LDATA32: + case S_LTHREAD32: + GetOrCreateVariableDecl(PdbCompilandSymId(modi, 0), sym_id); + ++iter; + break; + default: + ++iter; + continue; + } + } + } + }); +} + +static CVSymbolArray skipFunctionParameters(clang::Decl &decl, + const CVSymbolArray &symbols) { + clang::FunctionDecl *func_decl = llvm::dyn_cast<clang::FunctionDecl>(&decl); + if (!func_decl) + return symbols; + unsigned int params = func_decl->getNumParams(); + if (params == 0) + return symbols; + + CVSymbolArray result = symbols; + + while (!result.empty()) { + if (params == 0) + return result; + + CVSymbol sym = *result.begin(); + result.drop_front(); + + if (!isLocalVariableType(sym.kind())) + continue; + + --params; + } + return result; +} + +void PdbAstBuilder::ParseBlockChildren(PdbCompilandSymId block_id) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVSymbol sym = index.ReadSymbolRecord(block_id); + lldbassert(sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32 || + sym.kind() == S_BLOCK32 || sym.kind() == S_INLINESITE); + CompilandIndexItem &cii = + index.compilands().GetOrCreateCompiland(block_id.modi); + CVSymbolArray symbols = + cii.m_debug_stream.getSymbolArrayForScope(block_id.offset); + + // Function parameters should already have been created when the function was + // parsed. + if (sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32) + symbols = + skipFunctionParameters(*m_uid_to_decl[toOpaqueUid(block_id)], symbols); + + symbols.drop_front(); + auto begin = symbols.begin(); + while (begin != symbols.end()) { + PdbCompilandSymId child_sym_id(block_id.modi, begin.offset()); + GetOrCreateSymbolForId(child_sym_id); + if (begin->kind() == S_BLOCK32 || begin->kind() == S_INLINESITE) { + ParseBlockChildren(child_sym_id); + begin = symbols.at(getScopeEndOffset(*begin)); + } + ++begin; + } +} + +void PdbAstBuilder::ParseDeclsForSimpleContext(clang::DeclContext &context) { + + clang::Decl *decl = clang::Decl::castFromDeclContext(&context); + lldbassert(decl); + + auto iter = m_decl_to_status.find(decl); + lldbassert(iter != m_decl_to_status.end()); + + if (auto *tag = llvm::dyn_cast<clang::TagDecl>(&context)) { + CompleteTagDecl(*tag); + return; + } + + if (isFunctionDecl(context) || isBlockDecl(context)) { + PdbCompilandSymId block_id = PdbSymUid(iter->second.uid).asCompilandSym(); + ParseBlockChildren(block_id); + } +} + +void PdbAstBuilder::ParseDeclsForContext(clang::DeclContext &context) { + // Namespaces aren't explicitly represented in the debug info, and the only + // way to parse them is to parse all type info, demangling every single type + // and trying to reconstruct the DeclContext hierarchy this way. Since this + // is an expensive operation, we have to special case it so that we do other + // work (such as parsing the items that appear within the namespaces) at the + // same time. + if (context.isTranslationUnit()) { + ParseAllTypes(); + ParseAllFunctionsAndNonLocalVars(); + return; + } + + if (context.isNamespace()) { + ParseNamespace(context); + return; + } + + if (isTagDecl(context) || isFunctionDecl(context) || isBlockDecl(context)) { + ParseDeclsForSimpleContext(context); + return; + } +} + +CompilerDecl PdbAstBuilder::ToCompilerDecl(clang::Decl &decl) { + return m_clang.GetCompilerDecl(&decl); +} + +CompilerType PdbAstBuilder::ToCompilerType(clang::QualType qt) { + return {m_clang.weak_from_this(), qt.getAsOpaquePtr()}; +} + +CompilerDeclContext +PdbAstBuilder::ToCompilerDeclContext(clang::DeclContext &context) { + return m_clang.CreateDeclContext(&context); +} + +clang::Decl * PdbAstBuilder::FromCompilerDecl(CompilerDecl decl) { + return ClangUtil::GetDecl(decl); +} + +clang::DeclContext * +PdbAstBuilder::FromCompilerDeclContext(CompilerDeclContext context) { + return static_cast<clang::DeclContext *>(context.GetOpaqueDeclContext()); +} + +void PdbAstBuilder::Dump(Stream &stream) { + m_clang.Dump(stream.AsRawOstream()); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h new file mode 100644 index 000000000000..b7cad30c69c0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h @@ -0,0 +1,159 @@ +//===-- PdbAstBuilder.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBASTBUILDER_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBASTBUILDER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Threading.h" + +#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" + +#include "PdbIndex.h" +#include "PdbSymUid.h" +#include <optional> + +namespace clang { +class TagDecl; +class DeclContext; +class Decl; +class QualType; +class FunctionDecl; +class NamespaceDecl; +} // namespace clang + +namespace llvm { +namespace codeview { +class ProcSym; +} +} // namespace llvm + +namespace lldb_private { +class ClangASTImporter; +class ObjectFile; + +namespace npdb { +class PdbIndex; +struct VariableInfo; + +struct DeclStatus { + DeclStatus() = default; + DeclStatus(lldb::user_id_t uid, bool resolved) + : uid(uid), resolved(resolved) {} + lldb::user_id_t uid = 0; + bool resolved = false; +}; + +class PdbAstBuilder { +public: + // Constructors and Destructors + PdbAstBuilder(TypeSystemClang &clang); + + lldb_private::CompilerDeclContext GetTranslationUnitDecl(); + + std::optional<lldb_private::CompilerDecl> + GetOrCreateDeclForUid(PdbSymUid uid); + clang::DeclContext *GetOrCreateDeclContextForUid(PdbSymUid uid); + clang::DeclContext *GetParentDeclContext(PdbSymUid uid); + + clang::FunctionDecl *GetOrCreateFunctionDecl(PdbCompilandSymId func_id); + clang::FunctionDecl * + GetOrCreateInlinedFunctionDecl(PdbCompilandSymId inlinesite_id); + clang::BlockDecl *GetOrCreateBlockDecl(PdbCompilandSymId block_id); + clang::VarDecl *GetOrCreateVariableDecl(PdbCompilandSymId scope_id, + PdbCompilandSymId var_id); + clang::VarDecl *GetOrCreateVariableDecl(PdbGlobalSymId var_id); + clang::TypedefNameDecl *GetOrCreateTypedefDecl(PdbGlobalSymId id); + void ParseDeclsForContext(clang::DeclContext &context); + + clang::QualType GetBasicType(lldb::BasicType type); + clang::QualType GetOrCreateType(PdbTypeSymId type); + + bool CompleteTagDecl(clang::TagDecl &tag); + bool CompleteType(clang::QualType qt); + + CompilerDecl ToCompilerDecl(clang::Decl &decl); + CompilerType ToCompilerType(clang::QualType qt); + CompilerDeclContext ToCompilerDeclContext(clang::DeclContext &context); + clang::Decl *FromCompilerDecl(CompilerDecl decl); + clang::DeclContext *FromCompilerDeclContext(CompilerDeclContext context); + + TypeSystemClang &clang() { return m_clang; } + ClangASTImporter &GetClangASTImporter() { return m_importer; } + + void Dump(Stream &stream); + +private: + clang::Decl *TryGetDecl(PdbSymUid uid) const; + + using TypeIndex = llvm::codeview::TypeIndex; + + clang::QualType + CreatePointerType(const llvm::codeview::PointerRecord &pointer); + clang::QualType + CreateModifierType(const llvm::codeview::ModifierRecord &modifier); + clang::QualType CreateArrayType(const llvm::codeview::ArrayRecord &array); + clang::QualType CreateRecordType(PdbTypeSymId id, + const llvm::codeview::TagRecord &record); + clang::QualType CreateEnumType(PdbTypeSymId id, + const llvm::codeview::EnumRecord &record); + clang::QualType + CreateFunctionType(TypeIndex args_type_idx, TypeIndex return_type_idx, + llvm::codeview::CallingConvention calling_convention); + clang::QualType CreateType(PdbTypeSymId type); + + void CreateFunctionParameters(PdbCompilandSymId func_id, + clang::FunctionDecl &function_decl, + uint32_t param_count); + clang::Decl *GetOrCreateSymbolForId(PdbCompilandSymId id); + clang::VarDecl *CreateVariableDecl(PdbSymUid uid, + llvm::codeview::CVSymbol sym, + clang::DeclContext &scope); + clang::NamespaceDecl *GetOrCreateNamespaceDecl(const char *name, + clang::DeclContext &context); + clang::FunctionDecl *CreateFunctionDeclFromId(PdbTypeSymId func_tid, + PdbCompilandSymId func_sid); + clang::FunctionDecl * + CreateFunctionDecl(PdbCompilandSymId func_id, llvm::StringRef func_name, + TypeIndex func_ti, CompilerType func_ct, + uint32_t param_count, clang::StorageClass func_storage, + bool is_inline, clang::DeclContext *parent); + void ParseNamespace(clang::DeclContext &parent); + void ParseAllTypes(); + void ParseAllFunctionsAndNonLocalVars(); + void ParseDeclsForSimpleContext(clang::DeclContext &context); + void ParseBlockChildren(PdbCompilandSymId block_id); + + std::pair<clang::DeclContext *, std::string> + CreateDeclInfoForType(const llvm::codeview::TagRecord &record, TypeIndex ti); + std::pair<clang::DeclContext *, std::string> + CreateDeclInfoForUndecoratedName(llvm::StringRef uname); + clang::QualType CreateSimpleType(TypeIndex ti); + + TypeSystemClang &m_clang; + + ClangASTImporter m_importer; + llvm::once_flag m_parse_functions_and_non_local_vars; + llvm::once_flag m_parse_all_types; + llvm::DenseMap<clang::Decl *, DeclStatus> m_decl_to_status; + llvm::DenseMap<lldb::user_id_t, clang::Decl *> m_uid_to_decl; + llvm::DenseMap<lldb::user_id_t, clang::QualType> m_uid_to_type; + + // From class/struct's opaque_compiler_type_t to a set containing the pairs of + // method's name and CompilerType. + llvm::DenseMap<lldb::opaque_compiler_type_t, + llvm::SmallSet<std::pair<llvm::StringRef, CompilerType>, 8>> + m_cxx_record_map; + llvm::DenseSet<clang::NamespaceDecl *> m_parsed_namespaces; +}; + +} // namespace npdb +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBASTBUILDER_H diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp new file mode 100644 index 000000000000..f28509acbf79 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp @@ -0,0 +1,103 @@ +//===-- PdbFPOProgramToDWARFExpression.cpp --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "PdbFPOProgramToDWARFExpression.h" +#include "CodeViewRegisterMapping.h" + +#include "lldb/Symbol/PostfixExpression.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/Stream.h" +#include "llvm/ADT/DenseMap.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/EnumTables.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::postfix; + +static uint32_t ResolveLLDBRegisterNum(llvm::StringRef reg_name, llvm::Triple::ArchType arch_type) { + // lookup register name to get lldb register number + llvm::codeview::CPUType cpu_type; + switch (arch_type) { + case llvm::Triple::ArchType::aarch64: + cpu_type = llvm::codeview::CPUType::ARM64; + break; + + default: + cpu_type = llvm::codeview::CPUType::X64; + break; + } + + llvm::ArrayRef<llvm::EnumEntry<uint16_t>> register_names = + llvm::codeview::getRegisterNames(cpu_type); + auto it = llvm::find_if( + register_names, + [®_name](const llvm::EnumEntry<uint16_t> ®ister_entry) { + return reg_name.compare_insensitive(register_entry.Name) == 0; + }); + + if (it == register_names.end()) + return LLDB_INVALID_REGNUM; + + auto reg_id = static_cast<llvm::codeview::RegisterId>(it->Value); + return npdb::GetLLDBRegisterNumber(arch_type, reg_id); +} + +static Node *ResolveFPOProgram(llvm::StringRef program, + llvm::StringRef register_name, + llvm::Triple::ArchType arch_type, + llvm::BumpPtrAllocator &alloc) { + std::vector<std::pair<llvm::StringRef, Node *>> parsed = + postfix::ParseFPOProgram(program, alloc); + + for (auto it = parsed.begin(), end = parsed.end(); it != end; ++it) { + // Emplace valid dependent subtrees to make target assignment independent + // from predecessors. Resolve all other SymbolNodes as registers. + bool success = + ResolveSymbols(it->second, [&](SymbolNode &symbol) -> Node * { + for (const auto &pair : llvm::make_range(parsed.begin(), it)) { + if (pair.first == symbol.GetName()) + return pair.second; + } + + uint32_t reg_num = + ResolveLLDBRegisterNum(symbol.GetName().drop_front(1), arch_type); + + if (reg_num == LLDB_INVALID_REGNUM) + return nullptr; + + return MakeNode<RegisterNode>(alloc, reg_num); + }); + if (!success) + return nullptr; + + if (it->first == register_name) { + // found target assignment program - no need to parse further + return it->second; + } + } + + return nullptr; +} + +bool lldb_private::npdb::TranslateFPOProgramToDWARFExpression( + llvm::StringRef program, llvm::StringRef register_name, + llvm::Triple::ArchType arch_type, Stream &stream) { + llvm::BumpPtrAllocator node_alloc; + Node *target_program = + ResolveFPOProgram(program, register_name, arch_type, node_alloc); + if (target_program == nullptr) { + return false; + } + + ToDWARF(*target_program, stream); + return true; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.h new file mode 100644 index 000000000000..f6849f2083cc --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.h @@ -0,0 +1,28 @@ +//===-- PdbFPOProgramToDWARFExpression.h ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBFPOPROGRAMTODWARFEXPRESSION_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBFPOPROGRAMTODWARFEXPRESSION_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/TargetParser/Triple.h" + +namespace lldb_private { +class Stream; + +namespace npdb { + +bool TranslateFPOProgramToDWARFExpression(llvm::StringRef program, + llvm::StringRef register_name, + llvm::Triple::ArchType arch_type, + lldb_private::Stream &stream); + +} // namespace npdb +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp new file mode 100644 index 000000000000..ea778fc6cca6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp @@ -0,0 +1,196 @@ +//===-- PdbIndex.cpp ------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "PdbIndex.h" +#include "PdbUtil.h" + +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Error.h" + +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/lldb-defines.h" +#include <optional> + +using namespace lldb_private; +using namespace lldb_private::npdb; +using namespace llvm::codeview; +using namespace llvm::pdb; + +PdbIndex::PdbIndex() : m_cus(*this), m_va_to_modi(m_allocator) {} + +#define ASSIGN_PTR_OR_RETURN(result_ptr, expr) \ + { \ + auto expected_result = expr; \ + if (!expected_result) \ + return expected_result.takeError(); \ + result_ptr = &expected_result.get(); \ + } + +llvm::Expected<std::unique_ptr<PdbIndex>> +PdbIndex::create(llvm::pdb::PDBFile *file) { + lldbassert(file); + + std::unique_ptr<PdbIndex> result(new PdbIndex()); + ASSIGN_PTR_OR_RETURN(result->m_dbi, file->getPDBDbiStream()); + ASSIGN_PTR_OR_RETURN(result->m_tpi, file->getPDBTpiStream()); + ASSIGN_PTR_OR_RETURN(result->m_ipi, file->getPDBIpiStream()); + ASSIGN_PTR_OR_RETURN(result->m_info, file->getPDBInfoStream()); + ASSIGN_PTR_OR_RETURN(result->m_publics, file->getPDBPublicsStream()); + ASSIGN_PTR_OR_RETURN(result->m_globals, file->getPDBGlobalsStream()); + ASSIGN_PTR_OR_RETURN(result->m_symrecords, file->getPDBSymbolStream()); + + result->m_tpi->buildHashMap(); + + result->m_file = file; + + return std::move(result); +} + +lldb::addr_t PdbIndex::MakeVirtualAddress(uint16_t segment, + uint32_t offset) const { + uint32_t max_section = dbi().getSectionHeaders().size(); + // Segment indices are 1-based. + // If this is an absolute symbol, it's indicated by the magic section index + // |max_section+1|. In this case, the offset is meaningless, so just return. + if (segment == 0 || segment > max_section) + return LLDB_INVALID_ADDRESS; + + const llvm::object::coff_section &cs = dbi().getSectionHeaders()[segment - 1]; + return m_load_address + static_cast<lldb::addr_t>(cs.VirtualAddress) + + static_cast<lldb::addr_t>(offset); +} + +std::optional<uint16_t> PdbIndex::GetModuleIndexForAddr(uint16_t segment, + uint32_t offset) const { + return GetModuleIndexForVa(MakeVirtualAddress(segment, offset)); +} + +std::optional<uint16_t> PdbIndex::GetModuleIndexForVa(lldb::addr_t va) const { + auto iter = m_va_to_modi.find(va); + if (iter == m_va_to_modi.end()) + return std::nullopt; + + return iter.value(); +} + +void PdbIndex::ParseSectionContribs() { + class Visitor : public ISectionContribVisitor { + PdbIndex &m_ctx; + llvm::IntervalMap<uint64_t, uint16_t> &m_imap; + + public: + Visitor(PdbIndex &ctx, llvm::IntervalMap<uint64_t, uint16_t> &imap) + : m_ctx(ctx), m_imap(imap) {} + + void visit(const SectionContrib &C) override { + if (C.Size == 0) + return; + + uint64_t va = m_ctx.MakeVirtualAddress(C.ISect, C.Off); + if (va == LLDB_INVALID_ADDRESS) + return; + uint64_t end = va + C.Size; + // IntervalMap's start and end represent a closed range, not a half-open + // range, so we have to subtract 1. + m_imap.insert(va, end - 1, C.Imod); + } + void visit(const SectionContrib2 &C) override { visit(C.Base); } + }; + Visitor v(*this, m_va_to_modi); + dbi().visitSectionContributions(v); +} + +void PdbIndex::BuildAddrToSymbolMap(CompilandIndexItem &cci) { + lldbassert(cci.m_symbols_by_va.empty() && + "Addr to symbol map is already built!"); + uint16_t modi = cci.m_id.modi; + const CVSymbolArray &syms = cci.m_debug_stream.getSymbolArray(); + for (auto iter = syms.begin(); iter != syms.end(); ++iter) { + if (!SymbolHasAddress(*iter)) + continue; + + SegmentOffset so = GetSegmentAndOffset(*iter); + lldb::addr_t va = MakeVirtualAddress(so.segment, so.offset); + if (va == LLDB_INVALID_ADDRESS) + continue; + + PdbCompilandSymId cu_sym_id(modi, iter.offset()); + + // It's rare, but we could have multiple symbols with the same address + // because of identical comdat folding. Right now, the first one will win. + cci.m_symbols_by_va.insert(std::make_pair(va, PdbSymUid(cu_sym_id))); + } +} + +std::vector<SymbolAndUid> PdbIndex::FindSymbolsByVa(lldb::addr_t va) { + std::vector<SymbolAndUid> result; + + std::optional<uint16_t> modi = GetModuleIndexForVa(va); + if (!modi) + return result; + + CompilandIndexItem &cci = compilands().GetOrCreateCompiland(*modi); + if (cci.m_symbols_by_va.empty()) + BuildAddrToSymbolMap(cci); + + // The map is sorted by starting address of the symbol. So for example + // we could (in theory) have this situation + // + // [------------------] + // [----------] + // [-----------] + // [-------------] + // [----] + // [-----] + // ^ Address we're searching for + // In order to find this, we use the upper_bound of the key value which would + // be the first symbol whose starting address is higher than the element we're + // searching for. + + auto ub = cci.m_symbols_by_va.upper_bound(va); + + for (auto iter = cci.m_symbols_by_va.begin(); iter != ub; ++iter) { + PdbCompilandSymId cu_sym_id = iter->second.asCompilandSym(); + CVSymbol sym = ReadSymbolRecord(cu_sym_id); + + SegmentOffsetLength sol; + if (SymbolIsCode(sym)) + sol = GetSegmentOffsetAndLength(sym); + else + sol.so = GetSegmentAndOffset(sym); + + lldb::addr_t start = MakeVirtualAddress(sol.so.segment, sol.so.offset); + if (start == LLDB_INVALID_ADDRESS) + continue; + + lldb::addr_t end = start + sol.length; + if (va >= start && va < end) + result.push_back({std::move(sym), iter->second}); + } + + return result; +} + +CVSymbol PdbIndex::ReadSymbolRecord(PdbCompilandSymId cu_sym) const { + const CompilandIndexItem *cci = compilands().GetCompiland(cu_sym.modi); + auto iter = cci->m_debug_stream.getSymbolArray().at(cu_sym.offset); + lldbassert(iter != cci->m_debug_stream.getSymbolArray().end()); + return *iter; +} + +CVSymbol PdbIndex::ReadSymbolRecord(PdbGlobalSymId global) const { + return symrecords().readRecord(global.offset); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.h new file mode 100644 index 000000000000..796aa4c8dfd1 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.h @@ -0,0 +1,159 @@ +//===-- PdbIndex.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBINDEX_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBINDEX_H + +#include "lldb/lldb-types.h" +#include "llvm/ADT/IntervalMap.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +#include "CompileUnitIndex.h" +#include "PdbSymUid.h" + +#include <map> +#include <memory> +#include <optional> + +namespace llvm { +namespace pdb { +class DbiStream; +class TpiStream; +class InfoStream; +class PublicsStream; +class GlobalsStream; +class SymbolStream; +} // namespace pdb +} // namespace llvm + +namespace lldb_private { +namespace npdb { +struct SegmentOffset; + +/// PdbIndex - Lazy access to the important parts of a PDB file. +/// +/// This is a layer on top of LLVM's native PDB support libraries which cache +/// certain data when it is accessed the first time. The entire PDB file is +/// mapped into memory, and the underlying support libraries vend out memory +/// that is always backed by the file, so it is safe to hold StringRefs and +/// ArrayRefs into the backing memory as long as the PdbIndex instance is +/// alive. +class PdbIndex { + + /// The underlying PDB file. + llvm::pdb::PDBFile *m_file = nullptr; + + /// The DBI stream. This contains general high level information about the + /// features present in the PDB file, compile units (such as the information + /// necessary to locate full symbol information for each compile unit), + /// section contributions, and other data which is not specifically symbol or + /// type records. + llvm::pdb::DbiStream *m_dbi = nullptr; + + /// TPI (types) and IPI (indices) streams. These are both in the exact same + /// format with different data. Most type records are stored in the TPI + /// stream but certain specific types of records are stored in the IPI stream. + /// The IPI stream records can refer to the records in the TPI stream, but not + /// the other way around. + llvm::pdb::TpiStream *m_tpi = nullptr; + llvm::pdb::TpiStream *m_ipi = nullptr; + + /// This is called the "PDB Stream" in the Microsoft reference implementation. + /// It contains information about the structure of the file, as well as fields + /// used to match EXE and PDB. + llvm::pdb::InfoStream *m_info = nullptr; + + /// Publics stream. Is actually a serialized hash table where the keys are + /// addresses of symbols in the executable, and values are a record containing + /// mangled names and an index which can be used to locate more detailed info + /// about the symbol in the Symbol Records stream. The publics stream only + /// contains info about externally visible symbols. + llvm::pdb::PublicsStream *m_publics = nullptr; + + /// Globals stream. Contrary to its name, this does not contain information + /// about all "global variables" or "global functions". Rather, it is the + /// "global symbol table", i.e. it contains information about *every* symbol + /// in the executable. It is a hash table keyed on name, whose values are + /// indices into the symbol records stream to find the full record. + llvm::pdb::GlobalsStream *m_globals = nullptr; + + /// Symbol records stream. The publics and globals stream refer to records + /// in this stream. For some records, like constants and typedefs, the + /// complete record lives in this stream. For other symbol types, such as + /// functions, data, and other things that have been materialied into a + /// specific compile unit, the records here simply provide a reference + /// necessary to locate the full information. + llvm::pdb::SymbolStream *m_symrecords = nullptr; + + /// Index of all compile units, mapping identifier to |CompilandIndexItem| + /// instance. + CompileUnitIndex m_cus; + + /// An allocator for the interval maps + llvm::IntervalMap<lldb::addr_t, uint32_t>::Allocator m_allocator; + + /// Maps virtual address to module index + llvm::IntervalMap<lldb::addr_t, uint16_t> m_va_to_modi; + + /// The address at which the program has been loaded into memory. + lldb::addr_t m_load_address = 0; + + PdbIndex(); + + void BuildAddrToSymbolMap(CompilandIndexItem &cci); + +public: + static llvm::Expected<std::unique_ptr<PdbIndex>> create(llvm::pdb::PDBFile *); + + void SetLoadAddress(lldb::addr_t addr) { m_load_address = addr; } + lldb::addr_t GetLoadAddress() const { return m_load_address; } + void ParseSectionContribs(); + + llvm::pdb::PDBFile &pdb() { return *m_file; } + const llvm::pdb::PDBFile &pdb() const { return *m_file; } + + llvm::pdb::DbiStream &dbi() { return *m_dbi; } + const llvm::pdb::DbiStream &dbi() const { return *m_dbi; } + + llvm::pdb::TpiStream &tpi() { return *m_tpi; } + const llvm::pdb::TpiStream &tpi() const { return *m_tpi; } + + llvm::pdb::TpiStream &ipi() { return *m_ipi; } + const llvm::pdb::TpiStream &ipi() const { return *m_ipi; } + + llvm::pdb::InfoStream &info() { return *m_info; } + const llvm::pdb::InfoStream &info() const { return *m_info; } + + llvm::pdb::PublicsStream &publics() { return *m_publics; } + const llvm::pdb::PublicsStream &publics() const { return *m_publics; } + + llvm::pdb::GlobalsStream &globals() { return *m_globals; } + const llvm::pdb::GlobalsStream &globals() const { return *m_globals; } + + llvm::pdb::SymbolStream &symrecords() { return *m_symrecords; } + const llvm::pdb::SymbolStream &symrecords() const { return *m_symrecords; } + + CompileUnitIndex &compilands() { return m_cus; } + const CompileUnitIndex &compilands() const { return m_cus; } + + lldb::addr_t MakeVirtualAddress(uint16_t segment, uint32_t offset) const; + + std::vector<SymbolAndUid> FindSymbolsByVa(lldb::addr_t va); + + llvm::codeview::CVSymbol ReadSymbolRecord(PdbCompilandSymId cu_sym) const; + llvm::codeview::CVSymbol ReadSymbolRecord(PdbGlobalSymId global) const; + + std::optional<uint16_t> GetModuleIndexForAddr(uint16_t segment, + uint32_t offset) const; + std::optional<uint16_t> GetModuleIndexForVa(lldb::addr_t va) const; +}; +} // namespace npdb +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.cpp new file mode 100644 index 000000000000..67397d707110 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.cpp @@ -0,0 +1,160 @@ +//===-- PdbSymUid.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "PdbSymUid.h" + +using namespace lldb_private; +using namespace lldb_private::npdb; +using namespace llvm::codeview; + +namespace { +struct GenericIdRepr { + uint64_t tag : 4; + uint64_t data : 60; +}; + +struct CompilandIdRepr { + uint64_t tag : 4; + uint64_t modi : 16; + uint64_t unused : 44; +}; + +struct CompilandSymIdRepr { + uint64_t tag : 4; + uint64_t modi : 16; + uint64_t offset : 32; + uint64_t unused : 12; +}; + +struct GlobalSymIdRepr { + uint64_t tag : 4; + uint64_t offset : 32; + uint64_t pub : 1; + uint64_t unused : 27; +}; + +struct TypeSymIdRepr { + uint64_t tag : 4; + uint64_t index : 32; + uint64_t ipi : 1; + uint64_t unused : 27; +}; + +struct FieldListMemberIdRepr { + uint64_t tag : 4; + uint64_t index : 32; + uint64_t offset : 16; + uint64_t unused : 12; +}; + +static_assert(sizeof(CompilandIdRepr) == 8, "Invalid structure size!"); +static_assert(sizeof(CompilandSymIdRepr) == 8, "Invalid structure size!"); +static_assert(sizeof(GlobalSymIdRepr) == 8, "Invalid structure size!"); +static_assert(sizeof(TypeSymIdRepr) == 8, "Invalid structure size!"); +static_assert(sizeof(FieldListMemberIdRepr) == 8, "Invalid structure size!"); +} // namespace + +template <typename OutT, typename InT> static OutT repr_cast(const InT &value) { + OutT result; + ::memcpy(&result, &value, sizeof(value)); + return result; +} + +PdbSymUid::PdbSymUid(const PdbCompilandId &cid) { + CompilandIdRepr repr; + ::memset(&repr, 0, sizeof(repr)); + repr.modi = cid.modi; + repr.tag = static_cast<uint64_t>(PdbSymUidKind::Compiland); + m_repr = repr_cast<uint64_t>(repr); +} + +PdbSymUid::PdbSymUid(const PdbCompilandSymId &csid) { + CompilandSymIdRepr repr; + ::memset(&repr, 0, sizeof(repr)); + repr.modi = csid.modi; + repr.offset = csid.offset; + repr.tag = static_cast<uint64_t>(PdbSymUidKind::CompilandSym); + m_repr = repr_cast<uint64_t>(repr); +} + +PdbSymUid::PdbSymUid(const PdbGlobalSymId &gsid) { + GlobalSymIdRepr repr; + ::memset(&repr, 0, sizeof(repr)); + repr.pub = gsid.is_public; + repr.offset = gsid.offset; + repr.tag = static_cast<uint64_t>(PdbSymUidKind::GlobalSym); + m_repr = repr_cast<uint64_t>(repr); +} + +PdbSymUid::PdbSymUid(const PdbTypeSymId &tsid) { + TypeSymIdRepr repr; + ::memset(&repr, 0, sizeof(repr)); + repr.index = tsid.index.getIndex(); + repr.ipi = tsid.is_ipi; + repr.tag = static_cast<uint64_t>(PdbSymUidKind::Type); + m_repr = repr_cast<uint64_t>(repr); +} + +PdbSymUid::PdbSymUid(const PdbFieldListMemberId &flmid) { + FieldListMemberIdRepr repr; + ::memset(&repr, 0, sizeof(repr)); + repr.index = flmid.index.getIndex(); + repr.offset = flmid.offset; + repr.tag = static_cast<uint64_t>(PdbSymUidKind::FieldListMember); + m_repr = repr_cast<uint64_t>(repr); +} + +PdbSymUidKind PdbSymUid::kind() const { + GenericIdRepr generic = repr_cast<GenericIdRepr>(m_repr); + return static_cast<PdbSymUidKind>(generic.tag); +} + +PdbCompilandId PdbSymUid::asCompiland() const { + assert(kind() == PdbSymUidKind::Compiland); + auto repr = repr_cast<CompilandIdRepr>(m_repr); + PdbCompilandId result; + result.modi = repr.modi; + return result; +} + +PdbCompilandSymId PdbSymUid::asCompilandSym() const { + assert(kind() == PdbSymUidKind::CompilandSym); + auto repr = repr_cast<CompilandSymIdRepr>(m_repr); + PdbCompilandSymId result; + result.modi = repr.modi; + result.offset = repr.offset; + return result; +} + +PdbGlobalSymId PdbSymUid::asGlobalSym() const { + assert(kind() == PdbSymUidKind::GlobalSym || + kind() == PdbSymUidKind::PublicSym); + auto repr = repr_cast<GlobalSymIdRepr>(m_repr); + PdbGlobalSymId result; + result.is_public = repr.pub; + result.offset = repr.offset; + return result; +} + +PdbTypeSymId PdbSymUid::asTypeSym() const { + assert(kind() == PdbSymUidKind::Type); + auto repr = repr_cast<TypeSymIdRepr>(m_repr); + PdbTypeSymId result; + result.index.setIndex(repr.index); + result.is_ipi = repr.ipi; + return result; +} + +PdbFieldListMemberId PdbSymUid::asFieldListMember() const { + assert(kind() == PdbSymUidKind::FieldListMember); + auto repr = repr_cast<FieldListMemberIdRepr>(m_repr); + PdbFieldListMemberId result; + result.index.setIndex(repr.index); + result.offset = repr.offset; + return result; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h new file mode 100644 index 000000000000..3accd38d710e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h @@ -0,0 +1,125 @@ +//===-- PdbSymUid.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// A unique identification scheme for Pdb records. +// The scheme is to partition a 64-bit integer into an 8-bit tag field, which +// will contain some value from the PDB_SymType enumeration. The format of the +// other 48-bits depend on the tag, but must be sufficient to locate the +// corresponding entry in the underlying PDB file quickly. For example, for +// a compile unit, we use 2 bytes to represent the index, which allows fast +// access to the compile unit's information. +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBSYMUID_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBSYMUID_H + +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/Support/Compiler.h" + +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/lldb-types.h" + +namespace lldb_private { +namespace npdb { + +enum class PdbSymUidKind : uint8_t { + Compiland, + CompilandSym, + PublicSym, + GlobalSym, + Type, + FieldListMember +}; + +struct PdbCompilandId { + // 0-based index of module in PDB + uint16_t modi; +}; + +struct PdbCompilandSymId { + PdbCompilandSymId() = default; + PdbCompilandSymId(uint16_t modi, uint32_t offset) + : modi(modi), offset(offset) {} + // 0-based index of module in PDB + uint16_t modi = 0; + + // Offset of symbol's record in module stream. This is + // offset by 4 from the CVSymbolArray's notion of offset + // due to the debug magic at the beginning of the stream. + uint32_t offset = 0; +}; + +struct PdbGlobalSymId { + PdbGlobalSymId() = default; + PdbGlobalSymId(uint32_t offset, bool is_public) + : offset(offset), is_public(is_public) {} + + // Offset of symbol's record in globals or publics stream. + uint32_t offset = 0; + + // True if this symbol is in the public stream, false if it's in the globals + // stream. + bool is_public = false; +}; + +struct PdbTypeSymId { + PdbTypeSymId() = default; + PdbTypeSymId(llvm::codeview::TypeIndex index, bool is_ipi = false) + : index(index), is_ipi(is_ipi) {} + + // The index of the of the type in the TPI or IPI stream. + llvm::codeview::TypeIndex index; + + // True if this symbol comes from the IPI stream, false if it's from the TPI + // stream. + bool is_ipi = false; +}; + +struct PdbFieldListMemberId { + // The TypeIndex of the LF_FIELDLIST record. + llvm::codeview::TypeIndex index; + + // The offset from the beginning of the LF_FIELDLIST record to this record. + uint16_t offset = 0; +}; + +class PdbSymUid { + uint64_t m_repr = 0; + +public: + PdbSymUid() = default; + PdbSymUid(uint64_t repr) : m_repr(repr) {} + PdbSymUid(const PdbCompilandId &cid); + PdbSymUid(const PdbCompilandSymId &csid); + PdbSymUid(const PdbGlobalSymId &gsid); + PdbSymUid(const PdbTypeSymId &tsid); + PdbSymUid(const PdbFieldListMemberId &flmid); + + uint64_t toOpaqueId() const { return m_repr; } + + PdbSymUidKind kind() const; + + PdbCompilandId asCompiland() const; + PdbCompilandSymId asCompilandSym() const; + PdbGlobalSymId asGlobalSym() const; + PdbTypeSymId asTypeSym() const; + PdbFieldListMemberId asFieldListMember() const; +}; + +template <typename T> uint64_t toOpaqueUid(const T &cid) { + return PdbSymUid(cid).toOpaqueId(); +} + +struct SymbolAndUid { + llvm::codeview::CVSymbol sym; + PdbSymUid uid; +}; +} // namespace npdb +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp new file mode 100644 index 000000000000..888bd89a7262 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp @@ -0,0 +1,1120 @@ +//===-- PdbUtil.cpp -------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "PdbUtil.h" + +#include "DWARFLocationExpression.h" +#include "PdbIndex.h" +#include "PdbSymUid.h" + +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" + +#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h" +#include "Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/lldb-enumerations.h" + +using namespace lldb_private; +using namespace lldb_private::npdb; +using namespace llvm::codeview; +using namespace llvm::pdb; + +// The returned range list is guaranteed to be sorted and no overlaps between +// adjacent ranges because fields in LocalVariableAddrGap are unsigned integers. +static Variable::RangeList +MakeRangeList(const PdbIndex &index, const LocalVariableAddrRange &range, + llvm::ArrayRef<LocalVariableAddrGap> gaps) { + lldb::addr_t start = + index.MakeVirtualAddress(range.ISectStart, range.OffsetStart); + if (start == LLDB_INVALID_ADDRESS) + return {}; + lldb::addr_t end = start + range.Range; + + Variable::RangeList result; + while (!gaps.empty()) { + const LocalVariableAddrGap &gap = gaps.front(); + lldb::addr_t gap_start = start + gap.GapStartOffset; + result.Append(start, gap_start - start); + start = gap_start + gap.Range; + gaps = gaps.drop_front(); + } + + result.Append(start, end - start); + return result; +} + +namespace { +struct MemberLocations { + std::map<uint64_t, MemberValLocation> offset_to_location; + DWARFExpression expr; + bool is_dwarf = false; + + MemberLocations() = default; + MemberLocations(const DWARFExpression &expr) : expr(expr), is_dwarf(true) {} + MemberLocations(uint64_t offset, const MemberValLocation &member_loc) { + insert(offset, member_loc); + } + + void insert(uint64_t offset, const MemberValLocation &member_loc) { + offset_to_location[offset] = member_loc; + } + + struct Comparator { + public: + bool operator()(const MemberLocations &, const MemberLocations &) const { + return false; + } + }; +}; + +// A range map with address ranges to a map of pair of offset and locaitons. +typedef RangeDataVector<lldb::addr_t, lldb::addr_t, MemberLocations, 0, + MemberLocations::Comparator> + RangeMap; + +void AddMemberLocationRanges(RangeMap &location_map, uint64_t offset, + MemberValLocation member_loc, + const Variable::RangeList &ranges) { + RangeMap new_location_map; + auto add_overlap_region = [&](lldb::addr_t base, lldb::addr_t end, + RangeMap::Entry *entry) { + RangeMap::Entry overlap_region = {base, end - base, entry->data}; + overlap_region.data.insert(offset, member_loc); + new_location_map.Append(overlap_region); + }; + + for (const auto &range : ranges) { + lldb::addr_t base = range.GetRangeBase(); + lldb::addr_t end = range.GetRangeEnd(); + uint32_t base_idx = location_map.FindEntryIndexThatContainsOrFollows(base); + while (auto *entry = location_map.GetMutableEntryAtIndex(base_idx)) { + if (base >= end || entry->base >= end) + break; + if (entry->data.is_dwarf) + base = entry->GetRangeEnd(); + else { + lldb::addr_t entry_end = entry->GetRangeEnd(); + if (base > entry->base) { + if (end < entry_end) + new_location_map.Append({end, entry_end - end, entry->data}); + add_overlap_region(base, end < entry_end ? end : entry_end, entry); + entry->SetRangeEnd(base); + } else if (base < entry->base) { + new_location_map.Append( + {base, entry->base - base, {offset, member_loc}}); + if (entry_end == end) + entry->data.insert(offset, member_loc); + else { + add_overlap_region(entry->base, end, entry); + entry->ShrinkFront(end - entry->base); + } + } else { + if (end < entry_end) { + new_location_map.Append({end, entry_end, entry->data}); + entry->SetRangeEnd(end); + } + entry->data.insert(offset, member_loc); + } + base = entry_end; + } + ++base_idx; + } + if (base >= end) + continue; + new_location_map.Append({base, end - base, {offset, member_loc}}); + } + for (const auto &entry : new_location_map) + location_map.Append(entry); + if (!new_location_map.IsEmpty()) + location_map.Sort(); +} + +void AddDwarfRange(RangeMap &location_map, const DWARFExpression &expr, + const Variable::RangeList &ranges) { + if (!expr.IsValid()) + return; + RangeMap new_location_map; + for (const auto &range : ranges) { + lldb::addr_t base = range.GetRangeBase(); + lldb::addr_t end = range.GetRangeEnd(); + uint32_t base_idx = location_map.FindEntryIndexThatContains(base); + uint32_t end_idx = location_map.FindEntryIndexThatContains(end - 1); + // range is within an entry. + if (base_idx == end_idx && base_idx != UINT32_MAX) { + auto *entry = location_map.GetMutableEntryAtIndex(base_idx); + if (base > entry->base) { + new_location_map.Append({entry->base, base - entry->base, entry->data}); + entry->ShrinkFront(base - entry->base); + } + if (end == entry->GetRangeEnd()) + entry->data = expr; + else { + entry->ShrinkFront(end - base); + new_location_map.Append({base, end - base, expr}); + } + continue; + } + base_idx = location_map.FindEntryIndexThatContainsOrFollows(base); + if (auto *entry = location_map.GetMutableEntryAtIndex(base_idx)) { + if (entry->Contains(base) && entry->base != base) { + entry->SetRangeEnd(base); + ++base_idx; + } + } + end_idx = location_map.FindEntryIndexThatContainsOrFollows(end - 1); + if (auto *entry = location_map.GetMutableEntryAtIndex(end_idx)) { + if (entry->Contains(end - 1)) { + if (entry->GetRangeEnd() == end) + ++end_idx; + else + entry->ShrinkFront(end - entry->base); + } + } + + if (end_idx == UINT32_MAX) + end_idx = location_map.GetSize(); + // Erase existing ranges covered by new range. + location_map.Erase(base_idx, end_idx); + new_location_map.Append({base, end - base, expr}); + } + + for (const auto &entry : new_location_map) + location_map.Append(entry); + location_map.Sort(); +} +} // namespace + +CVTagRecord CVTagRecord::create(CVType type) { + assert(IsTagRecord(type) && "type is not a tag record!"); + switch (type.kind()) { + case LF_CLASS: + case LF_STRUCTURE: + case LF_INTERFACE: { + ClassRecord cr; + llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(type, cr)); + return CVTagRecord(std::move(cr)); + } + case LF_UNION: { + UnionRecord ur; + llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(type, ur)); + return CVTagRecord(std::move(ur)); + } + case LF_ENUM: { + EnumRecord er; + llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(type, er)); + return CVTagRecord(std::move(er)); + } + default: + llvm_unreachable("Unreachable!"); + } +} + +CVTagRecord::CVTagRecord(ClassRecord &&c) + : cvclass(std::move(c)), + m_kind(cvclass.Kind == TypeRecordKind::Struct ? Struct : Class) {} +CVTagRecord::CVTagRecord(UnionRecord &&u) + : cvunion(std::move(u)), m_kind(Union) {} +CVTagRecord::CVTagRecord(EnumRecord &&e) : cvenum(std::move(e)), m_kind(Enum) {} + +PDB_SymType lldb_private::npdb::CVSymToPDBSym(SymbolKind kind) { + switch (kind) { + case S_COMPILE3: + case S_OBJNAME: + return PDB_SymType::CompilandDetails; + case S_ENVBLOCK: + return PDB_SymType::CompilandEnv; + case S_THUNK32: + case S_TRAMPOLINE: + return PDB_SymType::Thunk; + case S_COFFGROUP: + return PDB_SymType::CoffGroup; + case S_EXPORT: + return PDB_SymType::Export; + case S_LPROC32: + case S_GPROC32: + case S_LPROC32_DPC: + return PDB_SymType::Function; + case S_PUB32: + return PDB_SymType::PublicSymbol; + case S_INLINESITE: + return PDB_SymType::InlineSite; + case S_LOCAL: + case S_BPREL32: + case S_REGREL32: + case S_MANCONSTANT: + case S_CONSTANT: + case S_LDATA32: + case S_GDATA32: + case S_LMANDATA: + case S_GMANDATA: + case S_LTHREAD32: + case S_GTHREAD32: + return PDB_SymType::Data; + case S_BLOCK32: + return PDB_SymType::Block; + case S_LABEL32: + return PDB_SymType::Label; + case S_CALLSITEINFO: + return PDB_SymType::CallSite; + case S_HEAPALLOCSITE: + return PDB_SymType::HeapAllocationSite; + case S_CALLEES: + return PDB_SymType::Callee; + case S_CALLERS: + return PDB_SymType::Caller; + default: + lldbassert(false && "Invalid symbol record kind!"); + } + return PDB_SymType::None; +} + +PDB_SymType lldb_private::npdb::CVTypeToPDBType(TypeLeafKind kind) { + switch (kind) { + case LF_ARRAY: + return PDB_SymType::ArrayType; + case LF_ARGLIST: + return PDB_SymType::FunctionSig; + case LF_BCLASS: + return PDB_SymType::BaseClass; + case LF_BINTERFACE: + return PDB_SymType::BaseInterface; + case LF_CLASS: + case LF_STRUCTURE: + case LF_INTERFACE: + case LF_UNION: + return PDB_SymType::UDT; + case LF_POINTER: + return PDB_SymType::PointerType; + case LF_ENUM: + return PDB_SymType::Enum; + case LF_PROCEDURE: + return PDB_SymType::FunctionSig; + case LF_BITFIELD: + return PDB_SymType::BuiltinType; + default: + lldbassert(false && "Invalid type record kind!"); + } + return PDB_SymType::None; +} + +bool lldb_private::npdb::SymbolHasAddress(const CVSymbol &sym) { + switch (sym.kind()) { + case S_GPROC32: + case S_LPROC32: + case S_GPROC32_ID: + case S_LPROC32_ID: + case S_LPROC32_DPC: + case S_LPROC32_DPC_ID: + case S_THUNK32: + case S_TRAMPOLINE: + case S_COFFGROUP: + case S_BLOCK32: + case S_LABEL32: + case S_CALLSITEINFO: + case S_HEAPALLOCSITE: + case S_LDATA32: + case S_GDATA32: + case S_LMANDATA: + case S_GMANDATA: + case S_LTHREAD32: + case S_GTHREAD32: + return true; + default: + return false; + } +} + +bool lldb_private::npdb::SymbolIsCode(const CVSymbol &sym) { + switch (sym.kind()) { + case S_GPROC32: + case S_LPROC32: + case S_GPROC32_ID: + case S_LPROC32_ID: + case S_LPROC32_DPC: + case S_LPROC32_DPC_ID: + case S_THUNK32: + case S_TRAMPOLINE: + case S_COFFGROUP: + case S_BLOCK32: + return true; + default: + return false; + } +} + +template <typename RecordT> RecordT createRecord(const CVSymbol &sym) { + RecordT record(static_cast<SymbolRecordKind>(sym.kind())); + cantFail(SymbolDeserializer::deserializeAs<RecordT>(sym, record)); + return record; +} + +template <typename RecordT> +static SegmentOffset GetSegmentAndOffset(const CVSymbol &sym) { + RecordT record = createRecord<RecordT>(sym); + return {record.Segment, record.CodeOffset}; +} + +template <> +SegmentOffset GetSegmentAndOffset<TrampolineSym>(const CVSymbol &sym) { + TrampolineSym record = createRecord<TrampolineSym>(sym); + return {record.ThunkSection, record.ThunkOffset}; +} + +template <> SegmentOffset GetSegmentAndOffset<Thunk32Sym>(const CVSymbol &sym) { + Thunk32Sym record = createRecord<Thunk32Sym>(sym); + return {record.Segment, record.Offset}; +} + +template <> +SegmentOffset GetSegmentAndOffset<CoffGroupSym>(const CVSymbol &sym) { + CoffGroupSym record = createRecord<CoffGroupSym>(sym); + return {record.Segment, record.Offset}; +} + +template <> SegmentOffset GetSegmentAndOffset<DataSym>(const CVSymbol &sym) { + DataSym record = createRecord<DataSym>(sym); + return {record.Segment, record.DataOffset}; +} + +template <> +SegmentOffset GetSegmentAndOffset<ThreadLocalDataSym>(const CVSymbol &sym) { + ThreadLocalDataSym record = createRecord<ThreadLocalDataSym>(sym); + return {record.Segment, record.DataOffset}; +} + +SegmentOffset lldb_private::npdb::GetSegmentAndOffset(const CVSymbol &sym) { + switch (sym.kind()) { + case S_GPROC32: + case S_LPROC32: + case S_GPROC32_ID: + case S_LPROC32_ID: + case S_LPROC32_DPC: + case S_LPROC32_DPC_ID: + return ::GetSegmentAndOffset<ProcSym>(sym); + case S_THUNK32: + return ::GetSegmentAndOffset<Thunk32Sym>(sym); + break; + case S_TRAMPOLINE: + return ::GetSegmentAndOffset<TrampolineSym>(sym); + break; + case S_COFFGROUP: + return ::GetSegmentAndOffset<CoffGroupSym>(sym); + break; + case S_BLOCK32: + return ::GetSegmentAndOffset<BlockSym>(sym); + break; + case S_LABEL32: + return ::GetSegmentAndOffset<LabelSym>(sym); + break; + case S_CALLSITEINFO: + return ::GetSegmentAndOffset<CallSiteInfoSym>(sym); + break; + case S_HEAPALLOCSITE: + return ::GetSegmentAndOffset<HeapAllocationSiteSym>(sym); + break; + case S_LDATA32: + case S_GDATA32: + case S_LMANDATA: + case S_GMANDATA: + return ::GetSegmentAndOffset<DataSym>(sym); + break; + case S_LTHREAD32: + case S_GTHREAD32: + return ::GetSegmentAndOffset<ThreadLocalDataSym>(sym); + break; + default: + lldbassert(false && "Record does not have a segment/offset!"); + } + return {0, 0}; +} + +template <typename RecordT> +SegmentOffsetLength GetSegmentOffsetAndLength(const CVSymbol &sym) { + RecordT record = createRecord<RecordT>(sym); + return {record.Segment, record.CodeOffset, record.CodeSize}; +} + +template <> +SegmentOffsetLength +GetSegmentOffsetAndLength<TrampolineSym>(const CVSymbol &sym) { + TrampolineSym record = createRecord<TrampolineSym>(sym); + return {record.ThunkSection, record.ThunkOffset, record.Size}; +} + +template <> +SegmentOffsetLength GetSegmentOffsetAndLength<Thunk32Sym>(const CVSymbol &sym) { + Thunk32Sym record = createRecord<Thunk32Sym>(sym); + return SegmentOffsetLength{record.Segment, record.Offset, record.Length}; +} + +template <> +SegmentOffsetLength +GetSegmentOffsetAndLength<CoffGroupSym>(const CVSymbol &sym) { + CoffGroupSym record = createRecord<CoffGroupSym>(sym); + return SegmentOffsetLength{record.Segment, record.Offset, record.Size}; +} + +SegmentOffsetLength +lldb_private::npdb::GetSegmentOffsetAndLength(const CVSymbol &sym) { + switch (sym.kind()) { + case S_GPROC32: + case S_LPROC32: + case S_GPROC32_ID: + case S_LPROC32_ID: + case S_LPROC32_DPC: + case S_LPROC32_DPC_ID: + return ::GetSegmentOffsetAndLength<ProcSym>(sym); + case S_THUNK32: + return ::GetSegmentOffsetAndLength<Thunk32Sym>(sym); + break; + case S_TRAMPOLINE: + return ::GetSegmentOffsetAndLength<TrampolineSym>(sym); + break; + case S_COFFGROUP: + return ::GetSegmentOffsetAndLength<CoffGroupSym>(sym); + break; + case S_BLOCK32: + return ::GetSegmentOffsetAndLength<BlockSym>(sym); + break; + default: + lldbassert(false && "Record does not have a segment/offset/length triple!"); + } + return {0, 0, 0}; +} + +bool lldb_private::npdb::IsForwardRefUdt(CVType cvt) { + ClassRecord cr; + UnionRecord ur; + EnumRecord er; + switch (cvt.kind()) { + case LF_CLASS: + case LF_STRUCTURE: + case LF_INTERFACE: + llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr)); + return cr.isForwardRef(); + case LF_UNION: + llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur)); + return ur.isForwardRef(); + case LF_ENUM: + llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er)); + return er.isForwardRef(); + default: + return false; + } +} + +bool lldb_private::npdb::IsTagRecord(llvm::codeview::CVType cvt) { + switch (cvt.kind()) { + case LF_CLASS: + case LF_STRUCTURE: + case LF_UNION: + case LF_ENUM: + return true; + default: + return false; + } +} + +bool lldb_private::npdb::IsClassStructUnion(llvm::codeview::CVType cvt) { + switch (cvt.kind()) { + case LF_CLASS: + case LF_STRUCTURE: + case LF_UNION: + return true; + default: + return false; + } +} + +bool lldb_private::npdb::IsForwardRefUdt(const PdbTypeSymId &id, + TpiStream &tpi) { + if (id.is_ipi || id.index.isSimple()) + return false; + return IsForwardRefUdt(tpi.getType(id.index)); +} + +bool lldb_private::npdb::IsTagRecord(const PdbTypeSymId &id, TpiStream &tpi) { + if (id.is_ipi || id.index.isSimple()) + return false; + return IsTagRecord(tpi.getType(id.index)); +} + +lldb::AccessType +lldb_private::npdb::TranslateMemberAccess(MemberAccess access) { + switch (access) { + case MemberAccess::Private: + return lldb::eAccessPrivate; + case MemberAccess::Protected: + return lldb::eAccessProtected; + case MemberAccess::Public: + return lldb::eAccessPublic; + case MemberAccess::None: + return lldb::eAccessNone; + } + llvm_unreachable("unreachable"); +} + +TypeIndex lldb_private::npdb::GetFieldListIndex(CVType cvt) { + switch (cvt.kind()) { + case LF_CLASS: + case LF_STRUCTURE: + case LF_INTERFACE: { + ClassRecord cr; + cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr)); + return cr.FieldList; + } + case LF_UNION: { + UnionRecord ur; + cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur)); + return ur.FieldList; + } + case LF_ENUM: { + EnumRecord er; + cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er)); + return er.FieldList; + } + default: + llvm_unreachable("Unreachable!"); + } +} + +TypeIndex lldb_private::npdb::LookThroughModifierRecord(CVType modifier) { + lldbassert(modifier.kind() == LF_MODIFIER); + ModifierRecord mr; + llvm::cantFail(TypeDeserializer::deserializeAs<ModifierRecord>(modifier, mr)); + return mr.ModifiedType; +} + +llvm::StringRef lldb_private::npdb::DropNameScope(llvm::StringRef name) { + return MSVCUndecoratedNameParser::DropScope(name); +} + +VariableInfo lldb_private::npdb::GetVariableNameInfo(CVSymbol sym) { + VariableInfo result = {}; + + if (sym.kind() == S_REGREL32) { + RegRelativeSym reg(SymbolRecordKind::RegRelativeSym); + cantFail(SymbolDeserializer::deserializeAs<RegRelativeSym>(sym, reg)); + result.type = reg.Type; + result.name = reg.Name; + return result; + } + + if (sym.kind() == S_REGISTER) { + RegisterSym reg(SymbolRecordKind::RegisterSym); + cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg)); + result.type = reg.Index; + result.name = reg.Name; + return result; + } + + if (sym.kind() == S_LOCAL) { + LocalSym local(SymbolRecordKind::LocalSym); + cantFail(SymbolDeserializer::deserializeAs<LocalSym>(sym, local)); + result.type = local.Type; + result.name = local.Name; + result.is_param = + ((local.Flags & LocalSymFlags::IsParameter) != LocalSymFlags::None); + return result; + } + + if (sym.kind() == S_GDATA32 || sym.kind() == S_LDATA32) { + DataSym data(SymbolRecordKind::DataSym); + cantFail(SymbolDeserializer::deserializeAs<DataSym>(sym, data)); + result.type = data.Type; + result.name = data.Name; + return result; + } + + if (sym.kind() == S_GTHREAD32 || sym.kind() == S_LTHREAD32) { + ThreadLocalDataSym data(SymbolRecordKind::ThreadLocalDataSym); + cantFail(SymbolDeserializer::deserializeAs<ThreadLocalDataSym>(sym, data)); + result.type = data.Type; + result.name = data.Name; + return result; + } + + if (sym.kind() == S_CONSTANT) { + ConstantSym constant(SymbolRecordKind::ConstantSym); + cantFail(SymbolDeserializer::deserializeAs<ConstantSym>(sym, constant)); + result.type = constant.Type; + result.name = constant.Name; + return result; + } + + lldbassert(false && "Invalid variable record kind!"); + return {}; +} + +static llvm::FixedStreamArray<FrameData>::Iterator +GetCorrespondingFrameData(lldb::addr_t load_addr, + const DebugFrameDataSubsectionRef &fpo_data, + const Variable::RangeList &ranges) { + lldbassert(!ranges.IsEmpty()); + + // assume that all variable ranges correspond to one frame data + using RangeListEntry = Variable::RangeList::Entry; + const RangeListEntry &range = ranges.GetEntryRef(0); + + auto it = fpo_data.begin(); + + // start by searching first frame data range containing variable range + for (; it != fpo_data.end(); ++it) { + RangeListEntry fd_range(load_addr + it->RvaStart, it->CodeSize); + + if (fd_range.Contains(range)) { + break; + } + } + + // then first most nested entry that still contains variable range + auto found = it; + for (; it != fpo_data.end(); ++it) { + RangeListEntry fd_range(load_addr + it->RvaStart, it->CodeSize); + + if (!fd_range.Contains(range)) { + break; + } + found = it; + } + + return found; +} + +static bool GetFrameDataProgram(PdbIndex &index, + const Variable::RangeList &ranges, + llvm::StringRef &out_program) { + const DebugFrameDataSubsectionRef &new_fpo_data = + index.dbi().getNewFpoRecords(); + + auto frame_data_it = + GetCorrespondingFrameData(index.GetLoadAddress(), new_fpo_data, ranges); + if (frame_data_it == new_fpo_data.end()) + return false; + + auto strings = index.pdb().getStringTable(); + if (!strings) { + consumeError(strings.takeError()); + return false; + } + out_program = cantFail(strings->getStringForID(frame_data_it->FrameFunc)); + return true; +} + +static RegisterId GetBaseFrameRegister(PdbIndex &index, + PdbCompilandSymId frame_proc_id, + bool is_parameter) { + CVSymbol frame_proc_cvs = index.ReadSymbolRecord(frame_proc_id); + if (frame_proc_cvs.kind() != S_FRAMEPROC) + return RegisterId::NONE; + + FrameProcSym frame_proc(SymbolRecordKind::FrameProcSym); + cantFail(SymbolDeserializer::deserializeAs<FrameProcSym>(frame_proc_cvs, + frame_proc)); + + CPUType cpu_type = index.compilands() + .GetCompiland(frame_proc_id.modi) + ->m_compile_opts->Machine; + + return is_parameter ? frame_proc.getParamFramePtrReg(cpu_type) + : frame_proc.getLocalFramePtrReg(cpu_type); +} + +VariableInfo lldb_private::npdb::GetVariableLocationInfo( + PdbIndex &index, PdbCompilandSymId var_id, Block &func_block, + lldb::ModuleSP module) { + + CVSymbol sym = index.ReadSymbolRecord(var_id); + + VariableInfo result = GetVariableNameInfo(sym); + + if (sym.kind() == S_REGREL32) { + RegRelativeSym reg(SymbolRecordKind::RegRelativeSym); + cantFail(SymbolDeserializer::deserializeAs<RegRelativeSym>(sym, reg)); + result.location = DWARFExpressionList( + module, MakeRegRelLocationExpression(reg.Register, reg.Offset, module), + nullptr); + return result; + } + + if (sym.kind() == S_REGISTER) { + RegisterSym reg(SymbolRecordKind::RegisterSym); + cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg)); + result.location = DWARFExpressionList( + module, MakeEnregisteredLocationExpression(reg.Register, module), + nullptr); + return result; + } + + if (sym.kind() == S_LOCAL) { + LocalSym local(SymbolRecordKind::LocalSym); + if (llvm::Error error = + SymbolDeserializer::deserializeAs<LocalSym>(sym, local)) { + llvm::consumeError(std::move(error)); + return result; + } + + PdbCompilandSymId loc_specifier_id(var_id.modi, + var_id.offset + sym.RecordData.size()); + CVSymbol loc_specifier_cvs; + // Only used for S_DEFRANGE_FRAMEPOINTER_REL. + RegisterId base_reg = RegisterId::NONE; + size_t type_size = GetSizeOfType(result.type, index.tpi()); + // A map from offset of a field in parent to size of the field. + std::map<uint64_t, size_t> offset_to_size; + + // When overlaps happens, always prefer the one that doesn't split the value + // into multiple locations and the location parsed first is perfered. + RangeMap location_map; + + // Iterate through all location records after S_LOCAL. They describe the + // value of this variable at different locations. + bool finished = false; + while (!finished) { + loc_specifier_cvs = index.ReadSymbolRecord(loc_specifier_id); + switch (loc_specifier_cvs.kind()) { + case S_DEFRANGE_FRAMEPOINTER_REL: { + DefRangeFramePointerRelSym loc( + SymbolRecordKind::DefRangeFramePointerRelSym); + if (llvm::Error error = + SymbolDeserializer::deserializeAs<DefRangeFramePointerRelSym>( + loc_specifier_cvs, loc)) { + llvm::consumeError(std::move(error)); + return result; + } + Variable::RangeList raw_ranges = + MakeRangeList(index, loc.Range, loc.Gaps); + if (base_reg == RegisterId::NONE) { + PdbCompilandSymId func_scope_id = + PdbSymUid(func_block.GetID()).asCompilandSym(); + CVSymbol func_block_cvs = index.ReadSymbolRecord(func_scope_id); + lldbassert(func_block_cvs.kind() == S_GPROC32 || + func_block_cvs.kind() == S_LPROC32); + PdbCompilandSymId frame_proc_id(func_scope_id.modi, + func_scope_id.offset + + func_block_cvs.length()); + base_reg = + GetBaseFrameRegister(index, frame_proc_id, result.is_param); + if (base_reg == RegisterId::NONE) + break; + } + DWARFExpression expr; + if (base_reg == RegisterId::VFRAME) { + llvm::StringRef program; + if (GetFrameDataProgram(index, raw_ranges, program)) + expr = MakeVFrameRelLocationExpression(program, loc.Hdr.Offset, + module); + else { + // invalid variable + } + } else + expr = MakeRegRelLocationExpression(base_reg, loc.Hdr.Offset, module); + AddDwarfRange(location_map, expr, raw_ranges); + break; + } + case S_DEFRANGE_REGISTER: { + DefRangeRegisterSym loc(SymbolRecordKind::DefRangeRegisterSym); + if (llvm::Error error = + SymbolDeserializer::deserializeAs<DefRangeRegisterSym>( + loc_specifier_cvs, loc)) { + llvm::consumeError(std::move(error)); + return result; + } + RegisterId reg_id = (RegisterId)(uint16_t)loc.Hdr.Register; + Variable::RangeList raw_ranges = + MakeRangeList(index, loc.Range, loc.Gaps); + DWARFExpression expr = + MakeEnregisteredLocationExpression(reg_id, module); + AddDwarfRange(location_map, expr, raw_ranges); + break; + } + case S_DEFRANGE_REGISTER_REL: { + DefRangeRegisterRelSym loc(SymbolRecordKind::DefRangeRegisterRelSym); + if (llvm::Error error = + SymbolDeserializer::deserializeAs<DefRangeRegisterRelSym>( + loc_specifier_cvs, loc)) { + llvm::consumeError(std::move(error)); + return result; + } + Variable::RangeList raw_ranges = + MakeRangeList(index, loc.Range, loc.Gaps); + RegisterId reg_id = (RegisterId)(uint16_t)loc.Hdr.Register; + DWARFExpression expr; + if (reg_id == RegisterId::VFRAME) { + llvm::StringRef program; + if (GetFrameDataProgram(index, raw_ranges, program)) + expr = MakeVFrameRelLocationExpression( + program, loc.Hdr.BasePointerOffset, module); + else { + // invalid variable + } + } else { + expr = MakeRegRelLocationExpression(reg_id, loc.Hdr.BasePointerOffset, + module); + } + // FIXME: If it's UDT, we need to know the size of the value in byte. + if (!loc.hasSpilledUDTMember()) + AddDwarfRange(location_map, expr, raw_ranges); + break; + } + case S_DEFRANGE_SUBFIELD_REGISTER: { + DefRangeSubfieldRegisterSym loc( + SymbolRecordKind::DefRangeSubfieldRegisterSym); + if (llvm::Error error = + SymbolDeserializer::deserializeAs<DefRangeSubfieldRegisterSym>( + loc_specifier_cvs, loc)) { + llvm::consumeError(std::move(error)); + return result; + } + + Variable::RangeList ranges = MakeRangeList(index, loc.Range, loc.Gaps); + uint32_t reg_size = + GetRegisterSize((RegisterId)(uint16_t)loc.Hdr.Register); + if (reg_size == 0) + break; + offset_to_size[loc.Hdr.OffsetInParent] = reg_size; + AddMemberLocationRanges(location_map, loc.Hdr.OffsetInParent, + {loc.Hdr.Register, 0, true}, ranges); + break; + } + // FIXME: Handle other kinds. LLVM only generates the 4 types of records + // above. MSVC generates other location types. + case S_DEFRANGE: + case S_DEFRANGE_SUBFIELD: + case S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: + break; + default: + finished = true; + break; + } + loc_specifier_id = PdbCompilandSymId( + loc_specifier_id.modi, + loc_specifier_id.offset + loc_specifier_cvs.RecordData.size()); + } + for (const auto &entry : location_map) { + DWARFExpression dwarf_expr = + entry.data.is_dwarf ? entry.data.expr + : MakeEnregisteredLocationExpressionForComposite( + entry.data.offset_to_location, + offset_to_size, type_size, module); + + result.location.AddExpression(entry.GetRangeBase(), entry.GetRangeEnd(), + dwarf_expr); + } + return result; + } + llvm_unreachable("Symbol is not a local variable!"); + return result; +} + +lldb::BasicType +lldb_private::npdb::GetCompilerTypeForSimpleKind(SimpleTypeKind kind) { + switch (kind) { + case SimpleTypeKind::Boolean128: + case SimpleTypeKind::Boolean16: + case SimpleTypeKind::Boolean32: + case SimpleTypeKind::Boolean64: + case SimpleTypeKind::Boolean8: + return lldb::eBasicTypeBool; + case SimpleTypeKind::Byte: + case SimpleTypeKind::UnsignedCharacter: + return lldb::eBasicTypeUnsignedChar; + case SimpleTypeKind::NarrowCharacter: + return lldb::eBasicTypeChar; + case SimpleTypeKind::SignedCharacter: + case SimpleTypeKind::SByte: + return lldb::eBasicTypeSignedChar; + case SimpleTypeKind::Character16: + return lldb::eBasicTypeChar16; + case SimpleTypeKind::Character32: + return lldb::eBasicTypeChar32; + case SimpleTypeKind::Character8: + return lldb::eBasicTypeChar8; + case SimpleTypeKind::Complex80: + return lldb::eBasicTypeLongDoubleComplex; + case SimpleTypeKind::Complex64: + return lldb::eBasicTypeDoubleComplex; + case SimpleTypeKind::Complex32: + return lldb::eBasicTypeFloatComplex; + case SimpleTypeKind::Float128: + case SimpleTypeKind::Float80: + return lldb::eBasicTypeLongDouble; + case SimpleTypeKind::Float64: + return lldb::eBasicTypeDouble; + case SimpleTypeKind::Float32: + return lldb::eBasicTypeFloat; + case SimpleTypeKind::Float16: + return lldb::eBasicTypeHalf; + case SimpleTypeKind::Int128: + return lldb::eBasicTypeInt128; + case SimpleTypeKind::Int64: + case SimpleTypeKind::Int64Quad: + return lldb::eBasicTypeLongLong; + case SimpleTypeKind::Int32: + return lldb::eBasicTypeInt; + case SimpleTypeKind::Int16: + case SimpleTypeKind::Int16Short: + return lldb::eBasicTypeShort; + case SimpleTypeKind::UInt128: + return lldb::eBasicTypeUnsignedInt128; + case SimpleTypeKind::UInt64: + case SimpleTypeKind::UInt64Quad: + return lldb::eBasicTypeUnsignedLongLong; + case SimpleTypeKind::HResult: + case SimpleTypeKind::UInt32: + return lldb::eBasicTypeUnsignedInt; + case SimpleTypeKind::UInt16: + case SimpleTypeKind::UInt16Short: + return lldb::eBasicTypeUnsignedShort; + case SimpleTypeKind::Int32Long: + return lldb::eBasicTypeLong; + case SimpleTypeKind::UInt32Long: + return lldb::eBasicTypeUnsignedLong; + case SimpleTypeKind::Void: + return lldb::eBasicTypeVoid; + case SimpleTypeKind::WideCharacter: + return lldb::eBasicTypeWChar; + default: + return lldb::eBasicTypeInvalid; + } +} + +size_t lldb_private::npdb::GetTypeSizeForSimpleKind(SimpleTypeKind kind) { + switch (kind) { + case SimpleTypeKind::Boolean128: + case SimpleTypeKind::Int128: + case SimpleTypeKind::UInt128: + case SimpleTypeKind::Float128: + return 16; + case SimpleTypeKind::Complex80: + case SimpleTypeKind::Float80: + return 10; + case SimpleTypeKind::Boolean64: + case SimpleTypeKind::Complex64: + case SimpleTypeKind::UInt64: + case SimpleTypeKind::UInt64Quad: + case SimpleTypeKind::Float64: + case SimpleTypeKind::Int64: + case SimpleTypeKind::Int64Quad: + return 8; + case SimpleTypeKind::Boolean32: + case SimpleTypeKind::Character32: + case SimpleTypeKind::Complex32: + case SimpleTypeKind::Float32: + case SimpleTypeKind::Int32: + case SimpleTypeKind::Int32Long: + case SimpleTypeKind::UInt32Long: + case SimpleTypeKind::HResult: + case SimpleTypeKind::UInt32: + return 4; + case SimpleTypeKind::Boolean16: + case SimpleTypeKind::Character16: + case SimpleTypeKind::Float16: + case SimpleTypeKind::Int16: + case SimpleTypeKind::Int16Short: + case SimpleTypeKind::UInt16: + case SimpleTypeKind::UInt16Short: + case SimpleTypeKind::WideCharacter: + return 2; + case SimpleTypeKind::Boolean8: + case SimpleTypeKind::Byte: + case SimpleTypeKind::UnsignedCharacter: + case SimpleTypeKind::NarrowCharacter: + case SimpleTypeKind::SignedCharacter: + case SimpleTypeKind::SByte: + case SimpleTypeKind::Character8: + return 1; + case SimpleTypeKind::Void: + default: + return 0; + } +} + +PdbTypeSymId lldb_private::npdb::GetBestPossibleDecl(PdbTypeSymId id, + TpiStream &tpi) { + if (id.index.isSimple()) + return id; + + CVType cvt = tpi.getType(id.index); + + // Only tag records have a best and a worst record. + if (!IsTagRecord(cvt)) + return id; + + // Tag records that are not forward decls are full decls, hence they are the + // best. + if (!IsForwardRefUdt(cvt)) + return id; + + return llvm::cantFail(tpi.findFullDeclForForwardRef(id.index)); +} + +template <typename RecordType> static size_t GetSizeOfTypeInternal(CVType cvt) { + RecordType record; + llvm::cantFail(TypeDeserializer::deserializeAs<RecordType>(cvt, record)); + return record.getSize(); +} + +size_t lldb_private::npdb::GetSizeOfType(PdbTypeSymId id, + llvm::pdb::TpiStream &tpi) { + if (id.index.isSimple()) { + switch (id.index.getSimpleMode()) { + case SimpleTypeMode::Direct: + return GetTypeSizeForSimpleKind(id.index.getSimpleKind()); + case SimpleTypeMode::NearPointer32: + case SimpleTypeMode::FarPointer32: + return 4; + case SimpleTypeMode::NearPointer64: + return 8; + case SimpleTypeMode::NearPointer128: + return 16; + default: + break; + } + return 0; + } + + TypeIndex index = id.index; + if (IsForwardRefUdt(index, tpi)) + index = llvm::cantFail(tpi.findFullDeclForForwardRef(index)); + + CVType cvt = tpi.getType(index); + switch (cvt.kind()) { + case LF_MODIFIER: + return GetSizeOfType({LookThroughModifierRecord(cvt)}, tpi); + case LF_ENUM: { + EnumRecord record; + llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, record)); + return GetSizeOfType({record.UnderlyingType}, tpi); + } + case LF_POINTER: + return GetSizeOfTypeInternal<PointerRecord>(cvt); + case LF_ARRAY: + return GetSizeOfTypeInternal<ArrayRecord>(cvt); + case LF_CLASS: + case LF_STRUCTURE: + case LF_INTERFACE: + return GetSizeOfTypeInternal<ClassRecord>(cvt); + case LF_UNION: + return GetSizeOfTypeInternal<UnionRecord>(cvt); + case LF_BITFIELD: { + BitFieldRecord record; + llvm::cantFail(TypeDeserializer::deserializeAs<BitFieldRecord>(cvt, record)); + return GetSizeOfType({record.Type}, tpi); + } + default: + break; + } + return 0; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h new file mode 100644 index 000000000000..1f888f4de1fe --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h @@ -0,0 +1,157 @@ +//===-- PdbUtil.h -----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBUTIL_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBUTIL_H + +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/lldb-enumerations.h" + +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +#include "PdbSymUid.h" + +#include <tuple> +#include <utility> + +namespace llvm { +namespace pdb { +class TpiStream; +} +} // namespace llvm + +namespace lldb_private { +namespace npdb { + +class PdbIndex; + +struct CVTagRecord { + enum Kind { Class, Struct, Union, Enum }; + + static CVTagRecord create(llvm::codeview::CVType type); + + Kind kind() const { return m_kind; } + + const llvm::codeview::TagRecord &asTag() const { + if (m_kind == Struct || m_kind == Class) + return cvclass; + if (m_kind == Enum) + return cvenum; + return cvunion; + } + + const llvm::codeview::ClassRecord &asClass() const { + assert(m_kind == Struct || m_kind == Class); + return cvclass; + } + + const llvm::codeview::EnumRecord &asEnum() const { + assert(m_kind == Enum); + return cvenum; + } + + const llvm::codeview::UnionRecord &asUnion() const { + assert(m_kind == Union); + return cvunion; + } + + llvm::StringRef name() const { + if (m_kind == Struct || m_kind == Union) + return cvclass.Name; + if (m_kind == Enum) + return cvenum.Name; + return cvunion.Name; + } + +private: + CVTagRecord(llvm::codeview::ClassRecord &&c); + CVTagRecord(llvm::codeview::UnionRecord &&u); + CVTagRecord(llvm::codeview::EnumRecord &&e); + union { + llvm::codeview::ClassRecord cvclass; + llvm::codeview::EnumRecord cvenum; + llvm::codeview::UnionRecord cvunion; + }; + Kind m_kind; +}; + +struct SegmentOffset { + SegmentOffset() = default; + SegmentOffset(uint16_t s, uint32_t o) : segment(s), offset(o) {} + uint16_t segment = 0; + uint32_t offset = 0; +}; + +struct SegmentOffsetLength { + SegmentOffsetLength() = default; + SegmentOffsetLength(uint16_t s, uint32_t o, uint32_t l) + : so(s, o), length(l) {} + SegmentOffset so; + uint32_t length = 0; +}; + +struct VariableInfo { + llvm::StringRef name; + llvm::codeview::TypeIndex type; + DWARFExpressionList location; + bool is_param; +}; + +llvm::pdb::PDB_SymType CVSymToPDBSym(llvm::codeview::SymbolKind kind); +llvm::pdb::PDB_SymType CVTypeToPDBType(llvm::codeview::TypeLeafKind kind); + +bool SymbolHasAddress(const llvm::codeview::CVSymbol &sym); +bool SymbolIsCode(const llvm::codeview::CVSymbol &sym); + +SegmentOffset GetSegmentAndOffset(const llvm::codeview::CVSymbol &sym); +SegmentOffsetLength +GetSegmentOffsetAndLength(const llvm::codeview::CVSymbol &sym); + +template <typename RecordT> bool IsValidRecord(const RecordT &sym) { + return true; +} + +inline bool IsValidRecord(const llvm::codeview::ProcRefSym &sym) { + // S_PROCREF symbols have 1-based module indices. + return sym.Module > 0; +} + +bool IsForwardRefUdt(llvm::codeview::CVType cvt); +bool IsTagRecord(llvm::codeview::CVType cvt); +bool IsClassStructUnion(llvm::codeview::CVType cvt); + +bool IsForwardRefUdt(const PdbTypeSymId &id, llvm::pdb::TpiStream &tpi); +bool IsTagRecord(const PdbTypeSymId &id, llvm::pdb::TpiStream &tpi); + +lldb::AccessType TranslateMemberAccess(llvm::codeview::MemberAccess access); +llvm::codeview::TypeIndex GetFieldListIndex(llvm::codeview::CVType cvt); +llvm::codeview::TypeIndex +LookThroughModifierRecord(llvm::codeview::CVType modifier); + +llvm::StringRef DropNameScope(llvm::StringRef name); + +VariableInfo GetVariableNameInfo(llvm::codeview::CVSymbol symbol); +VariableInfo GetVariableLocationInfo(PdbIndex &index, PdbCompilandSymId var_id, + Block &func_block, lldb::ModuleSP module); + +size_t GetTypeSizeForSimpleKind(llvm::codeview::SimpleTypeKind kind); +lldb::BasicType +GetCompilerTypeForSimpleKind(llvm::codeview::SimpleTypeKind kind); + +PdbTypeSymId GetBestPossibleDecl(PdbTypeSymId id, llvm::pdb::TpiStream &tpi); + +size_t GetSizeOfType(PdbTypeSymId id, llvm::pdb::TpiStream &tpi); + +} // namespace npdb +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp new file mode 100644 index 000000000000..7fded6a31a3a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp @@ -0,0 +1,2336 @@ +//===-- SymbolFileNativePDB.cpp -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "SymbolFileNativePDB.h" + +#include "Plugins/ExpressionParser/Clang/ClangUtil.h" +#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h" +#include "Plugins/ObjectFile/PDB/ObjectFilePDB.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/RecordName.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/Demangle/MicrosoftDemangle.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MemoryBuffer.h" + +#include "DWARFLocationExpression.h" +#include "PdbSymUid.h" +#include "PdbUtil.h" +#include "UdtRecordCompleter.h" +#include <optional> +#include <string_view> + +using namespace lldb; +using namespace lldb_private; +using namespace npdb; +using namespace llvm::codeview; +using namespace llvm::pdb; + +char SymbolFileNativePDB::ID; + +static lldb::LanguageType TranslateLanguage(PDB_Lang lang) { + switch (lang) { + case PDB_Lang::Cpp: + return lldb::LanguageType::eLanguageTypeC_plus_plus; + case PDB_Lang::C: + return lldb::LanguageType::eLanguageTypeC; + case PDB_Lang::Swift: + return lldb::LanguageType::eLanguageTypeSwift; + case PDB_Lang::Rust: + return lldb::LanguageType::eLanguageTypeRust; + case PDB_Lang::ObjC: + return lldb::LanguageType::eLanguageTypeObjC; + case PDB_Lang::ObjCpp: + return lldb::LanguageType::eLanguageTypeObjC_plus_plus; + default: + return lldb::LanguageType::eLanguageTypeUnknown; + } +} + +static std::unique_ptr<PDBFile> +loadMatchingPDBFile(std::string exe_path, llvm::BumpPtrAllocator &allocator) { + // Try to find a matching PDB for an EXE. + using namespace llvm::object; + auto expected_binary = createBinary(exe_path); + + // If the file isn't a PE/COFF executable, fail. + if (!expected_binary) { + llvm::consumeError(expected_binary.takeError()); + return nullptr; + } + OwningBinary<Binary> binary = std::move(*expected_binary); + + // TODO: Avoid opening the PE/COFF binary twice by reading this information + // directly from the lldb_private::ObjectFile. + auto *obj = llvm::dyn_cast<llvm::object::COFFObjectFile>(binary.getBinary()); + if (!obj) + return nullptr; + const llvm::codeview::DebugInfo *pdb_info = nullptr; + + // If it doesn't have a debug directory, fail. + llvm::StringRef pdb_file; + if (llvm::Error e = obj->getDebugPDBInfo(pdb_info, pdb_file)) { + consumeError(std::move(e)); + return nullptr; + } + + // If the file doesn't exist, perhaps the path specified at build time + // doesn't match the PDB's current location, so check the location of the + // executable. + if (!FileSystem::Instance().Exists(pdb_file)) { + const auto exe_dir = FileSpec(exe_path).CopyByRemovingLastPathComponent(); + const auto pdb_name = FileSpec(pdb_file).GetFilename().GetCString(); + pdb_file = exe_dir.CopyByAppendingPathComponent(pdb_name).GetPathAsConstString().GetStringRef(); + } + + // If the file is not a PDB or if it doesn't have a matching GUID, fail. + auto pdb = ObjectFilePDB::loadPDBFile(std::string(pdb_file), allocator); + if (!pdb) + return nullptr; + + auto expected_info = pdb->getPDBInfoStream(); + if (!expected_info) { + llvm::consumeError(expected_info.takeError()); + return nullptr; + } + llvm::codeview::GUID guid; + memcpy(&guid, pdb_info->PDB70.Signature, 16); + + if (expected_info->getGuid() != guid) + return nullptr; + return pdb; +} + +static bool IsFunctionPrologue(const CompilandIndexItem &cci, + lldb::addr_t addr) { + // FIXME: Implement this. + return false; +} + +static bool IsFunctionEpilogue(const CompilandIndexItem &cci, + lldb::addr_t addr) { + // FIXME: Implement this. + return false; +} + +static llvm::StringRef GetSimpleTypeName(SimpleTypeKind kind) { + switch (kind) { + case SimpleTypeKind::Boolean128: + case SimpleTypeKind::Boolean16: + case SimpleTypeKind::Boolean32: + case SimpleTypeKind::Boolean64: + case SimpleTypeKind::Boolean8: + return "bool"; + case SimpleTypeKind::Byte: + case SimpleTypeKind::UnsignedCharacter: + return "unsigned char"; + case SimpleTypeKind::NarrowCharacter: + return "char"; + case SimpleTypeKind::SignedCharacter: + case SimpleTypeKind::SByte: + return "signed char"; + case SimpleTypeKind::Character16: + return "char16_t"; + case SimpleTypeKind::Character32: + return "char32_t"; + case SimpleTypeKind::Character8: + return "char8_t"; + case SimpleTypeKind::Complex80: + case SimpleTypeKind::Complex64: + case SimpleTypeKind::Complex32: + return "complex"; + case SimpleTypeKind::Float128: + case SimpleTypeKind::Float80: + return "long double"; + case SimpleTypeKind::Float64: + return "double"; + case SimpleTypeKind::Float32: + return "float"; + case SimpleTypeKind::Float16: + return "single"; + case SimpleTypeKind::Int128: + return "__int128"; + case SimpleTypeKind::Int64: + case SimpleTypeKind::Int64Quad: + return "int64_t"; + case SimpleTypeKind::Int32: + return "int"; + case SimpleTypeKind::Int16: + return "short"; + case SimpleTypeKind::UInt128: + return "unsigned __int128"; + case SimpleTypeKind::UInt64: + case SimpleTypeKind::UInt64Quad: + return "uint64_t"; + case SimpleTypeKind::HResult: + return "HRESULT"; + case SimpleTypeKind::UInt32: + return "unsigned"; + case SimpleTypeKind::UInt16: + case SimpleTypeKind::UInt16Short: + return "unsigned short"; + case SimpleTypeKind::Int32Long: + return "long"; + case SimpleTypeKind::UInt32Long: + return "unsigned long"; + case SimpleTypeKind::Void: + return "void"; + case SimpleTypeKind::WideCharacter: + return "wchar_t"; + default: + return ""; + } +} + +static bool IsClassRecord(TypeLeafKind kind) { + switch (kind) { + case LF_STRUCTURE: + case LF_CLASS: + case LF_INTERFACE: + return true; + default: + return false; + } +} + +static std::optional<CVTagRecord> +GetNestedTagDefinition(const NestedTypeRecord &Record, + const CVTagRecord &parent, TpiStream &tpi) { + // An LF_NESTTYPE is essentially a nested typedef / using declaration, but it + // is also used to indicate the primary definition of a nested class. That is + // to say, if you have: + // struct A { + // struct B {}; + // using C = B; + // }; + // Then in the debug info, this will appear as: + // LF_STRUCTURE `A::B` [type index = N] + // LF_STRUCTURE `A` + // LF_NESTTYPE [name = `B`, index = N] + // LF_NESTTYPE [name = `C`, index = N] + // In order to accurately reconstruct the decl context hierarchy, we need to + // know which ones are actual definitions and which ones are just aliases. + + // If it's a simple type, then this is something like `using foo = int`. + if (Record.Type.isSimple()) + return std::nullopt; + + CVType cvt = tpi.getType(Record.Type); + + if (!IsTagRecord(cvt)) + return std::nullopt; + + // If it's an inner definition, then treat whatever name we have here as a + // single component of a mangled name. So we can inject it into the parent's + // mangled name to see if it matches. + CVTagRecord child = CVTagRecord::create(cvt); + std::string qname = std::string(parent.asTag().getUniqueName()); + if (qname.size() < 4 || child.asTag().getUniqueName().size() < 4) + return std::nullopt; + + // qname[3] is the tag type identifier (struct, class, union, etc). Since the + // inner tag type is not necessarily the same as the outer tag type, re-write + // it to match the inner tag type. + qname[3] = child.asTag().getUniqueName()[3]; + std::string piece; + if (qname[3] == 'W') + piece = "4"; + piece += Record.Name; + piece.push_back('@'); + qname.insert(4, std::move(piece)); + if (qname != child.asTag().UniqueName) + return std::nullopt; + + return std::move(child); +} + +void SymbolFileNativePDB::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + DebuggerInitialize); +} + +void SymbolFileNativePDB::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +void SymbolFileNativePDB::DebuggerInitialize(Debugger &debugger) {} + +llvm::StringRef SymbolFileNativePDB::GetPluginDescriptionStatic() { + return "Microsoft PDB debug symbol cross-platform file reader."; +} + +SymbolFile *SymbolFileNativePDB::CreateInstance(ObjectFileSP objfile_sp) { + return new SymbolFileNativePDB(std::move(objfile_sp)); +} + +SymbolFileNativePDB::SymbolFileNativePDB(ObjectFileSP objfile_sp) + : SymbolFileCommon(std::move(objfile_sp)) {} + +SymbolFileNativePDB::~SymbolFileNativePDB() = default; + +uint32_t SymbolFileNativePDB::CalculateAbilities() { + uint32_t abilities = 0; + if (!m_objfile_sp) + return 0; + + if (!m_index) { + // Lazily load and match the PDB file, but only do this once. + PDBFile *pdb_file; + if (auto *pdb = llvm::dyn_cast<ObjectFilePDB>(m_objfile_sp.get())) { + pdb_file = &pdb->GetPDBFile(); + } else { + m_file_up = loadMatchingPDBFile(m_objfile_sp->GetFileSpec().GetPath(), + m_allocator); + pdb_file = m_file_up.get(); + } + + if (!pdb_file) + return 0; + + auto expected_index = PdbIndex::create(pdb_file); + if (!expected_index) { + llvm::consumeError(expected_index.takeError()); + return 0; + } + m_index = std::move(*expected_index); + } + if (!m_index) + return 0; + + // We don't especially have to be precise here. We only distinguish between + // stripped and not stripped. + abilities = kAllAbilities; + + if (m_index->dbi().isStripped()) + abilities &= ~(Blocks | LocalVariables); + return abilities; +} + +void SymbolFileNativePDB::InitializeObject() { + m_obj_load_address = m_objfile_sp->GetModule() + ->GetObjectFile() + ->GetBaseAddress() + .GetFileAddress(); + m_index->SetLoadAddress(m_obj_load_address); + m_index->ParseSectionContribs(); + + auto ts_or_err = m_objfile_sp->GetModule()->GetTypeSystemForLanguage( + lldb::eLanguageTypeC_plus_plus); + if (auto err = ts_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Failed to initialize: {0}"); + } else { + if (auto ts = *ts_or_err) + ts->SetSymbolFile(this); + BuildParentMap(); + } +} + +uint32_t SymbolFileNativePDB::CalculateNumCompileUnits() { + const DbiModuleList &modules = m_index->dbi().modules(); + uint32_t count = modules.getModuleCount(); + if (count == 0) + return count; + + // The linker can inject an additional "dummy" compilation unit into the + // PDB. Ignore this special compile unit for our purposes, if it is there. + // It is always the last one. + DbiModuleDescriptor last = modules.getModuleDescriptor(count - 1); + if (last.getModuleName() == "* Linker *") + --count; + return count; +} + +Block &SymbolFileNativePDB::CreateBlock(PdbCompilandSymId block_id) { + CompilandIndexItem *cii = m_index->compilands().GetCompiland(block_id.modi); + CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(block_id.offset); + CompUnitSP comp_unit = GetOrCreateCompileUnit(*cii); + lldb::user_id_t opaque_block_uid = toOpaqueUid(block_id); + BlockSP child_block = std::make_shared<Block>(opaque_block_uid); + auto ts_or_err = GetTypeSystemForLanguage(comp_unit->GetLanguage()); + if (auto err = ts_or_err.takeError()) + return *child_block; + auto ts = *ts_or_err; + if (!ts) + return *child_block; + PdbAstBuilder* ast_builder = ts->GetNativePDBParser(); + + switch (sym.kind()) { + case S_GPROC32: + case S_LPROC32: { + // This is a function. It must be global. Creating the Function entry + // for it automatically creates a block for it. + FunctionSP func = GetOrCreateFunction(block_id, *comp_unit); + if (func) { + Block &block = func->GetBlock(false); + if (block.GetNumRanges() == 0) + block.AddRange(Block::Range(0, func->GetAddressRange().GetByteSize())); + return block; + } + break; + } + case S_BLOCK32: { + // This is a block. Its parent is either a function or another block. In + // either case, its parent can be viewed as a block (e.g. a function + // contains 1 big block. So just get the parent block and add this block + // to it. + BlockSym block(static_cast<SymbolRecordKind>(sym.kind())); + cantFail(SymbolDeserializer::deserializeAs<BlockSym>(sym, block)); + lldbassert(block.Parent != 0); + PdbCompilandSymId parent_id(block_id.modi, block.Parent); + Block &parent_block = GetOrCreateBlock(parent_id); + Function *func = parent_block.CalculateSymbolContextFunction(); + lldbassert(func); + lldb::addr_t block_base = + m_index->MakeVirtualAddress(block.Segment, block.CodeOffset); + lldb::addr_t func_base = + func->GetAddressRange().GetBaseAddress().GetFileAddress(); + if (block_base >= func_base) + child_block->AddRange(Block::Range(block_base - func_base, block.CodeSize)); + else { + GetObjectFile()->GetModule()->ReportError( + "S_BLOCK32 at modi: {0:d} offset: {1:d}: adding range " + "[{2:x16}-{3:x16}) which has a base that is less than the " + "function's " + "low PC 0x%" PRIx64 ". Please file a bug and attach the file at the " + "start of this error message", + block_id.modi, block_id.offset, block_base, + block_base + block.CodeSize, func_base); + } + parent_block.AddChild(child_block); + ast_builder->GetOrCreateBlockDecl(block_id); + m_blocks.insert({opaque_block_uid, child_block}); + break; + } + case S_INLINESITE: { + // This ensures line table is parsed first so we have inline sites info. + comp_unit->GetLineTable(); + + std::shared_ptr<InlineSite> inline_site = m_inline_sites[opaque_block_uid]; + Block &parent_block = GetOrCreateBlock(inline_site->parent_id); + parent_block.AddChild(child_block); + ast_builder->GetOrCreateInlinedFunctionDecl(block_id); + // Copy ranges from InlineSite to Block. + for (size_t i = 0; i < inline_site->ranges.GetSize(); ++i) { + auto *entry = inline_site->ranges.GetEntryAtIndex(i); + child_block->AddRange( + Block::Range(entry->GetRangeBase(), entry->GetByteSize())); + } + child_block->FinalizeRanges(); + + // Get the inlined function callsite info. + Declaration &decl = inline_site->inline_function_info->GetDeclaration(); + Declaration &callsite = inline_site->inline_function_info->GetCallSite(); + child_block->SetInlinedFunctionInfo( + inline_site->inline_function_info->GetName().GetCString(), nullptr, + &decl, &callsite); + m_blocks.insert({opaque_block_uid, child_block}); + break; + } + default: + lldbassert(false && "Symbol is not a block!"); + } + + return *child_block; +} + +lldb::FunctionSP SymbolFileNativePDB::CreateFunction(PdbCompilandSymId func_id, + CompileUnit &comp_unit) { + const CompilandIndexItem *cci = + m_index->compilands().GetCompiland(func_id.modi); + lldbassert(cci); + CVSymbol sym_record = cci->m_debug_stream.readSymbolAtOffset(func_id.offset); + + lldbassert(sym_record.kind() == S_LPROC32 || sym_record.kind() == S_GPROC32); + SegmentOffsetLength sol = GetSegmentOffsetAndLength(sym_record); + + auto file_vm_addr = + m_index->MakeVirtualAddress(sol.so.segment, sol.so.offset); + if (file_vm_addr == LLDB_INVALID_ADDRESS || file_vm_addr == 0) + return nullptr; + + AddressRange func_range(file_vm_addr, sol.length, + comp_unit.GetModule()->GetSectionList()); + if (!func_range.GetBaseAddress().IsValid()) + return nullptr; + + ProcSym proc(static_cast<SymbolRecordKind>(sym_record.kind())); + cantFail(SymbolDeserializer::deserializeAs<ProcSym>(sym_record, proc)); + if (proc.FunctionType == TypeIndex::None()) + return nullptr; + TypeSP func_type = GetOrCreateType(proc.FunctionType); + if (!func_type) + return nullptr; + + PdbTypeSymId sig_id(proc.FunctionType, false); + Mangled mangled(proc.Name); + FunctionSP func_sp = std::make_shared<Function>( + &comp_unit, toOpaqueUid(func_id), toOpaqueUid(sig_id), mangled, + func_type.get(), func_range); + + comp_unit.AddFunction(func_sp); + + auto ts_or_err = GetTypeSystemForLanguage(comp_unit.GetLanguage()); + if (auto err = ts_or_err.takeError()) + return func_sp; + auto ts = *ts_or_err; + if (!ts) + return func_sp; + ts->GetNativePDBParser()->GetOrCreateFunctionDecl(func_id); + + return func_sp; +} + +CompUnitSP +SymbolFileNativePDB::CreateCompileUnit(const CompilandIndexItem &cci) { + lldb::LanguageType lang = + cci.m_compile_opts ? TranslateLanguage(cci.m_compile_opts->getLanguage()) + : lldb::eLanguageTypeUnknown; + + LazyBool optimized = eLazyBoolNo; + if (cci.m_compile_opts && cci.m_compile_opts->hasOptimizations()) + optimized = eLazyBoolYes; + + llvm::SmallString<64> source_file_name = + m_index->compilands().GetMainSourceFile(cci); + FileSpec fs(llvm::sys::path::convert_to_slash( + source_file_name, llvm::sys::path::Style::windows_backslash)); + + CompUnitSP cu_sp = std::make_shared<CompileUnit>( + m_objfile_sp->GetModule(), nullptr, std::make_shared<SupportFile>(fs), + toOpaqueUid(cci.m_id), lang, optimized); + + SetCompileUnitAtIndex(cci.m_id.modi, cu_sp); + return cu_sp; +} + +lldb::TypeSP SymbolFileNativePDB::CreateModifierType(PdbTypeSymId type_id, + const ModifierRecord &mr, + CompilerType ct) { + TpiStream &stream = m_index->tpi(); + + std::string name; + if (mr.ModifiedType.isSimple()) + name = std::string(GetSimpleTypeName(mr.ModifiedType.getSimpleKind())); + else + name = computeTypeName(stream.typeCollection(), mr.ModifiedType); + Declaration decl; + lldb::TypeSP modified_type = GetOrCreateType(mr.ModifiedType); + + return MakeType(toOpaqueUid(type_id), ConstString(name), + modified_type->GetByteSize(nullptr), nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ct, + Type::ResolveState::Full); +} + +lldb::TypeSP +SymbolFileNativePDB::CreatePointerType(PdbTypeSymId type_id, + const llvm::codeview::PointerRecord &pr, + CompilerType ct) { + TypeSP pointee = GetOrCreateType(pr.ReferentType); + if (!pointee) + return nullptr; + + if (pr.isPointerToMember()) { + MemberPointerInfo mpi = pr.getMemberInfo(); + GetOrCreateType(mpi.ContainingType); + } + + Declaration decl; + return MakeType(toOpaqueUid(type_id), ConstString(), pr.getSize(), nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ct, + Type::ResolveState::Full); +} + +lldb::TypeSP SymbolFileNativePDB::CreateSimpleType(TypeIndex ti, + CompilerType ct) { + uint64_t uid = toOpaqueUid(PdbTypeSymId(ti, false)); + if (ti == TypeIndex::NullptrT()) { + Declaration decl; + return MakeType(uid, ConstString("std::nullptr_t"), 0, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ct, + Type::ResolveState::Full); + } + + if (ti.getSimpleMode() != SimpleTypeMode::Direct) { + TypeSP direct_sp = GetOrCreateType(ti.makeDirect()); + uint32_t pointer_size = 0; + switch (ti.getSimpleMode()) { + case SimpleTypeMode::FarPointer32: + case SimpleTypeMode::NearPointer32: + pointer_size = 4; + break; + case SimpleTypeMode::NearPointer64: + pointer_size = 8; + break; + default: + // 128-bit and 16-bit pointers unsupported. + return nullptr; + } + Declaration decl; + return MakeType(uid, ConstString(), pointer_size, nullptr, LLDB_INVALID_UID, + Type::eEncodingIsUID, decl, ct, Type::ResolveState::Full); + } + + if (ti.getSimpleKind() == SimpleTypeKind::NotTranslated) + return nullptr; + + size_t size = GetTypeSizeForSimpleKind(ti.getSimpleKind()); + llvm::StringRef type_name = GetSimpleTypeName(ti.getSimpleKind()); + + Declaration decl; + return MakeType(uid, ConstString(type_name), size, nullptr, LLDB_INVALID_UID, + Type::eEncodingIsUID, decl, ct, Type::ResolveState::Full); +} + +static std::string GetUnqualifiedTypeName(const TagRecord &record) { + if (!record.hasUniqueName()) { + MSVCUndecoratedNameParser parser(record.Name); + llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers(); + + return std::string(specs.back().GetBaseName()); + } + + llvm::ms_demangle::Demangler demangler; + std::string_view sv(record.UniqueName.begin(), record.UniqueName.size()); + llvm::ms_demangle::TagTypeNode *ttn = demangler.parseTagUniqueName(sv); + if (demangler.Error) + return std::string(record.Name); + + llvm::ms_demangle::IdentifierNode *idn = + ttn->QualifiedName->getUnqualifiedIdentifier(); + return idn->toString(); +} + +lldb::TypeSP +SymbolFileNativePDB::CreateClassStructUnion(PdbTypeSymId type_id, + const TagRecord &record, + size_t size, CompilerType ct) { + + std::string uname = GetUnqualifiedTypeName(record); + + // FIXME: Search IPI stream for LF_UDT_MOD_SRC_LINE. + Declaration decl; + return MakeType(toOpaqueUid(type_id), ConstString(uname), size, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ct, + Type::ResolveState::Forward); +} + +lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id, + const ClassRecord &cr, + CompilerType ct) { + return CreateClassStructUnion(type_id, cr, cr.getSize(), ct); +} + +lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id, + const UnionRecord &ur, + CompilerType ct) { + return CreateClassStructUnion(type_id, ur, ur.getSize(), ct); +} + +lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id, + const EnumRecord &er, + CompilerType ct) { + std::string uname = GetUnqualifiedTypeName(er); + + Declaration decl; + TypeSP underlying_type = GetOrCreateType(er.UnderlyingType); + + return MakeType(toOpaqueUid(type_id), ConstString(uname), + underlying_type->GetByteSize(nullptr), nullptr, + LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, + ct, lldb_private::Type::ResolveState::Forward); +} + +TypeSP SymbolFileNativePDB::CreateArrayType(PdbTypeSymId type_id, + const ArrayRecord &ar, + CompilerType ct) { + TypeSP element_type = GetOrCreateType(ar.ElementType); + + Declaration decl; + TypeSP array_sp = + MakeType(toOpaqueUid(type_id), ConstString(), ar.Size, nullptr, + LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, ct, + lldb_private::Type::ResolveState::Full); + array_sp->SetEncodingType(element_type.get()); + return array_sp; +} + +TypeSP SymbolFileNativePDB::CreateFunctionType(PdbTypeSymId type_id, + const MemberFunctionRecord &mfr, + CompilerType ct) { + Declaration decl; + return MakeType(toOpaqueUid(type_id), ConstString(), 0, nullptr, + LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, + ct, lldb_private::Type::ResolveState::Full); +} + +TypeSP SymbolFileNativePDB::CreateProcedureType(PdbTypeSymId type_id, + const ProcedureRecord &pr, + CompilerType ct) { + Declaration decl; + return MakeType(toOpaqueUid(type_id), ConstString(), 0, nullptr, + LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, + ct, lldb_private::Type::ResolveState::Full); +} + +TypeSP SymbolFileNativePDB::CreateType(PdbTypeSymId type_id, CompilerType ct) { + if (type_id.index.isSimple()) + return CreateSimpleType(type_id.index, ct); + + TpiStream &stream = type_id.is_ipi ? m_index->ipi() : m_index->tpi(); + CVType cvt = stream.getType(type_id.index); + + if (cvt.kind() == LF_MODIFIER) { + ModifierRecord modifier; + llvm::cantFail( + TypeDeserializer::deserializeAs<ModifierRecord>(cvt, modifier)); + return CreateModifierType(type_id, modifier, ct); + } + + if (cvt.kind() == LF_POINTER) { + PointerRecord pointer; + llvm::cantFail( + TypeDeserializer::deserializeAs<PointerRecord>(cvt, pointer)); + return CreatePointerType(type_id, pointer, ct); + } + + if (IsClassRecord(cvt.kind())) { + ClassRecord cr; + llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr)); + return CreateTagType(type_id, cr, ct); + } + + if (cvt.kind() == LF_ENUM) { + EnumRecord er; + llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er)); + return CreateTagType(type_id, er, ct); + } + + if (cvt.kind() == LF_UNION) { + UnionRecord ur; + llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur)); + return CreateTagType(type_id, ur, ct); + } + + if (cvt.kind() == LF_ARRAY) { + ArrayRecord ar; + llvm::cantFail(TypeDeserializer::deserializeAs<ArrayRecord>(cvt, ar)); + return CreateArrayType(type_id, ar, ct); + } + + if (cvt.kind() == LF_PROCEDURE) { + ProcedureRecord pr; + llvm::cantFail(TypeDeserializer::deserializeAs<ProcedureRecord>(cvt, pr)); + return CreateProcedureType(type_id, pr, ct); + } + if (cvt.kind() == LF_MFUNCTION) { + MemberFunctionRecord mfr; + llvm::cantFail(TypeDeserializer::deserializeAs<MemberFunctionRecord>(cvt, mfr)); + return CreateFunctionType(type_id, mfr, ct); + } + + return nullptr; +} + +TypeSP SymbolFileNativePDB::CreateAndCacheType(PdbTypeSymId type_id) { + // If they search for a UDT which is a forward ref, try and resolve the full + // decl and just map the forward ref uid to the full decl record. + std::optional<PdbTypeSymId> full_decl_uid; + if (IsForwardRefUdt(type_id, m_index->tpi())) { + auto expected_full_ti = + m_index->tpi().findFullDeclForForwardRef(type_id.index); + if (!expected_full_ti) + llvm::consumeError(expected_full_ti.takeError()); + else if (*expected_full_ti != type_id.index) { + full_decl_uid = PdbTypeSymId(*expected_full_ti, false); + + // It's possible that a lookup would occur for the full decl causing it + // to be cached, then a second lookup would occur for the forward decl. + // We don't want to create a second full decl, so make sure the full + // decl hasn't already been cached. + auto full_iter = m_types.find(toOpaqueUid(*full_decl_uid)); + if (full_iter != m_types.end()) { + TypeSP result = full_iter->second; + // Map the forward decl to the TypeSP for the full decl so we can take + // the fast path next time. + m_types[toOpaqueUid(type_id)] = result; + return result; + } + } + } + + PdbTypeSymId best_decl_id = full_decl_uid ? *full_decl_uid : type_id; + auto ts_or_err = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = ts_or_err.takeError()) + return nullptr; + auto ts = *ts_or_err; + if (!ts) + return nullptr; + + PdbAstBuilder* ast_builder = ts->GetNativePDBParser(); + clang::QualType qt = ast_builder->GetOrCreateType(best_decl_id); + if (qt.isNull()) + return nullptr; + + TypeSP result = CreateType(best_decl_id, ast_builder->ToCompilerType(qt)); + if (!result) + return nullptr; + + uint64_t best_uid = toOpaqueUid(best_decl_id); + m_types[best_uid] = result; + // If we had both a forward decl and a full decl, make both point to the new + // type. + if (full_decl_uid) + m_types[toOpaqueUid(type_id)] = result; + + return result; +} + +TypeSP SymbolFileNativePDB::GetOrCreateType(PdbTypeSymId type_id) { + // We can't use try_emplace / overwrite here because the process of creating + // a type could create nested types, which could invalidate iterators. So + // we have to do a 2-phase lookup / insert. + auto iter = m_types.find(toOpaqueUid(type_id)); + if (iter != m_types.end()) + return iter->second; + + TypeSP type = CreateAndCacheType(type_id); + if (type) + GetTypeList().Insert(type); + return type; +} + +VariableSP SymbolFileNativePDB::CreateGlobalVariable(PdbGlobalSymId var_id) { + CVSymbol sym = m_index->symrecords().readRecord(var_id.offset); + if (sym.kind() == S_CONSTANT) + return CreateConstantSymbol(var_id, sym); + + lldb::ValueType scope = eValueTypeInvalid; + TypeIndex ti; + llvm::StringRef name; + lldb::addr_t addr = 0; + uint16_t section = 0; + uint32_t offset = 0; + bool is_external = false; + switch (sym.kind()) { + case S_GDATA32: + is_external = true; + [[fallthrough]]; + case S_LDATA32: { + DataSym ds(sym.kind()); + llvm::cantFail(SymbolDeserializer::deserializeAs<DataSym>(sym, ds)); + ti = ds.Type; + scope = (sym.kind() == S_GDATA32) ? eValueTypeVariableGlobal + : eValueTypeVariableStatic; + name = ds.Name; + section = ds.Segment; + offset = ds.DataOffset; + addr = m_index->MakeVirtualAddress(ds.Segment, ds.DataOffset); + break; + } + case S_GTHREAD32: + is_external = true; + [[fallthrough]]; + case S_LTHREAD32: { + ThreadLocalDataSym tlds(sym.kind()); + llvm::cantFail( + SymbolDeserializer::deserializeAs<ThreadLocalDataSym>(sym, tlds)); + ti = tlds.Type; + name = tlds.Name; + section = tlds.Segment; + offset = tlds.DataOffset; + addr = m_index->MakeVirtualAddress(tlds.Segment, tlds.DataOffset); + scope = eValueTypeVariableThreadLocal; + break; + } + default: + llvm_unreachable("unreachable!"); + } + + CompUnitSP comp_unit; + std::optional<uint16_t> modi = m_index->GetModuleIndexForVa(addr); + if (!modi) { + return nullptr; + } + + CompilandIndexItem &cci = m_index->compilands().GetOrCreateCompiland(*modi); + comp_unit = GetOrCreateCompileUnit(cci); + + Declaration decl; + PdbTypeSymId tid(ti, false); + SymbolFileTypeSP type_sp = + std::make_shared<SymbolFileType>(*this, toOpaqueUid(tid)); + Variable::RangeList ranges; + auto ts_or_err = GetTypeSystemForLanguage(comp_unit->GetLanguage()); + if (auto err = ts_or_err.takeError()) + return nullptr; + auto ts = *ts_or_err; + if (!ts) + return nullptr; + + ts->GetNativePDBParser()->GetOrCreateVariableDecl(var_id); + + ModuleSP module_sp = GetObjectFile()->GetModule(); + DWARFExpressionList location( + module_sp, MakeGlobalLocationExpression(section, offset, module_sp), + nullptr); + + std::string global_name("::"); + global_name += name; + bool artificial = false; + bool location_is_constant_data = false; + bool static_member = false; + VariableSP var_sp = std::make_shared<Variable>( + toOpaqueUid(var_id), name.str().c_str(), global_name.c_str(), type_sp, + scope, comp_unit.get(), ranges, &decl, location, is_external, artificial, + location_is_constant_data, static_member); + + return var_sp; +} + +lldb::VariableSP +SymbolFileNativePDB::CreateConstantSymbol(PdbGlobalSymId var_id, + const CVSymbol &cvs) { + TpiStream &tpi = m_index->tpi(); + ConstantSym constant(cvs.kind()); + + llvm::cantFail(SymbolDeserializer::deserializeAs<ConstantSym>(cvs, constant)); + std::string global_name("::"); + global_name += constant.Name; + PdbTypeSymId tid(constant.Type, false); + SymbolFileTypeSP type_sp = + std::make_shared<SymbolFileType>(*this, toOpaqueUid(tid)); + + Declaration decl; + Variable::RangeList ranges; + ModuleSP module = GetObjectFile()->GetModule(); + DWARFExpressionList location(module, + MakeConstantLocationExpression( + constant.Type, tpi, constant.Value, module), + nullptr); + + bool external = false; + bool artificial = false; + bool location_is_constant_data = true; + bool static_member = false; + VariableSP var_sp = std::make_shared<Variable>( + toOpaqueUid(var_id), constant.Name.str().c_str(), global_name.c_str(), + type_sp, eValueTypeVariableGlobal, module.get(), ranges, &decl, location, + external, artificial, location_is_constant_data, static_member); + return var_sp; +} + +VariableSP +SymbolFileNativePDB::GetOrCreateGlobalVariable(PdbGlobalSymId var_id) { + auto emplace_result = m_global_vars.try_emplace(toOpaqueUid(var_id), nullptr); + if (emplace_result.second) { + if (VariableSP var_sp = CreateGlobalVariable(var_id)) + emplace_result.first->second = var_sp; + else + return nullptr; + } + + return emplace_result.first->second; +} + +lldb::TypeSP SymbolFileNativePDB::GetOrCreateType(TypeIndex ti) { + return GetOrCreateType(PdbTypeSymId(ti, false)); +} + +FunctionSP SymbolFileNativePDB::GetOrCreateFunction(PdbCompilandSymId func_id, + CompileUnit &comp_unit) { + auto emplace_result = m_functions.try_emplace(toOpaqueUid(func_id), nullptr); + if (emplace_result.second) + emplace_result.first->second = CreateFunction(func_id, comp_unit); + + return emplace_result.first->second; +} + +CompUnitSP +SymbolFileNativePDB::GetOrCreateCompileUnit(const CompilandIndexItem &cci) { + + auto emplace_result = + m_compilands.try_emplace(toOpaqueUid(cci.m_id), nullptr); + if (emplace_result.second) + emplace_result.first->second = CreateCompileUnit(cci); + + lldbassert(emplace_result.first->second); + return emplace_result.first->second; +} + +Block &SymbolFileNativePDB::GetOrCreateBlock(PdbCompilandSymId block_id) { + auto iter = m_blocks.find(toOpaqueUid(block_id)); + if (iter != m_blocks.end()) + return *iter->second; + + return CreateBlock(block_id); +} + +void SymbolFileNativePDB::ParseDeclsForContext( + lldb_private::CompilerDeclContext decl_ctx) { + TypeSystem* ts_or_err = decl_ctx.GetTypeSystem(); + if (!ts_or_err) + return; + PdbAstBuilder* ast_builder = ts_or_err->GetNativePDBParser(); + clang::DeclContext *context = ast_builder->FromCompilerDeclContext(decl_ctx); + if (!context) + return; + ast_builder->ParseDeclsForContext(*context); +} + +lldb::CompUnitSP SymbolFileNativePDB::ParseCompileUnitAtIndex(uint32_t index) { + if (index >= GetNumCompileUnits()) + return CompUnitSP(); + lldbassert(index < UINT16_MAX); + if (index >= UINT16_MAX) + return nullptr; + + CompilandIndexItem &item = m_index->compilands().GetOrCreateCompiland(index); + + return GetOrCreateCompileUnit(item); +} + +lldb::LanguageType SymbolFileNativePDB::ParseLanguage(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + PdbSymUid uid(comp_unit.GetID()); + lldbassert(uid.kind() == PdbSymUidKind::Compiland); + + CompilandIndexItem *item = + m_index->compilands().GetCompiland(uid.asCompiland().modi); + lldbassert(item); + if (!item->m_compile_opts) + return lldb::eLanguageTypeUnknown; + + return TranslateLanguage(item->m_compile_opts->getLanguage()); +} + +void SymbolFileNativePDB::AddSymbols(Symtab &symtab) {} + +size_t SymbolFileNativePDB::ParseFunctions(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + PdbSymUid uid{comp_unit.GetID()}; + lldbassert(uid.kind() == PdbSymUidKind::Compiland); + uint16_t modi = uid.asCompiland().modi; + CompilandIndexItem &cii = m_index->compilands().GetOrCreateCompiland(modi); + + size_t count = comp_unit.GetNumFunctions(); + const CVSymbolArray &syms = cii.m_debug_stream.getSymbolArray(); + for (auto iter = syms.begin(); iter != syms.end(); ++iter) { + if (iter->kind() != S_LPROC32 && iter->kind() != S_GPROC32) + continue; + + PdbCompilandSymId sym_id{modi, iter.offset()}; + + FunctionSP func = GetOrCreateFunction(sym_id, comp_unit); + } + + size_t new_count = comp_unit.GetNumFunctions(); + lldbassert(new_count >= count); + return new_count - count; +} + +static bool NeedsResolvedCompileUnit(uint32_t resolve_scope) { + // If any of these flags are set, we need to resolve the compile unit. + uint32_t flags = eSymbolContextCompUnit; + flags |= eSymbolContextVariable; + flags |= eSymbolContextFunction; + flags |= eSymbolContextBlock; + flags |= eSymbolContextLineEntry; + return (resolve_scope & flags) != 0; +} + +uint32_t SymbolFileNativePDB::ResolveSymbolContext( + const Address &addr, SymbolContextItem resolve_scope, SymbolContext &sc) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + uint32_t resolved_flags = 0; + lldb::addr_t file_addr = addr.GetFileAddress(); + + if (NeedsResolvedCompileUnit(resolve_scope)) { + std::optional<uint16_t> modi = m_index->GetModuleIndexForVa(file_addr); + if (!modi) + return 0; + CompUnitSP cu_sp = GetCompileUnitAtIndex(*modi); + if (!cu_sp) + return 0; + + sc.comp_unit = cu_sp.get(); + resolved_flags |= eSymbolContextCompUnit; + } + + if (resolve_scope & eSymbolContextFunction || + resolve_scope & eSymbolContextBlock) { + lldbassert(sc.comp_unit); + std::vector<SymbolAndUid> matches = m_index->FindSymbolsByVa(file_addr); + // Search the matches in reverse. This way if there are multiple matches + // (for example we are 3 levels deep in a nested scope) it will find the + // innermost one first. + for (const auto &match : llvm::reverse(matches)) { + if (match.uid.kind() != PdbSymUidKind::CompilandSym) + continue; + + PdbCompilandSymId csid = match.uid.asCompilandSym(); + CVSymbol cvs = m_index->ReadSymbolRecord(csid); + PDB_SymType type = CVSymToPDBSym(cvs.kind()); + if (type != PDB_SymType::Function && type != PDB_SymType::Block) + continue; + if (type == PDB_SymType::Function) { + sc.function = GetOrCreateFunction(csid, *sc.comp_unit).get(); + if (sc.function) { + Block &block = sc.function->GetBlock(true); + addr_t func_base = + sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); + addr_t offset = file_addr - func_base; + sc.block = block.FindInnermostBlockByOffset(offset); + } + } + + if (type == PDB_SymType::Block) { + Block &block = GetOrCreateBlock(csid); + sc.function = block.CalculateSymbolContextFunction(); + if (sc.function) { + sc.function->GetBlock(true); + addr_t func_base = + sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); + addr_t offset = file_addr - func_base; + sc.block = block.FindInnermostBlockByOffset(offset); + } + } + if (sc.function) + resolved_flags |= eSymbolContextFunction; + if (sc.block) + resolved_flags |= eSymbolContextBlock; + break; + } + } + + if (resolve_scope & eSymbolContextLineEntry) { + lldbassert(sc.comp_unit); + if (auto *line_table = sc.comp_unit->GetLineTable()) { + if (line_table->FindLineEntryByAddress(addr, sc.line_entry)) + resolved_flags |= eSymbolContextLineEntry; + } + } + + return resolved_flags; +} + +uint32_t SymbolFileNativePDB::ResolveSymbolContext( + const SourceLocationSpec &src_location_spec, + lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + const uint32_t prev_size = sc_list.GetSize(); + if (resolve_scope & eSymbolContextCompUnit) { + for (uint32_t cu_idx = 0, num_cus = GetNumCompileUnits(); cu_idx < num_cus; + ++cu_idx) { + CompileUnit *cu = ParseCompileUnitAtIndex(cu_idx).get(); + if (!cu) + continue; + + bool file_spec_matches_cu_file_spec = FileSpec::Match( + src_location_spec.GetFileSpec(), cu->GetPrimaryFile()); + if (file_spec_matches_cu_file_spec) { + cu->ResolveSymbolContext(src_location_spec, resolve_scope, sc_list); + break; + } + } + } + return sc_list.GetSize() - prev_size; +} + +bool SymbolFileNativePDB::ParseLineTable(CompileUnit &comp_unit) { + // Unfortunately LLDB is set up to parse the entire compile unit line table + // all at once, even if all it really needs is line info for a specific + // function. In the future it would be nice if it could set the sc.m_function + // member, and we could only get the line info for the function in question. + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + PdbSymUid cu_id(comp_unit.GetID()); + lldbassert(cu_id.kind() == PdbSymUidKind::Compiland); + uint16_t modi = cu_id.asCompiland().modi; + CompilandIndexItem *cii = m_index->compilands().GetCompiland(modi); + lldbassert(cii); + + // Parse DEBUG_S_LINES subsections first, then parse all S_INLINESITE records + // in this CU. Add line entries into the set first so that if there are line + // entries with same addres, the later is always more accurate than the + // former. + std::set<LineTable::Entry, LineTableEntryComparator> line_set; + + // This is basically a copy of the .debug$S subsections from all original COFF + // object files merged together with address relocations applied. We are + // looking for all DEBUG_S_LINES subsections. + for (const DebugSubsectionRecord &dssr : + cii->m_debug_stream.getSubsectionsArray()) { + if (dssr.kind() != DebugSubsectionKind::Lines) + continue; + + DebugLinesSubsectionRef lines; + llvm::BinaryStreamReader reader(dssr.getRecordData()); + if (auto EC = lines.initialize(reader)) { + llvm::consumeError(std::move(EC)); + return false; + } + + const LineFragmentHeader *lfh = lines.header(); + uint64_t virtual_addr = + m_index->MakeVirtualAddress(lfh->RelocSegment, lfh->RelocOffset); + if (virtual_addr == LLDB_INVALID_ADDRESS) + continue; + + for (const LineColumnEntry &group : lines) { + llvm::Expected<uint32_t> file_index_or_err = + GetFileIndex(*cii, group.NameIndex); + if (!file_index_or_err) + continue; + uint32_t file_index = file_index_or_err.get(); + lldbassert(!group.LineNumbers.empty()); + CompilandIndexItem::GlobalLineTable::Entry line_entry( + LLDB_INVALID_ADDRESS, 0); + for (const LineNumberEntry &entry : group.LineNumbers) { + LineInfo cur_info(entry.Flags); + + if (cur_info.isAlwaysStepInto() || cur_info.isNeverStepInto()) + continue; + + uint64_t addr = virtual_addr + entry.Offset; + + bool is_statement = cur_info.isStatement(); + bool is_prologue = IsFunctionPrologue(*cii, addr); + bool is_epilogue = IsFunctionEpilogue(*cii, addr); + + uint32_t lno = cur_info.getStartLine(); + + LineTable::Entry new_entry(addr, lno, 0, file_index, is_statement, false, + is_prologue, is_epilogue, false); + // Terminal entry has lower precedence than new entry. + auto iter = line_set.find(new_entry); + if (iter != line_set.end() && iter->is_terminal_entry) + line_set.erase(iter); + line_set.insert(new_entry); + + if (line_entry.GetRangeBase() != LLDB_INVALID_ADDRESS) { + line_entry.SetRangeEnd(addr); + cii->m_global_line_table.Append(line_entry); + } + line_entry.SetRangeBase(addr); + line_entry.data = {file_index, lno}; + } + LineInfo last_line(group.LineNumbers.back().Flags); + line_set.emplace(virtual_addr + lfh->CodeSize, last_line.getEndLine(), 0, + file_index, false, false, false, false, true); + + if (line_entry.GetRangeBase() != LLDB_INVALID_ADDRESS) { + line_entry.SetRangeEnd(virtual_addr + lfh->CodeSize); + cii->m_global_line_table.Append(line_entry); + } + } + } + + cii->m_global_line_table.Sort(); + + // Parse all S_INLINESITE in this CU. + const CVSymbolArray &syms = cii->m_debug_stream.getSymbolArray(); + for (auto iter = syms.begin(); iter != syms.end();) { + if (iter->kind() != S_LPROC32 && iter->kind() != S_GPROC32) { + ++iter; + continue; + } + + uint32_t record_offset = iter.offset(); + CVSymbol func_record = + cii->m_debug_stream.readSymbolAtOffset(record_offset); + SegmentOffsetLength sol = GetSegmentOffsetAndLength(func_record); + addr_t file_vm_addr = + m_index->MakeVirtualAddress(sol.so.segment, sol.so.offset); + if (file_vm_addr == LLDB_INVALID_ADDRESS) + continue; + + AddressRange func_range(file_vm_addr, sol.length, + comp_unit.GetModule()->GetSectionList()); + Address func_base = func_range.GetBaseAddress(); + PdbCompilandSymId func_id{modi, record_offset}; + + // Iterate all S_INLINESITEs in the function. + auto parse_inline_sites = [&](SymbolKind kind, PdbCompilandSymId id) { + if (kind != S_INLINESITE) + return false; + + ParseInlineSite(id, func_base); + + for (const auto &line_entry : + m_inline_sites[toOpaqueUid(id)]->line_entries) { + // If line_entry is not terminal entry, remove previous line entry at + // the same address and insert new one. Terminal entry inside an inline + // site might not be terminal entry for its parent. + if (!line_entry.is_terminal_entry) + line_set.erase(line_entry); + line_set.insert(line_entry); + } + // No longer useful after adding to line_set. + m_inline_sites[toOpaqueUid(id)]->line_entries.clear(); + return true; + }; + ParseSymbolArrayInScope(func_id, parse_inline_sites); + // Jump to the end of the function record. + iter = syms.at(getScopeEndOffset(func_record)); + } + + cii->m_global_line_table.Clear(); + + // Add line entries in line_set to line_table. + auto line_table = std::make_unique<LineTable>(&comp_unit); + std::unique_ptr<LineSequence> sequence( + line_table->CreateLineSequenceContainer()); + for (const auto &line_entry : line_set) { + line_table->AppendLineEntryToSequence( + sequence.get(), line_entry.file_addr, line_entry.line, + line_entry.column, line_entry.file_idx, + line_entry.is_start_of_statement, line_entry.is_start_of_basic_block, + line_entry.is_prologue_end, line_entry.is_epilogue_begin, + line_entry.is_terminal_entry); + } + line_table->InsertSequence(sequence.get()); + + if (line_table->GetSize() == 0) + return false; + + comp_unit.SetLineTable(line_table.release()); + return true; +} + +bool SymbolFileNativePDB::ParseDebugMacros(CompileUnit &comp_unit) { + // PDB doesn't contain information about macros + return false; +} + +llvm::Expected<uint32_t> +SymbolFileNativePDB::GetFileIndex(const CompilandIndexItem &cii, + uint32_t file_id) { + if (!cii.m_strings.hasChecksums() || !cii.m_strings.hasStrings()) + return llvm::make_error<RawError>(raw_error_code::no_entry); + + const auto &checksums = cii.m_strings.checksums().getArray(); + const auto &strings = cii.m_strings.strings(); + // Indices in this structure are actually offsets of records in the + // DEBUG_S_FILECHECKSUMS subsection. Those entries then have an index + // into the global PDB string table. + auto iter = checksums.at(file_id); + if (iter == checksums.end()) + return llvm::make_error<RawError>(raw_error_code::no_entry); + + llvm::Expected<llvm::StringRef> efn = strings.getString(iter->FileNameOffset); + if (!efn) { + return efn.takeError(); + } + + // LLDB wants the index of the file in the list of support files. + auto fn_iter = llvm::find(cii.m_file_list, *efn); + if (fn_iter != cii.m_file_list.end()) + return std::distance(cii.m_file_list.begin(), fn_iter); + return llvm::make_error<RawError>(raw_error_code::no_entry); +} + +bool SymbolFileNativePDB::ParseSupportFiles(CompileUnit &comp_unit, + SupportFileList &support_files) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + PdbSymUid cu_id(comp_unit.GetID()); + lldbassert(cu_id.kind() == PdbSymUidKind::Compiland); + CompilandIndexItem *cci = + m_index->compilands().GetCompiland(cu_id.asCompiland().modi); + lldbassert(cci); + + for (llvm::StringRef f : cci->m_file_list) { + FileSpec::Style style = + f.starts_with("/") ? FileSpec::Style::posix : FileSpec::Style::windows; + FileSpec spec(f, style); + support_files.Append(spec); + } + return true; +} + +bool SymbolFileNativePDB::ParseImportedModules( + const SymbolContext &sc, std::vector<SourceModule> &imported_modules) { + // PDB does not yet support module debug info + return false; +} + +void SymbolFileNativePDB::ParseInlineSite(PdbCompilandSymId id, + Address func_addr) { + lldb::user_id_t opaque_uid = toOpaqueUid(id); + if (m_inline_sites.contains(opaque_uid)) + return; + + addr_t func_base = func_addr.GetFileAddress(); + CompilandIndexItem *cii = m_index->compilands().GetCompiland(id.modi); + CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(id.offset); + CompUnitSP comp_unit = GetOrCreateCompileUnit(*cii); + + InlineSiteSym inline_site(static_cast<SymbolRecordKind>(sym.kind())); + cantFail(SymbolDeserializer::deserializeAs<InlineSiteSym>(sym, inline_site)); + PdbCompilandSymId parent_id(id.modi, inline_site.Parent); + + std::shared_ptr<InlineSite> inline_site_sp = + std::make_shared<InlineSite>(parent_id); + + // Get the inlined function declaration info. + auto iter = cii->m_inline_map.find(inline_site.Inlinee); + if (iter == cii->m_inline_map.end()) + return; + InlineeSourceLine inlinee_line = iter->second; + + const SupportFileList &files = comp_unit->GetSupportFiles(); + FileSpec decl_file; + llvm::Expected<uint32_t> file_index_or_err = + GetFileIndex(*cii, inlinee_line.Header->FileID); + if (!file_index_or_err) + return; + uint32_t file_offset = file_index_or_err.get(); + decl_file = files.GetFileSpecAtIndex(file_offset); + uint32_t decl_line = inlinee_line.Header->SourceLineNum; + std::unique_ptr<Declaration> decl_up = + std::make_unique<Declaration>(decl_file, decl_line); + + // Parse range and line info. + uint32_t code_offset = 0; + int32_t line_offset = 0; + std::optional<uint32_t> code_offset_base; + std::optional<uint32_t> code_offset_end; + std::optional<int32_t> cur_line_offset; + std::optional<int32_t> next_line_offset; + std::optional<uint32_t> next_file_offset; + + bool is_terminal_entry = false; + bool is_start_of_statement = true; + // The first instruction is the prologue end. + bool is_prologue_end = true; + + auto update_code_offset = [&](uint32_t code_delta) { + if (!code_offset_base) + code_offset_base = code_offset; + else if (!code_offset_end) + code_offset_end = *code_offset_base + code_delta; + }; + auto update_line_offset = [&](int32_t line_delta) { + line_offset += line_delta; + if (!code_offset_base || !cur_line_offset) + cur_line_offset = line_offset; + else + next_line_offset = line_offset; + ; + }; + auto update_file_offset = [&](uint32_t offset) { + if (!code_offset_base) + file_offset = offset; + else + next_file_offset = offset; + }; + + for (auto &annot : inline_site.annotations()) { + switch (annot.OpCode) { + case BinaryAnnotationsOpCode::CodeOffset: + case BinaryAnnotationsOpCode::ChangeCodeOffset: + case BinaryAnnotationsOpCode::ChangeCodeOffsetBase: + code_offset += annot.U1; + update_code_offset(annot.U1); + break; + case BinaryAnnotationsOpCode::ChangeLineOffset: + update_line_offset(annot.S1); + break; + case BinaryAnnotationsOpCode::ChangeCodeLength: + update_code_offset(annot.U1); + code_offset += annot.U1; + is_terminal_entry = true; + break; + case BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset: + code_offset += annot.U1; + update_code_offset(annot.U1); + update_line_offset(annot.S1); + break; + case BinaryAnnotationsOpCode::ChangeCodeLengthAndCodeOffset: + code_offset += annot.U2; + update_code_offset(annot.U2); + update_code_offset(annot.U1); + code_offset += annot.U1; + is_terminal_entry = true; + break; + case BinaryAnnotationsOpCode::ChangeFile: + update_file_offset(annot.U1); + break; + default: + break; + } + + // Add range if current range is finished. + if (code_offset_base && code_offset_end && cur_line_offset) { + inline_site_sp->ranges.Append(RangeSourceLineVector::Entry( + *code_offset_base, *code_offset_end - *code_offset_base, + decl_line + *cur_line_offset)); + // Set base, end, file offset and line offset for next range. + if (next_file_offset) + file_offset = *next_file_offset; + if (next_line_offset) { + cur_line_offset = next_line_offset; + next_line_offset = std::nullopt; + } + code_offset_base = is_terminal_entry ? std::nullopt : code_offset_end; + code_offset_end = next_file_offset = std::nullopt; + } + if (code_offset_base && cur_line_offset) { + if (is_terminal_entry) { + LineTable::Entry line_entry( + func_base + *code_offset_base, decl_line + *cur_line_offset, 0, + file_offset, false, false, false, false, true); + inline_site_sp->line_entries.push_back(line_entry); + } else { + LineTable::Entry line_entry(func_base + *code_offset_base, + decl_line + *cur_line_offset, 0, + file_offset, is_start_of_statement, false, + is_prologue_end, false, false); + inline_site_sp->line_entries.push_back(line_entry); + is_prologue_end = false; + is_start_of_statement = false; + } + } + if (is_terminal_entry) + is_start_of_statement = true; + is_terminal_entry = false; + } + + inline_site_sp->ranges.Sort(); + + // Get the inlined function callsite info. + std::unique_ptr<Declaration> callsite_up; + if (!inline_site_sp->ranges.IsEmpty()) { + auto *entry = inline_site_sp->ranges.GetEntryAtIndex(0); + addr_t base_offset = entry->GetRangeBase(); + if (cii->m_debug_stream.readSymbolAtOffset(parent_id.offset).kind() == + S_INLINESITE) { + // Its parent is another inline site, lookup parent site's range vector + // for callsite line. + ParseInlineSite(parent_id, func_base); + std::shared_ptr<InlineSite> parent_site = + m_inline_sites[toOpaqueUid(parent_id)]; + FileSpec &parent_decl_file = + parent_site->inline_function_info->GetDeclaration().GetFile(); + if (auto *parent_entry = + parent_site->ranges.FindEntryThatContains(base_offset)) { + callsite_up = + std::make_unique<Declaration>(parent_decl_file, parent_entry->data); + } + } else { + // Its parent is a function, lookup global line table for callsite. + if (auto *entry = cii->m_global_line_table.FindEntryThatContains( + func_base + base_offset)) { + const FileSpec &callsite_file = + files.GetFileSpecAtIndex(entry->data.first); + callsite_up = + std::make_unique<Declaration>(callsite_file, entry->data.second); + } + } + } + + // Get the inlined function name. + CVType inlinee_cvt = m_index->ipi().getType(inline_site.Inlinee); + std::string inlinee_name; + if (inlinee_cvt.kind() == LF_MFUNC_ID) { + MemberFuncIdRecord mfr; + cantFail( + TypeDeserializer::deserializeAs<MemberFuncIdRecord>(inlinee_cvt, mfr)); + LazyRandomTypeCollection &types = m_index->tpi().typeCollection(); + inlinee_name.append(std::string(types.getTypeName(mfr.ClassType))); + inlinee_name.append("::"); + inlinee_name.append(mfr.getName().str()); + } else if (inlinee_cvt.kind() == LF_FUNC_ID) { + FuncIdRecord fir; + cantFail(TypeDeserializer::deserializeAs<FuncIdRecord>(inlinee_cvt, fir)); + TypeIndex parent_idx = fir.getParentScope(); + if (!parent_idx.isNoneType()) { + LazyRandomTypeCollection &ids = m_index->ipi().typeCollection(); + inlinee_name.append(std::string(ids.getTypeName(parent_idx))); + inlinee_name.append("::"); + } + inlinee_name.append(fir.getName().str()); + } + inline_site_sp->inline_function_info = std::make_shared<InlineFunctionInfo>( + inlinee_name.c_str(), llvm::StringRef(), decl_up.get(), + callsite_up.get()); + + m_inline_sites[opaque_uid] = inline_site_sp; +} + +size_t SymbolFileNativePDB::ParseBlocksRecursive(Function &func) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + PdbCompilandSymId func_id = PdbSymUid(func.GetID()).asCompilandSym(); + // After we iterate through inline sites inside the function, we already get + // all the info needed, removing from the map to save memory. + std::set<uint64_t> remove_uids; + auto parse_blocks = [&](SymbolKind kind, PdbCompilandSymId id) { + if (kind == S_GPROC32 || kind == S_LPROC32 || kind == S_BLOCK32 || + kind == S_INLINESITE) { + GetOrCreateBlock(id); + if (kind == S_INLINESITE) + remove_uids.insert(toOpaqueUid(id)); + return true; + } + return false; + }; + size_t count = ParseSymbolArrayInScope(func_id, parse_blocks); + for (uint64_t uid : remove_uids) { + m_inline_sites.erase(uid); + } + return count; +} + +size_t SymbolFileNativePDB::ParseSymbolArrayInScope( + PdbCompilandSymId parent_id, + llvm::function_ref<bool(SymbolKind, PdbCompilandSymId)> fn) { + CompilandIndexItem *cii = m_index->compilands().GetCompiland(parent_id.modi); + CVSymbolArray syms = + cii->m_debug_stream.getSymbolArrayForScope(parent_id.offset); + + size_t count = 1; + for (auto iter = syms.begin(); iter != syms.end(); ++iter) { + PdbCompilandSymId child_id(parent_id.modi, iter.offset()); + if (fn(iter->kind(), child_id)) + ++count; + } + + return count; +} + +void SymbolFileNativePDB::DumpClangAST(Stream &s) { + auto ts_or_err = GetTypeSystemForLanguage(eLanguageTypeC_plus_plus); + if (!ts_or_err) + return; + auto ts = *ts_or_err; + TypeSystemClang *clang = llvm::dyn_cast_or_null<TypeSystemClang>(ts.get()); + if (!clang) + return; + clang->GetNativePDBParser()->Dump(s); +} + +void SymbolFileNativePDB::FindGlobalVariables( + ConstString name, const CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, VariableList &variables) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>; + + std::vector<SymbolAndOffset> results = m_index->globals().findRecordsByName( + name.GetStringRef(), m_index->symrecords()); + for (const SymbolAndOffset &result : results) { + switch (result.second.kind()) { + case SymbolKind::S_GDATA32: + case SymbolKind::S_LDATA32: + case SymbolKind::S_GTHREAD32: + case SymbolKind::S_LTHREAD32: + case SymbolKind::S_CONSTANT: { + PdbGlobalSymId global(result.first, false); + if (VariableSP var = GetOrCreateGlobalVariable(global)) + variables.AddVariable(var); + break; + } + default: + continue; + } + } +} + +void SymbolFileNativePDB::FindFunctions( + const Module::LookupInfo &lookup_info, + const CompilerDeclContext &parent_decl_ctx, bool include_inlines, + SymbolContextList &sc_list) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + ConstString name = lookup_info.GetLookupName(); + FunctionNameType name_type_mask = lookup_info.GetNameTypeMask(); + if (name_type_mask & eFunctionNameTypeFull) + name = lookup_info.GetName(); + + // For now we only support lookup by method name or full name. + if (!(name_type_mask & eFunctionNameTypeFull || + name_type_mask & eFunctionNameTypeMethod)) + return; + + using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>; + + std::vector<SymbolAndOffset> matches = m_index->globals().findRecordsByName( + name.GetStringRef(), m_index->symrecords()); + for (const SymbolAndOffset &match : matches) { + if (match.second.kind() != S_PROCREF && match.second.kind() != S_LPROCREF) + continue; + ProcRefSym proc(match.second.kind()); + cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(match.second, proc)); + + if (!IsValidRecord(proc)) + continue; + + CompilandIndexItem &cci = + m_index->compilands().GetOrCreateCompiland(proc.modi()); + SymbolContext sc; + + sc.comp_unit = GetOrCreateCompileUnit(cci).get(); + PdbCompilandSymId func_id(proc.modi(), proc.SymOffset); + sc.function = GetOrCreateFunction(func_id, *sc.comp_unit).get(); + + sc_list.Append(sc); + } +} + +void SymbolFileNativePDB::FindFunctions(const RegularExpression ®ex, + bool include_inlines, + SymbolContextList &sc_list) {} + +void SymbolFileNativePDB::FindTypes(const lldb_private::TypeQuery &query, + lldb_private::TypeResults &results) { + + // Make sure we haven't already searched this SymbolFile before. + if (results.AlreadySearched(this)) + return; + + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + + std::vector<TypeIndex> matches = + m_index->tpi().findRecordsByName(query.GetTypeBasename().GetStringRef()); + + for (TypeIndex type_idx : matches) { + TypeSP type_sp = GetOrCreateType(type_idx); + if (!type_sp) + continue; + + // We resolved a type. Get the fully qualified name to ensure it matches. + ConstString name = type_sp->GetQualifiedName(); + TypeQuery type_match(name.GetStringRef(), TypeQueryOptions::e_exact_match); + if (query.ContextMatches(type_match.GetContextRef())) { + results.InsertUnique(type_sp); + if (results.Done(query)) + return; + } + } +} + +void SymbolFileNativePDB::FindTypesByName(llvm::StringRef name, + uint32_t max_matches, + TypeMap &types) { + + std::vector<TypeIndex> matches = m_index->tpi().findRecordsByName(name); + if (max_matches > 0 && max_matches < matches.size()) + matches.resize(max_matches); + + for (TypeIndex ti : matches) { + TypeSP type = GetOrCreateType(ti); + if (!type) + continue; + + types.Insert(type); + } +} + +size_t SymbolFileNativePDB::ParseTypes(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + // Only do the full type scan the first time. + if (m_done_full_type_scan) + return 0; + + const size_t old_count = GetTypeList().GetSize(); + LazyRandomTypeCollection &types = m_index->tpi().typeCollection(); + + // First process the entire TPI stream. + for (auto ti = types.getFirst(); ti; ti = types.getNext(*ti)) { + TypeSP type = GetOrCreateType(*ti); + if (type) + (void)type->GetFullCompilerType(); + } + + // Next look for S_UDT records in the globals stream. + for (const uint32_t gid : m_index->globals().getGlobalsTable()) { + PdbGlobalSymId global{gid, false}; + CVSymbol sym = m_index->ReadSymbolRecord(global); + if (sym.kind() != S_UDT) + continue; + + UDTSym udt = llvm::cantFail(SymbolDeserializer::deserializeAs<UDTSym>(sym)); + bool is_typedef = true; + if (IsTagRecord(PdbTypeSymId{udt.Type, false}, m_index->tpi())) { + CVType cvt = m_index->tpi().getType(udt.Type); + llvm::StringRef name = CVTagRecord::create(cvt).name(); + if (name == udt.Name) + is_typedef = false; + } + + if (is_typedef) + GetOrCreateTypedef(global); + } + + const size_t new_count = GetTypeList().GetSize(); + + m_done_full_type_scan = true; + + return new_count - old_count; +} + +size_t +SymbolFileNativePDB::ParseVariablesForCompileUnit(CompileUnit &comp_unit, + VariableList &variables) { + PdbSymUid sym_uid(comp_unit.GetID()); + lldbassert(sym_uid.kind() == PdbSymUidKind::Compiland); + return 0; +} + +VariableSP SymbolFileNativePDB::CreateLocalVariable(PdbCompilandSymId scope_id, + PdbCompilandSymId var_id, + bool is_param) { + ModuleSP module = GetObjectFile()->GetModule(); + Block &block = GetOrCreateBlock(scope_id); + // Get function block. + Block *func_block = █ + while (func_block->GetParent()) { + func_block = func_block->GetParent(); + } + Address addr; + func_block->GetStartAddress(addr); + VariableInfo var_info = + GetVariableLocationInfo(*m_index, var_id, *func_block, module); + Function *func = func_block->CalculateSymbolContextFunction(); + if (!func) + return nullptr; + // Use empty dwarf expr if optimized away so that it won't be filtered out + // when lookuping local variables in this scope. + if (!var_info.location.IsValid()) + var_info.location = DWARFExpressionList(module, DWARFExpression(), nullptr); + var_info.location.SetFuncFileAddress( + func->GetAddressRange().GetBaseAddress().GetFileAddress()); + CompilandIndexItem *cii = m_index->compilands().GetCompiland(var_id.modi); + CompUnitSP comp_unit_sp = GetOrCreateCompileUnit(*cii); + TypeSP type_sp = GetOrCreateType(var_info.type); + if (!type_sp) + return nullptr; + std::string name = var_info.name.str(); + Declaration decl; + SymbolFileTypeSP sftype = + std::make_shared<SymbolFileType>(*this, type_sp->GetID()); + + is_param |= var_info.is_param; + ValueType var_scope = + is_param ? eValueTypeVariableArgument : eValueTypeVariableLocal; + bool external = false; + bool artificial = false; + bool location_is_constant_data = false; + bool static_member = false; + Variable::RangeList scope_ranges; + VariableSP var_sp = std::make_shared<Variable>( + toOpaqueUid(var_id), name.c_str(), name.c_str(), sftype, var_scope, + &block, scope_ranges, &decl, var_info.location, external, artificial, + location_is_constant_data, static_member); + if (!is_param) { + auto ts_or_err = GetTypeSystemForLanguage(comp_unit_sp->GetLanguage()); + if (auto err = ts_or_err.takeError()) + return nullptr; + auto ts = *ts_or_err; + if (!ts) + return nullptr; + + ts->GetNativePDBParser()->GetOrCreateVariableDecl(scope_id, var_id); + } + m_local_variables[toOpaqueUid(var_id)] = var_sp; + return var_sp; +} + +VariableSP SymbolFileNativePDB::GetOrCreateLocalVariable( + PdbCompilandSymId scope_id, PdbCompilandSymId var_id, bool is_param) { + auto iter = m_local_variables.find(toOpaqueUid(var_id)); + if (iter != m_local_variables.end()) + return iter->second; + + return CreateLocalVariable(scope_id, var_id, is_param); +} + +TypeSP SymbolFileNativePDB::CreateTypedef(PdbGlobalSymId id) { + CVSymbol sym = m_index->ReadSymbolRecord(id); + lldbassert(sym.kind() == SymbolKind::S_UDT); + + UDTSym udt = llvm::cantFail(SymbolDeserializer::deserializeAs<UDTSym>(sym)); + + TypeSP target_type = GetOrCreateType(udt.Type); + + auto ts_or_err = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = ts_or_err.takeError()) + return nullptr; + auto ts = *ts_or_err; + if (!ts) + return nullptr; + + ts->GetNativePDBParser()->GetOrCreateTypedefDecl(id); + + Declaration decl; + return MakeType( + toOpaqueUid(id), ConstString(udt.Name), target_type->GetByteSize(nullptr), + nullptr, target_type->GetID(), lldb_private::Type::eEncodingIsTypedefUID, + decl, target_type->GetForwardCompilerType(), + lldb_private::Type::ResolveState::Forward); +} + +TypeSP SymbolFileNativePDB::GetOrCreateTypedef(PdbGlobalSymId id) { + auto iter = m_types.find(toOpaqueUid(id)); + if (iter != m_types.end()) + return iter->second; + + return CreateTypedef(id); +} + +size_t SymbolFileNativePDB::ParseVariablesForBlock(PdbCompilandSymId block_id) { + Block &block = GetOrCreateBlock(block_id); + + size_t count = 0; + + CompilandIndexItem *cii = m_index->compilands().GetCompiland(block_id.modi); + CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(block_id.offset); + uint32_t params_remaining = 0; + switch (sym.kind()) { + case S_GPROC32: + case S_LPROC32: { + ProcSym proc(static_cast<SymbolRecordKind>(sym.kind())); + cantFail(SymbolDeserializer::deserializeAs<ProcSym>(sym, proc)); + CVType signature = m_index->tpi().getType(proc.FunctionType); + if (signature.kind() == LF_PROCEDURE) { + ProcedureRecord sig; + if (llvm::Error e = TypeDeserializer::deserializeAs<ProcedureRecord>( + signature, sig)) { + llvm::consumeError(std::move(e)); + return 0; + } + params_remaining = sig.getParameterCount(); + } else if (signature.kind() == LF_MFUNCTION) { + MemberFunctionRecord sig; + if (llvm::Error e = TypeDeserializer::deserializeAs<MemberFunctionRecord>( + signature, sig)) { + llvm::consumeError(std::move(e)); + return 0; + } + params_remaining = sig.getParameterCount(); + } else + return 0; + break; + } + case S_BLOCK32: + break; + case S_INLINESITE: + break; + default: + lldbassert(false && "Symbol is not a block!"); + return 0; + } + + VariableListSP variables = block.GetBlockVariableList(false); + if (!variables) { + variables = std::make_shared<VariableList>(); + block.SetVariableList(variables); + } + + CVSymbolArray syms = limitSymbolArrayToScope( + cii->m_debug_stream.getSymbolArray(), block_id.offset); + + // Skip the first record since it's a PROC32 or BLOCK32, and there's + // no point examining it since we know it's not a local variable. + syms.drop_front(); + auto iter = syms.begin(); + auto end = syms.end(); + + while (iter != end) { + uint32_t record_offset = iter.offset(); + CVSymbol variable_cvs = *iter; + PdbCompilandSymId child_sym_id(block_id.modi, record_offset); + ++iter; + + // If this is a block or inline site, recurse into its children and then + // skip it. + if (variable_cvs.kind() == S_BLOCK32 || + variable_cvs.kind() == S_INLINESITE) { + uint32_t block_end = getScopeEndOffset(variable_cvs); + count += ParseVariablesForBlock(child_sym_id); + iter = syms.at(block_end); + continue; + } + + bool is_param = params_remaining > 0; + VariableSP variable; + switch (variable_cvs.kind()) { + case S_REGREL32: + case S_REGISTER: + case S_LOCAL: + variable = GetOrCreateLocalVariable(block_id, child_sym_id, is_param); + if (is_param) + --params_remaining; + if (variable) + variables->AddVariableIfUnique(variable); + break; + default: + break; + } + } + + // Pass false for set_children, since we call this recursively so that the + // children will call this for themselves. + block.SetDidParseVariables(true, false); + + return count; +} + +size_t SymbolFileNativePDB::ParseVariablesForContext(const SymbolContext &sc) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + lldbassert(sc.function || sc.comp_unit); + + VariableListSP variables; + if (sc.block) { + PdbSymUid block_id(sc.block->GetID()); + + size_t count = ParseVariablesForBlock(block_id.asCompilandSym()); + return count; + } + + if (sc.function) { + PdbSymUid block_id(sc.function->GetID()); + + size_t count = ParseVariablesForBlock(block_id.asCompilandSym()); + return count; + } + + if (sc.comp_unit) { + variables = sc.comp_unit->GetVariableList(false); + if (!variables) { + variables = std::make_shared<VariableList>(); + sc.comp_unit->SetVariableList(variables); + } + return ParseVariablesForCompileUnit(*sc.comp_unit, *variables); + } + + llvm_unreachable("Unreachable!"); +} + +CompilerDecl SymbolFileNativePDB::GetDeclForUID(lldb::user_id_t uid) { + auto ts_or_err = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = ts_or_err.takeError()) + return CompilerDecl(); + auto ts = *ts_or_err; + if (!ts) + return {}; + + if (auto decl = ts->GetNativePDBParser()->GetOrCreateDeclForUid(uid)) + return *decl; + return CompilerDecl(); +} + +CompilerDeclContext +SymbolFileNativePDB::GetDeclContextForUID(lldb::user_id_t uid) { + auto ts_or_err = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = ts_or_err.takeError()) + return {}; + auto ts = *ts_or_err; + if (!ts) + return {}; + + PdbAstBuilder *ast_builder = ts->GetNativePDBParser(); + clang::DeclContext *context = + ast_builder->GetOrCreateDeclContextForUid(PdbSymUid(uid)); + if (!context) + return {}; + + return ast_builder->ToCompilerDeclContext(*context); +} + +CompilerDeclContext +SymbolFileNativePDB::GetDeclContextContainingUID(lldb::user_id_t uid) { + auto ts_or_err = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = ts_or_err.takeError()) + return CompilerDeclContext(); + auto ts = *ts_or_err; + if (!ts) + return {}; + + PdbAstBuilder *ast_builder = ts->GetNativePDBParser(); + clang::DeclContext *context = ast_builder->GetParentDeclContext(PdbSymUid(uid)); + if (!context) + return CompilerDeclContext(); + return ast_builder->ToCompilerDeclContext(*context); +} + +Type *SymbolFileNativePDB::ResolveTypeUID(lldb::user_id_t type_uid) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + auto iter = m_types.find(type_uid); + // lldb should not be passing us non-sensical type uids. the only way it + // could have a type uid in the first place is if we handed it out, in which + // case we should know about the type. However, that doesn't mean we've + // instantiated it yet. We can vend out a UID for a future type. So if the + // type doesn't exist, let's instantiate it now. + if (iter != m_types.end()) + return &*iter->second; + + PdbSymUid uid(type_uid); + lldbassert(uid.kind() == PdbSymUidKind::Type); + PdbTypeSymId type_id = uid.asTypeSym(); + if (type_id.index.isNoneType()) + return nullptr; + + TypeSP type_sp = CreateAndCacheType(type_id); + if (!type_sp) + return nullptr; + return &*type_sp; +} + +std::optional<SymbolFile::ArrayInfo> +SymbolFileNativePDB::GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) { + return std::nullopt; +} + +bool SymbolFileNativePDB::CompleteType(CompilerType &compiler_type) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + auto ts = compiler_type.GetTypeSystem(); + auto clang_type_system = ts.dyn_cast_or_null<TypeSystemClang>(); + if (!clang_type_system) + return false; + + PdbAstBuilder *ast_builder = + static_cast<PdbAstBuilder *>(clang_type_system->GetNativePDBParser()); + if (ast_builder && + ast_builder->GetClangASTImporter().CanImport(compiler_type)) + return ast_builder->GetClangASTImporter().CompleteType(compiler_type); + clang::QualType qt = + clang::QualType::getFromOpaquePtr(compiler_type.GetOpaqueQualType()); + + return ast_builder->CompleteType(qt); +} + +void SymbolFileNativePDB::GetTypes(lldb_private::SymbolContextScope *sc_scope, + TypeClass type_mask, + lldb_private::TypeList &type_list) {} + +CompilerDeclContext SymbolFileNativePDB::FindNamespace( + ConstString name, const CompilerDeclContext &parent_decl_ctx, bool) { + return {}; +} + +llvm::Expected<lldb::TypeSystemSP> +SymbolFileNativePDB::GetTypeSystemForLanguage(lldb::LanguageType language) { + auto type_system_or_err = + m_objfile_sp->GetModule()->GetTypeSystemForLanguage(language); + if (type_system_or_err) + if (auto ts = *type_system_or_err) + ts->SetSymbolFile(this); + return type_system_or_err; +} + +uint64_t SymbolFileNativePDB::GetDebugInfoSize(bool load_all_debug_info) { + // PDB files are a separate file that contains all debug info. + return m_index->pdb().getFileSize(); +} + +void SymbolFileNativePDB::BuildParentMap() { + LazyRandomTypeCollection &types = m_index->tpi().typeCollection(); + + llvm::DenseMap<TypeIndex, TypeIndex> forward_to_full; + llvm::DenseMap<TypeIndex, TypeIndex> full_to_forward; + + struct RecordIndices { + TypeIndex forward; + TypeIndex full; + }; + + llvm::StringMap<RecordIndices> record_indices; + + for (auto ti = types.getFirst(); ti; ti = types.getNext(*ti)) { + CVType type = types.getType(*ti); + if (!IsTagRecord(type)) + continue; + + CVTagRecord tag = CVTagRecord::create(type); + + RecordIndices &indices = record_indices[tag.asTag().getUniqueName()]; + if (tag.asTag().isForwardRef()) + indices.forward = *ti; + else + indices.full = *ti; + + if (indices.full != TypeIndex::None() && + indices.forward != TypeIndex::None()) { + forward_to_full[indices.forward] = indices.full; + full_to_forward[indices.full] = indices.forward; + } + + // We're looking for LF_NESTTYPE records in the field list, so ignore + // forward references (no field list), and anything without a nested class + // (since there won't be any LF_NESTTYPE records). + if (tag.asTag().isForwardRef() || !tag.asTag().containsNestedClass()) + continue; + + struct ProcessTpiStream : public TypeVisitorCallbacks { + ProcessTpiStream(PdbIndex &index, TypeIndex parent, + const CVTagRecord &parent_cvt, + llvm::DenseMap<TypeIndex, TypeIndex> &parents) + : index(index), parents(parents), parent(parent), + parent_cvt(parent_cvt) {} + + PdbIndex &index; + llvm::DenseMap<TypeIndex, TypeIndex> &parents; + + unsigned unnamed_type_index = 1; + TypeIndex parent; + const CVTagRecord &parent_cvt; + + llvm::Error visitKnownMember(CVMemberRecord &CVR, + NestedTypeRecord &Record) override { + std::string unnamed_type_name; + if (Record.Name.empty()) { + unnamed_type_name = + llvm::formatv("<unnamed-type-$S{0}>", unnamed_type_index).str(); + Record.Name = unnamed_type_name; + ++unnamed_type_index; + } + std::optional<CVTagRecord> tag = + GetNestedTagDefinition(Record, parent_cvt, index.tpi()); + if (!tag) + return llvm::ErrorSuccess(); + + parents[Record.Type] = parent; + return llvm::ErrorSuccess(); + } + }; + + CVType field_list_cvt = m_index->tpi().getType(tag.asTag().FieldList); + ProcessTpiStream process(*m_index, *ti, tag, m_parent_types); + FieldListRecord field_list; + if (llvm::Error error = TypeDeserializer::deserializeAs<FieldListRecord>( + field_list_cvt, field_list)) + llvm::consumeError(std::move(error)); + if (llvm::Error error = visitMemberRecordStream(field_list.Data, process)) + llvm::consumeError(std::move(error)); + } + + // Now that we know the forward -> full mapping of all type indices, we can + // re-write all the indices. At the end of this process, we want a mapping + // consisting of fwd -> full and full -> full for all child -> parent indices. + // We can re-write the values in place, but for the keys, we must save them + // off so that we don't modify the map in place while also iterating it. + std::vector<TypeIndex> full_keys; + std::vector<TypeIndex> fwd_keys; + for (auto &entry : m_parent_types) { + TypeIndex key = entry.first; + TypeIndex value = entry.second; + + auto iter = forward_to_full.find(value); + if (iter != forward_to_full.end()) + entry.second = iter->second; + + iter = forward_to_full.find(key); + if (iter != forward_to_full.end()) + fwd_keys.push_back(key); + else + full_keys.push_back(key); + } + for (TypeIndex fwd : fwd_keys) { + TypeIndex full = forward_to_full[fwd]; + TypeIndex parent_idx = m_parent_types[fwd]; + m_parent_types[full] = parent_idx; + } + for (TypeIndex full : full_keys) { + TypeIndex fwd = full_to_forward[full]; + m_parent_types[fwd] = m_parent_types[full]; + } +} + +std::optional<PdbCompilandSymId> +SymbolFileNativePDB::FindSymbolScope(PdbCompilandSymId id) { + CVSymbol sym = m_index->ReadSymbolRecord(id); + if (symbolOpensScope(sym.kind())) { + // If this exact symbol opens a scope, we can just directly access its + // parent. + id.offset = getScopeParentOffset(sym); + // Global symbols have parent offset of 0. Return std::nullopt to indicate + // this. + if (id.offset == 0) + return std::nullopt; + return id; + } + + // Otherwise we need to start at the beginning and iterate forward until we + // reach (or pass) this particular symbol + CompilandIndexItem &cii = m_index->compilands().GetOrCreateCompiland(id.modi); + const CVSymbolArray &syms = cii.m_debug_stream.getSymbolArray(); + + auto begin = syms.begin(); + auto end = syms.at(id.offset); + std::vector<PdbCompilandSymId> scope_stack; + + while (begin != end) { + if (begin.offset() > id.offset) { + // We passed it. We couldn't even find this symbol record. + lldbassert(false && "Invalid compiland symbol id!"); + return std::nullopt; + } + + // We haven't found the symbol yet. Check if we need to open or close the + // scope stack. + if (symbolOpensScope(begin->kind())) { + // We can use the end offset of the scope to determine whether or not + // we can just outright skip this entire scope. + uint32_t scope_end = getScopeEndOffset(*begin); + if (scope_end < id.offset) { + begin = syms.at(scope_end); + } else { + // The symbol we're looking for is somewhere in this scope. + scope_stack.emplace_back(id.modi, begin.offset()); + } + } else if (symbolEndsScope(begin->kind())) { + scope_stack.pop_back(); + } + ++begin; + } + if (scope_stack.empty()) + return std::nullopt; + // We have a match! Return the top of the stack + return scope_stack.back(); +} + +std::optional<llvm::codeview::TypeIndex> +SymbolFileNativePDB::GetParentType(llvm::codeview::TypeIndex ti) { + auto parent_iter = m_parent_types.find(ti); + if (parent_iter == m_parent_types.end()) + return std::nullopt; + return parent_iter->second; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h new file mode 100644 index 000000000000..669c44aa131e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h @@ -0,0 +1,286 @@ +//===-- SymbolFileNativePDB.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_SYMBOLFILENATIVEPDB_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_SYMBOLFILENATIVEPDB_H + +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/SymbolFile.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +#include "CompileUnitIndex.h" +#include "PdbIndex.h" +#include "PdbAstBuilder.h" +#include <optional> + +namespace clang { +class TagDecl; +} + +namespace llvm { +namespace codeview { +class ClassRecord; +class EnumRecord; +class ModifierRecord; +class PointerRecord; +struct UnionRecord; +} // namespace codeview +} // namespace llvm + +namespace lldb_private { + +namespace npdb { + +class SymbolFileNativePDB : public SymbolFileCommon { + friend class UdtRecordCompleter; + + /// LLVM RTTI support. + static char ID; + +public: + /// LLVM RTTI support. + /// \{ + bool isA(const void *ClassID) const override { + return ClassID == &ID || SymbolFileCommon::isA(ClassID); + } + static bool classof(const SymbolFile *obj) { return obj->isA(&ID); } + /// \} + + // Static Functions + static void Initialize(); + + static void Terminate(); + + static void DebuggerInitialize(Debugger &debugger); + + static llvm::StringRef GetPluginNameStatic() { return "native-pdb"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + static SymbolFile *CreateInstance(lldb::ObjectFileSP objfile_sp); + + // Constructors and Destructors + SymbolFileNativePDB(lldb::ObjectFileSP objfile_sp); + + ~SymbolFileNativePDB() override; + + uint32_t CalculateAbilities() override; + + void InitializeObject() override; + + uint64_t GetDebugInfoSize(bool load_all_debug_info = false) override; + + // Compile Unit function calls + + void + ParseDeclsForContext(lldb_private::CompilerDeclContext decl_ctx) override; + + lldb::LanguageType + ParseLanguage(lldb_private::CompileUnit &comp_unit) override; + + size_t ParseFunctions(lldb_private::CompileUnit &comp_unit) override; + + bool ParseLineTable(lldb_private::CompileUnit &comp_unit) override; + + bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override; + + bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit, + SupportFileList &support_files) override; + size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override; + + bool ParseImportedModules( + const SymbolContext &sc, + std::vector<lldb_private::SourceModule> &imported_modules) override; + + size_t ParseBlocksRecursive(Function &func) override; + + void FindGlobalVariables(ConstString name, + const CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, + VariableList &variables) override; + + size_t ParseVariablesForContext(const SymbolContext &sc) override; + + void AddSymbols(Symtab &symtab) override; + + CompilerDecl GetDeclForUID(lldb::user_id_t uid) override; + CompilerDeclContext GetDeclContextForUID(lldb::user_id_t uid) override; + CompilerDeclContext GetDeclContextContainingUID(lldb::user_id_t uid) override; + Type *ResolveTypeUID(lldb::user_id_t type_uid) override; + std::optional<ArrayInfo> GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, + const lldb_private::ExecutionContext *exe_ctx) override; + + bool CompleteType(CompilerType &compiler_type) override; + uint32_t ResolveSymbolContext(const Address &so_addr, + lldb::SymbolContextItem resolve_scope, + SymbolContext &sc) override; + uint32_t ResolveSymbolContext(const SourceLocationSpec &src_location_spec, + lldb::SymbolContextItem resolve_scope, + SymbolContextList &sc_list) override; + + void GetTypes(SymbolContextScope *sc_scope, lldb::TypeClass type_mask, + TypeList &type_list) override; + + void FindFunctions(const Module::LookupInfo &lookup_info, + const CompilerDeclContext &parent_decl_ctx, + bool include_inlines, SymbolContextList &sc_list) override; + + void FindFunctions(const RegularExpression ®ex, bool include_inlines, + SymbolContextList &sc_list) override; + + std::optional<PdbCompilandSymId> FindSymbolScope(PdbCompilandSymId id); + + void FindTypes(const lldb_private::TypeQuery &match, + lldb_private::TypeResults &results) override; + + llvm::Expected<lldb::TypeSystemSP> + GetTypeSystemForLanguage(lldb::LanguageType language) override; + + CompilerDeclContext FindNamespace(ConstString name, + const CompilerDeclContext &parent_decl_ctx, + bool only_root_namespaces) override; + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + llvm::pdb::PDBFile &GetPDBFile() { return m_index->pdb(); } + const llvm::pdb::PDBFile &GetPDBFile() const { return m_index->pdb(); } + + PdbIndex &GetIndex() { return *m_index; }; + + void DumpClangAST(Stream &s) override; + + std::optional<llvm::codeview::TypeIndex> + GetParentType(llvm::codeview::TypeIndex ti); + +private: + struct LineTableEntryComparator { + bool operator()(const lldb_private::LineTable::Entry &lhs, + const lldb_private::LineTable::Entry &rhs) const { + return lhs.file_addr < rhs.file_addr; + } + }; + + // From address range relative to function base to source line number. + using RangeSourceLineVector = + lldb_private::RangeDataVector<uint32_t, uint32_t, int32_t>; + // InlineSite contains information in a S_INLINESITE record. + struct InlineSite { + PdbCompilandSymId parent_id; + std::shared_ptr<InlineFunctionInfo> inline_function_info; + RangeSourceLineVector ranges; + std::vector<lldb_private::LineTable::Entry> line_entries; + InlineSite(PdbCompilandSymId parent_id) : parent_id(parent_id){}; + }; + + void BuildParentMap(); + + uint32_t CalculateNumCompileUnits() override; + + lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override; + + void FindTypesByName(llvm::StringRef name, uint32_t max_matches, + TypeMap &types); + + lldb::TypeSP CreateModifierType(PdbTypeSymId type_id, + const llvm::codeview::ModifierRecord &mr, + CompilerType ct); + lldb::TypeSP CreatePointerType(PdbTypeSymId type_id, + const llvm::codeview::PointerRecord &pr, + CompilerType ct); + lldb::TypeSP CreateSimpleType(llvm::codeview::TypeIndex ti, CompilerType ct); + lldb::TypeSP CreateTagType(PdbTypeSymId type_id, + const llvm::codeview::ClassRecord &cr, + CompilerType ct); + lldb::TypeSP CreateTagType(PdbTypeSymId type_id, + const llvm::codeview::EnumRecord &er, + CompilerType ct); + lldb::TypeSP CreateTagType(PdbTypeSymId type_id, + const llvm::codeview::UnionRecord &ur, + CompilerType ct); + lldb::TypeSP CreateArrayType(PdbTypeSymId type_id, + const llvm::codeview::ArrayRecord &ar, + CompilerType ct); + lldb::TypeSP CreateFunctionType(PdbTypeSymId type_id, + const llvm::codeview::MemberFunctionRecord &pr, + CompilerType ct); + lldb::TypeSP CreateProcedureType(PdbTypeSymId type_id, + const llvm::codeview::ProcedureRecord &pr, + CompilerType ct); + lldb::TypeSP CreateClassStructUnion(PdbTypeSymId type_id, + const llvm::codeview::TagRecord &record, + size_t size, CompilerType ct); + + lldb::FunctionSP GetOrCreateFunction(PdbCompilandSymId func_id, + CompileUnit &comp_unit); + lldb::CompUnitSP GetOrCreateCompileUnit(const CompilandIndexItem &cci); + lldb::TypeSP GetOrCreateType(PdbTypeSymId type_id); + lldb::TypeSP GetOrCreateType(llvm::codeview::TypeIndex ti); + lldb::VariableSP GetOrCreateGlobalVariable(PdbGlobalSymId var_id); + Block &GetOrCreateBlock(PdbCompilandSymId block_id); + lldb::VariableSP GetOrCreateLocalVariable(PdbCompilandSymId scope_id, + PdbCompilandSymId var_id, + bool is_param); + lldb::TypeSP GetOrCreateTypedef(PdbGlobalSymId id); + + lldb::FunctionSP CreateFunction(PdbCompilandSymId func_id, + CompileUnit &comp_unit); + Block &CreateBlock(PdbCompilandSymId block_id); + lldb::VariableSP CreateLocalVariable(PdbCompilandSymId scope_id, + PdbCompilandSymId var_id, bool is_param); + lldb::TypeSP CreateTypedef(PdbGlobalSymId id); + lldb::CompUnitSP CreateCompileUnit(const CompilandIndexItem &cci); + lldb::TypeSP CreateType(PdbTypeSymId type_id, CompilerType ct); + lldb::TypeSP CreateAndCacheType(PdbTypeSymId type_id); + lldb::VariableSP CreateGlobalVariable(PdbGlobalSymId var_id); + lldb::VariableSP CreateConstantSymbol(PdbGlobalSymId var_id, + const llvm::codeview::CVSymbol &cvs); + size_t ParseVariablesForCompileUnit(CompileUnit &comp_unit, + VariableList &variables); + size_t ParseVariablesForBlock(PdbCompilandSymId block_id); + + llvm::Expected<uint32_t> GetFileIndex(const CompilandIndexItem &cii, + uint32_t file_id); + + size_t ParseSymbolArrayInScope( + PdbCompilandSymId parent, + llvm::function_ref<bool(llvm::codeview::SymbolKind, PdbCompilandSymId)> + fn); + + void ParseInlineSite(PdbCompilandSymId inline_site_id, Address func_addr); + + llvm::BumpPtrAllocator m_allocator; + + lldb::addr_t m_obj_load_address = 0; + bool m_done_full_type_scan = false; + // UID for anonymous union and anonymous struct as they don't have entities in + // pdb debug info. + lldb::user_id_t anonymous_id = LLDB_INVALID_UID - 1; + + std::unique_ptr<llvm::pdb::PDBFile> m_file_up; + std::unique_ptr<PdbIndex> m_index; + + llvm::DenseMap<lldb::user_id_t, lldb::VariableSP> m_global_vars; + llvm::DenseMap<lldb::user_id_t, lldb::VariableSP> m_local_variables; + llvm::DenseMap<lldb::user_id_t, lldb::BlockSP> m_blocks; + llvm::DenseMap<lldb::user_id_t, lldb::FunctionSP> m_functions; + llvm::DenseMap<lldb::user_id_t, lldb::CompUnitSP> m_compilands; + llvm::DenseMap<lldb::user_id_t, lldb::TypeSP> m_types; + llvm::DenseMap<lldb::user_id_t, std::shared_ptr<InlineSite>> m_inline_sites; + llvm::DenseMap<llvm::codeview::TypeIndex, llvm::codeview::TypeIndex> + m_parent_types; +}; + +} // namespace npdb +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_SYMBOLFILENATIVEPDB_H diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp new file mode 100644 index 000000000000..17c5f6118603 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp @@ -0,0 +1,501 @@ +#include "UdtRecordCompleter.h" + +#include "PdbAstBuilder.h" +#include "PdbIndex.h" +#include "PdbSymUid.h" +#include "PdbUtil.h" + +#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" +#include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h" +#include "Plugins/ExpressionParser/Clang/ClangUtil.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "SymbolFileNativePDB.h" +#include "lldb/Core/Address.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include <optional> + +using namespace llvm::codeview; +using namespace llvm::pdb; +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::npdb; + +using Error = llvm::Error; + +UdtRecordCompleter::UdtRecordCompleter( + PdbTypeSymId id, CompilerType &derived_ct, clang::TagDecl &tag_decl, + PdbAstBuilder &ast_builder, PdbIndex &index, + llvm::DenseMap<clang::Decl *, DeclStatus> &decl_to_status, + llvm::DenseMap<lldb::opaque_compiler_type_t, + llvm::SmallSet<std::pair<llvm::StringRef, CompilerType>, 8>> + &cxx_record_map) + : m_id(id), m_derived_ct(derived_ct), m_tag_decl(tag_decl), + m_ast_builder(ast_builder), m_index(index), + m_decl_to_status(decl_to_status), m_cxx_record_map(cxx_record_map) { + CVType cvt = m_index.tpi().getType(m_id.index); + switch (cvt.kind()) { + case LF_ENUM: + m_cvr.er.Options = ClassOptions::None; + llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, m_cvr.er)); + break; + case LF_UNION: + m_cvr.ur.Options = ClassOptions::None; + llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, m_cvr.ur)); + m_layout.bit_size = m_cvr.ur.getSize() * 8; + m_record.record.kind = Member::Union; + break; + case LF_CLASS: + case LF_STRUCTURE: + m_cvr.cr.Options = ClassOptions::None; + llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, m_cvr.cr)); + m_layout.bit_size = m_cvr.cr.getSize() * 8; + m_record.record.kind = Member::Struct; + break; + default: + llvm_unreachable("unreachable!"); + } +} + +clang::QualType UdtRecordCompleter::AddBaseClassForTypeIndex( + llvm::codeview::TypeIndex ti, llvm::codeview::MemberAccess access, + std::optional<uint64_t> vtable_idx) { + PdbTypeSymId type_id(ti); + clang::QualType qt = m_ast_builder.GetOrCreateType(type_id); + + CVType udt_cvt = m_index.tpi().getType(ti); + + std::unique_ptr<clang::CXXBaseSpecifier> base_spec = + m_ast_builder.clang().CreateBaseClassSpecifier( + qt.getAsOpaquePtr(), TranslateMemberAccess(access), + vtable_idx.has_value(), udt_cvt.kind() == LF_CLASS); + if (!base_spec) + return {}; + + m_bases.push_back( + std::make_pair(vtable_idx.value_or(0), std::move(base_spec))); + + return qt; +} + +void UdtRecordCompleter::AddMethod(llvm::StringRef name, TypeIndex type_idx, + MemberAccess access, MethodOptions options, + MemberAttributes attrs) { + clang::QualType method_qt = + m_ast_builder.GetOrCreateType(PdbTypeSymId(type_idx)); + if (method_qt.isNull()) + return; + CompilerType method_ct = m_ast_builder.ToCompilerType(method_qt); + TypeSystemClang::RequireCompleteType(method_ct); + lldb::opaque_compiler_type_t derived_opaque_ty = + m_derived_ct.GetOpaqueQualType(); + auto iter = m_cxx_record_map.find(derived_opaque_ty); + if (iter != m_cxx_record_map.end()) { + if (iter->getSecond().contains({name, method_ct})) { + return; + } + } + + lldb::AccessType access_type = TranslateMemberAccess(access); + bool is_artificial = (options & MethodOptions::CompilerGenerated) == + MethodOptions::CompilerGenerated; + m_ast_builder.clang().AddMethodToCXXRecordType( + derived_opaque_ty, name.data(), nullptr, method_ct, + access_type, attrs.isVirtual(), attrs.isStatic(), false, false, false, + is_artificial); + + m_cxx_record_map[derived_opaque_ty].insert({name, method_ct}); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + BaseClassRecord &base) { + clang::QualType base_qt = + AddBaseClassForTypeIndex(base.Type, base.getAccess()); + + if (base_qt.isNull()) + return llvm::Error::success(); + auto decl = + m_ast_builder.clang().GetAsCXXRecordDecl(base_qt.getAsOpaquePtr()); + lldbassert(decl); + + auto offset = clang::CharUnits::fromQuantity(base.getBaseOffset()); + m_layout.base_offsets.insert(std::make_pair(decl, offset)); + + return llvm::Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + VirtualBaseClassRecord &base) { + AddBaseClassForTypeIndex(base.BaseType, base.getAccess(), base.VTableIndex); + + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + ListContinuationRecord &cont) { + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + VFPtrRecord &vfptr) { + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember( + CVMemberRecord &cvr, StaticDataMemberRecord &static_data_member) { + clang::QualType member_type = + m_ast_builder.GetOrCreateType(PdbTypeSymId(static_data_member.Type)); + if (member_type.isNull()) + return llvm::Error::success(); + + CompilerType member_ct = m_ast_builder.ToCompilerType(member_type); + + lldb::AccessType access = + TranslateMemberAccess(static_data_member.getAccess()); + auto decl = TypeSystemClang::AddVariableToRecordType( + m_derived_ct, static_data_member.Name, member_ct, access); + + // Static constant members may be a const[expr] declaration. + // Query the symbol's value as the variable initializer if valid. + if (member_ct.IsConst() && member_ct.IsCompleteType()) { + std::string qual_name = decl->getQualifiedNameAsString(); + + auto results = + m_index.globals().findRecordsByName(qual_name, m_index.symrecords()); + + for (const auto &result : results) { + if (result.second.kind() == SymbolKind::S_CONSTANT) { + ConstantSym constant(SymbolRecordKind::ConstantSym); + cantFail(SymbolDeserializer::deserializeAs<ConstantSym>(result.second, + constant)); + + clang::QualType qual_type = decl->getType(); + unsigned type_width = decl->getASTContext().getIntWidth(qual_type); + unsigned constant_width = constant.Value.getBitWidth(); + + if (qual_type->isIntegralOrEnumerationType()) { + if (type_width >= constant_width) { + TypeSystemClang::SetIntegerInitializerForVariable( + decl, constant.Value.extOrTrunc(type_width)); + } else { + LLDB_LOG(GetLog(LLDBLog::AST), + "Class '{0}' has a member '{1}' of type '{2}' ({3} bits) " + "which resolves to a wider constant value ({4} bits). " + "Ignoring constant.", + m_derived_ct.GetTypeName(), static_data_member.Name, + member_ct.GetTypeName(), type_width, constant_width); + } + } else { + lldb::BasicType basic_type_enum = member_ct.GetBasicTypeEnumeration(); + switch (basic_type_enum) { + case lldb::eBasicTypeFloat: + case lldb::eBasicTypeDouble: + case lldb::eBasicTypeLongDouble: + if (type_width == constant_width) { + TypeSystemClang::SetFloatingInitializerForVariable( + decl, basic_type_enum == lldb::eBasicTypeFloat + ? llvm::APFloat(constant.Value.bitsToFloat()) + : llvm::APFloat(constant.Value.bitsToDouble())); + decl->setConstexpr(true); + } else { + LLDB_LOG( + GetLog(LLDBLog::AST), + "Class '{0}' has a member '{1}' of type '{2}' ({3} bits) " + "which resolves to a constant value of mismatched width " + "({4} bits). Ignoring constant.", + m_derived_ct.GetTypeName(), static_data_member.Name, + member_ct.GetTypeName(), type_width, constant_width); + } + break; + default: + break; + } + } + break; + } + } + } + + // FIXME: Add a PdbSymUid namespace for field list members and update + // the m_uid_to_decl map with this decl. + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + NestedTypeRecord &nested) { + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + DataMemberRecord &data_member) { + + uint64_t offset = data_member.FieldOffset * 8; + uint32_t bitfield_width = 0; + + TypeIndex ti(data_member.Type); + if (!ti.isSimple()) { + CVType cvt = m_index.tpi().getType(ti); + if (cvt.kind() == LF_BITFIELD) { + BitFieldRecord bfr; + llvm::cantFail(TypeDeserializer::deserializeAs<BitFieldRecord>(cvt, bfr)); + offset += bfr.BitOffset; + bitfield_width = bfr.BitSize; + ti = bfr.Type; + } + } + + clang::QualType member_qt = m_ast_builder.GetOrCreateType(PdbTypeSymId(ti)); + if (member_qt.isNull()) + return Error::success(); + TypeSystemClang::RequireCompleteType(m_ast_builder.ToCompilerType(member_qt)); + lldb::AccessType access = TranslateMemberAccess(data_member.getAccess()); + size_t field_size = + bitfield_width ? bitfield_width : GetSizeOfType(ti, m_index.tpi()) * 8; + if (field_size == 0) + return Error::success(); + m_record.CollectMember(data_member.Name, offset, field_size, member_qt, access, + bitfield_width); + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + OneMethodRecord &one_method) { + AddMethod(one_method.Name, one_method.Type, one_method.getAccess(), + one_method.getOptions(), one_method.Attrs); + + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + OverloadedMethodRecord &overloaded) { + TypeIndex method_list_idx = overloaded.MethodList; + + CVType method_list_type = m_index.tpi().getType(method_list_idx); + assert(method_list_type.kind() == LF_METHODLIST); + + MethodOverloadListRecord method_list; + llvm::cantFail(TypeDeserializer::deserializeAs<MethodOverloadListRecord>( + method_list_type, method_list)); + + for (const OneMethodRecord &method : method_list.Methods) + AddMethod(overloaded.Name, method.Type, method.getAccess(), + method.getOptions(), method.Attrs); + + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + EnumeratorRecord &enumerator) { + Declaration decl; + llvm::StringRef name = DropNameScope(enumerator.getName()); + + m_ast_builder.clang().AddEnumerationValueToEnumerationType( + m_derived_ct, decl, name.str().c_str(), enumerator.Value); + return Error::success(); +} + +void UdtRecordCompleter::complete() { + // Ensure the correct order for virtual bases. + llvm::stable_sort(m_bases, llvm::less_first()); + + std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> bases; + bases.reserve(m_bases.size()); + for (auto &ib : m_bases) + bases.push_back(std::move(ib.second)); + + TypeSystemClang &clang = m_ast_builder.clang(); + // Make sure all base classes refer to complete types and not forward + // declarations. If we don't do this, clang will crash with an + // assertion in the call to clang_type.TransferBaseClasses() + for (const auto &base_class : bases) { + clang::TypeSourceInfo *type_source_info = + base_class->getTypeSourceInfo(); + if (type_source_info) { + TypeSystemClang::RequireCompleteType( + clang.GetType(type_source_info->getType())); + } + } + + clang.TransferBaseClasses(m_derived_ct.GetOpaqueQualType(), std::move(bases)); + + clang.AddMethodOverridesForCXXRecordType(m_derived_ct.GetOpaqueQualType()); + FinishRecord(); + TypeSystemClang::BuildIndirectFields(m_derived_ct); + TypeSystemClang::CompleteTagDeclarationDefinition(m_derived_ct); + + if (auto *record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(&m_tag_decl)) { + m_ast_builder.GetClangASTImporter().SetRecordLayout(record_decl, m_layout); + } +} + +uint64_t +UdtRecordCompleter::AddMember(TypeSystemClang &clang, Member *field, + uint64_t bit_offset, CompilerType parent_ct, + ClangASTImporter::LayoutInfo &parent_layout, + clang::DeclContext *parent_decl_ctx) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + clang.GetSymbolFile()->GetBackingSymbolFile()); + clang::FieldDecl *field_decl = nullptr; + uint64_t bit_size = 0; + switch (field->kind) { + case Member::Field: { + field_decl = TypeSystemClang::AddFieldToRecordType( + parent_ct, field->name, m_ast_builder.ToCompilerType(field->qt), + field->access, field->bitfield_width); + bit_size = field->bit_size; + break; + }; + case Member::Struct: + case Member::Union: { + clang::TagTypeKind kind = field->kind == Member::Struct + ? clang::TagTypeKind::Struct + : clang::TagTypeKind::Union; + ClangASTMetadata metadata; + metadata.SetUserID(pdb->anonymous_id); + metadata.SetIsDynamicCXXType(false); + CompilerType record_ct = clang.CreateRecordType( + parent_decl_ctx, OptionalClangModuleID(), lldb::eAccessPublic, "", + llvm::to_underlying(kind), lldb::eLanguageTypeC_plus_plus, &metadata); + TypeSystemClang::StartTagDeclarationDefinition(record_ct); + ClangASTImporter::LayoutInfo layout; + clang::DeclContext *decl_ctx = clang.GetDeclContextForType(record_ct); + for (const auto &member : field->fields) { + uint64_t member_offset = field->kind == Member::Struct + ? member->bit_offset - field->base_offset + : 0; + uint64_t member_bit_size = AddMember(clang, member.get(), member_offset, + record_ct, layout, decl_ctx); + if (field->kind == Member::Struct) + bit_size = std::max(bit_size, member_offset + member_bit_size); + else + bit_size = std::max(bit_size, member_bit_size); + } + layout.bit_size = bit_size; + TypeSystemClang::CompleteTagDeclarationDefinition(record_ct); + clang::RecordDecl *record_decl = clang.GetAsRecordDecl(record_ct); + m_ast_builder.GetClangASTImporter().SetRecordLayout(record_decl, layout); + field_decl = TypeSystemClang::AddFieldToRecordType( + parent_ct, "", record_ct, lldb::eAccessPublic, 0); + // Mark this record decl as completed. + DeclStatus status; + status.resolved = true; + status.uid = pdb->anonymous_id--; + m_decl_to_status.insert({record_decl, status}); + break; + }; + } + // FIXME: Add a PdbSymUid namespace for field list members and update + // the m_uid_to_decl map with this decl. + parent_layout.field_offsets.insert({field_decl, bit_offset}); + return bit_size; +} + +void UdtRecordCompleter::FinishRecord() { + TypeSystemClang &clang = m_ast_builder.clang(); + clang::DeclContext *decl_ctx = + m_ast_builder.GetOrCreateDeclContextForUid(m_id); + m_record.ConstructRecord(); + // Maybe we should check the construsted record size with the size in pdb. If + // they mismatch, it might be pdb has fields info missing. + for (const auto &field : m_record.record.fields) { + AddMember(clang, field.get(), field->bit_offset, m_derived_ct, m_layout, + decl_ctx); + } +} + +void UdtRecordCompleter::Record::CollectMember( + llvm::StringRef name, uint64_t offset, uint64_t field_size, + clang::QualType qt, lldb::AccessType access, uint64_t bitfield_width) { + fields_map[offset].push_back(std::make_unique<Member>( + name, offset, field_size, qt, access, bitfield_width)); + if (start_offset > offset) + start_offset = offset; +} + +void UdtRecordCompleter::Record::ConstructRecord() { + // For anonymous unions in a struct, msvc generated pdb doesn't have the + // entity for that union. So, we need to construct anonymous union and struct + // based on field offsets. The final AST is likely not matching the exact + // original AST, but the memory layout is preseved. + // After we collecting all fields in visitKnownMember, we have all fields in + // increasing offset order in m_fields. Since we are iterating in increase + // offset order, if the current offset is equal to m_start_offset, we insert + // it as direct field of top level record. If the current offset is greater + // than m_start_offset, we should be able to find a field in end_offset_map + // whose end offset is less than or equal to current offset. (if not, it might + // be missing field info. We will ignore the field in this case. e.g. Field A + // starts at 0 with size 4 bytes, and Field B starts at 2 with size 4 bytes. + // Normally, there must be something which ends at/before 2.) Then we will + // append current field to the end of parent record. If parent is struct, we + // can just grow it. If parent is a field, it's a field inside an union. We + // convert it into an anonymous struct containing old field and new field. + + // The end offset to a vector of field/struct that ends at the offset. + std::map<uint64_t, std::vector<Member *>> end_offset_map; + for (auto &pair : fields_map) { + uint64_t offset = pair.first; + auto &fields = pair.second; + lldbassert(offset >= start_offset); + Member *parent = &record; + if (offset > start_offset) { + // Find the field with largest end offset that is <= offset. If it's less + // than offset, it indicates there are padding bytes between end offset + // and offset. + lldbassert(!end_offset_map.empty()); + auto iter = end_offset_map.lower_bound(offset); + if (iter == end_offset_map.end()) + --iter; + else if (iter->first > offset) { + if (iter == end_offset_map.begin()) + continue; + --iter; + } + if (iter->second.empty()) + continue; + parent = iter->second.back(); + iter->second.pop_back(); + } + // If it's a field, then the field is inside a union, so we can safely + // increase its size by converting it to a struct to hold multiple fields. + if (parent->kind == Member::Field) + parent->ConvertToStruct(); + + if (fields.size() == 1) { + uint64_t end_offset = offset + fields.back()->bit_size; + parent->fields.push_back(std::move(fields.back())); + if (parent->kind == Member::Struct) { + end_offset_map[end_offset].push_back(parent); + } else { + lldbassert(parent == &record && + "If parent is union, it must be the top level record."); + end_offset_map[end_offset].push_back(parent->fields.back().get()); + } + } else { + if (parent->kind == Member::Struct) { + parent->fields.push_back(std::make_unique<Member>(Member::Union)); + parent = parent->fields.back().get(); + parent->bit_offset = offset; + } else { + lldbassert(parent == &record && + "If parent is union, it must be the top level record."); + } + for (auto &field : fields) { + int64_t bit_size = field->bit_size; + parent->fields.push_back(std::move(field)); + end_offset_map[offset + bit_size].push_back( + parent->fields.back().get()); + } + } + } +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h new file mode 100644 index 000000000000..e6e91d0f2c3e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h @@ -0,0 +1,146 @@ +//===-- UdtRecordCompleter.h ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H + +#include "PdbAstBuilder.h" +#include "PdbSymUid.h" +#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" +#include <optional> + +namespace clang { +class CXXBaseSpecifier; +class QualType; +class TagDecl; +} // namespace clang + +namespace llvm { +namespace pdb { +class TpiStream; +class GlobalsStream; +} +} // namespace llvm + +namespace lldb_private { +class Type; +class CompilerType; +namespace npdb { +class PdbAstBuilder; +class PdbIndex; + +class UdtRecordCompleter : public llvm::codeview::TypeVisitorCallbacks { + using IndexedBase = + std::pair<uint64_t, std::unique_ptr<clang::CXXBaseSpecifier>>; + + union UdtTagRecord { + UdtTagRecord() {} + llvm::codeview::UnionRecord ur; + llvm::codeview::ClassRecord cr; + llvm::codeview::EnumRecord er; + } m_cvr; + + PdbTypeSymId m_id; + CompilerType &m_derived_ct; + clang::TagDecl &m_tag_decl; + PdbAstBuilder &m_ast_builder; + PdbIndex &m_index; + std::vector<IndexedBase> m_bases; + ClangASTImporter::LayoutInfo m_layout; + llvm::DenseMap<clang::Decl *, DeclStatus> &m_decl_to_status; + llvm::DenseMap<lldb::opaque_compiler_type_t, + llvm::SmallSet<std::pair<llvm::StringRef, CompilerType>, 8>> + &m_cxx_record_map; + +public: + UdtRecordCompleter( + PdbTypeSymId id, CompilerType &derived_ct, clang::TagDecl &tag_decl, + PdbAstBuilder &ast_builder, PdbIndex &index, + llvm::DenseMap<clang::Decl *, DeclStatus> &decl_to_status, + llvm::DenseMap<lldb::opaque_compiler_type_t, + llvm::SmallSet<std::pair<llvm::StringRef, CompilerType>, + 8>> &cxx_record_map); + +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + llvm::Error visitKnownMember(llvm::codeview::CVMemberRecord &CVR, \ + llvm::codeview::Name##Record &Record) override; +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + + struct Member; + using MemberUP = std::unique_ptr<Member>; + + struct Member { + enum Kind { Field, Struct, Union } kind; + // Following are only used for field. + llvm::StringRef name; + uint64_t bit_offset; + uint64_t bit_size; + clang::QualType qt; + lldb::AccessType access; + uint32_t bitfield_width; + // Following are Only used for struct or union. + uint64_t base_offset; + llvm::SmallVector<MemberUP, 1> fields; + + Member() = default; + Member(Kind kind) + : kind(kind), name(), bit_offset(0), bit_size(0), qt(), + access(lldb::eAccessPublic), bitfield_width(0), base_offset(0) {} + Member(llvm::StringRef name, uint64_t bit_offset, uint64_t bit_size, + clang::QualType qt, lldb::AccessType access, uint32_t bitfield_width) + : kind(Field), name(name), bit_offset(bit_offset), bit_size(bit_size), + qt(qt), access(access), bitfield_width(bitfield_width), + base_offset(0) {} + void ConvertToStruct() { + kind = Struct; + base_offset = bit_offset; + fields.push_back(std::make_unique<Member>(name, bit_offset, bit_size, qt, + access, bitfield_width)); + name = llvm::StringRef(); + qt = clang::QualType(); + access = lldb::eAccessPublic; + bit_offset = bit_size = bitfield_width = 0; + } + }; + + struct Record { + // Top level record. + Member record; + uint64_t start_offset = UINT64_MAX; + std::map<uint64_t, llvm::SmallVector<MemberUP, 1>> fields_map; + void CollectMember(llvm::StringRef name, uint64_t offset, + uint64_t field_size, clang::QualType qt, + lldb::AccessType access, uint64_t bitfield_width); + void ConstructRecord(); + }; + void complete(); + +private: + Record m_record; + clang::QualType AddBaseClassForTypeIndex( + llvm::codeview::TypeIndex ti, llvm::codeview::MemberAccess access, + std::optional<uint64_t> vtable_idx = std::optional<uint64_t>()); + void AddMethod(llvm::StringRef name, llvm::codeview::TypeIndex type_idx, + llvm::codeview::MemberAccess access, + llvm::codeview::MethodOptions options, + llvm::codeview::MemberAttributes attrs); + void FinishRecord(); + uint64_t AddMember(TypeSystemClang &clang, Member *field, uint64_t bit_offset, + CompilerType parent_ct, + ClangASTImporter::LayoutInfo &parent_layout, + clang::DeclContext *decl_ctx); +}; + +} // namespace npdb +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H |