gcc/gcc/dwarf2codeview.cc
Mark Harmstone f1f8d4c511 Flatten anonymous structs in CodeView types
If a CodeView struct, class, or union has as a member an anonymous
struct, class, or union, this gets flattened. The sub-struct's members
will appear as if they were part of their parent.

For this, we move part of get_type_num_struct into a new function
add_to_fieldlist, which also handles creating an LF_INDEX overflow item
if an LF_FIELDLIST grows too large. This is because add_struct_member
now calls itself recursively, and so needs to handle overflows itself.

gcc/
	* dwarf2codeview.cc (add_to_fieldlist): New function.
	(add_struct_member): Call recursively to flatten structs, and call
	add_to_fieldlist.
	(add_struct_static_member): Call add_to_fieldlist.
	(add_struct_function): Call add_to_fieldlist.
	(add_struct_inheritance): Call add_to_fieldlist.
	(add_struct_nested_type): Call add_to_fieldlist.
	(get_type_num_struct): Move code to add_to_fieldlist, and move
	responsibility for this to subfunctions.
2024-11-19 01:06:38 +00:00

6744 lines
160 KiB
C++

/* 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
<http://www.gnu.org/licenses/>. */
/* 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_MFUNCTION = 0x1009,
LF_ARGLIST = 0x1201,
LF_FIELDLIST = 0x1203,
LF_BITFIELD = 0x1205,
LF_METHODLIST = 0x1206,
LF_BCLASS = 0x1400,
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_STMEMBER = 0x150e,
LF_METHOD = 0x150f,
LF_NESTTYPE = 0x1510,
LF_ONEMETHOD = 0x1511,
LF_FUNC_ID = 0x1601,
LF_MFUNC_ID = 0x1602,
LF_STRING_ID = 0x1605,
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 <struct codeview_string>
{
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 <codeview_type>
{
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
{
uint16_t attributes;
uint32_t type;
char *name;
} lf_static_member;
struct
{
uint16_t method_attribute;
uint32_t method_type;
char *name;
} lf_onemethod;
struct
{
uint16_t count;
uint32_t method_list;
char *name;
} lf_method;
struct
{
uint16_t attributes;
uint32_t base_class_type;
codeview_integer offset;
} lf_bclass;
struct
{
uint32_t type;
char *name;
} lf_nesttype;
};
};
struct lf_methodlist_entry
{
uint16_t method_attribute;
uint32_t method_type;
};
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;
uint32_t containing_class;
uint16_t ptr_to_mem_type;
} 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
{
uint32_t parent_type;
uint32_t function_type;
char *name;
} lf_mfunc_id;
struct
{
uint32_t substring;
char *string;
} lf_string_id;
struct
{
uint32_t return_type;
uint32_t containing_class_type;
uint32_t this_type;
uint8_t calling_convention;
uint8_t attributes;
uint16_t num_parameters;
uint32_t arglist;
int32_t this_adjustment;
} lf_mfunction;
struct
{
unsigned int count;
lf_methodlist_entry *entries;
} lf_methodlist;
};
};
struct codeview_deferred_type
{
struct codeview_deferred_type *next;
dw_die_ref type;
};
struct string_id_hasher : nofree_ptr_hash <struct codeview_custom_type>
{
typedef const char *compare_type;
static hashval_t hash (const codeview_custom_type *x)
{
return htab_hash_string (x->lf_string_id.string);
}
static bool equal (const codeview_custom_type *x, const char *y)
{
return !strcmp (x->lf_string_id.string, y);
}
};
struct codeview_method
{
uint16_t attribute;
uint32_t type;
char *name;
unsigned int count;
struct codeview_method *next;
struct codeview_method *last;
};
struct method_hasher : nofree_ptr_hash <struct codeview_method>
{
typedef const char *compare_type;
static hashval_t hash (const codeview_method *x)
{
return htab_hash_string (x->name);
}
static bool equal (const codeview_method *x, const char *y)
{
return !strcmp (x->name, y);
}
};
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<string_hasher> *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<die_hasher> *types_htab;
static codeview_custom_type *custom_types, *last_custom_type;
static codeview_deferred_type *deferred_types, *last_deferred_type;
static hash_table<string_id_hasher> *string_id_htab;
static uint32_t get_type_num (dw_die_ref type, bool in_struct, bool no_fwd_ref);
static uint32_t get_type_num_subroutine_type (dw_die_ref type, bool in_struct,
uint32_t containing_class_type,
uint32_t this_type,
int32_t this_adjustment);
static void write_cv_padding (size_t padding);
static void flush_deferred_types (void);
/* 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<string_hasher> (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);
}
/* There's no way to mark the range of a static local variable in an optimized
function: there's no S_DEFRANGE_* symbol for this, and you can't have
S_BLOCK32 symbols. So instead we have to loop through after the S_FRAMEPROC
has been written, and write the S_LDATA32s at the end. */
static void
write_optimized_static_local_vars (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
{
c = dw_get_die_sib (c);
switch (dw_get_die_tag (c))
{
case DW_TAG_variable:
{
dw_attr_node *loc;
dw_loc_descr_ref loc_ref;
loc = get_AT (c, DW_AT_location);
if (!loc)
break;
if (loc->dw_attr_val.val_class != dw_val_class_loc)
break;
loc_ref = loc->dw_attr_val.v.val_loc;
if (!loc_ref)
break;
if (loc_ref->dw_loc_opc != DW_OP_addr)
break;
write_local_s_ldata32 (c, loc_ref);
break;
}
case DW_TAG_lexical_block:
write_optimized_static_local_vars (c);
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 ();
write_optimized_static_local_vars (s->function.die);
}
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;
(following only if CV_PTR_MODE_PMEM or CV_PTR_MODE_PMFUNC in attributes)
uint32_t containing_class;
uint16_t ptr_to_mem_type;
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_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);
if ((t->lf_pointer.attributes & CV_PTR_MODE_MASK) == CV_PTR_MODE_PMEM
|| (t->lf_pointer.attributes & CV_PTR_MODE_MASK) == CV_PTR_MODE_PMFUNC)
{
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_pointer.containing_class);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_pointer.ptr_to_mem_type);
putc ('\n', asm_out_file);
write_cv_padding (2);
}
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;
case LF_STMEMBER:
/* This is lf_static_member in binutils and lfSTMember in Microsoft's
cvinfo.h:
struct lf_static_member
{
uint16_t kind;
uint16_t attributes;
uint32_t type;
char name[];
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, LF_STMEMBER);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, v->lf_static_member.attributes);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, v->lf_static_member.type);
putc ('\n', asm_out_file);
name_len = strlen (v->lf_static_member.name) + 1;
ASM_OUTPUT_ASCII (asm_out_file, v->lf_static_member.name, name_len);
leaf_len = 8 + name_len;
write_cv_padding (4 - (leaf_len % 4));
free (v->lf_static_member.name);
break;
case LF_ONEMETHOD:
/* This is lf_onemethod in binutils and lfOneMethod in Microsoft's
cvinfo.h:
struct lf_onemethod
{
uint16_t kind;
uint16_t method_attribute;
uint32_t method_type;
char name[];
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, LF_ONEMETHOD);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, v->lf_onemethod.method_attribute);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, v->lf_onemethod.method_type);
putc ('\n', asm_out_file);
name_len = strlen (v->lf_onemethod.name) + 1;
ASM_OUTPUT_ASCII (asm_out_file, v->lf_onemethod.name, name_len);
leaf_len = 8 + name_len;
write_cv_padding (4 - (leaf_len % 4));
free (v->lf_onemethod.name);
break;
case LF_METHOD:
/* This is lf_method in binutils and lfMethod in Microsoft's
cvinfo.h:
struct lf_method
{
uint16_t kind;
uint16_t count;
uint32_t method_list;
char name[];
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, LF_METHOD);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, v->lf_method.count);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, v->lf_method.method_list);
putc ('\n', asm_out_file);
name_len = strlen (v->lf_method.name) + 1;
ASM_OUTPUT_ASCII (asm_out_file, v->lf_method.name, name_len);
leaf_len = 8 + name_len;
write_cv_padding (4 - (leaf_len % 4));
free (v->lf_method.name);
break;
case LF_BCLASS:
/* This is lf_bclass in binutils and lfBClass in Microsoft's
cvinfo.h:
struct lf_bclass
{
uint16_t kind;
uint16_t attributes;
uint32_t base_class_type;
uint16_t offset;
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, LF_BCLASS);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, v->lf_bclass.attributes);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, v->lf_bclass.base_class_type);
putc ('\n', asm_out_file);
leaf_len = 8 + write_cv_integer (&v->lf_bclass.offset);
write_cv_padding (4 - (leaf_len % 4));
break;
case LF_NESTTYPE:
/* This is lf_nest_type in binutils and lfNestType in Microsoft's
cvinfo.h:
struct lf_nest_type
{
uint16_t kind;
uint16_t padding;
uint32_t type;
char name[];
} ATTRIBUTE_PACKED;
*/
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, LF_NESTTYPE);
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_nesttype.type);
putc ('\n', asm_out_file);
name_len = strlen (v->lf_nesttype.name) + 1;
ASM_OUTPUT_ASCII (asm_out_file, v->lf_nesttype.name, name_len);
leaf_len = 8 + name_len;
write_cv_padding (4 - (leaf_len % 4));
free (v->lf_nesttype.name);
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[] = "<unnamed-tag>";
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[] = "<unnamed-tag>";
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 an LF_MFUNC_ID type, which is the version of LF_FUNC_ID for struct
functions. Instead of an LF_STRING_ID for the parent scope, we write the
type number of the parent struct. */
static void
write_lf_mfunc_id (codeview_custom_type *t)
{
size_t name_len;
/* This is lf_mfunc_id in binutils and lfMFuncId in Microsoft's cvinfo.h:
struct lf_mfunc_id
{
uint16_t size;
uint16_t kind;
uint32_t parent_type;
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_mfunc_id.parent_type);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_mfunc_id.function_type);
putc ('\n', asm_out_file);
name_len = strlen (t->lf_mfunc_id.name) + 1;
ASM_OUTPUT_ASCII (asm_out_file, t->lf_mfunc_id.name, name_len);
write_cv_padding (4 - (name_len % 4));
free (t->lf_mfunc_id.name);
asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
}
/* Write an LF_STRING_ID type, which provides a deduplicated string that other
types can reference. */
static void
write_lf_string_id (codeview_custom_type *t)
{
size_t string_len;
/* This is lf_string_id in binutils and lfStringId in Microsoft's cvinfo.h:
struct lf_string_id
{
uint16_t size;
uint16_t kind;
uint32_t substring;
char string[];
} 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_string_id.substring);
putc ('\n', asm_out_file);
string_len = strlen (t->lf_string_id.string) + 1;
ASM_OUTPUT_ASCII (asm_out_file, t->lf_string_id.string, string_len);
write_cv_padding (4 - (string_len % 4));
free (t->lf_string_id.string);
asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
}
/* Write an LF_MFUNCTION type, representing a member function. This is the
struct-scoped equivalent of the LF_PROCEDURE type. */
static void
write_lf_mfunction (codeview_custom_type *t)
{
/* This is lf_mfunction in binutils and lfMFunc in Microsoft's cvinfo.h:
struct lf_mfunction
{
uint16_t size;
uint16_t kind;
uint32_t return_type;
uint32_t containing_class_type;
uint32_t this_type;
uint8_t calling_convention;
uint8_t attributes;
uint16_t num_parameters;
uint32_t arglist;
int32_t this_adjustment;
} 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_mfunction.return_type);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_mfunction.containing_class_type);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_mfunction.this_type);
putc ('\n', asm_out_file);
fputs (integer_asm_op (1, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_mfunction.calling_convention);
putc ('\n', asm_out_file);
fputs (integer_asm_op (1, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_mfunction.attributes);
putc ('\n', asm_out_file);
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_mfunction.num_parameters);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_mfunction.arglist);
putc ('\n', asm_out_file);
fputs (integer_asm_op (4, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_mfunction.this_adjustment);
putc ('\n', asm_out_file);
asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
}
/* Write an LF_METHODLIST type, which is an array of type numbers for
LF_MFUNCTION types. Overloaded functions are represented by a LF_METHOD
subtype in the field list, which points to a LF_METHODLIST type for the
function's various forms. */
static void
write_lf_methodlist (codeview_custom_type *t)
{
/* This is lf_methodlist in binutils and lMethodList in Microsoft's cvinfo.h:
struct lf_methodlist_entry
{
uint16_t method_attribute;
uint16_t padding;
uint32_t method_type;
} ATTRIBUTE_PACKED;
struct lf_methodlist
{
uint16_t size;
uint16_t kind;
struct lf_methodlist_entry entries[];
} 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);
for (unsigned int i = 0; i < t->lf_methodlist.count; i++)
{
fputs (integer_asm_op (2, false), asm_out_file);
fprint_whex (asm_out_file, t->lf_methodlist.entries[i].method_attribute);
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, t->lf_methodlist.entries[i].method_type);
putc ('\n', asm_out_file);
}
free (t->lf_methodlist.entries);
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;
case LF_MFUNC_ID:
write_lf_mfunc_id (custom_types);
break;
case LF_STRING_ID:
write_lf_string_id (custom_types);
break;
case LF_MFUNCTION:
write_lf_mfunction (custom_types);
break;
case LF_METHODLIST:
write_lf_methodlist (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 we reference a nested struct but not its parent, add_deferred_type
gets called if we create a forward reference for this, even though we've
already flushed this in codeview_debug_early_finish. In this case we will
need to flush this list again. */
flush_deferred_types ();
if (custom_types)
write_custom_types ();
if (types_htab)
delete types_htab;
if (string_id_htab)
delete string_id_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;
}
/* Return the name of a DIE, traversing its parents in order to construct a
C++-style name if necessary. */
static char *
get_name (dw_die_ref die)
{
dw_die_ref decl = get_AT_ref (die, DW_AT_specification);
dw_die_ref parent;
const char *name;
char *str;
size_t len;
static const char anon[] = "<unnamed-tag>";
static const char sep[] = "::";
if (decl)
die = decl;
name = get_AT_string (die, DW_AT_name);
if (!name)
return NULL;
parent = dw_get_die_parent (die);
if (!parent || dw_get_die_tag (parent) == DW_TAG_compile_unit)
return xstrdup (name);
len = strlen (name);
while (parent && dw_get_die_tag (parent) != DW_TAG_compile_unit)
{
const char *ns_name = get_AT_string (parent, DW_AT_name);
len += sizeof (sep) - 1;
if (ns_name)
len += strlen (ns_name);
else
len += sizeof (anon) - 1;
parent = dw_get_die_parent (parent);
}
str = (char *) xmalloc (len + 1);
str[len] = 0;
len -= strlen (name);
memcpy (str + len, name, strlen (name));
parent = dw_get_die_parent (die);
while (parent && dw_get_die_tag (parent) != DW_TAG_compile_unit)
{
const char *ns_name = get_AT_string (parent, DW_AT_name);
len -= sizeof (sep) - 1;
memcpy (str + len, sep, sizeof (sep) - 1);
if (ns_name)
{
len -= strlen (ns_name);
memcpy (str + len, ns_name, strlen (ns_name));
}
else
{
len -= sizeof (anon) - 1;
memcpy (str + len, anon, sizeof (anon) - 1);
}
parent = dw_get_die_parent (parent);
}
return str;
}
/* 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 = get_name (type);
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 = get_name (type);
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 = get_name (type);
add_custom_type (ct);
if (!get_AT_flag (type, DW_AT_declaration))
add_deferred_type (type);
return ct->num;
}
/* Add a new subtype to an LF_FIELDLIST type, and handle overflows if
necessary. */
static void
add_to_fieldlist (codeview_custom_type **ct, uint16_t *num_members,
codeview_subtype *el, size_t el_len)
{
/* 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)++;
}
/* 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;
}
/* Create an LF_MEMBER field list subtype for a struct member, returning its
pointer in el and its size in el_len. */
static void
add_struct_member (dw_die_ref c, uint16_t accessibility,
codeview_custom_type **ct, uint16_t *num_members,
unsigned int base_offset)
{
codeview_subtype *el;
size_t el_len;
dw_die_ref type = get_AT_ref (c, DW_AT_type);
unsigned int offset;
offset = base_offset + get_AT_unsigned (c, DW_AT_data_member_location);
/* If the data member is actually an anonymous struct, class, or union,
follow MSVC by flattening this into its parent. */
if (!get_AT_string (c, DW_AT_name) && type
&& (dw_get_die_tag (type) == DW_TAG_structure_type
|| dw_get_die_tag (type) == DW_TAG_class_type
|| dw_get_die_tag (type) == DW_TAG_union_type))
{
dw_die_ref c2, first_child;
first_child = dw_get_die_child (type);
c2 = first_child;
do
{
c2 = dw_get_die_sib (c2);
if (dw_get_die_tag (c2) == DW_TAG_member)
add_struct_member (c2, accessibility, ct, num_members, offset);
}
while (c2 != first_child);
return;
}
el = (codeview_subtype *) xmalloc (sizeof (*el));
el->next = NULL;
el->kind = LF_MEMBER;
el->lf_member.attributes = accessibility;
if (get_AT (c, DW_AT_data_bit_offset))
el->lf_member.type = create_bitfield (c);
else
el->lf_member.type = get_type_num (type, true, false);
el->lf_member.offset.neg = false;
el->lf_member.offset.num = offset;
el_len = 11 + 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_to_fieldlist (ct, num_members, el, el_len);
}
/* Create an LF_STMEMBER field list subtype for a static struct member,
returning its pointer in el and its size in el_len. */
static void
add_struct_static_member (dw_die_ref c, uint16_t accessibility,
codeview_custom_type **ct, uint16_t *num_members)
{
codeview_subtype *el;
size_t el_len;
el = (codeview_subtype *) xmalloc (sizeof (*el));
el->next = NULL;
el->kind = LF_STMEMBER;
el->lf_static_member.attributes = accessibility;
el->lf_static_member.type = get_type_num (get_AT_ref (c, DW_AT_type),
true, false);
el->lf_static_member.name = xstrdup (get_AT_string (c, DW_AT_name));
el_len = 9 + strlen (el->lf_static_member.name);
if (el_len % 4)
el_len += 4 - (el_len % 4);
add_to_fieldlist (ct, num_members, el, el_len);
}
/* Create a field list subtype for a struct function, returning its pointer in
el and its size in el_len. If the function is not overloaded, create an
LF_ONEMETHOD subtype pointing to the LF_MFUNCTION. Otherwise, add an
LF_METHODLIST type of the function's forms, and create an LF_METHOD subtype
pointing to this. */
static void
add_struct_function (dw_die_ref c, hash_table<method_hasher> *method_htab,
codeview_custom_type **ct, uint16_t *num_members)
{
const char *name = get_AT_string (c, DW_AT_name);
codeview_method **slot, *meth;
codeview_subtype *el;
size_t el_len;
slot = method_htab->find_slot_with_hash (name, htab_hash_string (name),
NO_INSERT);
if (!slot)
return;
meth = *slot;
el = (codeview_subtype *) xmalloc (sizeof (*el));
el->next = NULL;
if (meth->count == 1)
{
el->kind = LF_ONEMETHOD;
el->lf_onemethod.method_attribute = meth->attribute;
el->lf_onemethod.method_type = meth->type;
el->lf_onemethod.name = xstrdup (name);
el_len = 9 + strlen (el->lf_onemethod.name);
}
else
{
codeview_custom_type *ct;
lf_methodlist_entry *ent;
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
ct->kind = LF_METHODLIST;
ct->lf_methodlist.count = meth->count;
ct->lf_methodlist.entries = (lf_methodlist_entry *)
xmalloc (meth->count * sizeof (lf_methodlist_entry));
ent = ct->lf_methodlist.entries;
for (codeview_method *m = meth; m; m = m->next)
{
ent->method_attribute = m->attribute;
ent->method_type = m->type;
ent++;
}
add_custom_type (ct);
el->kind = LF_METHOD;
el->lf_method.count = meth->count;
el->lf_method.method_list = ct->num;
el->lf_method.name = xstrdup (name);
el_len = 9 + strlen (el->lf_method.name);
}
if (el_len % 4)
el_len += 4 - (el_len % 4);
add_to_fieldlist (ct, num_members, el, el_len);
method_htab->remove_elt_with_hash (name, htab_hash_string (name));
while (meth)
{
codeview_method *next = meth->next;
free (meth->name);
free (meth);
meth = next;
}
}
/* Create a field list subtype that records the base class that a struct
inherits from. */
static void
add_struct_inheritance (dw_die_ref c, uint16_t accessibility,
codeview_custom_type **ct, uint16_t *num_members)
{
codeview_subtype *el;
size_t el_len;
/* FIXME: if DW_AT_virtuality is DW_VIRTUALITY_virtual this is a virtual
base class, and we should be issuing an LF_VBCLASS record
instead. */
if (get_AT_unsigned (c, DW_AT_virtuality) == DW_VIRTUALITY_virtual)
return;
el = (codeview_subtype *) xmalloc (sizeof (*el));
el->next = NULL;
el->kind = LF_BCLASS;
el->lf_bclass.attributes = accessibility;
el->lf_bclass.base_class_type = get_type_num (get_AT_ref (c, DW_AT_type),
true, false);
el->lf_bclass.offset.neg = false;
el->lf_bclass.offset.num = get_AT_unsigned (c, DW_AT_data_member_location);
el_len = 10 + cv_integer_len (&el->lf_bclass.offset);
if (el_len % 4)
el_len += 4 - (el_len % 4);
add_to_fieldlist (ct, num_members, el, el_len);
}
/* Create a new LF_MFUNCTION type for a struct function, add it to the
types_htab hash table, and return its type number. */
static uint32_t
get_mfunction_type (dw_die_ref c)
{
uint32_t containing_class_type, this_type, mfunction_type;
dw_die_ref obj_pointer;
codeview_type **slot, *t;
containing_class_type = get_type_num (dw_get_die_parent (c), true, false);
obj_pointer = get_AT_ref (c, DW_AT_object_pointer);
if (obj_pointer && dw_get_die_tag (obj_pointer) == DW_TAG_formal_parameter)
{
this_type = get_type_num (get_AT_ref (obj_pointer, DW_AT_type),
true, false);
}
else
{
this_type = 0;
}
mfunction_type = get_type_num_subroutine_type (c, true, containing_class_type,
this_type, 0);
slot = types_htab->find_slot_with_hash (c, htab_hash_pointer (c), INSERT);
t = (codeview_type *) xmalloc (sizeof (codeview_type));
t->die = c;
t->num = mfunction_type;
t->is_fwd_ref = false;
*slot = t;
return mfunction_type;
}
/* Translate a DWARF DW_AT_accessibility constant into its CodeView
equivalent. If implicit, follow the C++ rules. */
static uint16_t
get_accessibility (dw_die_ref c)
{
switch (get_AT_unsigned (c, DW_AT_accessibility))
{
case DW_ACCESS_private:
return CV_ACCESS_PRIVATE;
case DW_ACCESS_protected:
return CV_ACCESS_PROTECTED;
case DW_ACCESS_public:
return CV_ACCESS_PUBLIC;
/* Members in a C++ struct or union are public by default, members
in a class are private. */
default:
if (dw_get_die_tag (dw_get_die_parent (c)) == DW_TAG_class_type)
return CV_ACCESS_PRIVATE;
else
return CV_ACCESS_PUBLIC;
}
}
/* Returns true if the struct function pointed to by die is an instantiated
template function. These are skipped in CodeView struct definitions, as
otherwise the same type might not be deduplicated across different TUs. */
static bool
is_templated_func (dw_die_ref die)
{
dw_die_ref c = dw_get_die_child (die);
if (!c)
return false;
do
{
c = dw_get_die_sib (c);
if (dw_get_die_tag (c) == DW_TAG_template_type_param)
return true;
}
while (c != dw_get_die_child (die));
return false;
}
/* Create a field list subtype that records that a struct has a nested type
contained within it. */
static void
add_struct_nested_type (dw_die_ref c, codeview_custom_type **ct,
uint16_t *num_members)
{
const char *name = get_AT_string (c, DW_AT_name);
codeview_subtype *el;
size_t name_len, el_len;
if (!name)
return;
name_len = strlen (name);
el = (codeview_subtype *) xmalloc (sizeof (*el));
el->next = NULL;
el->kind = LF_NESTTYPE;
el->lf_nesttype.type = get_type_num (c, true, false);
el->lf_nesttype.name = xstrdup (name);
el_len = 9 + name_len;
if (el_len % 4)
el_len += 4 - (el_len % 4);
add_to_fieldlist (ct, num_members, el, el_len);
}
/* 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 parent, first_child;
codeview_custom_type *ct;
uint16_t num_members = 0;
uint32_t last_type = 0;
parent = dw_get_die_parent(type);
if (parent && (dw_get_die_tag (parent) == DW_TAG_structure_type
|| dw_get_die_tag (parent) == DW_TAG_class_type
|| dw_get_die_tag (parent) == DW_TAG_union_type))
get_type_num (parent, true, false);
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)
{
hash_table<method_hasher> *method_htab = NULL;
dw_die_ref c;
/* First, loop through and record any non-templated member functions.
This is because overloaded and non-overloaded functions are expressed
differently in CodeView, so we need to have a hash table on the name
to know how to record it later on. */
c = first_child;
do
{
c = dw_get_die_sib (c);
if (dw_get_die_tag (c) == DW_TAG_subprogram)
{
const char *name = get_AT_string (c, DW_AT_name);
codeview_method *meth, **slot;
if (is_templated_func (c))
continue;
if (!method_htab)
method_htab = new hash_table<method_hasher> (10);
meth = (codeview_method *) xmalloc (sizeof (*meth));
slot = method_htab->find_slot_with_hash (name,
htab_hash_string (name),
INSERT);
meth->attribute = get_accessibility (c);
if (!get_AT_ref (c, DW_AT_object_pointer))
meth->attribute |= CV_METHOD_STATIC;
meth->type = get_mfunction_type (c);
meth->next = NULL;
if (*slot)
{
if ((*slot)->last)
(*slot)->last->next = meth;
else
(*slot)->next = meth;
(*slot)->last = meth;
(*slot)->count++;
meth->name = NULL;
}
else
{
meth->name = xstrdup (name);
meth->last = NULL;
meth->count = 1;
*slot = meth;
}
}
}
while (c != first_child);
/* Now loop through again and record the actual members. */
c = first_child;
do
{
uint16_t accessibility;
c = dw_get_die_sib (c);
accessibility = get_accessibility (c);
switch (dw_get_die_tag (c))
{
case DW_TAG_member:
add_struct_member (c, accessibility, &ct, &num_members, 0);
break;
case DW_TAG_variable:
add_struct_static_member (c, accessibility, &ct, &num_members);
break;
case DW_TAG_subprogram:
if (!is_templated_func (c))
add_struct_function (c, method_htab, &ct, &num_members);
break;
case DW_TAG_inheritance:
add_struct_inheritance (c, accessibility, &ct, &num_members);
break;
case DW_TAG_structure_type:
case DW_TAG_class_type:
case DW_TAG_union_type:
case DW_TAG_enumeration_type:
add_struct_nested_type (c, &ct, &num_members);
break;
default:
break;
}
}
while (c != first_child);
if (method_htab)
delete method_htab;
}
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);
ct->lf_structure.name = get_name (type);
add_custom_type (ct);
return ct->num;
}
/* Process a DW_TAG_subroutine_type DIE, adding an LF_ARGLIST and an
LF_PROCEDURE or LF_MFUNCTION type, and returning the number of the
latter. */
static uint32_t
get_type_num_subroutine_type (dw_die_ref type, bool in_struct,
uint32_t containing_class_type,
uint32_t this_type, int32_t this_adjustment)
{
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;
}
/* Handle pointer to member function. */
if (containing_class_type == 0)
{
dw_die_ref obj_ptr = get_AT_ref (type, DW_AT_object_pointer);
if (obj_ptr)
{
dw_die_ref obj_ptr_type = get_AT_ref (obj_ptr, DW_AT_type);
if (obj_ptr_type
&& dw_get_die_tag (obj_ptr_type) == DW_TAG_pointer_type)
{
dw_die_ref cont_class = get_AT_ref (obj_ptr_type, DW_AT_type);
if (dw_get_die_tag (cont_class) == DW_TAG_const_type)
cont_class = get_AT_ref (cont_class, DW_AT_type);
containing_class_type = get_type_num (cont_class, in_struct,
false);
this_type = get_type_num (obj_ptr_type, in_struct, false);
}
}
}
/* 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;
/* We ignore "this" params here. */
if (get_AT_flag (c, DW_AT_artificial) != 0)
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);
if (get_AT_flag (c, DW_AT_artificial) != 0)
continue;
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 or LF_MFUNCTION type. */
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
if (containing_class_type != 0)
{
ct->kind = LF_MFUNCTION;
ct->lf_mfunction.return_type = return_type;
ct->lf_mfunction.containing_class_type = containing_class_type;
ct->lf_mfunction.this_type = this_type;
ct->lf_mfunction.calling_convention = 0;
ct->lf_mfunction.attributes = 0;
ct->lf_mfunction.num_parameters = num_args;
ct->lf_mfunction.arglist = arglist_type;
ct->lf_mfunction.this_adjustment = this_adjustment;
}
else
{
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;
}
/* Translate a DW_TAG_ptr_to_member_type DIE, that is a pointer to member
function or field, into an LF_POINTER record. */
static uint32_t
get_type_num_ptr_to_member_type (dw_die_ref type, bool in_struct)
{
uint32_t base_type_num;
uint32_t containing_class;
dw_die_ref base_type;
codeview_custom_type *ct;
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;
containing_class = get_type_num (get_AT_ref (type, DW_AT_containing_type),
in_struct, false);
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 (TARGET_64BIT)
{
ct->lf_pointer.attributes = CV_PTR_64;
ct->lf_pointer.attributes |= 8 << 13;
}
else
{
ct->lf_pointer.attributes = CV_PTR_NEAR32;
ct->lf_pointer.attributes |= 4 << 13;
}
ct->lf_pointer.containing_class = containing_class;
if (base_type && dw_get_die_tag (base_type) == DW_TAG_subroutine_type)
{
ct->lf_pointer.attributes |= CV_PTR_MODE_PMFUNC;
ct->lf_pointer.ptr_to_mem_type = CV_PMTYPE_F_Single;
}
else
{
ct->lf_pointer.attributes |= CV_PTR_MODE_PMEM;
ct->lf_pointer.ptr_to_mem_type = CV_PMTYPE_D_Single;
}
add_custom_type (ct);
return ct->num;
}
/* Return the type number that corresponds to a DW_TAG_typedef DIE: either the
type number of the base type, or follow MSVC in having a special value
for the HRESULT used by COM. */
static uint32_t
get_type_num_typedef (dw_die_ref type, bool in_struct)
{
uint32_t num;
num = get_type_num (get_AT_ref (type, DW_AT_type), in_struct, false);
if (num == T_LONG)
{
const char *name = get_AT_string (type, DW_AT_name);
/* longs typedef'd as "HRESULT" get their own type */
if (name && !strcmp (name, "HRESULT"))
num = T_HRESULT;
}
return num;
}
/* 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<die_hasher> (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:
num = get_type_num_typedef (type, in_struct);
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, 0, 0, 0);
break;
case DW_TAG_ptr_to_member_type:
num = get_type_num_ptr_to_member_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 = get_name (die);
s->data_symbol.die = die;
if (last_sym)
last_sym->next = s;
else
sym = s;
last_sym = s;
}
/* Return the type number of the LF_STRING_ID entry corresponding to the given
string, creating a new one if necessary. */
static uint32_t
add_string_id (const char *s)
{
codeview_custom_type **slot;
codeview_custom_type *ct;
if (!string_id_htab)
string_id_htab = new hash_table<string_id_hasher> (10);
slot = string_id_htab->find_slot_with_hash (s, htab_hash_string (s),
INSERT);
if (*slot)
return (*slot)->num;
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
ct->kind = LF_STRING_ID;
ct->lf_string_id.substring = 0;
ct->lf_string_id.string = xstrdup (s);
add_custom_type (ct);
*slot = ct;
return ct->num;
}
/* Return the type number of the LF_STRING_ID corresponding to the given DIE's
parent, or 0 if it is in the global scope. */
static uint32_t
get_scope_string_id (dw_die_ref die)
{
dw_die_ref decl = get_AT_ref (die, DW_AT_specification);
char *name;
uint32_t ret;
if (decl)
die = decl;
die = dw_get_die_parent (die);
if (!die)
return 0;
if (dw_get_die_tag (die) == DW_TAG_compile_unit)
return 0;
name = get_name (die);
if (!name)
return 0;
ret = add_string_id (name);
free (name);
return ret;
}
/* Add an LF_FUNC_ID type and return its number (see write_lf_func_id). */
static uint32_t
add_lf_func_id (dw_die_ref die, const char *name)
{
uint32_t function_type, scope_type;
codeview_custom_type *ct;
function_type = get_type_num_subroutine_type (die, false, 0, 0, 0);
scope_type = get_scope_string_id (die);
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
ct->kind = LF_FUNC_ID;
ct->lf_func_id.parent_scope = scope_type;
ct->lf_func_id.function_type = function_type;
ct->lf_func_id.name = xstrdup (name);
add_custom_type (ct);
return ct->num;
}
/* Add an LF_MFUNC_ID type and return its number (see write_lf_mfunc_id). */
static uint32_t
add_lf_mfunc_id (dw_die_ref die, const char *name)
{
uint32_t function_type = 0, parent_type;
codeview_custom_type *ct;
dw_die_ref spec = get_AT_ref (die, DW_AT_specification);
parent_type = get_type_num (dw_get_die_parent (spec), false, false);
if (types_htab)
{
codeview_type **slot;
slot = types_htab->find_slot_with_hash (spec, htab_hash_pointer (spec),
NO_INSERT);
if (slot && *slot)
function_type = (*slot)->num;
}
if (function_type == 0)
{
function_type = get_type_num_subroutine_type (die, false, parent_type,
0, 0);
}
ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
ct->next = NULL;
ct->kind = LF_MFUNC_ID;
ct->lf_mfunc_id.parent_type = parent_type;
ct->lf_mfunc_id.function_type = function_type;
ct->lf_mfunc_id.name = xstrdup (name);
add_custom_type (ct);
return ct->num;
}
/* 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)
{
const char *name = get_AT_string (die, DW_AT_name);
uint32_t func_id_type;
codeview_symbol *s;
dw_die_ref spec = get_AT_ref (die, DW_AT_specification);
bool do_mfunc_id = false;
if (!name)
return;
if (spec && dw_get_die_parent (spec))
{
switch (dw_get_die_tag (dw_get_die_parent (spec)))
{
case DW_TAG_class_type:
case DW_TAG_structure_type:
case DW_TAG_union_type:
do_mfunc_id = true;
break;
default:
break;
}
}
if (do_mfunc_id)
func_id_type = add_lf_mfunc_id (die, name);
else
func_id_type = add_lf_func_id (die, name);
/* 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 = get_name (die);
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