/* Generate CodeView debugging info from the GCC DWARF.
Copyright (C) 2023 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
. */
/* See gas/codeview.h in binutils for more about the constants and structs
listed below. References to Microsoft files refer to Microsoft's PDB
repository: https://github.com/microsoft/microsoft-pdb. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "target.h"
#include "output.h"
#include "errors.h"
#include "md5.h"
#include "function.h"
#include "version.h"
#include "tree.h"
#include "langhooks.h"
#include "dwarf2out.h"
#include "dwarf2codeview.h"
#include "rtl.h"
#ifdef CODEVIEW_DEBUGGING_INFO
#define CV_SIGNATURE_C13 4
#define DEBUG_S_SYMBOLS 0xf1
#define DEBUG_S_LINES 0xf2
#define DEBUG_S_STRINGTABLE 0xf3
#define DEBUG_S_FILECHKSMS 0xf4
#define CHKSUM_TYPE_MD5 1
#define CV_CFL_80386 0x03
#define CV_CFL_X64 0xD0
#define CV_CFL_C 0x00
#define CV_CFL_CXX 0x01
#define FIRST_TYPE 0x1000
#define LINE_LABEL "Lcvline"
#define END_FUNC_LABEL "Lcvendfunc"
#define SYMBOL_START_LABEL "Lcvsymstart"
#define SYMBOL_END_LABEL "Lcvsymend"
/* There's two bytes available for each type's size, but follow MSVC's lead in
capping the LF_FIELDLIST size at fb00 (minus 8 bytes for the LF_INDEX
pointing to the overflow entry). */
#define MAX_FIELDLIST_SIZE 0xfaf8
#define HASH_SIZE 16
/* This is enum SYM_ENUM_e in Microsoft's cvinfo.h. */
enum cv_sym_type {
S_END = 0x0006,
S_FRAMEPROC = 0x1012,
S_BLOCK32 = 0x1103,
S_REGISTER = 0x1106,
S_LDATA32 = 0x110c,
S_GDATA32 = 0x110d,
S_REGREL32 = 0x1111,
S_COMPILE3 = 0x113c,
S_LOCAL = 0x113e,
S_DEFRANGE_REGISTER = 0x1141,
S_DEFRANGE_REGISTER_REL = 0x1145,
S_LPROC32_ID = 0x1146,
S_GPROC32_ID = 0x1147,
S_PROC_ID_END = 0x114f
};
/* This is enum LEAF_ENUM_e in Microsoft's cvinfo.h. */
enum cv_leaf_type {
LF_PAD1 = 0xf1,
LF_PAD2 = 0xf2,
LF_PAD3 = 0xf3,
LF_MODIFIER = 0x1001,
LF_POINTER = 0x1002,
LF_PROCEDURE = 0x1008,
LF_ARGLIST = 0x1201,
LF_FIELDLIST = 0x1203,
LF_BITFIELD = 0x1205,
LF_INDEX = 0x1404,
LF_ENUMERATE = 0x1502,
LF_ARRAY = 0x1503,
LF_CLASS = 0x1504,
LF_STRUCTURE = 0x1505,
LF_UNION = 0x1506,
LF_ENUM = 0x1507,
LF_MEMBER = 0x150d,
LF_FUNC_ID = 0x1601,
LF_CHAR = 0x8000,
LF_SHORT = 0x8001,
LF_USHORT = 0x8002,
LF_LONG = 0x8003,
LF_ULONG = 0x8004,
LF_QUADWORD = 0x8009,
LF_UQUADWORD = 0x800a
};
/* These come from enum CV_HREG_e in Microsoft's cvconst.h. */
enum cv_x86_register {
CV_REG_NONE = 0,
CV_REG_AL = 1,
CV_REG_CL = 2,
CV_REG_DL = 3,
CV_REG_BL = 4,
CV_REG_AH = 5,
CV_REG_CH = 6,
CV_REG_DH = 7,
CV_REG_BH = 8,
CV_REG_AX = 9,
CV_REG_CX = 10,
CV_REG_DX = 11,
CV_REG_BX = 12,
CV_REG_SP = 13,
CV_REG_BP = 14,
CV_REG_SI = 15,
CV_REG_DI = 16,
CV_REG_EAX = 17,
CV_REG_ECX = 18,
CV_REG_EDX = 19,
CV_REG_EBX = 20,
CV_REG_ESP = 21,
CV_REG_EBP = 22,
CV_REG_ESI = 23,
CV_REG_EDI = 24,
CV_REG_ES = 25,
CV_REG_CS = 26,
CV_REG_SS = 27,
CV_REG_DS = 28,
CV_REG_FS = 29,
CV_REG_GS = 30,
CV_REG_IP = 31,
CV_REG_FLAGS = 32,
CV_REG_EIP = 33,
CV_REG_EFLAGS = 34,
CV_REG_TEMP = 40,
CV_REG_TEMPH = 41,
CV_REG_QUOTE = 42,
CV_REG_PCDR3 = 43,
CV_REG_PCDR4 = 44,
CV_REG_PCDR5 = 45,
CV_REG_PCDR6 = 46,
CV_REG_PCDR7 = 47,
CV_REG_CR0 = 80,
CV_REG_CR1 = 81,
CV_REG_CR2 = 82,
CV_REG_CR3 = 83,
CV_REG_CR4 = 84,
CV_REG_DR0 = 90,
CV_REG_DR1 = 91,
CV_REG_DR2 = 92,
CV_REG_DR3 = 93,
CV_REG_DR4 = 94,
CV_REG_DR5 = 95,
CV_REG_DR6 = 96,
CV_REG_DR7 = 97,
CV_REG_GDTR = 110,
CV_REG_GDTL = 111,
CV_REG_IDTR = 112,
CV_REG_IDTL = 113,
CV_REG_LDTR = 114,
CV_REG_TR = 115,
CV_REG_PSEUDO1 = 116,
CV_REG_PSEUDO2 = 117,
CV_REG_PSEUDO3 = 118,
CV_REG_PSEUDO4 = 119,
CV_REG_PSEUDO5 = 120,
CV_REG_PSEUDO6 = 121,
CV_REG_PSEUDO7 = 122,
CV_REG_PSEUDO8 = 123,
CV_REG_PSEUDO9 = 124,
CV_REG_ST0 = 128,
CV_REG_ST1 = 129,
CV_REG_ST2 = 130,
CV_REG_ST3 = 131,
CV_REG_ST4 = 132,
CV_REG_ST5 = 133,
CV_REG_ST6 = 134,
CV_REG_ST7 = 135,
CV_REG_CTRL = 136,
CV_REG_STAT = 137,
CV_REG_TAG = 138,
CV_REG_FPIP = 139,
CV_REG_FPCS = 140,
CV_REG_FPDO = 141,
CV_REG_FPDS = 142,
CV_REG_ISEM = 143,
CV_REG_FPEIP = 144,
CV_REG_FPEDO = 145,
CV_REG_MM0 = 146,
CV_REG_MM1 = 147,
CV_REG_MM2 = 148,
CV_REG_MM3 = 149,
CV_REG_MM4 = 150,
CV_REG_MM5 = 151,
CV_REG_MM6 = 152,
CV_REG_MM7 = 153,
CV_REG_XMM0 = 154,
CV_REG_XMM1 = 155,
CV_REG_XMM2 = 156,
CV_REG_XMM3 = 157,
CV_REG_XMM4 = 158,
CV_REG_XMM5 = 159,
CV_REG_XMM6 = 160,
CV_REG_XMM7 = 161,
CV_REG_XMM00 = 162,
CV_REG_XMM01 = 163,
CV_REG_XMM02 = 164,
CV_REG_XMM03 = 165,
CV_REG_XMM10 = 166,
CV_REG_XMM11 = 167,
CV_REG_XMM12 = 168,
CV_REG_XMM13 = 169,
CV_REG_XMM20 = 170,
CV_REG_XMM21 = 171,
CV_REG_XMM22 = 172,
CV_REG_XMM23 = 173,
CV_REG_XMM30 = 174,
CV_REG_XMM31 = 175,
CV_REG_XMM32 = 176,
CV_REG_XMM33 = 177,
CV_REG_XMM40 = 178,
CV_REG_XMM41 = 179,
CV_REG_XMM42 = 180,
CV_REG_XMM43 = 181,
CV_REG_XMM50 = 182,
CV_REG_XMM51 = 183,
CV_REG_XMM52 = 184,
CV_REG_XMM53 = 185,
CV_REG_XMM60 = 186,
CV_REG_XMM61 = 187,
CV_REG_XMM62 = 188,
CV_REG_XMM63 = 189,
CV_REG_XMM70 = 190,
CV_REG_XMM71 = 191,
CV_REG_XMM72 = 192,
CV_REG_XMM73 = 193,
CV_REG_XMM0L = 194,
CV_REG_XMM1L = 195,
CV_REG_XMM2L = 196,
CV_REG_XMM3L = 197,
CV_REG_XMM4L = 198,
CV_REG_XMM5L = 199,
CV_REG_XMM6L = 200,
CV_REG_XMM7L = 201,
CV_REG_XMM0H = 202,
CV_REG_XMM1H = 203,
CV_REG_XMM2H = 204,
CV_REG_XMM3H = 205,
CV_REG_XMM4H = 206,
CV_REG_XMM5H = 207,
CV_REG_XMM6H = 208,
CV_REG_XMM7H = 209,
CV_REG_MXCSR = 211,
CV_REG_EDXEAX = 212,
CV_REG_EMM0L = 220,
CV_REG_EMM1L = 221,
CV_REG_EMM2L = 222,
CV_REG_EMM3L = 223,
CV_REG_EMM4L = 224,
CV_REG_EMM5L = 225,
CV_REG_EMM6L = 226,
CV_REG_EMM7L = 227,
CV_REG_EMM0H = 228,
CV_REG_EMM1H = 229,
CV_REG_EMM2H = 230,
CV_REG_EMM3H = 231,
CV_REG_EMM4H = 232,
CV_REG_EMM5H = 233,
CV_REG_EMM6H = 234,
CV_REG_EMM7H = 235,
CV_REG_MM00 = 236,
CV_REG_MM01 = 237,
CV_REG_MM10 = 238,
CV_REG_MM11 = 239,
CV_REG_MM20 = 240,
CV_REG_MM21 = 241,
CV_REG_MM30 = 242,
CV_REG_MM31 = 243,
CV_REG_MM40 = 244,
CV_REG_MM41 = 245,
CV_REG_MM50 = 246,
CV_REG_MM51 = 247,
CV_REG_MM60 = 248,
CV_REG_MM61 = 249,
CV_REG_MM70 = 250,
CV_REG_MM71 = 251,
CV_REG_YMM0 = 252,
CV_REG_YMM1 = 253,
CV_REG_YMM2 = 254,
CV_REG_YMM3 = 255,
CV_REG_YMM4 = 256,
CV_REG_YMM5 = 257,
CV_REG_YMM6 = 258,
CV_REG_YMM7 = 259,
CV_REG_YMM0H = 260,
CV_REG_YMM1H = 261,
CV_REG_YMM2H = 262,
CV_REG_YMM3H = 263,
CV_REG_YMM4H = 264,
CV_REG_YMM5H = 265,
CV_REG_YMM6H = 266,
CV_REG_YMM7H = 267,
CV_REG_YMM0I0 = 268,
CV_REG_YMM0I1 = 269,
CV_REG_YMM0I2 = 270,
CV_REG_YMM0I3 = 271,
CV_REG_YMM1I0 = 272,
CV_REG_YMM1I1 = 273,
CV_REG_YMM1I2 = 274,
CV_REG_YMM1I3 = 275,
CV_REG_YMM2I0 = 276,
CV_REG_YMM2I1 = 277,
CV_REG_YMM2I2 = 278,
CV_REG_YMM2I3 = 279,
CV_REG_YMM3I0 = 280,
CV_REG_YMM3I1 = 281,
CV_REG_YMM3I2 = 282,
CV_REG_YMM3I3 = 283,
CV_REG_YMM4I0 = 284,
CV_REG_YMM4I1 = 285,
CV_REG_YMM4I2 = 286,
CV_REG_YMM4I3 = 287,
CV_REG_YMM5I0 = 288,
CV_REG_YMM5I1 = 289,
CV_REG_YMM5I2 = 290,
CV_REG_YMM5I3 = 291,
CV_REG_YMM6I0 = 292,
CV_REG_YMM6I1 = 293,
CV_REG_YMM6I2 = 294,
CV_REG_YMM6I3 = 295,
CV_REG_YMM7I0 = 296,
CV_REG_YMM7I1 = 297,
CV_REG_YMM7I2 = 298,
CV_REG_YMM7I3 = 299,
CV_REG_YMM0F0 = 300,
CV_REG_YMM0F1 = 301,
CV_REG_YMM0F2 = 302,
CV_REG_YMM0F3 = 303,
CV_REG_YMM0F4 = 304,
CV_REG_YMM0F5 = 305,
CV_REG_YMM0F6 = 306,
CV_REG_YMM0F7 = 307,
CV_REG_YMM1F0 = 308,
CV_REG_YMM1F1 = 309,
CV_REG_YMM1F2 = 310,
CV_REG_YMM1F3 = 311,
CV_REG_YMM1F4 = 312,
CV_REG_YMM1F5 = 313,
CV_REG_YMM1F6 = 314,
CV_REG_YMM1F7 = 315,
CV_REG_YMM2F0 = 316,
CV_REG_YMM2F1 = 317,
CV_REG_YMM2F2 = 318,
CV_REG_YMM2F3 = 319,
CV_REG_YMM2F4 = 320,
CV_REG_YMM2F5 = 321,
CV_REG_YMM2F6 = 322,
CV_REG_YMM2F7 = 323,
CV_REG_YMM3F0 = 324,
CV_REG_YMM3F1 = 325,
CV_REG_YMM3F2 = 326,
CV_REG_YMM3F3 = 327,
CV_REG_YMM3F4 = 328,
CV_REG_YMM3F5 = 329,
CV_REG_YMM3F6 = 330,
CV_REG_YMM3F7 = 331,
CV_REG_YMM4F0 = 332,
CV_REG_YMM4F1 = 333,
CV_REG_YMM4F2 = 334,
CV_REG_YMM4F3 = 335,
CV_REG_YMM4F4 = 336,
CV_REG_YMM4F5 = 337,
CV_REG_YMM4F6 = 338,
CV_REG_YMM4F7 = 339,
CV_REG_YMM5F0 = 340,
CV_REG_YMM5F1 = 341,
CV_REG_YMM5F2 = 342,
CV_REG_YMM5F3 = 343,
CV_REG_YMM5F4 = 344,
CV_REG_YMM5F5 = 345,
CV_REG_YMM5F6 = 346,
CV_REG_YMM5F7 = 347,
CV_REG_YMM6F0 = 348,
CV_REG_YMM6F1 = 349,
CV_REG_YMM6F2 = 350,
CV_REG_YMM6F3 = 351,
CV_REG_YMM6F4 = 352,
CV_REG_YMM6F5 = 353,
CV_REG_YMM6F6 = 354,
CV_REG_YMM6F7 = 355,
CV_REG_YMM7F0 = 356,
CV_REG_YMM7F1 = 357,
CV_REG_YMM7F2 = 358,
CV_REG_YMM7F3 = 359,
CV_REG_YMM7F4 = 360,
CV_REG_YMM7F5 = 361,
CV_REG_YMM7F6 = 362,
CV_REG_YMM7F7 = 363,
CV_REG_YMM0D0 = 364,
CV_REG_YMM0D1 = 365,
CV_REG_YMM0D2 = 366,
CV_REG_YMM0D3 = 367,
CV_REG_YMM1D0 = 368,
CV_REG_YMM1D1 = 369,
CV_REG_YMM1D2 = 370,
CV_REG_YMM1D3 = 371,
CV_REG_YMM2D0 = 372,
CV_REG_YMM2D1 = 373,
CV_REG_YMM2D2 = 374,
CV_REG_YMM2D3 = 375,
CV_REG_YMM3D0 = 376,
CV_REG_YMM3D1 = 377,
CV_REG_YMM3D2 = 378,
CV_REG_YMM3D3 = 379,
CV_REG_YMM4D0 = 380,
CV_REG_YMM4D1 = 381,
CV_REG_YMM4D2 = 382,
CV_REG_YMM4D3 = 383,
CV_REG_YMM5D0 = 384,
CV_REG_YMM5D1 = 385,
CV_REG_YMM5D2 = 386,
CV_REG_YMM5D3 = 387,
CV_REG_YMM6D0 = 388,
CV_REG_YMM6D1 = 389,
CV_REG_YMM6D2 = 390,
CV_REG_YMM6D3 = 391,
CV_REG_YMM7D0 = 392,
CV_REG_YMM7D1 = 393,
CV_REG_YMM7D2 = 394,
CV_REG_YMM7D3 = 395,
CV_REG_BND0 = 396,
CV_REG_BND1 = 397,
CV_REG_BND2 = 398,
CV_REG_BND3 = 399
};
enum cv_amd64_register {
CV_AMD64_NOREG = 0,
CV_AMD64_AL = 1,
CV_AMD64_CL = 2,
CV_AMD64_DL = 3,
CV_AMD64_BL = 4,
CV_AMD64_AH = 5,
CV_AMD64_CH = 6,
CV_AMD64_DH = 7,
CV_AMD64_BH = 8,
CV_AMD64_AX = 9,
CV_AMD64_CX = 10,
CV_AMD64_DX = 11,
CV_AMD64_BX = 12,
CV_AMD64_SP = 13,
CV_AMD64_BP = 14,
CV_AMD64_SI = 15,
CV_AMD64_DI = 16,
CV_AMD64_EAX = 17,
CV_AMD64_ECX = 18,
CV_AMD64_EDX = 19,
CV_AMD64_EBX = 20,
CV_AMD64_ESP = 21,
CV_AMD64_EBP = 22,
CV_AMD64_ESI = 23,
CV_AMD64_EDI = 24,
CV_AMD64_ES = 25,
CV_AMD64_CS = 26,
CV_AMD64_SS = 27,
CV_AMD64_DS = 28,
CV_AMD64_FS = 29,
CV_AMD64_GS = 30,
CV_AMD64_FLAGS = 32,
CV_AMD64_RIP = 33,
CV_AMD64_EFLAGS = 34,
CV_AMD64_CR0 = 80,
CV_AMD64_CR1 = 81,
CV_AMD64_CR2 = 82,
CV_AMD64_CR3 = 83,
CV_AMD64_CR4 = 84,
CV_AMD64_CR8 = 88,
CV_AMD64_DR0 = 90,
CV_AMD64_DR1 = 91,
CV_AMD64_DR2 = 92,
CV_AMD64_DR3 = 93,
CV_AMD64_DR4 = 94,
CV_AMD64_DR5 = 95,
CV_AMD64_DR6 = 96,
CV_AMD64_DR7 = 97,
CV_AMD64_DR8 = 98,
CV_AMD64_DR9 = 99,
CV_AMD64_DR10 = 100,
CV_AMD64_DR11 = 101,
CV_AMD64_DR12 = 102,
CV_AMD64_DR13 = 103,
CV_AMD64_DR14 = 104,
CV_AMD64_DR15 = 105,
CV_AMD64_GDTR = 110,
CV_AMD64_GDTL = 111,
CV_AMD64_IDTR = 112,
CV_AMD64_IDTL = 113,
CV_AMD64_LDTR = 114,
CV_AMD64_TR = 115,
CV_AMD64_ST0 = 128,
CV_AMD64_ST1 = 129,
CV_AMD64_ST2 = 130,
CV_AMD64_ST3 = 131,
CV_AMD64_ST4 = 132,
CV_AMD64_ST5 = 133,
CV_AMD64_ST6 = 134,
CV_AMD64_ST7 = 135,
CV_AMD64_CTRL = 136,
CV_AMD64_STAT = 137,
CV_AMD64_TAG = 138,
CV_AMD64_FPIP = 139,
CV_AMD64_FPCS = 140,
CV_AMD64_FPDO = 141,
CV_AMD64_FPDS = 142,
CV_AMD64_ISEM = 143,
CV_AMD64_FPEIP = 144,
CV_AMD64_FPEDO = 145,
CV_AMD64_MM0 = 146,
CV_AMD64_MM1 = 147,
CV_AMD64_MM2 = 148,
CV_AMD64_MM3 = 149,
CV_AMD64_MM4 = 150,
CV_AMD64_MM5 = 151,
CV_AMD64_MM6 = 152,
CV_AMD64_MM7 = 153,
CV_AMD64_XMM0 = 154,
CV_AMD64_XMM1 = 155,
CV_AMD64_XMM2 = 156,
CV_AMD64_XMM3 = 157,
CV_AMD64_XMM4 = 158,
CV_AMD64_XMM5 = 159,
CV_AMD64_XMM6 = 160,
CV_AMD64_XMM7 = 161,
CV_AMD64_XMM0_0 = 162,
CV_AMD64_XMM0_1 = 163,
CV_AMD64_XMM0_2 = 164,
CV_AMD64_XMM0_3 = 165,
CV_AMD64_XMM1_0 = 166,
CV_AMD64_XMM1_1 = 167,
CV_AMD64_XMM1_2 = 168,
CV_AMD64_XMM1_3 = 169,
CV_AMD64_XMM2_0 = 170,
CV_AMD64_XMM2_1 = 171,
CV_AMD64_XMM2_2 = 172,
CV_AMD64_XMM2_3 = 173,
CV_AMD64_XMM3_0 = 174,
CV_AMD64_XMM3_1 = 175,
CV_AMD64_XMM3_2 = 176,
CV_AMD64_XMM3_3 = 177,
CV_AMD64_XMM4_0 = 178,
CV_AMD64_XMM4_1 = 179,
CV_AMD64_XMM4_2 = 180,
CV_AMD64_XMM4_3 = 181,
CV_AMD64_XMM5_0 = 182,
CV_AMD64_XMM5_1 = 183,
CV_AMD64_XMM5_2 = 184,
CV_AMD64_XMM5_3 = 185,
CV_AMD64_XMM6_0 = 186,
CV_AMD64_XMM6_1 = 187,
CV_AMD64_XMM6_2 = 188,
CV_AMD64_XMM6_3 = 189,
CV_AMD64_XMM7_0 = 190,
CV_AMD64_XMM7_1 = 191,
CV_AMD64_XMM7_2 = 192,
CV_AMD64_XMM7_3 = 193,
CV_AMD64_XMM0L = 194,
CV_AMD64_XMM1L = 195,
CV_AMD64_XMM2L = 196,
CV_AMD64_XMM3L = 197,
CV_AMD64_XMM4L = 198,
CV_AMD64_XMM5L = 199,
CV_AMD64_XMM6L = 200,
CV_AMD64_XMM7L = 201,
CV_AMD64_XMM0H = 202,
CV_AMD64_XMM1H = 203,
CV_AMD64_XMM2H = 204,
CV_AMD64_XMM3H = 205,
CV_AMD64_XMM4H = 206,
CV_AMD64_XMM5H = 207,
CV_AMD64_XMM6H = 208,
CV_AMD64_XMM7H = 209,
CV_AMD64_MXCSR = 211,
CV_AMD64_EMM0L = 220,
CV_AMD64_EMM1L = 221,
CV_AMD64_EMM2L = 222,
CV_AMD64_EMM3L = 223,
CV_AMD64_EMM4L = 224,
CV_AMD64_EMM5L = 225,
CV_AMD64_EMM6L = 226,
CV_AMD64_EMM7L = 227,
CV_AMD64_EMM0H = 228,
CV_AMD64_EMM1H = 229,
CV_AMD64_EMM2H = 230,
CV_AMD64_EMM3H = 231,
CV_AMD64_EMM4H = 232,
CV_AMD64_EMM5H = 233,
CV_AMD64_EMM6H = 234,
CV_AMD64_EMM7H = 235,
CV_AMD64_MM00 = 236,
CV_AMD64_MM01 = 237,
CV_AMD64_MM10 = 238,
CV_AMD64_MM11 = 239,
CV_AMD64_MM20 = 240,
CV_AMD64_MM21 = 241,
CV_AMD64_MM30 = 242,
CV_AMD64_MM31 = 243,
CV_AMD64_MM40 = 244,
CV_AMD64_MM41 = 245,
CV_AMD64_MM50 = 246,
CV_AMD64_MM51 = 247,
CV_AMD64_MM60 = 248,
CV_AMD64_MM61 = 249,
CV_AMD64_MM70 = 250,
CV_AMD64_MM71 = 251,
CV_AMD64_XMM8 = 252,
CV_AMD64_XMM9 = 253,
CV_AMD64_XMM10 = 254,
CV_AMD64_XMM11 = 255,
CV_AMD64_XMM12 = 256,
CV_AMD64_XMM13 = 257,
CV_AMD64_XMM14 = 258,
CV_AMD64_XMM15 = 259,
CV_AMD64_XMM8_0 = 260,
CV_AMD64_XMM8_1 = 261,
CV_AMD64_XMM8_2 = 262,
CV_AMD64_XMM8_3 = 263,
CV_AMD64_XMM9_0 = 264,
CV_AMD64_XMM9_1 = 265,
CV_AMD64_XMM9_2 = 266,
CV_AMD64_XMM9_3 = 267,
CV_AMD64_XMM10_0 = 268,
CV_AMD64_XMM10_1 = 269,
CV_AMD64_XMM10_2 = 270,
CV_AMD64_XMM10_3 = 271,
CV_AMD64_XMM11_0 = 272,
CV_AMD64_XMM11_1 = 273,
CV_AMD64_XMM11_2 = 274,
CV_AMD64_XMM11_3 = 275,
CV_AMD64_XMM12_0 = 276,
CV_AMD64_XMM12_1 = 277,
CV_AMD64_XMM12_2 = 278,
CV_AMD64_XMM12_3 = 279,
CV_AMD64_XMM13_0 = 280,
CV_AMD64_XMM13_1 = 281,
CV_AMD64_XMM13_2 = 282,
CV_AMD64_XMM13_3 = 283,
CV_AMD64_XMM14_0 = 284,
CV_AMD64_XMM14_1 = 285,
CV_AMD64_XMM14_2 = 286,
CV_AMD64_XMM14_3 = 287,
CV_AMD64_XMM15_0 = 288,
CV_AMD64_XMM15_1 = 289,
CV_AMD64_XMM15_2 = 290,
CV_AMD64_XMM15_3 = 291,
CV_AMD64_XMM8L = 292,
CV_AMD64_XMM9L = 293,
CV_AMD64_XMM10L = 294,
CV_AMD64_XMM11L = 295,
CV_AMD64_XMM12L = 296,
CV_AMD64_XMM13L = 297,
CV_AMD64_XMM14L = 298,
CV_AMD64_XMM15L = 299,
CV_AMD64_XMM8H = 300,
CV_AMD64_XMM9H = 301,
CV_AMD64_XMM10H = 302,
CV_AMD64_XMM11H = 303,
CV_AMD64_XMM12H = 304,
CV_AMD64_XMM13H = 305,
CV_AMD64_XMM14H = 306,
CV_AMD64_XMM15H = 307,
CV_AMD64_EMM8L = 308,
CV_AMD64_EMM9L = 309,
CV_AMD64_EMM10L = 310,
CV_AMD64_EMM11L = 311,
CV_AMD64_EMM12L = 312,
CV_AMD64_EMM13L = 313,
CV_AMD64_EMM14L = 314,
CV_AMD64_EMM15L = 315,
CV_AMD64_EMM8H = 316,
CV_AMD64_EMM9H = 317,
CV_AMD64_EMM10H = 318,
CV_AMD64_EMM11H = 319,
CV_AMD64_EMM12H = 320,
CV_AMD64_EMM13H = 321,
CV_AMD64_EMM14H = 322,
CV_AMD64_EMM15H = 323,
CV_AMD64_SIL = 324,
CV_AMD64_DIL = 325,
CV_AMD64_BPL = 326,
CV_AMD64_SPL = 327,
CV_AMD64_RAX = 328,
CV_AMD64_RBX = 329,
CV_AMD64_RCX = 330,
CV_AMD64_RDX = 331,
CV_AMD64_RSI = 332,
CV_AMD64_RDI = 333,
CV_AMD64_RBP = 334,
CV_AMD64_RSP = 335,
CV_AMD64_R8 = 336,
CV_AMD64_R9 = 337,
CV_AMD64_R10 = 338,
CV_AMD64_R11 = 339,
CV_AMD64_R12 = 340,
CV_AMD64_R13 = 341,
CV_AMD64_R14 = 342,
CV_AMD64_R15 = 343,
CV_AMD64_R8B = 344,
CV_AMD64_R9B = 345,
CV_AMD64_R10B = 346,
CV_AMD64_R11B = 347,
CV_AMD64_R12B = 348,
CV_AMD64_R13B = 349,
CV_AMD64_R14B = 350,
CV_AMD64_R15B = 351,
CV_AMD64_R8W = 352,
CV_AMD64_R9W = 353,
CV_AMD64_R10W = 354,
CV_AMD64_R11W = 355,
CV_AMD64_R12W = 356,
CV_AMD64_R13W = 357,
CV_AMD64_R14W = 358,
CV_AMD64_R15W = 359,
CV_AMD64_R8D = 360,
CV_AMD64_R9D = 361,
CV_AMD64_R10D = 362,
CV_AMD64_R11D = 363,
CV_AMD64_R12D = 364,
CV_AMD64_R13D = 365,
CV_AMD64_R14D = 366,
CV_AMD64_R15D = 367,
CV_AMD64_YMM0 = 368,
CV_AMD64_YMM1 = 369,
CV_AMD64_YMM2 = 370,
CV_AMD64_YMM3 = 371,
CV_AMD64_YMM4 = 372,
CV_AMD64_YMM5 = 373,
CV_AMD64_YMM6 = 374,
CV_AMD64_YMM7 = 375,
CV_AMD64_YMM8 = 376,
CV_AMD64_YMM9 = 377,
CV_AMD64_YMM10 = 378,
CV_AMD64_YMM11 = 379,
CV_AMD64_YMM12 = 380,
CV_AMD64_YMM13 = 381,
CV_AMD64_YMM14 = 382,
CV_AMD64_YMM15 = 383,
CV_AMD64_YMM0H = 384,
CV_AMD64_YMM1H = 385,
CV_AMD64_YMM2H = 386,
CV_AMD64_YMM3H = 387,
CV_AMD64_YMM4H = 388,
CV_AMD64_YMM5H = 389,
CV_AMD64_YMM6H = 390,
CV_AMD64_YMM7H = 391,
CV_AMD64_YMM8H = 392,
CV_AMD64_YMM9H = 393,
CV_AMD64_YMM10H = 394,
CV_AMD64_YMM11H = 395,
CV_AMD64_YMM12H = 396,
CV_AMD64_YMM13H = 397,
CV_AMD64_YMM14H = 398,
CV_AMD64_YMM15H = 399,
CV_AMD64_XMM0IL = 400,
CV_AMD64_XMM1IL = 401,
CV_AMD64_XMM2IL = 402,
CV_AMD64_XMM3IL = 403,
CV_AMD64_XMM4IL = 404,
CV_AMD64_XMM5IL = 405,
CV_AMD64_XMM6IL = 406,
CV_AMD64_XMM7IL = 407,
CV_AMD64_XMM8IL = 408,
CV_AMD64_XMM9IL = 409,
CV_AMD64_XMM10IL = 410,
CV_AMD64_XMM11IL = 411,
CV_AMD64_XMM12IL = 412,
CV_AMD64_XMM13IL = 413,
CV_AMD64_XMM14IL = 414,
CV_AMD64_XMM15IL = 415,
CV_AMD64_XMM0IH = 416,
CV_AMD64_XMM1IH = 417,
CV_AMD64_XMM2IH = 418,
CV_AMD64_XMM3IH = 419,
CV_AMD64_XMM4IH = 420,
CV_AMD64_XMM5IH = 421,
CV_AMD64_XMM6IH = 422,
CV_AMD64_XMM7IH = 423,
CV_AMD64_XMM8IH = 424,
CV_AMD64_XMM9IH = 425,
CV_AMD64_XMM10IH = 426,
CV_AMD64_XMM11IH = 427,
CV_AMD64_XMM12IH = 428,
CV_AMD64_XMM13IH = 429,
CV_AMD64_XMM14IH = 430,
CV_AMD64_XMM15IH = 431,
CV_AMD64_YMM0I0 = 432,
CV_AMD64_YMM0I1 = 433,
CV_AMD64_YMM0I2 = 434,
CV_AMD64_YMM0I3 = 435,
CV_AMD64_YMM1I0 = 436,
CV_AMD64_YMM1I1 = 437,
CV_AMD64_YMM1I2 = 438,
CV_AMD64_YMM1I3 = 439,
CV_AMD64_YMM2I0 = 440,
CV_AMD64_YMM2I1 = 441,
CV_AMD64_YMM2I2 = 442,
CV_AMD64_YMM2I3 = 443,
CV_AMD64_YMM3I0 = 444,
CV_AMD64_YMM3I1 = 445,
CV_AMD64_YMM3I2 = 446,
CV_AMD64_YMM3I3 = 447,
CV_AMD64_YMM4I0 = 448,
CV_AMD64_YMM4I1 = 449,
CV_AMD64_YMM4I2 = 450,
CV_AMD64_YMM4I3 = 451,
CV_AMD64_YMM5I0 = 452,
CV_AMD64_YMM5I1 = 453,
CV_AMD64_YMM5I2 = 454,
CV_AMD64_YMM5I3 = 455,
CV_AMD64_YMM6I0 = 456,
CV_AMD64_YMM6I1 = 457,
CV_AMD64_YMM6I2 = 458,
CV_AMD64_YMM6I3 = 459,
CV_AMD64_YMM7I0 = 460,
CV_AMD64_YMM7I1 = 461,
CV_AMD64_YMM7I2 = 462,
CV_AMD64_YMM7I3 = 463,
CV_AMD64_YMM8I0 = 464,
CV_AMD64_YMM8I1 = 465,
CV_AMD64_YMM8I2 = 466,
CV_AMD64_YMM8I3 = 467,
CV_AMD64_YMM9I0 = 468,
CV_AMD64_YMM9I1 = 469,
CV_AMD64_YMM9I2 = 470,
CV_AMD64_YMM9I3 = 471,
CV_AMD64_YMM10I0 = 472,
CV_AMD64_YMM10I1 = 473,
CV_AMD64_YMM10I2 = 474,
CV_AMD64_YMM10I3 = 475,
CV_AMD64_YMM11I0 = 476,
CV_AMD64_YMM11I1 = 477,
CV_AMD64_YMM11I2 = 478,
CV_AMD64_YMM11I3 = 479,
CV_AMD64_YMM12I0 = 480,
CV_AMD64_YMM12I1 = 481,
CV_AMD64_YMM12I2 = 482,
CV_AMD64_YMM12I3 = 483,
CV_AMD64_YMM13I0 = 484,
CV_AMD64_YMM13I1 = 485,
CV_AMD64_YMM13I2 = 486,
CV_AMD64_YMM13I3 = 487,
CV_AMD64_YMM14I0 = 488,
CV_AMD64_YMM14I1 = 489,
CV_AMD64_YMM14I2 = 490,
CV_AMD64_YMM14I3 = 491,
CV_AMD64_YMM15I0 = 492,
CV_AMD64_YMM15I1 = 493,
CV_AMD64_YMM15I2 = 494,
CV_AMD64_YMM15I3 = 495,
CV_AMD64_YMM0F0 = 496,
CV_AMD64_YMM0F1 = 497,
CV_AMD64_YMM0F2 = 498,
CV_AMD64_YMM0F3 = 499,
CV_AMD64_YMM0F4 = 500,
CV_AMD64_YMM0F5 = 501,
CV_AMD64_YMM0F6 = 502,
CV_AMD64_YMM0F7 = 503,
CV_AMD64_YMM1F0 = 504,
CV_AMD64_YMM1F1 = 505,
CV_AMD64_YMM1F2 = 506,
CV_AMD64_YMM1F3 = 507,
CV_AMD64_YMM1F4 = 508,
CV_AMD64_YMM1F5 = 509,
CV_AMD64_YMM1F6 = 510,
CV_AMD64_YMM1F7 = 511,
CV_AMD64_YMM2F0 = 512,
CV_AMD64_YMM2F1 = 513,
CV_AMD64_YMM2F2 = 514,
CV_AMD64_YMM2F3 = 515,
CV_AMD64_YMM2F4 = 516,
CV_AMD64_YMM2F5 = 517,
CV_AMD64_YMM2F6 = 518,
CV_AMD64_YMM2F7 = 519,
CV_AMD64_YMM3F0 = 520,
CV_AMD64_YMM3F1 = 521,
CV_AMD64_YMM3F2 = 522,
CV_AMD64_YMM3F3 = 523,
CV_AMD64_YMM3F4 = 524,
CV_AMD64_YMM3F5 = 525,
CV_AMD64_YMM3F6 = 526,
CV_AMD64_YMM3F7 = 527,
CV_AMD64_YMM4F0 = 528,
CV_AMD64_YMM4F1 = 529,
CV_AMD64_YMM4F2 = 530,
CV_AMD64_YMM4F3 = 531,
CV_AMD64_YMM4F4 = 532,
CV_AMD64_YMM4F5 = 533,
CV_AMD64_YMM4F6 = 534,
CV_AMD64_YMM4F7 = 535,
CV_AMD64_YMM5F0 = 536,
CV_AMD64_YMM5F1 = 537,
CV_AMD64_YMM5F2 = 538,
CV_AMD64_YMM5F3 = 539,
CV_AMD64_YMM5F4 = 540,
CV_AMD64_YMM5F5 = 541,
CV_AMD64_YMM5F6 = 542,
CV_AMD64_YMM5F7 = 543,
CV_AMD64_YMM6F0 = 544,
CV_AMD64_YMM6F1 = 545,
CV_AMD64_YMM6F2 = 546,
CV_AMD64_YMM6F3 = 547,
CV_AMD64_YMM6F4 = 548,
CV_AMD64_YMM6F5 = 549,
CV_AMD64_YMM6F6 = 550,
CV_AMD64_YMM6F7 = 551,
CV_AMD64_YMM7F0 = 552,
CV_AMD64_YMM7F1 = 553,
CV_AMD64_YMM7F2 = 554,
CV_AMD64_YMM7F3 = 555,
CV_AMD64_YMM7F4 = 556,
CV_AMD64_YMM7F5 = 557,
CV_AMD64_YMM7F6 = 558,
CV_AMD64_YMM7F7 = 559,
CV_AMD64_YMM8F0 = 560,
CV_AMD64_YMM8F1 = 561,
CV_AMD64_YMM8F2 = 562,
CV_AMD64_YMM8F3 = 563,
CV_AMD64_YMM8F4 = 564,
CV_AMD64_YMM8F5 = 565,
CV_AMD64_YMM8F6 = 566,
CV_AMD64_YMM8F7 = 567,
CV_AMD64_YMM9F0 = 568,
CV_AMD64_YMM9F1 = 569,
CV_AMD64_YMM9F2 = 570,
CV_AMD64_YMM9F3 = 571,
CV_AMD64_YMM9F4 = 572,
CV_AMD64_YMM9F5 = 573,
CV_AMD64_YMM9F6 = 574,
CV_AMD64_YMM9F7 = 575,
CV_AMD64_YMM10F0 = 576,
CV_AMD64_YMM10F1 = 577,
CV_AMD64_YMM10F2 = 578,
CV_AMD64_YMM10F3 = 579,
CV_AMD64_YMM10F4 = 580,
CV_AMD64_YMM10F5 = 581,
CV_AMD64_YMM10F6 = 582,
CV_AMD64_YMM10F7 = 583,
CV_AMD64_YMM11F0 = 584,
CV_AMD64_YMM11F1 = 585,
CV_AMD64_YMM11F2 = 586,
CV_AMD64_YMM11F3 = 587,
CV_AMD64_YMM11F4 = 588,
CV_AMD64_YMM11F5 = 589,
CV_AMD64_YMM11F6 = 590,
CV_AMD64_YMM11F7 = 591,
CV_AMD64_YMM12F0 = 592,
CV_AMD64_YMM12F1 = 593,
CV_AMD64_YMM12F2 = 594,
CV_AMD64_YMM12F3 = 595,
CV_AMD64_YMM12F4 = 596,
CV_AMD64_YMM12F5 = 597,
CV_AMD64_YMM12F6 = 598,
CV_AMD64_YMM12F7 = 599,
CV_AMD64_YMM13F0 = 600,
CV_AMD64_YMM13F1 = 601,
CV_AMD64_YMM13F2 = 602,
CV_AMD64_YMM13F3 = 603,
CV_AMD64_YMM13F4 = 604,
CV_AMD64_YMM13F5 = 605,
CV_AMD64_YMM13F6 = 606,
CV_AMD64_YMM13F7 = 607,
CV_AMD64_YMM14F0 = 608,
CV_AMD64_YMM14F1 = 609,
CV_AMD64_YMM14F2 = 610,
CV_AMD64_YMM14F3 = 611,
CV_AMD64_YMM14F4 = 612,
CV_AMD64_YMM14F5 = 613,
CV_AMD64_YMM14F6 = 614,
CV_AMD64_YMM14F7 = 615,
CV_AMD64_YMM15F0 = 616,
CV_AMD64_YMM15F1 = 617,
CV_AMD64_YMM15F2 = 618,
CV_AMD64_YMM15F3 = 619,
CV_AMD64_YMM15F4 = 620,
CV_AMD64_YMM15F5 = 621,
CV_AMD64_YMM15F6 = 622,
CV_AMD64_YMM15F7 = 623,
CV_AMD64_YMM0D0 = 624,
CV_AMD64_YMM0D1 = 625,
CV_AMD64_YMM0D2 = 626,
CV_AMD64_YMM0D3 = 627,
CV_AMD64_YMM1D0 = 628,
CV_AMD64_YMM1D1 = 629,
CV_AMD64_YMM1D2 = 630,
CV_AMD64_YMM1D3 = 631,
CV_AMD64_YMM2D0 = 632,
CV_AMD64_YMM2D1 = 633,
CV_AMD64_YMM2D2 = 634,
CV_AMD64_YMM2D3 = 635,
CV_AMD64_YMM3D0 = 636,
CV_AMD64_YMM3D1 = 637,
CV_AMD64_YMM3D2 = 638,
CV_AMD64_YMM3D3 = 639,
CV_AMD64_YMM4D0 = 640,
CV_AMD64_YMM4D1 = 641,
CV_AMD64_YMM4D2 = 642,
CV_AMD64_YMM4D3 = 643,
CV_AMD64_YMM5D0 = 644,
CV_AMD64_YMM5D1 = 645,
CV_AMD64_YMM5D2 = 646,
CV_AMD64_YMM5D3 = 647,
CV_AMD64_YMM6D0 = 648,
CV_AMD64_YMM6D1 = 649,
CV_AMD64_YMM6D2 = 650,
CV_AMD64_YMM6D3 = 651,
CV_AMD64_YMM7D0 = 652,
CV_AMD64_YMM7D1 = 653,
CV_AMD64_YMM7D2 = 654,
CV_AMD64_YMM7D3 = 655,
CV_AMD64_YMM8D0 = 656,
CV_AMD64_YMM8D1 = 657,
CV_AMD64_YMM8D2 = 658,
CV_AMD64_YMM8D3 = 659,
CV_AMD64_YMM9D0 = 660,
CV_AMD64_YMM9D1 = 661,
CV_AMD64_YMM9D2 = 662,
CV_AMD64_YMM9D3 = 663,
CV_AMD64_YMM10D0 = 664,
CV_AMD64_YMM10D1 = 665,
CV_AMD64_YMM10D2 = 666,
CV_AMD64_YMM10D3 = 667,
CV_AMD64_YMM11D0 = 668,
CV_AMD64_YMM11D1 = 669,
CV_AMD64_YMM11D2 = 670,
CV_AMD64_YMM11D3 = 671,
CV_AMD64_YMM12D0 = 672,
CV_AMD64_YMM12D1 = 673,
CV_AMD64_YMM12D2 = 674,
CV_AMD64_YMM12D3 = 675,
CV_AMD64_YMM13D0 = 676,
CV_AMD64_YMM13D1 = 677,
CV_AMD64_YMM13D2 = 678,
CV_AMD64_YMM13D3 = 679,
CV_AMD64_YMM14D0 = 680,
CV_AMD64_YMM14D1 = 681,
CV_AMD64_YMM14D2 = 682,
CV_AMD64_YMM14D3 = 683,
CV_AMD64_YMM15D0 = 684,
CV_AMD64_YMM15D1 = 685,
CV_AMD64_YMM15D2 = 686,
CV_AMD64_YMM15D3 = 687
};
struct codeview_string
{
codeview_string *next;
uint32_t offset;
char *string;
};
struct string_hasher : free_ptr_hash
{
typedef const char *compare_type;
static hashval_t hash (const codeview_string *x)
{
return htab_hash_string (x->string);
}
static bool equal (const codeview_string *x, const char *y)
{
return !strcmp (x->string, y);
}
static void mark_empty (codeview_string *x)
{
if (x->string)
{
free (x->string);
x->string = NULL;
}
}
static void remove (codeview_string *&x)
{
free (x->string);
}
};
struct codeview_source_file
{
codeview_source_file *next;
unsigned int file_num;
uint32_t string_offset;
char *filename;
uint8_t hash[HASH_SIZE];
};
struct codeview_line
{
codeview_line *next;
unsigned int line_no;
unsigned int label_num;
};
struct codeview_line_block
{
codeview_line_block *next;
uint32_t file_id;
unsigned int num_lines;
codeview_line *lines, *last_line;
};
struct codeview_function
{
codeview_function *next;
function *func;
unsigned int end_label;
codeview_line_block *blocks, *last_block;
};
struct codeview_symbol
{
codeview_symbol *next;
enum cv_sym_type kind;
union
{
struct
{
uint32_t type;
char *name;
dw_die_ref die;
} data_symbol;
struct
{
uint32_t parent;
uint32_t end;
uint32_t next;
uint32_t type;
uint8_t flags;
char *name;
dw_die_ref die;
} function;
};
};
struct codeview_type
{
dw_die_ref die;
uint32_t num;
bool is_fwd_ref;
};
struct die_hasher : free_ptr_hash
{
typedef dw_die_ref compare_type;
static hashval_t hash (const codeview_type *x)
{
return htab_hash_pointer (x->die);
}
static bool equal (const codeview_type *x, const dw_die_ref y)
{
return x->die == y;
}
};
struct codeview_integer
{
bool neg;
uint64_t num;
};
struct codeview_subtype
{
struct codeview_subtype *next;
enum cv_leaf_type kind;
union
{
struct
{
char *name;
struct codeview_integer value;
} lf_enumerate;
struct
{
uint32_t type_num;
} lf_index;
struct
{
uint16_t attributes;
uint32_t type;
codeview_integer offset;
char *name;
} lf_member;
};
};
struct codeview_custom_type
{
struct codeview_custom_type *next;
uint32_t num;
enum cv_leaf_type kind;
union
{
struct
{
uint32_t base_type;
uint32_t attributes;
} lf_pointer;
struct
{
uint32_t base_type;
uint16_t modifier;
} lf_modifier;
struct
{
size_t length;
codeview_subtype *subtypes;
codeview_subtype *last_subtype;
} lf_fieldlist;
struct
{
uint16_t count;
uint16_t properties;
uint32_t underlying_type;
uint32_t fieldlist;
char *name;
} lf_enum;
struct
{
uint16_t num_members;
uint16_t properties;
uint32_t field_list;
uint32_t derived_from;
uint32_t vshape;
codeview_integer length;
char *name;
} lf_structure;
struct
{
uint32_t element_type;
uint32_t index_type;
codeview_integer length_in_bytes;
} lf_array;
struct
{
uint32_t base_type;
uint8_t length;
uint8_t position;
} lf_bitfield;
struct
{
uint32_t return_type;
uint8_t calling_convention;
uint8_t attributes;
uint16_t num_parameters;
uint32_t arglist;
} lf_procedure;
struct
{
uint32_t num_entries;
uint32_t *args;
} lf_arglist;
struct
{
uint32_t parent_scope;
uint32_t function_type;
char *name;
} lf_func_id;
};
};
struct codeview_deferred_type
{
struct codeview_deferred_type *next;
dw_die_ref type;
};
static unsigned int line_label_num;
static unsigned int func_label_num;
static unsigned int sym_label_num;
static codeview_source_file *files, *last_file;
static unsigned int num_files;
static uint32_t string_offset = 1;
static hash_table *strings_htab;
static codeview_string *strings, *last_string;
static codeview_function *funcs, *last_func;
static const char* last_filename;
static uint32_t last_file_id;
static codeview_symbol *sym, *last_sym;
static hash_table *types_htab;
static codeview_custom_type *custom_types, *last_custom_type;
static codeview_deferred_type *deferred_types, *last_deferred_type;
static uint32_t get_type_num (dw_die_ref type, bool in_struct, bool no_fwd_ref);
/* Record new line number against the current function. */
void
codeview_source_line (unsigned int line_no, const char *filename)
{
codeview_line *l;
uint32_t file_id = last_file_id;
unsigned int label_num = ++line_label_num;
targetm.asm_out.internal_label (asm_out_file, LINE_LABEL, label_num);
if (!last_func || last_func->func != cfun)
{
codeview_function *f = (codeview_function *)
xmalloc (sizeof (codeview_function));
f->next = NULL;
f->func = cfun;
f->end_label = 0;
f->blocks = f->last_block = NULL;
if (!funcs)
funcs = f;
else
last_func->next = f;
last_func = f;
}
if (filename != last_filename)
{
codeview_source_file *sf = files;
while (sf)
{
if (!strcmp (sf->filename, filename))
{
/* 0x18 is the size of the checksum entry for each file.
0x6 bytes for the header, plus 0x10 bytes for the hash,
then padded to a multiple of 4. */
file_id = sf->file_num * 0x18;
last_filename = filename;
last_file_id = file_id;
break;
}
sf = sf->next;
}
}
if (!last_func->last_block || last_func->last_block->file_id != file_id)
{
codeview_line_block *b;
b = (codeview_line_block *) xmalloc (sizeof (codeview_line_block));
b->next = NULL;
b->file_id = file_id;
b->num_lines = 0;
b->lines = b->last_line = NULL;
if (!last_func->blocks)
last_func->blocks = b;
else
last_func->last_block->next = b;
last_func->last_block = b;
}
if (last_func->last_block->last_line
&& last_func->last_block->last_line->line_no == line_no)
return;
l = (codeview_line *) xmalloc (sizeof (codeview_line));
l->next = NULL;
l->line_no = line_no;
l->label_num = label_num;
if (!last_func->last_block->lines)
last_func->last_block->lines = l;
else
last_func->last_block->last_line->next = l;
last_func->last_block->last_line = l;
last_func->last_block->num_lines++;
}
/* Adds string to the string table, returning its offset. If already present,
this returns the offset of the existing string. */
static uint32_t
add_string (const char *string)
{
codeview_string **slot;
codeview_string *s;
size_t len;
if (!strings_htab)
strings_htab = new hash_table (10);
slot = strings_htab->find_slot_with_hash (string, htab_hash_string (string),
INSERT);
if (*slot)
return (*slot)->offset;
s = (codeview_string *) xmalloc (sizeof (codeview_string));
len = strlen (string);
s->next = NULL;
s->offset = string_offset;
string_offset += len + 1;
s->string = xstrdup (string);
if (last_string)
last_string->next = s;
else
strings = s;
last_string = s;
*slot = s;
return s->offset;
}
/* A new source file has been encountered - record the details and calculate
its hash. */
void
codeview_start_source_file (const char *filename)
{
codeview_source_file *sf;
char *path;
uint32_t string_offset;
FILE *f;
path = lrealpath (filename);
string_offset = add_string (path);
free (path);
sf = files;
while (sf)
{
if (sf->string_offset == string_offset)
return;
sf = sf->next;
}
sf = (codeview_source_file *) xmalloc (sizeof (codeview_source_file));
sf->next = NULL;
sf->file_num = num_files;
sf->string_offset = string_offset;
sf->filename = xstrdup (filename);
f = fopen (filename, "r");
if (!f)
internal_error ("could not open %s for reading", filename);
if (md5_stream (f, sf->hash))
{
fclose (f);
internal_error ("md5_stream failed");
}
fclose (f);
if (last_file)
last_file->next = sf;
else
files = sf;
last_file = sf;
num_files++;
}
/* Write out the strings table into the .debug$S section. The linker will
parse this, and handle the deduplication and hashing for all the object
files. */
static void
write_strings_table (void)
{
codeview_string *string;
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, DEBUG_S_STRINGTABLE);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
asm_fprintf (asm_out_file, "%LLcv_strings_end - %LLcv_strings_start\n");
asm_fprintf (asm_out_file, "%LLcv_strings_start:\n");
/* The first entry is always an empty string. */
fputs (integer_asm_op (1, false), asm_out_file);
fprint_whex (asm_out_file, 0);
putc ('\n', asm_out_file);
string = strings;
while (string)
{
ASM_OUTPUT_ASCII (asm_out_file, string->string,
strlen (string->string) + 1);
string = string->next;
}
delete strings_htab;
asm_fprintf (asm_out_file, "%LLcv_strings_end:\n");
ASM_OUTPUT_ALIGN (asm_out_file, 2);
}
/* Write out the file checksums data into the .debug$S section. */
static void
write_source_files (void)
{
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, DEBUG_S_FILECHKSMS);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
asm_fprintf (asm_out_file,
"%LLcv_filechksms_end - %LLcv_filechksms_start\n");
asm_fprintf (asm_out_file, "%LLcv_filechksms_start:\n");
while (files)
{
codeview_source_file *next = files->next;
/* This is struct file_checksum in binutils, or filedata in Microsoft's
dumpsym7.cpp:
struct file_checksum
{
uint32_t file_id;
uint8_t checksum_length;
uint8_t checksum_type;
} ATTRIBUTE_PACKED;
followed then by the bytes of the hash, padded to the next 4 bytes.
file_id here is actually the offset in the strings table. */
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, files->string_offset);
putc ('\n', asm_out_file);
fputs (integer_asm_op (1, false), asm_out_file);
fprint_whex (asm_out_file, HASH_SIZE);
putc ('\n', asm_out_file);
fputs (integer_asm_op (1, false), asm_out_file);
fprint_whex (asm_out_file, CHKSUM_TYPE_MD5);
putc ('\n', asm_out_file);
for (unsigned int i = 0; i < HASH_SIZE; i++)
{
fputs (integer_asm_op (1, false), asm_out_file);
fprint_whex (asm_out_file, files->hash[i]);
putc ('\n', asm_out_file);
}
ASM_OUTPUT_ALIGN (asm_out_file, 2);
free (files->filename);
free (files);
files = next;
}
asm_fprintf (asm_out_file, "%LLcv_filechksms_end:\n");
}
/* Write out the line number information for each function into the
.debug$S section. */
static void
write_line_numbers (void)
{
unsigned int func_num = 0;
while (funcs)
{
codeview_function *next = funcs->next;
unsigned int first_label_num;
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, DEBUG_S_LINES);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
asm_fprintf (asm_out_file, "%LLcv_lines%u_end - %LLcv_lines%u_start\n",
func_num, func_num);
asm_fprintf (asm_out_file, "%LLcv_lines%u_start:\n", func_num);
/* Output the header (struct cv_lines_header in binutils or
CV_DebugSLinesHeader_t in Microsoft's cvinfo.h):
struct cv_lines_header
{
uint32_t offset;
uint16_t section;
uint16_t flags;
uint32_t length;
};
*/
asm_fprintf (asm_out_file, "\t.secrel32\t%L" LINE_LABEL "%u\n",
funcs->blocks->lines->label_num);
asm_fprintf (asm_out_file, "\t.secidx\t%L" LINE_LABEL "%u\n",
funcs->blocks->lines->label_num);
/* flags */
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, 0);
putc ('\n', asm_out_file);
first_label_num = funcs->blocks->lines->label_num;
/* length */
fputs (integer_asm_op (4, false), asm_out_file);
asm_fprintf (asm_out_file,
"%L" END_FUNC_LABEL "%u - %L" LINE_LABEL "%u\n",
funcs->end_label, first_label_num);
while (funcs->blocks)
{
codeview_line_block *next = funcs->blocks->next;
/* Next comes the blocks, each block being a part of a function
within the same source file (struct cv_lines_block in binutils or
CV_DebugSLinesFileBlockHeader_t in Microsoft's cvinfo.h):
struct cv_lines_block
{
uint32_t file_id;
uint32_t num_lines;
uint32_t length;
};
*/
/* file ID */
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, funcs->blocks->file_id);
putc ('\n', asm_out_file);
/* number of lines */
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, funcs->blocks->num_lines);
putc ('\n', asm_out_file);
/* length of code block: (num_lines * sizeof (struct cv_line)) +
sizeof (struct cv_lines_block) */
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, (funcs->blocks->num_lines * 0x8) + 0xc);
putc ('\n', asm_out_file);
while (funcs->blocks->lines)
{
codeview_line *next = funcs->blocks->lines->next;
/* Finally comes the line number information (struct cv_line in
binutils or CV_Line_t in Microsoft's cvinfo.h):
struct cv_line
{
uint32_t offset;
uint32_t line_no;
};
Strictly speaking line_no is a bitfield: the bottom 24 bits
are the line number, and the top bit means "is a statement".
*/
fputs (integer_asm_op (4, false), asm_out_file);
asm_fprintf (asm_out_file,
"%L" LINE_LABEL "%u - %L" LINE_LABEL "%u\n",
funcs->blocks->lines->label_num, first_label_num);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file,
0x80000000
| (funcs->blocks->lines->line_no & 0xffffff));
putc ('\n', asm_out_file);
free (funcs->blocks->lines);
funcs->blocks->lines = next;
}
free (funcs->blocks);
funcs->blocks = next;
}
free (funcs);
asm_fprintf (asm_out_file, "%LLcv_lines%u_end:\n", func_num);
func_num++;
funcs = next;
}
}
/* Treat cold sections as separate functions, for the purposes of line
numbers. */
void
codeview_switch_text_section (void)
{
codeview_function *f;
if (last_func && last_func->end_label == 0)
{
unsigned int label_num = ++func_label_num;
targetm.asm_out.internal_label (asm_out_file, END_FUNC_LABEL,
label_num);
last_func->end_label = label_num;
}
f = (codeview_function *) xmalloc (sizeof (codeview_function));
f->next = NULL;
f->func = cfun;
f->end_label = 0;
f->blocks = f->last_block = NULL;
if (!funcs)
funcs = f;
else
last_func->next = f;
last_func = f;
}
/* Mark the end of the current function. */
void
codeview_end_epilogue (void)
{
if (last_func && last_func->end_label == 0)
{
unsigned int label_num = ++func_label_num;
targetm.asm_out.internal_label (asm_out_file, END_FUNC_LABEL,
label_num);
last_func->end_label = label_num;
}
}
/* Return the CodeView constant for the selected architecture. */
static uint16_t
target_processor (void)
{
if (TARGET_64BIT)
return CV_CFL_X64;
else
return CV_CFL_80386;
}
/* Return the CodeView constant for the language being used. */
static uint32_t
language_constant (void)
{
const char *language_string = lang_hooks.name;
if (startswith (language_string, "GNU C++"))
return CV_CFL_CXX;
else if (startswith (language_string, "GNU C"))
return CV_CFL_C;
return 0;
}
/* Write a S_COMPILE3 symbol, which records the details of the compiler
being used. */
static void
write_compile3_symbol (void)
{
unsigned int label_num = ++sym_label_num;
static const char compiler_name[] = "GCC ";
/* This is struct COMPILESYM3 in binutils and Microsoft's cvinfo.h:
struct COMPILESYM3
{
uint16_t length;
uint16_t type;
uint32_t flags;
uint16_t machine;
uint16_t frontend_major;
uint16_t frontend_minor;
uint16_t frontend_build;
uint16_t frontend_qfe;
uint16_t backend_major;
uint16_t backend_minor;
uint16_t backend_build;
uint16_t backend_qfe;
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file,
"%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
label_num, label_num);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, S_COMPILE3);
putc ('\n', asm_out_file);
/* Microsoft has the flags as a bitfield, with the bottom 8 bits being the
language constant, and the reset being MSVC-specific stuff. */
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, language_constant ());
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, target_processor ());
putc ('\n', asm_out_file);
/* Write 8 uint16_ts for the frontend and backend versions. As with GAS, we
zero these, as it's easier to record the version in the compiler
string. */
for (unsigned int i = 0; i < 8; i++)
{
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, 0);
putc ('\n', asm_out_file);
}
ASM_OUTPUT_ASCII (asm_out_file, compiler_name, sizeof (compiler_name) - 1);
ASM_OUTPUT_ASCII (asm_out_file, version_string, strlen (version_string) + 1);
ASM_OUTPUT_ALIGN (asm_out_file, 2);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
}
/* Write an S_GDATA32 symbol, representing a global variable, or an S_LDATA32
symbol, for a static global variable. */
static void
write_data_symbol (codeview_symbol *s)
{
unsigned int label_num = ++sym_label_num;
dw_attr_node *loc;
dw_loc_descr_ref loc_ref;
/* This is struct datasym in binutils:
struct datasym
{
uint16_t size;
uint16_t kind;
uint32_t type;
uint32_t offset;
uint16_t section;
char name[];
} ATTRIBUTE_PACKED;
*/
/* Extract the DW_AT_location attribute from the DIE, and make sure it's in
in a format we can parse. */
loc = get_AT (s->data_symbol.die, DW_AT_location);
if (!loc)
goto end;
if (loc->dw_attr_val.val_class != dw_val_class_loc)
goto end;
loc_ref = loc->dw_attr_val.v.val_loc;
if (!loc_ref || loc_ref->dw_loc_opc != DW_OP_addr)
goto end;
/* Output the S_GDATA32 / S_LDATA32 record. */
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file,
"%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
label_num, label_num);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, s->kind);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, s->data_symbol.type);
putc ('\n', asm_out_file);
asm_fprintf (asm_out_file, "\t.secrel32 ");
output_addr_const (asm_out_file, loc_ref->dw_loc_oprnd1.v.val_addr);
fputc ('\n', asm_out_file);
asm_fprintf (asm_out_file, "\t.secidx ");
output_addr_const (asm_out_file, loc_ref->dw_loc_oprnd1.v.val_addr);
fputc ('\n', asm_out_file);
ASM_OUTPUT_ASCII (asm_out_file, s->data_symbol.name,
strlen (s->data_symbol.name) + 1);
ASM_OUTPUT_ALIGN (asm_out_file, 2);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
end:
free (s->data_symbol.name);
}
/* Write an S_LOCAL symbol, representing an optimized variable. This is then
followed by various S_DEFRANGE_* symbols, which describe how to find the
value of a variable and the range for which this is valid. */
static void
write_s_local (dw_die_ref die)
{
unsigned int label_num = ++sym_label_num;
const char *name = get_AT_string (die, DW_AT_name);
uint32_t type;
/* This is struct LOCALSYM in Microsoft's cvinfo.h:
struct LOCALSYM {
uint16_t reclen;
uint16_t rectyp;
uint32_t typind;
uint16_t flags;
char name[];
};
*/
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file,
"%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
label_num, label_num);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, S_LOCAL);
putc ('\n', asm_out_file);
type = get_type_num (get_AT_ref (die, DW_AT_type), false, false);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, type);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, 0);
putc ('\n', asm_out_file);
ASM_OUTPUT_ASCII (asm_out_file, name, strlen (name) + 1);
ASM_OUTPUT_ALIGN (asm_out_file, 2);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
}
/* Write an S_LDATA32 symbol, representing a static variable within a function.
This symbol can also appear outside of a function block - see
write_data_symbol. */
static void
write_local_s_ldata32 (dw_die_ref die, dw_loc_descr_ref loc_ref)
{
unsigned int label_num = ++sym_label_num;
const char *name = get_AT_string (die, DW_AT_name);
uint32_t type;
/* This is struct datasym in binutils:
struct datasym
{
uint16_t size;
uint16_t kind;
uint32_t type;
uint32_t offset;
uint16_t section;
char name[];
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file,
"%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
label_num, label_num);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, S_LDATA32);
putc ('\n', asm_out_file);
type = get_type_num (get_AT_ref (die, DW_AT_type), false, false);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, type);
putc ('\n', asm_out_file);
asm_fprintf (asm_out_file, "\t.secrel32 ");
output_addr_const (asm_out_file, loc_ref->dw_loc_oprnd1.v.val_addr);
fputc ('\n', asm_out_file);
asm_fprintf (asm_out_file, "\t.secidx ");
output_addr_const (asm_out_file, loc_ref->dw_loc_oprnd1.v.val_addr);
fputc ('\n', asm_out_file);
ASM_OUTPUT_ASCII (asm_out_file, name, strlen (name) + 1);
ASM_OUTPUT_ALIGN (asm_out_file, 2);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
}
/* Try to translate a DWARF register number into its CodeView equivalent. */
static uint16_t
dwarf_reg_to_cv (unsigned int regno)
{
static const cv_amd64_register amd64_reg_mapping[] = {
CV_AMD64_RAX,
CV_AMD64_RDX,
CV_AMD64_RCX,
CV_AMD64_RBX,
CV_AMD64_RSI,
CV_AMD64_RDI,
CV_AMD64_RBP,
CV_AMD64_RSP,
CV_AMD64_R8,
CV_AMD64_R9,
CV_AMD64_R10,
CV_AMD64_R11,
CV_AMD64_R12,
CV_AMD64_R13,
CV_AMD64_R14,
CV_AMD64_R15,
CV_AMD64_RIP,
CV_AMD64_XMM0,
CV_AMD64_XMM1,
CV_AMD64_XMM2,
CV_AMD64_XMM3,
CV_AMD64_XMM4,
CV_AMD64_XMM5,
CV_AMD64_XMM6,
CV_AMD64_XMM7,
CV_AMD64_XMM8,
CV_AMD64_XMM9,
CV_AMD64_XMM10,
CV_AMD64_XMM11,
CV_AMD64_XMM12,
CV_AMD64_XMM13,
CV_AMD64_XMM14,
CV_AMD64_XMM15,
CV_AMD64_ST0,
CV_AMD64_ST1,
CV_AMD64_ST2,
CV_AMD64_ST3,
CV_AMD64_ST4,
CV_AMD64_ST5,
CV_AMD64_ST6,
CV_AMD64_ST7,
CV_AMD64_NOREG,
CV_AMD64_NOREG,
CV_AMD64_NOREG,
CV_AMD64_NOREG,
CV_AMD64_NOREG,
CV_AMD64_NOREG,
CV_AMD64_NOREG,
CV_AMD64_NOREG,
CV_AMD64_EFLAGS,
CV_AMD64_ES,
CV_AMD64_CS,
CV_AMD64_SS,
CV_AMD64_DS,
CV_AMD64_FS,
CV_AMD64_GS,
CV_AMD64_NOREG,
CV_AMD64_NOREG,
CV_AMD64_NOREG,
CV_AMD64_NOREG,
CV_AMD64_NOREG,
CV_AMD64_NOREG,
CV_AMD64_TR,
CV_AMD64_LDTR,
CV_AMD64_MXCSR,
CV_AMD64_CTRL,
CV_AMD64_STAT
};
static const cv_x86_register x86_reg_mapping[] = {
CV_REG_EAX,
CV_REG_ECX,
CV_REG_EDX,
CV_REG_EBX,
CV_REG_EBP,
CV_REG_ESP,
CV_REG_ESI,
CV_REG_EDI,
CV_REG_EIP,
CV_REG_EFLAGS,
CV_REG_CS,
CV_REG_SS,
CV_REG_DS,
CV_REG_ES,
CV_REG_FS,
CV_REG_GS,
CV_REG_ST0,
CV_REG_ST1,
CV_REG_ST2,
CV_REG_ST3,
CV_REG_ST4,
CV_REG_ST5,
CV_REG_ST6,
CV_REG_ST7,
CV_REG_CTRL,
CV_REG_STAT,
CV_REG_TAG,
CV_REG_FPCS,
CV_REG_FPIP,
CV_REG_FPDS,
CV_REG_FPDO,
CV_REG_NONE,
CV_REG_XMM0,
CV_REG_XMM1,
CV_REG_XMM2,
CV_REG_XMM3,
CV_REG_XMM4,
CV_REG_XMM5,
CV_REG_XMM6,
CV_REG_XMM7,
CV_REG_MXCSR
};
if (TARGET_64BIT)
{
if (regno < sizeof (amd64_reg_mapping) / sizeof (*amd64_reg_mapping))
return amd64_reg_mapping[regno];
return CV_AMD64_NOREG;
}
else
{
if (regno < sizeof (x86_reg_mapping) / sizeof (*x86_reg_mapping))
return x86_reg_mapping[regno];
return CV_REG_NONE;
}
}
/* Write an S_REGISTER symbol, representing an unoptimized variable that has
been assigned to a register. */
static void
write_s_register (dw_die_ref die, dw_loc_descr_ref loc_ref)
{
unsigned int label_num = ++sym_label_num;
const char *name = get_AT_string (die, DW_AT_name);
uint16_t regno;
uint32_t type;
/* This is struct regsym in binutils and REGSYM in Microsoft's cvinfo.h:
struct regsym
{
uint16_t size;
uint16_t kind;
uint32_t type;
uint16_t reg;
char name[];
} ATTRIBUTE_PACKED;
*/
if (loc_ref->dw_loc_opc == DW_OP_regx)
regno = dwarf_reg_to_cv (loc_ref->dw_loc_oprnd1.v.val_int);
else
regno = dwarf_reg_to_cv (loc_ref->dw_loc_opc - DW_OP_reg0);
if (regno == 0)
return;
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file,
"%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
label_num, label_num);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, S_REGISTER);
putc ('\n', asm_out_file);
type = get_type_num (get_AT_ref (die, DW_AT_type), false, false);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, type);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, regno);
putc ('\n', asm_out_file);
ASM_OUTPUT_ASCII (asm_out_file, name, strlen (name) + 1);
ASM_OUTPUT_ALIGN (asm_out_file, 2);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
}
/* Write an S_REGREL32 symbol in order to represent an unoptimized stack
variable. The memory address is given by a register value plus an offset,
so we need to parse the function's DW_AT_frame_base attribute for this. */
static void
write_fbreg_variable (dw_die_ref die, dw_loc_descr_ref loc_ref,
dw_loc_descr_ref fbloc)
{
unsigned int label_num = ++sym_label_num;
const char *name = get_AT_string (die, DW_AT_name);
uint32_t type;
uint16_t regno;
int offset;
/* This is struct regrel in binutils and REGREL32 in Microsoft's cvinfo.h:
struct regrel
{
uint16_t size;
uint16_t kind;
uint32_t offset;
uint32_t type;
uint16_t reg;
char name[];
} ATTRIBUTE_PACKED;
*/
if (!fbloc)
return;
if (fbloc->dw_loc_opc >= DW_OP_breg0 && fbloc->dw_loc_opc <= DW_OP_breg31)
{
regno = dwarf_reg_to_cv (fbloc->dw_loc_opc - DW_OP_breg0);
offset = fbloc->dw_loc_oprnd1.v.val_int;
}
else if (fbloc->dw_loc_opc == DW_OP_bregx)
{
regno = dwarf_reg_to_cv (fbloc->dw_loc_oprnd1.v.val_int);
offset = fbloc->dw_loc_oprnd2.v.val_int;
}
else
{
return;
}
if (loc_ref->dw_loc_oprnd1.val_class != dw_val_class_unsigned_const)
return;
offset += loc_ref->dw_loc_oprnd1.v.val_int;
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file,
"%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
label_num, label_num);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, S_REGREL32);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, offset);
putc ('\n', asm_out_file);
type = get_type_num (get_AT_ref (die, DW_AT_type), false, false);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, type);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, regno);
putc ('\n', asm_out_file);
ASM_OUTPUT_ASCII (asm_out_file, name, strlen (name) + 1);
ASM_OUTPUT_ALIGN (asm_out_file, 2);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
}
/* Write an S_DEFRANGE_REGISTER symbol, which describes a range for which an
S_LOCAL variable is held in a certain register. */
static void
write_defrange_register (dw_loc_descr_ref expr, rtx range_start, rtx range_end)
{
unsigned int label_num = ++sym_label_num;
uint16_t regno;
/* This is defrange_register in binutils and DEFRANGESYMREGISTER in
Microsoft's cvinfo.h:
struct lvar_addr_range
{
uint32_t offset;
uint16_t section;
uint16_t length;
} ATTRIBUTE_PACKED;
struct lvar_addr_gap {
uint16_t offset;
uint16_t length;
} ATTRIBUTE_PACKED;
struct defrange_register
{
uint16_t size;
uint16_t kind;
uint16_t reg;
uint16_t attributes;
struct lvar_addr_range range;
struct lvar_addr_gap gaps[];
} ATTRIBUTE_PACKED;
*/
if (expr->dw_loc_opc == DW_OP_regx)
regno = dwarf_reg_to_cv (expr->dw_loc_oprnd1.v.val_int);
else
regno = dwarf_reg_to_cv (expr->dw_loc_opc - DW_OP_reg0);
if (regno == 0)
return;
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file,
"%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
label_num, label_num);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, S_DEFRANGE_REGISTER);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, regno);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, 0);
putc ('\n', asm_out_file);
asm_fprintf (asm_out_file, "\t.secrel32 ");
output_addr_const (asm_out_file, range_start);
fputc ('\n', asm_out_file);
asm_fprintf (asm_out_file, "\t.secidx ");
output_addr_const (asm_out_file, range_start);
fputc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
output_addr_const (asm_out_file, range_end);
fputs (" - ", asm_out_file);
output_addr_const (asm_out_file, range_start);
putc ('\n', asm_out_file);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
}
/* Write an S_DEFRANGE_REGISTER_REL symbol, which describes a range for which
an S_LOCAL variable is held in memory given by the value of a certain
register plus an offset. */
static void
write_defrange_register_rel (dw_loc_descr_ref expr, dw_loc_descr_ref fbloc,
rtx range_start, rtx range_end)
{
unsigned int label_num = ++sym_label_num;
uint16_t regno;
int offset;
/* This is defrange_register_rel in binutils and DEFRANGESYMREGISTERREL in
Microsoft's cvinfo.h:
struct lvar_addr_range
{
uint32_t offset;
uint16_t section;
uint16_t length;
} ATTRIBUTE_PACKED;
struct lvar_addr_gap {
uint16_t offset;
uint16_t length;
} ATTRIBUTE_PACKED;
struct defrange_register_rel
{
uint16_t size;
uint16_t kind;
uint16_t reg;
uint16_t offset_parent;
uint32_t offset_register;
struct lvar_addr_range range;
struct lvar_addr_gap gaps[];
} ATTRIBUTE_PACKED;
*/
if (!fbloc)
return;
if (fbloc->dw_loc_opc >= DW_OP_breg0 && fbloc->dw_loc_opc <= DW_OP_breg31)
{
regno = dwarf_reg_to_cv (fbloc->dw_loc_opc - DW_OP_breg0);
offset = fbloc->dw_loc_oprnd1.v.val_int;
}
else if (fbloc->dw_loc_opc == DW_OP_bregx)
{
regno = dwarf_reg_to_cv (fbloc->dw_loc_oprnd1.v.val_int);
offset = fbloc->dw_loc_oprnd2.v.val_int;
}
else
{
return;
}
if (expr->dw_loc_oprnd1.val_class != dw_val_class_unsigned_const)
return;
offset += expr->dw_loc_oprnd1.v.val_int;
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file,
"%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
label_num, label_num);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, S_DEFRANGE_REGISTER_REL);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, regno);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, 0);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, offset);
putc ('\n', asm_out_file);
asm_fprintf (asm_out_file, "\t.secrel32 ");
output_addr_const (asm_out_file, range_start);
fputc ('\n', asm_out_file);
asm_fprintf (asm_out_file, "\t.secidx ");
output_addr_const (asm_out_file, range_start);
fputc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
output_addr_const (asm_out_file, range_end);
fputs (" - ", asm_out_file);
output_addr_const (asm_out_file, range_start);
putc ('\n', asm_out_file);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
}
/* Try to write an S_DEFRANGE_* symbol for the given DWARF location. */
static void
write_optimized_local_variable_loc (dw_loc_descr_ref expr,
dw_loc_descr_ref fbloc, rtx range_start,
rtx range_end)
{
if (expr->dw_loc_next)
return;
if (!range_start)
return;
if (!range_end)
return;
switch (expr->dw_loc_opc)
{
case DW_OP_reg0:
case DW_OP_reg1:
case DW_OP_reg2:
case DW_OP_reg3:
case DW_OP_reg4:
case DW_OP_reg5:
case DW_OP_reg6:
case DW_OP_reg7:
case DW_OP_reg8:
case DW_OP_reg9:
case DW_OP_reg10:
case DW_OP_reg11:
case DW_OP_reg12:
case DW_OP_reg13:
case DW_OP_reg14:
case DW_OP_reg15:
case DW_OP_reg16:
case DW_OP_reg17:
case DW_OP_reg18:
case DW_OP_reg19:
case DW_OP_reg20:
case DW_OP_reg21:
case DW_OP_reg22:
case DW_OP_reg23:
case DW_OP_reg24:
case DW_OP_reg25:
case DW_OP_reg26:
case DW_OP_reg27:
case DW_OP_reg28:
case DW_OP_reg29:
case DW_OP_reg30:
case DW_OP_reg31:
case DW_OP_regx:
write_defrange_register (expr, range_start, range_end);
break;
case DW_OP_fbreg:
write_defrange_register_rel (expr, fbloc, range_start, range_end);
break;
default:
break;
}
}
/* Write an optimized local variable, given by an S_LOCAL symbol followed by
any number of S_DEFRANGE_* symbols. We can't mix and match optimized and
unoptimized variables in the same function, so even if it stays in the same
place for the whole block we need to write an S_LOCAL. */
static void
write_optimized_local_variable (dw_die_ref die, dw_loc_descr_ref fbloc,
rtx block_start, rtx block_end)
{
dw_attr_node *loc;
dw_loc_list_ref loc_list;
loc = get_AT (die, DW_AT_location);
if (!loc)
return;
switch (loc->dw_attr_val.val_class)
{
case dw_val_class_loc_list:
loc_list = loc->dw_attr_val.v.val_loc_list;
write_s_local (die);
while (loc_list)
{
rtx range_start = NULL, range_end = NULL;
if (loc_list->begin)
range_start = gen_rtx_SYMBOL_REF (Pmode, loc_list->begin);
if (loc_list->end)
range_end = gen_rtx_SYMBOL_REF (Pmode, loc_list->end);
write_optimized_local_variable_loc (loc_list->expr, fbloc,
range_start, range_end);
loc_list = loc_list->dw_loc_next;
}
break;
case dw_val_class_loc:
write_s_local (die);
write_optimized_local_variable_loc (loc->dw_attr_val.v.val_loc, fbloc,
block_start, block_end);
break;
default:
break;
}
}
/* Write a symbol representing an unoptimized variable within a function, if
we're able to translate the DIE's DW_AT_location into its CodeView
equivalent. */
static void
write_unoptimized_local_variable (dw_die_ref die, dw_loc_descr_ref fbloc)
{
dw_attr_node *loc;
dw_loc_descr_ref loc_ref;
loc = get_AT (die, DW_AT_location);
if (!loc)
return;
if (loc->dw_attr_val.val_class != dw_val_class_loc)
return;
loc_ref = loc->dw_attr_val.v.val_loc;
if (!loc_ref)
return;
switch (loc_ref->dw_loc_opc)
{
case DW_OP_addr:
write_local_s_ldata32 (die, loc_ref);
break;
case DW_OP_reg0:
case DW_OP_reg1:
case DW_OP_reg2:
case DW_OP_reg3:
case DW_OP_reg4:
case DW_OP_reg5:
case DW_OP_reg6:
case DW_OP_reg7:
case DW_OP_reg8:
case DW_OP_reg9:
case DW_OP_reg10:
case DW_OP_reg11:
case DW_OP_reg12:
case DW_OP_reg13:
case DW_OP_reg14:
case DW_OP_reg15:
case DW_OP_reg16:
case DW_OP_reg17:
case DW_OP_reg18:
case DW_OP_reg19:
case DW_OP_reg20:
case DW_OP_reg21:
case DW_OP_reg22:
case DW_OP_reg23:
case DW_OP_reg24:
case DW_OP_reg25:
case DW_OP_reg26:
case DW_OP_reg27:
case DW_OP_reg28:
case DW_OP_reg29:
case DW_OP_reg30:
case DW_OP_reg31:
case DW_OP_regx:
write_s_register (die, loc_ref);
break;
case DW_OP_fbreg:
write_fbreg_variable (die, loc_ref, fbloc);
break;
default:
break;
}
}
/* Translate a DW_TAG_lexical_block DIE into an S_BLOCK32 symbol, representing
a block within an unoptimized function. Returns false if we're not able
to resolve the location, which will prevent the caller from issuing an
unneeded S_END. */
static bool
write_s_block32 (dw_die_ref die)
{
unsigned int label_num = ++sym_label_num;
dw_attr_node *loc_low, *loc_high;
const char *label_low, *label_high;
rtx rtx_low, rtx_high;
/* This is struct blocksym in binutils and BLOCKSYM32 in Microsoft's
cvinfo.h:
struct blocksym
{
uint16_t size;
uint16_t kind;
uint32_t parent;
uint32_t end;
uint32_t len;
uint32_t offset;
uint16_t section;
char name[];
} ATTRIBUTE_PACKED;
*/
loc_low = get_AT (die, DW_AT_low_pc);
if (!loc_low)
return false;
if (loc_low->dw_attr_val.val_class != dw_val_class_lbl_id)
return false;
label_low = loc_low->dw_attr_val.v.val_lbl_id;
if (!label_low)
return false;
rtx_low = gen_rtx_SYMBOL_REF (Pmode, label_low);
loc_high = get_AT (die, DW_AT_high_pc);
if (!loc_high)
return false;
if (loc_high->dw_attr_val.val_class != dw_val_class_high_pc)
return false;
label_high = loc_high->dw_attr_val.v.val_lbl_id;
if (!label_high)
return false;
rtx_high = gen_rtx_SYMBOL_REF (Pmode, label_high);
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file,
"%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
label_num, label_num);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, S_BLOCK32);
putc ('\n', asm_out_file);
/* The parent and end fields get filled in by the linker. */
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, 0);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, 0);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
output_addr_const (asm_out_file, rtx_high);
fputs (" - ", asm_out_file);
output_addr_const (asm_out_file, rtx_low);
putc ('\n', asm_out_file);
asm_fprintf (asm_out_file, "\t.secrel32 ");
output_addr_const (asm_out_file, rtx_low);
fputc ('\n', asm_out_file);
asm_fprintf (asm_out_file, "\t.secidx ");
output_addr_const (asm_out_file, rtx_low);
fputc ('\n', asm_out_file);
ASM_OUTPUT_ASCII (asm_out_file, "", 1);
ASM_OUTPUT_ALIGN (asm_out_file, 2);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
return true;
}
/* Write an S_END symbol, which is used to finish off a number of different
symbol types. Here we use it to mark the S_BLOCK32 as finished. */
static void
write_s_end (void)
{
unsigned int label_num = ++sym_label_num;
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file,
"%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
label_num, label_num);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, S_END);
putc ('\n', asm_out_file);
ASM_OUTPUT_ALIGN (asm_out_file, 2);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
}
/* Write the S_FRAMEPROC symbol, which is supposed to give information about
the function frame. It doesn't seem to be really used in modern versions of
MSVC, which is why we zero-out everything here. You still need to write it
though, otherwise windbg won't necessarily show all the local variables. */
static void
write_s_frameproc (void)
{
unsigned int label_num = ++sym_label_num;
/* This is struct FRAMEPROCSYM in Microsoft's cvinfo.h:
struct frameprocsym
{
uint16_t size;
uint16_t kind;
uint32_t frame_size;
uint32_t padding_size;
uint32_t padding_offset;
uint32_t saved_registers_size;
uint32_t exception_handler_offset;
uint16_t exception_handler_section;
uint32_t flags;
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file,
"%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
label_num, label_num);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, S_FRAMEPROC);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, 0);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, 0);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, 0);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, 0);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, 0);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, 0);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, 0);
putc ('\n', asm_out_file);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
}
/* Loop through the DIEs in an unoptimized function, writing out any variables
or blocks that we encounter. */
static void
write_unoptimized_function_vars (dw_die_ref die, dw_loc_descr_ref fbloc)
{
dw_die_ref first_child, c;
first_child = dw_get_die_child (die);
if (!first_child)
return;
c = first_child;
do
{
c = dw_get_die_sib (c);
switch (dw_get_die_tag (c))
{
case DW_TAG_formal_parameter:
case DW_TAG_variable:
write_unoptimized_local_variable (c, fbloc);
break;
case DW_TAG_lexical_block:
{
bool block_started = write_s_block32 (c);
write_unoptimized_function_vars (c, fbloc);
if (block_started)
write_s_end ();
break;
}
default:
break;
}
}
while (c != first_child);
}
/* Write the variables in an optimized function or block. There's no S_BLOCK32s
here, with the range determining the lifetime of a variable. Unfortunately
for us CodeView is much less expressive than DWARF when it comes to variable
locations, so some degree of "optimized out"s is inevitable. */
static void
write_optimized_function_vars (dw_die_ref die, dw_loc_descr_ref fbloc,
rtx block_start, rtx block_end)
{
dw_die_ref first_child, c;
first_child = dw_get_die_child (die);
if (!first_child)
return;
c = first_child;
do
{
c = dw_get_die_sib (c);
switch (dw_get_die_tag (c))
{
case DW_TAG_formal_parameter:
case DW_TAG_variable:
write_optimized_local_variable (c, fbloc, block_start, block_end);
break;
case DW_TAG_lexical_block:
{
dw_attr_node *loc_low, *loc_high;
const char *label_low, *label_high;
rtx rtx_low, rtx_high;
loc_low = get_AT (die, DW_AT_low_pc);
if (!loc_low)
break;
if (loc_low->dw_attr_val.val_class != dw_val_class_lbl_id)
break;
label_low = loc_low->dw_attr_val.v.val_lbl_id;
if (!label_low)
break;
rtx_low = gen_rtx_SYMBOL_REF (Pmode, label_low);
loc_high = get_AT (die, DW_AT_high_pc);
if (!loc_high)
break;
if (loc_high->dw_attr_val.val_class != dw_val_class_high_pc)
break;
label_high = loc_high->dw_attr_val.v.val_lbl_id;
if (!label_high)
break;
rtx_high = gen_rtx_SYMBOL_REF (Pmode, label_high);
write_optimized_function_vars (c, fbloc, rtx_low, rtx_high);
break;
}
default:
break;
}
}
while (c != first_child);
}
/* Write an S_GPROC32_ID symbol, representing a global function, or an
S_LPROC32_ID symbol, for a static function. */
static void
write_function (codeview_symbol *s)
{
unsigned int label_num = ++sym_label_num;
dw_attr_node *loc_low, *loc_high, *frame_base;
const char *label_low, *label_high;
rtx rtx_low, rtx_high;
dw_loc_descr_ref fbloc = NULL;
/* This is struct procsym in binutils and PROCSYM32 in Microsoft's cvinfo.h:
struct procsym
{
uint16_t size;
uint16_t kind;
uint32_t parent;
uint32_t end;
uint32_t next;
uint32_t proc_len;
uint32_t debug_start;
uint32_t debug_end;
uint32_t type;
uint32_t offset;
uint16_t section;
uint8_t flags;
char name[];
} ATTRIBUTE_PACKED;
*/
loc_low = get_AT (s->function.die, DW_AT_low_pc);
if (!loc_low)
goto end;
if (loc_low->dw_attr_val.val_class != dw_val_class_lbl_id)
goto end;
label_low = loc_low->dw_attr_val.v.val_lbl_id;
if (!label_low)
goto end;
rtx_low = gen_rtx_SYMBOL_REF (Pmode, label_low);
loc_high = get_AT (s->function.die, DW_AT_high_pc);
if (!loc_high)
goto end;
if (loc_high->dw_attr_val.val_class != dw_val_class_high_pc)
goto end;
label_high = loc_high->dw_attr_val.v.val_lbl_id;
if (!label_high)
goto end;
rtx_high = gen_rtx_SYMBOL_REF (Pmode, label_high);
/* Output the S_GPROC32_ID / S_LPROC32_ID record. */
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file,
"%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
label_num, label_num);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, s->kind);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, s->function.parent);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, s->function.end);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, s->function.next);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
output_addr_const (asm_out_file, rtx_high);
fputs (" - ", asm_out_file);
output_addr_const (asm_out_file, rtx_low);
putc ('\n', asm_out_file);
/* FIXME - debug_start should be the end of the prologue, and debug_end
the beginning of the epilogue. Do the whole function for
now. */
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, 0);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
output_addr_const (asm_out_file, rtx_high);
fputs (" - ", asm_out_file);
output_addr_const (asm_out_file, rtx_low);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, s->function.type);
putc ('\n', asm_out_file);
asm_fprintf (asm_out_file, "\t.secrel32 ");
output_addr_const (asm_out_file, rtx_low);
fputc ('\n', asm_out_file);
asm_fprintf (asm_out_file, "\t.secidx ");
output_addr_const (asm_out_file, rtx_low);
fputc ('\n', asm_out_file);
fputs (integer_asm_op (1, false), asm_out_file);
fprint_whex (asm_out_file, s->function.flags);
putc ('\n', asm_out_file);
ASM_OUTPUT_ASCII (asm_out_file, s->function.name,
strlen (s->function.name) + 1);
ASM_OUTPUT_ALIGN (asm_out_file, 2);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
frame_base = get_AT (s->function.die, DW_AT_frame_base);
if (frame_base && frame_base->dw_attr_val.val_class == dw_val_class_loc)
fbloc = frame_base->dw_attr_val.v.val_loc;
if (flag_var_tracking)
{
write_optimized_function_vars (s->function.die, fbloc, rtx_low,
rtx_high);
write_s_frameproc ();
}
else
{
write_s_frameproc ();
write_unoptimized_function_vars (s->function.die, fbloc);
}
/* Output the S_PROC_ID_END record. */
label_num = ++sym_label_num;
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file,
"%L" SYMBOL_END_LABEL "%u - %L" SYMBOL_START_LABEL "%u\n",
label_num, label_num);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_START_LABEL, label_num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, S_PROC_ID_END);
putc ('\n', asm_out_file);
targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num);
end:
free (s->function.name);
}
/* Write the CodeView symbols into the .debug$S section. */
static void
write_codeview_symbols (void)
{
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, DEBUG_S_SYMBOLS);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
asm_fprintf (asm_out_file, "%LLcv_syms_end - %LLcv_syms_start\n");
asm_fprintf (asm_out_file, "%LLcv_syms_start:\n");
write_compile3_symbol ();
while (sym)
{
codeview_symbol *n = sym->next;
switch (sym->kind)
{
case S_LDATA32:
case S_GDATA32:
write_data_symbol (sym);
break;
case S_LPROC32_ID:
case S_GPROC32_ID:
write_function (sym);
break;
default:
break;
}
free (sym);
sym = n;
}
asm_fprintf (asm_out_file, "%LLcv_syms_end:\n");
}
/* Write an LF_POINTER type. */
static void
write_lf_pointer (codeview_custom_type *t)
{
/* This is lf_pointer in binutils and lfPointer in Microsoft's cvinfo.h:
struct lf_pointer
{
uint16_t size;
uint16_t kind;
uint32_t base_type;
uint32_t attributes;
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
t->num, t->num);
asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->kind);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_pointer.base_type);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_pointer.attributes);
putc ('\n', asm_out_file);
asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
}
/* All CodeView type definitions have to be aligned to a four-byte boundary,
so write some padding bytes if necessary. These have to be specific values:
LF_PAD3, LF_PAD2, LF_PAD1. */
static void
write_cv_padding (size_t padding)
{
if (padding == 4 || padding == 0)
return;
if (padding == 3)
{
fputs (integer_asm_op (1, false), asm_out_file);
fprint_whex (asm_out_file, LF_PAD3);
putc ('\n', asm_out_file);
}
if (padding >= 2)
{
fputs (integer_asm_op (1, false), asm_out_file);
fprint_whex (asm_out_file, LF_PAD2);
putc ('\n', asm_out_file);
}
fputs (integer_asm_op (1, false), asm_out_file);
fprint_whex (asm_out_file, LF_PAD1);
putc ('\n', asm_out_file);
}
/* Write an LF_MODIFIER type, representing a const and/or volatile modification
of another type. */
static void
write_lf_modifier (codeview_custom_type *t)
{
/* This is lf_modifier in binutils and lfModifier in Microsoft's cvinfo.h:
struct lf_modifier
{
uint16_t size;
uint16_t kind;
uint32_t base_type;
uint16_t modifier;
uint16_t padding;
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
t->num, t->num);
asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->kind);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_modifier.base_type);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_modifier.modifier);
putc ('\n', asm_out_file);
write_cv_padding (2);
asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
}
/* Write a CodeView extensible integer. If the value is non-negative and
< 0x8000, the value gets written directly as an uint16_t. Otherwise, we
output two bytes for the integer type (LF_CHAR, LF_SHORT, ...), and the
actual value follows. Returns the total number of bytes written. */
static size_t
write_cv_integer (codeview_integer *i)
{
if (i->neg)
{
if (i->num <= 0x80)
{
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, LF_CHAR);
putc ('\n', asm_out_file);
fputs (integer_asm_op (1, false), asm_out_file);
fprint_whex (asm_out_file, -i->num);
putc ('\n', asm_out_file);
return 3;
}
else if (i->num <= 0x8000)
{
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, LF_SHORT);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, -i->num);
putc ('\n', asm_out_file);
return 4;
}
else if (i->num <= 0x80000000)
{
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, LF_LONG);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, -i->num);
putc ('\n', asm_out_file);
return 6;
}
else
{
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, LF_QUADWORD);
putc ('\n', asm_out_file);
fputs (integer_asm_op (8, false), asm_out_file);
fprint_whex (asm_out_file, -i->num);
putc ('\n', asm_out_file);
return 10;
}
}
else
{
if (i->num <= 0x7fff)
{
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, i->num);
putc ('\n', asm_out_file);
return 2;
}
else if (i->num <= 0xffff)
{
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, LF_USHORT);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, i->num);
putc ('\n', asm_out_file);
return 4;
}
else if (i->num <= 0xffffffff)
{
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, LF_ULONG);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, i->num);
putc ('\n', asm_out_file);
return 6;
}
else
{
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, LF_UQUADWORD);
putc ('\n', asm_out_file);
fputs (integer_asm_op (8, false), asm_out_file);
fprint_whex (asm_out_file, i->num);
putc ('\n', asm_out_file);
return 10;
}
}
}
/* Return the extra size needed for an extensible integer. */
static size_t
cv_integer_len (codeview_integer *i)
{
if (i->neg)
{
if (i->num <= 0x80)
return sizeof (int8_t);
else if (i->num <= 0x8000)
return sizeof (int16_t);
else if (i->num <= 0x80000000)
return sizeof (int32_t);
else
return sizeof (int64_t);
}
else
{
if (i->num <= 0x7fff)
return 0;
else if (i->num <= 0xffff)
return sizeof (uint16_t);
else if (i->num <= 0xffffffff)
return sizeof (uint32_t);
else
return sizeof (uint64_t);
}
}
/* Write an LF_FIELDLIST type, which is a container for various subtypes. This
has two uses: for the values in an enum, and for the member, operators etc.
for a struct, class, or union. */
static void
write_lf_fieldlist (codeview_custom_type *t)
{
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
t->num, t->num);
asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->kind);
putc ('\n', asm_out_file);
while (t->lf_fieldlist.subtypes)
{
codeview_subtype *v = t->lf_fieldlist.subtypes;
codeview_subtype *next = v->next;
size_t name_len, leaf_len;
switch (v->kind)
{
case LF_ENUMERATE:
/* This is lf_enumerate in binutils and lfEnumerate in Microsoft's
cvinfo.h:
struct lf_enumerate
{
uint16_t kind;
uint16_t attributes;
uint16_t value;
(then actual value if value >= 0x8000)
char name[];
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, LF_ENUMERATE);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, CV_ACCESS_PUBLIC);
putc ('\n', asm_out_file);
leaf_len = 4 + write_cv_integer (&v->lf_enumerate.value);
name_len = strlen (v->lf_enumerate.name) + 1;
ASM_OUTPUT_ASCII (asm_out_file, v->lf_enumerate.name, name_len);
leaf_len += name_len;
write_cv_padding (4 - (leaf_len % 4));
free (v->lf_enumerate.name);
break;
case LF_MEMBER:
/* This is lf_member in binutils and lfMember in Microsoft's
cvinfo.h:
struct lf_member
{
uint16_t kind;
uint16_t attributes;
uint32_t type;
uint16_t offset;
char name[];
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, LF_MEMBER);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, v->lf_member.attributes);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, v->lf_member.type);
putc ('\n', asm_out_file);
leaf_len = 8 + write_cv_integer (&v->lf_member.offset);
if (v->lf_member.name)
{
name_len = strlen (v->lf_member.name) + 1;
ASM_OUTPUT_ASCII (asm_out_file, v->lf_member.name, name_len);
}
else
{
name_len = 1;
ASM_OUTPUT_ASCII (asm_out_file, "", name_len);
}
leaf_len += name_len;
write_cv_padding (4 - (leaf_len % 4));
free (v->lf_member.name);
break;
case LF_INDEX:
/* This is lf_index in binutils and lfIndex in Microsoft's cvinfo.h:
struct lf_index
{
uint16_t kind;
uint16_t padding;
uint32_t index;
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, LF_INDEX);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, 0);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, v->lf_index.type_num);
putc ('\n', asm_out_file);
break;
default:
break;
}
t->lf_fieldlist.subtypes = next;
free (v);
}
asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
}
/* Write an LF_ENUM type. */
static void
write_lf_enum (codeview_custom_type *t)
{
size_t name_len, leaf_len;
/* This is lf_enum in binutils and lfEnum in Microsoft's cvinfo.h:
struct lf_enum
{
uint16_t size;
uint16_t kind;
uint16_t num_elements;
uint16_t properties;
uint32_t underlying_type;
uint32_t field_list;
char name[];
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
t->num, t->num);
asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->kind);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_enum.count);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_enum.properties);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_enum.underlying_type);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_enum.fieldlist);
putc ('\n', asm_out_file);
name_len = strlen (t->lf_enum.name) + 1;
ASM_OUTPUT_ASCII (asm_out_file, t->lf_enum.name, name_len);
leaf_len = 14 + name_len;
write_cv_padding (4 - (leaf_len % 4));
free (t->lf_enum.name);
asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
}
/* Write an LF_STRUCTURE or LF_CLASS type (the two have the same structure). */
static void
write_lf_structure (codeview_custom_type *t)
{
size_t name_len, leaf_len;
/* This is lf_class in binutils and lfClass in Microsoft's cvinfo.h:
struct lf_class
{
uint16_t size;
uint16_t kind;
uint16_t num_members;
uint16_t properties;
uint32_t field_list;
uint32_t derived_from;
uint32_t vshape;
uint16_t length;
char name[];
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
t->num, t->num);
asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->kind);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_structure.num_members);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_structure.properties);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_structure.field_list);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_structure.derived_from);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_structure.vshape);
putc ('\n', asm_out_file);
leaf_len = 20 + write_cv_integer (&t->lf_structure.length);
if (t->lf_structure.name)
{
name_len = strlen (t->lf_structure.name) + 1;
ASM_OUTPUT_ASCII (asm_out_file, t->lf_structure.name, name_len);
}
else
{
static const char unnamed_struct[] = "";
name_len = sizeof (unnamed_struct);
ASM_OUTPUT_ASCII (asm_out_file, unnamed_struct, name_len);
}
leaf_len += name_len;
write_cv_padding (4 - (leaf_len % 4));
free (t->lf_structure.name);
asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
}
/* Write an LF_UNION type. */
static void
write_lf_union (codeview_custom_type *t)
{
size_t name_len, leaf_len;
/* This is lf_union in binutils and lfUnion in Microsoft's cvinfo.h:
struct lf_union
{
uint16_t size;
uint16_t kind;
uint16_t num_members;
uint16_t properties;
uint32_t field_list;
uint16_t length;
char name[];
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
t->num, t->num);
asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->kind);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_structure.num_members);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_structure.properties);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_structure.field_list);
putc ('\n', asm_out_file);
leaf_len = 12 + write_cv_integer (&t->lf_structure.length);
if (t->lf_structure.name)
{
name_len = strlen (t->lf_structure.name) + 1;
ASM_OUTPUT_ASCII (asm_out_file, t->lf_structure.name, name_len);
}
else
{
static const char unnamed_struct[] = "";
name_len = sizeof (unnamed_struct);
ASM_OUTPUT_ASCII (asm_out_file, unnamed_struct, name_len);
}
leaf_len += name_len;
write_cv_padding (4 - (leaf_len % 4));
free (t->lf_structure.name);
asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
}
/* Write an LF_ARRAY type. */
static void
write_lf_array (codeview_custom_type *t)
{
size_t leaf_len;
/* This is lf_array in binutils and lfArray in Microsoft's cvinfo.h:
struct lf_array
{
uint16_t size;
uint16_t kind;
uint32_t element_type;
uint32_t index_type;
uint16_t length_in_bytes;
char name[];
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
t->num, t->num);
asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->kind);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_array.element_type);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_array.index_type);
putc ('\n', asm_out_file);
leaf_len = 13 + write_cv_integer (&t->lf_array.length_in_bytes);
ASM_OUTPUT_ASCII (asm_out_file, "", 1);
write_cv_padding (4 - (leaf_len % 4));
asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
}
/* Write an LF_BITFIELD type. */
static void
write_lf_bitfield (codeview_custom_type *t)
{
/* This is lf_bitfield in binutils and lfBitfield in Microsoft's cvinfo.h:
struct lf_bitfield
{
uint16_t size;
uint16_t kind;
uint32_t base_type;
uint8_t length;
uint8_t position;
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
t->num, t->num);
asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->kind);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_bitfield.base_type);
putc ('\n', asm_out_file);
fputs (integer_asm_op (1, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_bitfield.length);
putc ('\n', asm_out_file);
fputs (integer_asm_op (1, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_bitfield.position);
putc ('\n', asm_out_file);
write_cv_padding (2);
asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
}
/* Write an LF_PROCEDURE type. Function pointers are implemented as pointers
to one of these. */
static void
write_lf_procedure (codeview_custom_type *t)
{
/* This is lf_procedure in binutils and lfProc in Microsoft's cvinfo.h:
struct lf_procedure
{
uint16_t size;
uint16_t kind;
uint32_t return_type;
uint8_t calling_convention;
uint8_t attributes;
uint16_t num_parameters;
uint32_t arglist;
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
t->num, t->num);
asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->kind);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_procedure.return_type);
putc ('\n', asm_out_file);
fputs (integer_asm_op (1, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_procedure.calling_convention);
putc ('\n', asm_out_file);
fputs (integer_asm_op (1, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_procedure.attributes);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_procedure.num_parameters);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_procedure.arglist);
putc ('\n', asm_out_file);
asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
}
/* Write an LF_ARGLIST type. This is just a list of other types. LF_PROCEDURE
entries point to one of these. */
static void
write_lf_arglist (codeview_custom_type *t)
{
/* This is lf_arglist in binutils and lfArgList in Microsoft's cvinfo.h:
struct lf_arglist
{
uint16_t size;
uint16_t kind;
uint32_t num_entries;
uint32_t args[];
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
t->num, t->num);
asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->kind);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_arglist.num_entries);
putc ('\n', asm_out_file);
for (uint32_t i = 0; i < t->lf_arglist.num_entries; i++)
{
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_arglist.args[i]);
putc ('\n', asm_out_file);
}
free (t->lf_arglist.args);
asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
}
/* Write an LF_FUNC_ID type, which marries together a function type with its
name. This will end up in the alternative types stream in the final PDB,
but we can just stick it in the normal .debug$T section. */
static void
write_lf_func_id (codeview_custom_type *t)
{
size_t name_len;
/* This is lf_func_id in binutils and lfFuncId in Microsoft's cvinfo.h:
struct lf_func_id
{
uint16_t size;
uint16_t kind;
uint32_t parent_scope;
uint32_t function_type;
char name[];
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
t->num, t->num);
asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->kind);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_func_id.parent_scope);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_func_id.function_type);
putc ('\n', asm_out_file);
name_len = strlen (t->lf_func_id.name) + 1;
ASM_OUTPUT_ASCII (asm_out_file, t->lf_func_id.name, name_len);
write_cv_padding (4 - (name_len % 4));
free (t->lf_func_id.name);
asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
}
/* Write the .debug$T section, which contains all of our custom type
definitions. */
static void
write_custom_types (void)
{
targetm.asm_out.named_section (".debug$T", SECTION_DEBUG, NULL);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, CV_SIGNATURE_C13);
putc ('\n', asm_out_file);
while (custom_types)
{
codeview_custom_type *n = custom_types->next;
switch (custom_types->kind)
{
case LF_POINTER:
write_lf_pointer (custom_types);
break;
case LF_MODIFIER:
write_lf_modifier (custom_types);
break;
case LF_FIELDLIST:
write_lf_fieldlist (custom_types);
break;
case LF_ENUM:
write_lf_enum (custom_types);
break;
case LF_STRUCTURE:
case LF_CLASS:
write_lf_structure (custom_types);
break;
case LF_UNION:
write_lf_union (custom_types);
break;
case LF_ARRAY:
write_lf_array (custom_types);
break;
case LF_BITFIELD:
write_lf_bitfield (custom_types);
break;
case LF_PROCEDURE:
write_lf_procedure (custom_types);
break;
case LF_ARGLIST:
write_lf_arglist (custom_types);
break;
case LF_FUNC_ID:
write_lf_func_id (custom_types);
break;
default:
break;
}
free (custom_types);
custom_types = n;
}
}
/* Finish CodeView debug info emission. */
void
codeview_debug_finish (void)
{
targetm.asm_out.named_section (".debug$S", SECTION_DEBUG, NULL);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, CV_SIGNATURE_C13);
putc ('\n', asm_out_file);
write_strings_table ();
write_source_files ();
write_line_numbers ();
write_codeview_symbols ();
if (custom_types)
write_custom_types ();
if (types_htab)
delete types_htab;
}
/* Translate a DWARF base type (DW_TAG_base_type) into its CodeView
equivalent. */
static uint32_t
get_type_num_base_type (dw_die_ref type)
{
unsigned int size = get_AT_unsigned (type, DW_AT_byte_size);
switch (get_AT_unsigned (type, DW_AT_encoding))
{
case DW_ATE_signed_char:
{
const char *name = get_AT_string (type, DW_AT_name);
if (size != 1)
return 0;
if (name && !strcmp (name, "signed char"))
return T_CHAR;
else
return T_RCHAR;
}
case DW_ATE_unsigned_char:
if (size != 1)
return 0;
return T_UCHAR;
case DW_ATE_signed:
switch (size)
{
case 2:
return T_SHORT;
case 4:
{
const char *name = get_AT_string (type, DW_AT_name);
if (name && !strcmp (name, "int"))
return T_INT4;
else
return T_LONG;
}
case 8:
return T_QUAD;
default:
return 0;
}
case DW_ATE_unsigned:
switch (size)
{
case 2:
{
const char *name = get_AT_string (type, DW_AT_name);
if (name && !strcmp (name, "wchar_t"))
return T_WCHAR;
else
return T_USHORT;
}
case 4:
{
const char *name = get_AT_string (type, DW_AT_name);
if (name && !strcmp (name, "unsigned int"))
return T_UINT4;
else
return T_ULONG;
}
case 8:
return T_UQUAD;
default:
return 0;
}
case DW_ATE_UTF:
switch (size)
{
case 1:
return T_CHAR8;
case 2:
return T_CHAR16;
case 4:
return T_CHAR32;
default:
return 0;
}
case DW_ATE_float:
switch (size)
{
case 4:
return T_REAL32;
case 8:
return T_REAL64;
case 12:
return T_REAL80;
case 16:
return T_REAL128;
default:
return 0;
}
case DW_ATE_boolean:
if (size == 1)
return T_BOOL08;
else
return 0;
default:
return 0;
}
}
/* Add a new codeview_custom_type to our singly-linked custom_types list. */
static void
add_custom_type (codeview_custom_type *ct)
{
uint32_t num;
if (last_custom_type)
{
num = last_custom_type->num + 1;
last_custom_type->next = ct;
}
else
{
num = FIRST_TYPE;
custom_types = ct;
}
last_custom_type = ct;
ct->num = num;
}
/* Process a DW_TAG_pointer_type DIE. If this is a pointer to a builtin
type, return the predefined constant for this. Otherwise, add a new
LF_POINTER type and return its number. */
static uint32_t
get_type_num_pointer_type (dw_die_ref type, bool in_struct)
{
uint32_t base_type_num, byte_size;
dw_die_ref base_type;
codeview_custom_type *ct;
byte_size = get_AT_unsigned (type, DW_AT_byte_size);
if (byte_size != 4 && byte_size != 8)
return 0;
base_type = get_AT_ref (type, DW_AT_type);
/* If DW_AT_type is not set, this must be a void pointer. */
if (!base_type)
return byte_size == 4 ? T_32PVOID : T_64PVOID;
base_type_num = get_type_num (base_type, in_struct, false);
if (base_type_num == 0)
return 0;
/* Pointers to builtin types have predefined type numbers, with the top byte
determining the pointer size - 0x0400 for a 32-bit pointer and 0x0600
for 64-bit. */
if (base_type_num < FIRST_TYPE && !(base_type_num & 0xff00))
{
if (byte_size == 4)
return CV_POINTER_32 | base_type_num;
else
return CV_POINTER_64 | base_type_num;
}
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
ct->kind = LF_POINTER;
ct->lf_pointer.base_type = base_type_num;
if (byte_size == 4)
ct->lf_pointer.attributes = CV_PTR_NEAR32;
else
ct->lf_pointer.attributes = CV_PTR_64;
ct->lf_pointer.attributes |= byte_size << 13;
add_custom_type (ct);
return ct->num;
}
/* Process a DW_TAG_reference_type or DW_TAG_rvalue_reference_type DIE, add a
new LF_POINTER type, and return its number. */
static uint32_t
get_type_num_reference_type (dw_die_ref type, bool in_struct, bool rvref)
{
uint32_t base_type_num, byte_size;
dw_die_ref base_type;
codeview_custom_type *ct;
byte_size = get_AT_unsigned (type, DW_AT_byte_size);
if (byte_size != 4 && byte_size != 8)
return 0;
base_type = get_AT_ref (type, DW_AT_type);
base_type_num = get_type_num (base_type, in_struct, false);
if (base_type_num == 0)
return 0;
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
ct->kind = LF_POINTER;
ct->lf_pointer.base_type = base_type_num;
ct->lf_pointer.attributes = rvref ? CV_PTR_MODE_RVREF : CV_PTR_MODE_LVREF;
if (byte_size == 4)
ct->lf_pointer.attributes |= CV_PTR_NEAR32;
else
ct->lf_pointer.attributes |= CV_PTR_64;
ct->lf_pointer.attributes |= byte_size << 13;
add_custom_type (ct);
return ct->num;
}
/* Process a DW_TAG_const_type DIE, adding an LF_MODIFIER type and returning
its number. */
static uint32_t
get_type_num_const_type (dw_die_ref type, bool in_struct)
{
dw_die_ref base_type;
uint32_t base_type_num;
codeview_custom_type *ct;
bool is_volatile = false;
base_type = get_AT_ref (type, DW_AT_type);
/* Handle case when this is a const volatile type - we only need one
LF_MODIFIER for this. */
if (base_type && dw_get_die_tag (base_type) == DW_TAG_volatile_type)
{
is_volatile = true;
base_type = get_AT_ref (base_type, DW_AT_type);
}
if (!base_type)
{
base_type_num = T_VOID;
}
else
{
base_type_num = get_type_num (base_type, in_struct, false);
if (base_type_num == 0)
return 0;
}
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
ct->kind = LF_MODIFIER;
ct->lf_modifier.base_type = base_type_num;
ct->lf_modifier.modifier = MOD_const;
if (is_volatile)
ct->lf_modifier.modifier |= MOD_volatile;
add_custom_type (ct);
return ct->num;
}
/* Process a DW_TAG_volatile_type DIE, adding an LF_MODIFIER type and
returning its number. */
static uint32_t
get_type_num_volatile_type (dw_die_ref type, bool in_struct)
{
dw_die_ref base_type;
uint32_t base_type_num;
codeview_custom_type *ct;
base_type = get_AT_ref (type, DW_AT_type);
if (base_type)
{
base_type_num = get_type_num (base_type, in_struct, false);
if (base_type_num == 0)
return 0;
}
else
{
base_type_num = T_VOID;
}
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
ct->kind = LF_MODIFIER;
ct->lf_modifier.base_type = base_type_num;
ct->lf_modifier.modifier = MOD_volatile;
add_custom_type (ct);
return ct->num;
}
/* Add a forward declaration for an enum. This is legal from C++11 onwards. */
static uint32_t
add_enum_forward_def (dw_die_ref type)
{
codeview_custom_type *ct;
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
ct->kind = LF_ENUM;
ct->lf_enum.count = 0;
ct->lf_enum.properties = CV_PROP_FWDREF;
ct->lf_enum.underlying_type = get_type_num (get_AT_ref (type, DW_AT_type),
false, false);
ct->lf_enum.fieldlist = 0;
ct->lf_enum.name = xstrdup (get_AT_string (type, DW_AT_name));
add_custom_type (ct);
return ct->num;
}
/* Process a DW_TAG_enumeration_type DIE, adding an LF_FIELDLIST and an LF_ENUM
type, returning the number of the latter. */
static uint32_t
get_type_num_enumeration_type (dw_die_ref type, bool in_struct)
{
dw_die_ref first_child;
codeview_custom_type *ct;
uint16_t count = 0;
uint32_t last_type = 0;
if (get_AT_flag (type, DW_AT_declaration))
return add_enum_forward_def (type);
/* First, add an LF_FIELDLIST for the enum's values. We don't need to worry
about deduplication here, as ld will take care of that for us. If there's
a lot of entries, add more LF_FIELDLISTs with LF_INDEXes pointing to
the overflow lists. */
first_child = dw_get_die_child (type);
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
ct->kind = LF_FIELDLIST;
ct->lf_fieldlist.length = 0;
ct->lf_fieldlist.subtypes = NULL;
ct->lf_fieldlist.last_subtype = NULL;
if (first_child)
{
dw_die_ref c;
c = first_child;
do
{
dw_attr_node *att;
codeview_subtype *el;
size_t el_len;
c = dw_get_die_sib (c);
if (dw_get_die_tag (c) != DW_TAG_enumerator)
continue;
att = get_AT (c, DW_AT_const_value);
if (!att)
continue;
el = (codeview_subtype *) xmalloc (sizeof (*el));
el->next = NULL;
el->kind = LF_ENUMERATE;
switch (AT_class (att))
{
case dw_val_class_unsigned_const:
case dw_val_class_unsigned_const_implicit:
el->lf_enumerate.value.neg = false;
el->lf_enumerate.value.num = att->dw_attr_val.v.val_unsigned;
break;
case dw_val_class_const:
case dw_val_class_const_implicit:
if (att->dw_attr_val.v.val_int < 0)
{
el->lf_enumerate.value.neg = true;
el->lf_enumerate.value.num = -att->dw_attr_val.v.val_int;
}
else
{
el->lf_enumerate.value.neg = false;
el->lf_enumerate.value.num = att->dw_attr_val.v.val_int;
}
break;
default:
free (el);
continue;
}
el->lf_enumerate.name = xstrdup (get_AT_string (c, DW_AT_name));
el_len = 7 + strlen (el->lf_enumerate.name);
el_len += cv_integer_len (&el->lf_enumerate.value);
if (el_len % 4)
el_len += 4 - (el_len % 4);
if (ct->lf_fieldlist.length + el_len > MAX_FIELDLIST_SIZE)
{
codeview_subtype *idx;
codeview_custom_type *ct2;
idx = (codeview_subtype *) xmalloc (sizeof (*idx));
idx->next = NULL;
idx->kind = LF_INDEX;
idx->lf_index.type_num = 0;
ct->lf_fieldlist.last_subtype->next = idx;
ct->lf_fieldlist.last_subtype = idx;
ct2 = (codeview_custom_type *)
xmalloc (sizeof (codeview_custom_type));
ct2->next = ct;
ct2->kind = LF_FIELDLIST;
ct2->lf_fieldlist.length = 0;
ct2->lf_fieldlist.subtypes = NULL;
ct2->lf_fieldlist.last_subtype = NULL;
ct = ct2;
}
ct->lf_fieldlist.length += el_len;
if (ct->lf_fieldlist.last_subtype)
ct->lf_fieldlist.last_subtype->next = el;
else
ct->lf_fieldlist.subtypes = el;
ct->lf_fieldlist.last_subtype = el;
count++;
}
while (c != first_child);
}
while (ct)
{
codeview_custom_type *ct2;
ct2 = ct->next;
ct->next = NULL;
if (ct->lf_fieldlist.last_subtype->kind == LF_INDEX)
ct->lf_fieldlist.last_subtype->lf_index.type_num = last_type;
add_custom_type (ct);
last_type = ct->num;
ct = ct2;
}
/* Now add an LF_ENUM, pointing to the LF_FIELDLIST we just added. */
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
ct->kind = LF_ENUM;
ct->lf_enum.count = count;
ct->lf_enum.properties = 0;
ct->lf_enum.underlying_type = get_type_num (get_AT_ref (type, DW_AT_type),
in_struct, false);
ct->lf_enum.fieldlist = last_type;
ct->lf_enum.name = xstrdup (get_AT_string (type, DW_AT_name));
add_custom_type (ct);
return ct->num;
}
/* Add a DIE to our deferred_types list. This happens when we have a struct
with a pointer to a type that hasn't been defined yet, but which gets
defined later on. */
static void
add_deferred_type (dw_die_ref type)
{
codeview_deferred_type *def;
def = (codeview_deferred_type *) xmalloc (sizeof (codeview_deferred_type));
def->next = NULL;
def->type = type;
if (!deferred_types)
deferred_types = def;
else
last_deferred_type->next = def;
last_deferred_type = def;
}
/* Flush the contents of our deferred_types list. This happens after everything
else has been written. We call get_type_num to ensure that a type gets
added to custom_types, if it hasn't been already. */
static void
flush_deferred_types (void)
{
while (deferred_types)
{
codeview_deferred_type *next;
next = deferred_types->next;
get_type_num (deferred_types->type, false, true);
free (deferred_types);
deferred_types = next;
}
last_deferred_type = NULL;
}
/* Add a forward definition for a struct, class, or union. */
static uint32_t
add_struct_forward_def (dw_die_ref type)
{
codeview_custom_type *ct;
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
switch (dw_get_die_tag (type))
{
case DW_TAG_class_type:
ct->kind = LF_CLASS;
break;
case DW_TAG_structure_type:
ct->kind = LF_STRUCTURE;
break;
case DW_TAG_union_type:
ct->kind = LF_UNION;
break;
default:
break;
}
ct->lf_structure.num_members = 0;
ct->lf_structure.properties = CV_PROP_FWDREF;
ct->lf_structure.field_list = 0;
ct->lf_structure.derived_from = 0;
ct->lf_structure.vshape = 0;
ct->lf_structure.length.neg = false;
ct->lf_structure.length.num = 0;
ct->lf_structure.name = xstrdup (get_AT_string (type, DW_AT_name));
add_custom_type (ct);
if (!get_AT_flag (type, DW_AT_declaration))
add_deferred_type (type);
return ct->num;
}
/* Add an LF_BITFIELD type, returning its number. DWARF represents bitfields
as members in a struct with a DW_AT_data_bit_offset attribute, whereas in
CodeView they're a distinct type. */
static uint32_t
create_bitfield (dw_die_ref c)
{
codeview_custom_type *ct;
uint32_t base_type;
base_type = get_type_num (get_AT_ref (c, DW_AT_type), true, false);
if (base_type == 0)
return 0;
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
ct->kind = LF_BITFIELD;
ct->lf_bitfield.base_type = base_type;
ct->lf_bitfield.length = get_AT_unsigned (c, DW_AT_bit_size);
ct->lf_bitfield.position = get_AT_unsigned (c, DW_AT_data_bit_offset);
add_custom_type (ct);
return ct->num;
}
/* Process a DW_TAG_structure_type, DW_TAG_class_type, or DW_TAG_union_type
DIE, add an LF_FIELDLIST and an LF_STRUCTURE / LF_CLASS / LF_UNION type,
and return the number of the latter. */
static uint32_t
get_type_num_struct (dw_die_ref type, bool in_struct, bool *is_fwd_ref)
{
dw_die_ref first_child;
codeview_custom_type *ct;
uint16_t num_members = 0;
uint32_t last_type = 0;
const char *name;
if ((in_struct && get_AT_string (type, DW_AT_name))
|| get_AT_flag (type, DW_AT_declaration))
{
*is_fwd_ref = true;
return add_struct_forward_def (type);
}
*is_fwd_ref = false;
/* First, add an LF_FIELDLIST for the structure's members. We don't need to
worry about deduplication here, as ld will take care of that for us.
If there's a lot of entries, add more LF_FIELDLISTs with LF_INDEXes
pointing to the overflow lists. */
first_child = dw_get_die_child (type);
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
ct->kind = LF_FIELDLIST;
ct->lf_fieldlist.length = 0;
ct->lf_fieldlist.subtypes = NULL;
ct->lf_fieldlist.last_subtype = NULL;
if (first_child)
{
dw_die_ref c;
c = first_child;
do
{
codeview_subtype *el;
size_t el_len;
c = dw_get_die_sib (c);
if (dw_get_die_tag (c) != DW_TAG_member)
continue;
el = (codeview_subtype *) xmalloc (sizeof (*el));
el->next = NULL;
el->kind = LF_MEMBER;
switch (get_AT_unsigned (c, DW_AT_accessibility))
{
case DW_ACCESS_private:
el->lf_member.attributes = CV_ACCESS_PRIVATE;
break;
case DW_ACCESS_protected:
el->lf_member.attributes = CV_ACCESS_PROTECTED;
break;
case DW_ACCESS_public:
el->lf_member.attributes = CV_ACCESS_PUBLIC;
break;
/* Members in a C++ struct or union are public by default, members
in a class are private. */
default:
if (dw_get_die_tag (type) == DW_TAG_class_type)
el->lf_member.attributes = CV_ACCESS_PRIVATE;
else
el->lf_member.attributes = CV_ACCESS_PUBLIC;
break;
}
if (get_AT (c, DW_AT_data_bit_offset))
el->lf_member.type = create_bitfield (c);
else
el->lf_member.type = get_type_num (get_AT_ref (c, DW_AT_type),
true, false);
el->lf_member.offset.neg = false;
el->lf_member.offset.num = get_AT_unsigned (c,
DW_AT_data_member_location);
el_len = 11;
el_len += cv_integer_len (&el->lf_member.offset);
if (get_AT_string (c, DW_AT_name))
{
el->lf_member.name = xstrdup (get_AT_string (c, DW_AT_name));
el_len += strlen (el->lf_member.name);
}
else
{
el->lf_member.name = NULL;
}
if (el_len % 4)
el_len += 4 - (el_len % 4);
/* Add an LF_INDEX subtype if everything's too big for one
LF_FIELDLIST. */
if (ct->lf_fieldlist.length + el_len > MAX_FIELDLIST_SIZE)
{
codeview_subtype *idx;
codeview_custom_type *ct2;
idx = (codeview_subtype *) xmalloc (sizeof (*idx));
idx->next = NULL;
idx->kind = LF_INDEX;
idx->lf_index.type_num = 0;
ct->lf_fieldlist.last_subtype->next = idx;
ct->lf_fieldlist.last_subtype = idx;
ct2 = (codeview_custom_type *)
xmalloc (sizeof (codeview_custom_type));
ct2->next = ct;
ct2->kind = LF_FIELDLIST;
ct2->lf_fieldlist.length = 0;
ct2->lf_fieldlist.subtypes = NULL;
ct2->lf_fieldlist.last_subtype = NULL;
ct = ct2;
}
ct->lf_fieldlist.length += el_len;
if (ct->lf_fieldlist.last_subtype)
ct->lf_fieldlist.last_subtype->next = el;
else
ct->lf_fieldlist.subtypes = el;
ct->lf_fieldlist.last_subtype = el;
num_members++;
}
while (c != first_child);
}
while (ct)
{
codeview_custom_type *ct2;
ct2 = ct->next;
ct->next = NULL;
if (ct->lf_fieldlist.last_subtype
&& ct->lf_fieldlist.last_subtype->kind == LF_INDEX)
{
ct->lf_fieldlist.last_subtype->lf_index.type_num = last_type;
}
add_custom_type (ct);
last_type = ct->num;
ct = ct2;
}
/* Now add an LF_STRUCTURE / LF_CLASS / LF_UNION, pointing to the
LF_FIELDLIST we just added. */
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
switch (dw_get_die_tag (type))
{
case DW_TAG_class_type:
ct->kind = LF_CLASS;
break;
case DW_TAG_structure_type:
ct->kind = LF_STRUCTURE;
break;
case DW_TAG_union_type:
ct->kind = LF_UNION;
break;
default:
break;
}
ct->lf_structure.num_members = num_members;
ct->lf_structure.properties = 0;
ct->lf_structure.field_list = last_type;
ct->lf_structure.derived_from = 0;
ct->lf_structure.vshape = 0;
ct->lf_structure.length.neg = false;
ct->lf_structure.length.num = get_AT_unsigned (type, DW_AT_byte_size);
name = get_AT_string (type, DW_AT_name);
if (name)
ct->lf_structure.name = xstrdup (name);
else
ct->lf_structure.name = NULL;
add_custom_type (ct);
return ct->num;
}
/* Process a DW_TAG_subroutine_type DIE, adding an LF_ARGLIST and an
LF_PROCEDURE type, and returning the number of the latter. */
static uint32_t
get_type_num_subroutine_type (dw_die_ref type, bool in_struct)
{
codeview_custom_type *ct;
uint32_t return_type, arglist_type;
uint16_t num_args;
dw_die_ref first_child;
/* Find the return type. */
if (get_AT_ref (type, DW_AT_type))
{
return_type = get_type_num (get_AT_ref (type, DW_AT_type), in_struct,
false);
if (return_type == 0)
return 0;
}
else
{
return_type = T_VOID;
}
/* Count the arguments. */
first_child = dw_get_die_child (type);
num_args = 0;
if (first_child)
{
dw_die_ref c;
c = first_child;
do
{
c = dw_get_die_sib (c);
if (dw_get_die_tag (c) != DW_TAG_formal_parameter
&& dw_get_die_tag (c) != DW_TAG_unspecified_parameters)
continue;
num_args++;
}
while (c != first_child);
}
/* Create an LF_ARGLIST for the arguments. If this is a duplicate, ld
will take care of this for us. */
first_child = dw_get_die_child (type);
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
ct->kind = LF_ARGLIST;
ct->lf_arglist.num_entries = num_args;
if (num_args > 0)
{
dw_die_ref c;
uint32_t *argptr;
ct->lf_arglist.args = (uint32_t *) xmalloc (sizeof (uint32_t) * num_args);
argptr = ct->lf_arglist.args;
c = first_child;
do
{
c = dw_get_die_sib (c);
switch (dw_get_die_tag (c))
{
case DW_TAG_formal_parameter:
*argptr = get_type_num (get_AT_ref (c, DW_AT_type), in_struct,
false);
argptr++;
break;
case DW_TAG_unspecified_parameters:
*argptr = 0;
argptr++;
break;
default:
break;
}
}
while (c != first_child);
}
else
{
ct->lf_arglist.args = NULL;
}
add_custom_type (ct);
arglist_type = ct->num;
/* Finally, create an LF_PROCEDURE. */
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
ct->kind = LF_PROCEDURE;
ct->lf_procedure.return_type = return_type;
ct->lf_procedure.calling_convention = 0;
ct->lf_procedure.attributes = 0;
ct->lf_procedure.num_parameters = num_args;
ct->lf_procedure.arglist = arglist_type;
add_custom_type (ct);
return ct->num;
}
/* Process a DW_TAG_array_type DIE, adding an LF_ARRAY type and returning its
number. */
static uint32_t
get_type_num_array_type (dw_die_ref type, bool in_struct)
{
dw_die_ref base_type, t, first_child, c, *dimension_arr;
uint64_t size = 0;
unsigned int dimensions, i;
uint32_t element_type;
base_type = get_AT_ref (type, DW_AT_type);
if (!base_type)
return 0;
/* We need to know the size of our base type. Loop through until we find
it. */
t = base_type;
while (t && size == 0)
{
switch (dw_get_die_tag (t))
{
case DW_TAG_const_type:
case DW_TAG_volatile_type:
case DW_TAG_typedef:
case DW_TAG_enumeration_type:
t = get_AT_ref (t, DW_AT_type);
break;
case DW_TAG_base_type:
case DW_TAG_structure_type:
case DW_TAG_class_type:
case DW_TAG_union_type:
case DW_TAG_pointer_type:
case DW_TAG_reference_type:
case DW_TAG_rvalue_reference_type:
size = get_AT_unsigned (t, DW_AT_byte_size);
break;
default:
return 0;
}
}
if (size == 0)
return 0;
first_child = dw_get_die_child (type);
if (!first_child)
return 0;
element_type = get_type_num (base_type, in_struct, false);
if (element_type == 0)
return 0;
/* Create an array of our DW_TAG_subrange_type children, in reverse order.
We have to do this because unlike DWARF CodeView doesn't have
multidimensional arrays, so instead we do arrays of arrays. */
dimensions = 0;
c = first_child;
do
{
c = dw_get_die_sib (c);
if (dw_get_die_tag (c) != DW_TAG_subrange_type)
continue;
dimensions++;
}
while (c != first_child);
if (dimensions == 0)
return 0;
dimension_arr = (dw_die_ref *) xmalloc (sizeof (dw_die_ref) * dimensions);
c = first_child;
i = 0;
do
{
c = dw_get_die_sib (c);
if (dw_get_die_tag (c) != DW_TAG_subrange_type)
continue;
dimension_arr[dimensions - i - 1] = c;
i++;
}
while (c != first_child);
/* Record an LF_ARRAY entry for each array dimension. If this leads to
duplicate types, ld will take care of it for us. */
for (i = 0; i < dimensions; i++)
{
codeview_custom_type *ct;
dw_die_ref index;
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
size *= get_AT_unsigned (dimension_arr[i], DW_AT_upper_bound) + 1;
index = get_AT_ref (dimension_arr[i], DW_AT_type);
ct->next = NULL;
ct->kind = LF_ARRAY;
ct->lf_array.element_type = element_type;
ct->lf_array.index_type = get_type_num (index, in_struct, false);
ct->lf_array.length_in_bytes.neg = false;
ct->lf_array.length_in_bytes.num = size;
add_custom_type (ct);
element_type = ct->num;
}
free (dimension_arr);
return element_type;
}
/* Process a DIE representing a type definition, add a CodeView type if
necessary, and return its number. If it's something we can't handle, return
0. We keep a hash table so that we're not adding the same type multiple
times - though if we do it's not disastrous, as ld will deduplicate
everything for us. */
static uint32_t
get_type_num (dw_die_ref type, bool in_struct, bool no_fwd_ref)
{
codeview_type **slot, *t;
uint32_t num;
bool is_fwd_ref;
if (!type)
return 0;
if (!types_htab)
types_htab = new hash_table (10);
slot = types_htab->find_slot_with_hash (type, htab_hash_pointer (type),
NO_INSERT);
if (slot && *slot && (!no_fwd_ref || !(*slot)->is_fwd_ref))
return (*slot)->num;
is_fwd_ref = false;
switch (dw_get_die_tag (type))
{
case DW_TAG_base_type:
num = get_type_num_base_type (type);
break;
case DW_TAG_typedef:
/* FIXME - signed longs typedef'd as "HRESULT" should get their
own type (T_HRESULT) */
num = get_type_num (get_AT_ref (type, DW_AT_type), in_struct, false);
break;
case DW_TAG_pointer_type:
num = get_type_num_pointer_type (type, in_struct);
break;
case DW_TAG_reference_type:
num = get_type_num_reference_type (type, in_struct, false);
break;
case DW_TAG_rvalue_reference_type:
num = get_type_num_reference_type (type, in_struct, true);
break;
case DW_TAG_const_type:
num = get_type_num_const_type (type, in_struct);
break;
case DW_TAG_volatile_type:
num = get_type_num_volatile_type (type, in_struct);
break;
case DW_TAG_enumeration_type:
num = get_type_num_enumeration_type (type, in_struct);
break;
case DW_TAG_structure_type:
case DW_TAG_class_type:
case DW_TAG_union_type:
num = get_type_num_struct (type, in_struct, &is_fwd_ref);
break;
case DW_TAG_array_type:
num = get_type_num_array_type (type, in_struct);
break;
case DW_TAG_subroutine_type:
num = get_type_num_subroutine_type (type, in_struct);
break;
default:
num = 0;
break;
}
/* Check hash table again, and account for the fact that self-referential
structs will have created a forward reference to themselves. */
slot = types_htab->find_slot_with_hash (type, htab_hash_pointer (type),
INSERT);
if (*slot && (*slot)->is_fwd_ref && !is_fwd_ref)
{
(*slot)->num = num;
(*slot)->is_fwd_ref = false;
return num;
}
t = (codeview_type *) xmalloc (sizeof (codeview_type));
t->die = type;
t->num = num;
t->is_fwd_ref = is_fwd_ref;
*slot = t;
return t->num;
}
/* Process a DW_TAG_variable DIE, and add an S_GDATA32 or S_LDATA32 symbol for
this. */
static void
add_variable (dw_die_ref die)
{
codeview_symbol *s;
const char *name;
name = get_AT_string (die, DW_AT_name);
if (!name)
return;
s = (codeview_symbol *) xmalloc (sizeof (codeview_symbol));
s->next = NULL;
s->kind = get_AT (die, DW_AT_external) ? S_GDATA32 : S_LDATA32;
s->data_symbol.type = get_type_num (get_AT_ref (die, DW_AT_type), false,
false);
s->data_symbol.name = xstrdup (name);
s->data_symbol.die = die;
if (last_sym)
last_sym->next = s;
else
sym = s;
last_sym = s;
}
/* Process a DW_TAG_subprogram DIE, and add an S_GPROC32_ID or S_LPROC32_ID
symbol for this. */
static void
add_function (dw_die_ref die)
{
codeview_custom_type *ct;
const char *name = get_AT_string (die, DW_AT_name);
uint32_t function_type, func_id_type;
codeview_symbol *s;
if (!name)
return;
/* Add an LF_FUNC_ID type for this function. */
function_type = get_type_num_subroutine_type (die, false);
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
ct->kind = LF_FUNC_ID;
ct->lf_func_id.parent_scope = 0;
ct->lf_func_id.function_type = function_type;
ct->lf_func_id.name = xstrdup (name);
add_custom_type (ct);
func_id_type = ct->num;
/* Add an S_GPROC32_ID / S_LPROC32_ID symbol. */
s = (codeview_symbol *) xmalloc (sizeof (codeview_symbol));
s->next = NULL;
s->kind = get_AT (die, DW_AT_external) ? S_GPROC32_ID : S_LPROC32_ID;
s->function.parent = 0;
s->function.end = 0;
s->function.next = 0;
s->function.type = func_id_type;
s->function.flags = 0;
s->function.name = xstrdup (name);
s->function.die = die;
if (last_sym)
last_sym->next = s;
else
sym = s;
last_sym = s;
}
/* Loop through the DIEs that have been output for our TU, and add CodeView
symbols for them. */
void
codeview_debug_early_finish (dw_die_ref die)
{
dw_die_ref first_child, c;
first_child = dw_get_die_child (die);
if (!first_child)
return;
c = first_child;
do
{
switch (dw_get_die_tag (c))
{
case DW_TAG_variable:
add_variable (c);
break;
case DW_TAG_subprogram:
add_function (c);
break;
default:
break;
}
c = dw_get_die_sib (c);
}
while (c != first_child);
flush_deferred_types ();
}
#endif