1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
|
#!/usr/bin/env python3
# Copyright (c) Meta Platforms, Inc. and affiliates.
# SPDX-License-Identifier: LGPL-2.1-or-later
import argparse
import keyword
import re
import sys
from typing import Dict, List, NamedTuple, Sequence, TextIO, cast
class DwarfConstant(NamedTuple):
name: str
value: int
class DwarfConstantType(NamedTuple):
name: str
constants: Sequence[DwarfConstant]
def parse_dwarf_constants(f: TextIO) -> Sequence[DwarfConstantType]:
types: Dict[str, Dict[str, int]] = {
type_name: {}
for type_name in (
"DW_ACCESS",
"DW_ADDR",
"DW_AT",
"DW_ATE",
"DW_CC",
"DW_CFA",
"DW_CHILDREN",
"DW_DEFAULTED",
"DW_DS",
"DW_DSC",
"DW_EH_PE",
"DW_END",
"DW_FORM",
"DW_ID",
"DW_IDX",
"DW_INL",
"DW_LANG",
"DW_LLE",
"DW_LNCT",
"DW_LNE",
"DW_LNS",
"DW_MACINFO",
"DW_MACRO",
"DW_OP",
"DW_ORD",
"DW_RLE",
"DW_SECT",
"DW_TAG",
"DW_UT",
"DW_VIRTUALITY",
"DW_VIS",
)
}
for match in re.finditer(
r"^\s*#\s*define\s+(" + "|".join(types) + r")_(\w+)\s+(\S+)",
sys.stdin.read(),
flags=re.MULTILINE,
):
type_name = match.group(1)
name = match.group(2)
value = int(match.group(3), 0)
if (type_name, name) in {
# Typos in the wild that libdwarf includes but we don't want.
("DW_AT", "stride"), # "DWARF3 (do not use)"
("DW_CFA", "low_user"), # "Incorrect spelling, do not use"
("DW_TAG", "namelist_items"), # "SGI misspelling/typo"
("DW_TAG", "template_type_param"), # "DWARF2 inconsistent"
("DW_TAG", "template_value_param"), # "DWARF2 inconsistent"
# libdwarf probably included this one to be consistent with the
# standard DWARF template_foo_parameter names, but it's called
# DW_TAG_GNU_template_template_param everywhere else.
("DW_TAG", "GNU_template_template_parameter"),
# This name isn't mentioned in any version of the DWARF standard.
("DW_CFA", "extended"),
}:
continue
# Typos in libdwarf itself.
elif (type_name, name) == ("DW_CFA", "high_user"):
name = "hi_user"
elif (type_name, name) == ("DW_IDX", "hi_user"):
value = 0x3FFF
elif (type_name, name) == ("DW_LANG", "Haskel"):
name = "Haskell"
if types[type_name].setdefault(name, value) != value:
raise ValueError(f"{type_name}_{name} redefined with different value")
result = [
DwarfConstantType(
name=type_name,
constants=[DwarfConstant(name, value) for name, value in constants.items()],
)
for type_name, constants in types.items()
]
def insert_after(
type_name: str, after_name: str, insert_constant: DwarfConstant
) -> None:
for constant_type in result:
if constant_type.name == type_name:
break
else:
raise ValueError()
constants = cast(List[DwarfConstant], constant_type.constants)
for i, constant in enumerate(constants):
if constant.name == after_name:
break
else:
raise ValueError()
constants.insert(i + 1, insert_constant)
insert_after("DW_EH_PE", "sdata8", DwarfConstant("signed", 0x8))
insert_after("DW_EH_PE", "aligned", DwarfConstant("indirect", 0x80))
return result
_DWARF_CONSTANTS_WANT_STR = {"DW_OP", "DW_TAG"}
def gen_dwarf_constants_h(
dwarf_constants: Sequence[DwarfConstantType], f: TextIO
) -> None:
f.write(
"""\
// Copyright (c) Meta Platforms, Inc. and affiliates.
// SPDX-License-Identifier: LGPL-2.1-or-later
// Generated by scripts/gen_dwarf_constants.py.
/**
* @file
*
* DWARF constant definitions.
*
* This file defines the following for each known DWARF constant type:
*
* 1. An X macro defining all of the known names and values of the type:
* `DW_FOO_DEFINITIONS`.
* 2. Enumerators defining the constants: `DW_FOO_a`, `DW_FOO_b`, etc.
* 3. For select types, a function to translate a value to its name:
* `dw_foo_str()`.
*/
#ifndef DWARF_CONSTANTS_H
#define DWARF_CONSTANTS_H
#define X(name, value) name = value,
"""
)
for constant_type in dwarf_constants:
f.write(
f"""
#define {constant_type.name}_DEFINITIONS \\
"""
)
for i, constant in enumerate(constant_type.constants):
end = " \\" if i < len(constant_type.constants) - 1 else ""
f.write(
f"\tX({constant_type.name}_{constant.name}, 0x{constant.value:x}){end}\n"
)
f.write(f"enum {{ {constant_type.name}_DEFINITIONS }};\n")
if constant_type.name in _DWARF_CONSTANTS_WANT_STR:
f.write(
f"""\
#define {constant_type.name}_STR_UNKNOWN_FORMAT "{constant_type.name}_<0x%x>"
#define {constant_type.name}_STR_BUF_LEN (sizeof({constant_type.name}_STR_UNKNOWN_FORMAT) - 2 + 2 * sizeof(int))
/**
* Get the name of a `{constant_type.name}` value.
*
* @return Static string if the value is known or @p buf if the value is
* unknown.
*/
const char *{constant_type.name.lower()}_str(int value, char buf[static {constant_type.name}_STR_BUF_LEN]);
"""
)
f.write(
"""
#undef X
#endif /* DWARF_CONSTANTS_H */
"""
)
def gen_dwarf_constants_c(
dwarf_constants: Sequence[DwarfConstantType], f: TextIO
) -> None:
f.write(
"""\
// Copyright (c) Meta Platforms, Inc. and affiliates.
// SPDX-License-Identifier: LGPL-2.1-or-later
// Generated by scripts/gen_dwarf_constants.py.
#include <stdio.h>
#include "dwarf_constants.h"
#define X(name, _) if (value == name) return #name;
"""
)
for constant_type in dwarf_constants:
if constant_type.name in _DWARF_CONSTANTS_WANT_STR:
f.write(
f"""
const char *{constant_type.name.lower()}_str(int value, char buf[static {constant_type.name}_STR_BUF_LEN])
{{
{constant_type.name}_DEFINITIONS
snprintf(buf, {constant_type.name}_STR_BUF_LEN, {constant_type.name}_STR_UNKNOWN_FORMAT, value);
return buf;
}}
"""
)
f.write(
"""
#undef X
"""
)
def gen_tests_dwarf_py(dwarf_constants: Sequence[DwarfConstantType], f: TextIO) -> None:
f.write(
"""\
# Copyright (c) Meta Platforms, Inc. and affiliates.
# SPDX-License-Identifier: LGPL-2.1-or-later
# Generated by scripts/gen_dwarf_constants.py.
import enum
"""
)
for constant_type in dwarf_constants:
f.write(f"\n\nclass {constant_type.name}(enum.IntEnum):\n")
for constant in constant_type.constants:
name = constant.name
if keyword.iskeyword(name):
name += "_"
f.write(f" {name} = 0x{constant.value:X}")
if name == "name":
f.write(" # type: ignore")
f.write("\n")
def main() -> None:
argparse.ArgumentParser(
description="Generate libdrgn/dwarf_constants.h, libdrgn/dwarf_constants.c, and tests/dwarf.py from libdwarf/src/lib/libdwarf/dwarf.h (read from standard input)"
).parse_args()
dwarf_constants = parse_dwarf_constants(sys.stdin)
with open("libdrgn/dwarf_constants.h", "w") as f:
gen_dwarf_constants_h(dwarf_constants, f)
with open("libdrgn/dwarf_constants.c", "w") as f:
gen_dwarf_constants_c(dwarf_constants, f)
with open("tests/dwarf.py", "w") as f:
gen_tests_dwarf_py(dwarf_constants, f)
if __name__ == "__main__":
main()
|