binutils-gdb/gdb/gdbarch.py

395 lines
15 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
# Architecture commands for GDB, the GNU debugger.
#
# Copyright (C) 1998-2024 Free Software Foundation, Inc.
#
# This file is part of GDB.
#
# This program 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 of the License, or
# (at your option) any later version.
#
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
import textwrap
# gdbarch_components is imported only for its side-effect of filling
# `gdbarch_types.components`.
import gdbarch_components # noqa: F401 # type: ignore
import gdbcopyright
from gdbarch_types import Component, Function, Info, Value, components
def indentation(n_columns: int):
"""Return string with tabs and spaces to indent line to N_COLUMNS."""
return "\t" * (n_columns // 8) + " " * (n_columns % 8)
copyright = gdbcopyright.copyright(
"gdbarch.py", "Dynamic architecture support for GDB, the GNU debugger."
)
def info(c: Component):
"Filter function to only allow Info components."
return type(c) is Info
def not_info(c: Component):
"Filter function to omit Info components."
return type(c) is not Info
with open("gdbarch-gen.h", "w") as f:
print(copyright, file=f)
print(file=f)
print(file=f)
print(file=f)
print("/* The following are pre-initialized by GDBARCH. */", file=f)
# Do Info components first.
for c in filter(info, components):
print(file=f)
print(
f"""extern {c.type} gdbarch_{c.name} (struct gdbarch *gdbarch);
/* set_gdbarch_{c.name}() - not applicable - pre-initialized. */""",
file=f,
)
print(file=f)
print(file=f)
print("/* The following are initialized by the target dependent code. */", file=f)
# Generate decls for accessors, setters, and predicates for all
# non-Info components.
for c in filter(not_info, components):
if c.comment:
print(file=f)
comment = c.comment.split("\n")
if comment[0] == "":
comment = comment[1:]
if comment[-1] == "":
comment = comment[:-1]
print("/* ", file=f, end="")
print(comment[0], file=f, end="")
if len(comment) > 1:
print(file=f)
print(
textwrap.indent("\n".join(comment[1:]), prefix=" "),
end="",
file=f,
)
print(" */", file=f)
if c.predicate:
print(file=f)
print(f"extern bool gdbarch_{c.name}_p (struct gdbarch *gdbarch);", file=f)
print(file=f)
if isinstance(c, Value):
print(
f"extern {c.type} gdbarch_{c.name} (struct gdbarch *gdbarch);",
file=f,
)
print(
f"extern void set_gdbarch_{c.name} (struct gdbarch *gdbarch, {c.type} {c.name});",
file=f,
)
else:
assert isinstance(c, Function)
print(
f"typedef {c.type} ({c.ftype()}) ({c.param_list()});",
file=f,
)
if c.implement:
print(
f"extern {c.type} gdbarch_{c.name} ({c.set_list()});",
file=f,
)
print(
f"extern void set_gdbarch_{c.name} (struct gdbarch *gdbarch, {c.ftype()} *{c.name});",
file=f,
)
with open("gdbarch.c", "w") as f:
print(copyright, file=f)
print(file=f)
print(file=f)
print("/* Maintain the struct gdbarch object. */", file=f)
print(file=f)
#
# The struct definition body.
#
print("struct gdbarch", file=f)
print("{", file=f)
print(" /* Has this architecture been fully initialized? */", file=f)
print(" bool initialized_p = false;", file=f)
print(file=f)
print(" /* An obstack bound to the lifetime of the architecture. */", file=f)
print(" auto_obstack obstack;", file=f)
print(" /* Registry. */", file=f)
print(" registry<gdbarch> registry_fields;", file=f)
print(file=f)
print(" /* basic architectural information. */", file=f)
for c in filter(info, components):
print(f" {c.type} {c.name};", file=f)
print(file=f)
print(" /* target specific vector. */", file=f)
print(" gdbarch_tdep_up tdep;", file=f)
print(" gdbarch_dump_tdep_ftype *dump_tdep = nullptr;", file=f)
print(file=f)
for c in filter(not_info, components):
if isinstance(c, Function):
print(f" gdbarch_{c.name}_ftype *", file=f, end="")
else:
print(f" {c.type} ", file=f, end="")
print(f"{c.name} = ", file=f, end="")
if c.predefault is not None:
print(f"{c.predefault};", file=f)
elif isinstance(c, Value):
print("0;", file=f)
else:
assert isinstance(c, Function)
print("nullptr;", file=f)
print("};", file=f)
print(file=f)
#
# Initialization.
#
print("/* Create a new ``struct gdbarch'' based on information provided by", file=f)
print(" ``struct gdbarch_info''. */", file=f)
print(file=f)
print("struct gdbarch *", file=f)
print("gdbarch_alloc (const struct gdbarch_info *info,", file=f)
print(" gdbarch_tdep_up tdep)", file=f)
print("{", file=f)
print(" struct gdbarch *gdbarch;", file=f)
print("", file=f)
print(" gdbarch = new struct gdbarch;", file=f)
print(file=f)
print(" gdbarch->tdep = std::move (tdep);", file=f)
print(file=f)
for c in filter(info, components):
print(f" gdbarch->{c.name} = info->{c.name};", file=f)
print(file=f)
print(" return gdbarch;", file=f)
print("}", file=f)
print(file=f)
print(file=f)
print(file=f)
#
# Post-initialization validation and updating
#
print("/* Ensure that all values in a GDBARCH are reasonable. */", file=f)
print(file=f)
print("static void", file=f)
print("verify_gdbarch (struct gdbarch *gdbarch)", file=f)
print("{", file=f)
print(" string_file log;", file=f)
print(file=f)
print(" /* fundamental */", file=f)
print(" if (gdbarch->byte_order == BFD_ENDIAN_UNKNOWN)", file=f)
print(""" log.puts ("\\n\\tbyte-order");""", file=f)
print(" if (gdbarch->bfd_arch_info == NULL)", file=f)
print(""" log.puts ("\\n\\tbfd_arch_info");""", file=f)
print(
" /* Check those that need to be defined for the given multi-arch level. */",
file=f,
)
for c in filter(not_info, components):
# An opportunity to write in the 'postdefault' value. We
# change field's value to the postdefault if its current value
# is not different to the initial value of the field.
if c.postdefault is not None:
init_value = c.predefault or "0"
print(f" if (gdbarch->{c.name} == {init_value})", file=f)
print(f" gdbarch->{c.name} = {c.postdefault};", file=f)
# Now validate the value.
gdb/gdbarch: remove the 'invalid=None' state from gdbarch_components.py This commit ensures that the 'invalid' property of all components is either True, False, or a string. Additionally, this commit allows a component to have both a predicate and for the 'invalid' property to be a string. Removing the option for 'invalid' to be None allows us to simplify the algorithms in gdbarch.py a little. Allowing a component to have both a predicate and an 'invalid' string means that we can validate the value that a tdep sets into a field, but also allow a predicate to ensure that the field has changed from the default. This functionality isn't going to be used in this series, but I have tested it locally and believe that it would work, and this might make it easier for others to add new components in the future. In gdbarch_types.py, I've updated the type annotations to show that the 'invalid' field should not be None, and I've changed the default for this field from None to False. The change to using False as the default is temporary. Later in this series I'm going to change the default to True, but we need more fixes before that can be done. Additionally, in gdbarch_types.py I've removed an assert from Component.get_predicate. This assert ensured that we didn't have the predicate field set to True and the invalid field set to a string. However, no component currently uses this configuration, and after this commit, that combination is now supported, so the assert can be removed. As a consequence of the gdbarch_types.py changes we see some additional comments generated in gdbarch.c about verification being skipped due to the invalid field being False. This comment is inline with plenty of other getters that also have a similar comment. Plenty of the getters do have validation, so I think it is reasonable to have a comment noting that the validation has been skipped for a specific reason, rather than due to some bug. In gdbarch_components.py I've had to add 'invalid=True' for two components: gcore_bfd_target and max_insn_length, without this the validation in the gdbarch getter would disappear. And in gdbarch.py I've reworked the logic for generating the verify_gdbarch function, and for generating the getter functions. The logic for generating the getter functions is still not ideal, I ended up having to add this additional logic block: elif c.postdefault is not None and c.predefault is not None: print(" /* Check variable changed from pre-default. */", file=f) print(f" gdb_assert (gdbarch->{c.name} != {c.predefault});", file=f) which was needed to ensure we continued to generate the same code as before, without this the fact that invalid is now False when it would previously have been None, meant that we dropped the gdb_assert in favour of a comment like: print(f" /* Skip verify of {c.name}, invalid_p == 0 */", file=f) which is clearly not a good change. We could potentially look at improving this in a later commit, but I don't plan to do that in this series. Approved-By: Simon Marchi <simon.marchi@efficios.com>
2023-03-06 22:39:28 +08:00
if isinstance(c.invalid, str):
print(f" if ({c.invalid})", file=f)
print(f""" log.puts ("\\n\\t{c.name}");""", file=f)
gdb/gdbarch: remove the 'invalid=None' state from gdbarch_components.py This commit ensures that the 'invalid' property of all components is either True, False, or a string. Additionally, this commit allows a component to have both a predicate and for the 'invalid' property to be a string. Removing the option for 'invalid' to be None allows us to simplify the algorithms in gdbarch.py a little. Allowing a component to have both a predicate and an 'invalid' string means that we can validate the value that a tdep sets into a field, but also allow a predicate to ensure that the field has changed from the default. This functionality isn't going to be used in this series, but I have tested it locally and believe that it would work, and this might make it easier for others to add new components in the future. In gdbarch_types.py, I've updated the type annotations to show that the 'invalid' field should not be None, and I've changed the default for this field from None to False. The change to using False as the default is temporary. Later in this series I'm going to change the default to True, but we need more fixes before that can be done. Additionally, in gdbarch_types.py I've removed an assert from Component.get_predicate. This assert ensured that we didn't have the predicate field set to True and the invalid field set to a string. However, no component currently uses this configuration, and after this commit, that combination is now supported, so the assert can be removed. As a consequence of the gdbarch_types.py changes we see some additional comments generated in gdbarch.c about verification being skipped due to the invalid field being False. This comment is inline with plenty of other getters that also have a similar comment. Plenty of the getters do have validation, so I think it is reasonable to have a comment noting that the validation has been skipped for a specific reason, rather than due to some bug. In gdbarch_components.py I've had to add 'invalid=True' for two components: gcore_bfd_target and max_insn_length, without this the validation in the gdbarch getter would disappear. And in gdbarch.py I've reworked the logic for generating the verify_gdbarch function, and for generating the getter functions. The logic for generating the getter functions is still not ideal, I ended up having to add this additional logic block: elif c.postdefault is not None and c.predefault is not None: print(" /* Check variable changed from pre-default. */", file=f) print(f" gdb_assert (gdbarch->{c.name} != {c.predefault});", file=f) which was needed to ensure we continued to generate the same code as before, without this the fact that invalid is now False when it would previously have been None, meant that we dropped the gdb_assert in favour of a comment like: print(f" /* Skip verify of {c.name}, invalid_p == 0 */", file=f) which is clearly not a good change. We could potentially look at improving this in a later commit, but I don't plan to do that in this series. Approved-By: Simon Marchi <simon.marchi@efficios.com>
2023-03-06 22:39:28 +08:00
elif c.predicate:
print(f" /* Skip verify of {c.name}, has predicate. */", file=f)
elif c.invalid:
if c.postdefault is not None:
# This component has its 'invalid' field set to True, but
# also has a postdefault. This makes no sense, the
# postdefault will have been applied above, so this field
# will not have a zero value.
raise Exception(
f"component {c.name} has postdefault and invalid set to True"
)
else:
init_value = c.predefault or "0"
print(f" if (gdbarch->{c.name} == {init_value})", file=f)
print(f""" log.puts ("\\n\\t{c.name}");""", file=f)
gdb/gdbarch: compare some fields against 0 verify_gdbarch After the previous commit, which removes the predicate function gdbarch_register_type_p, I noticed that the gdbarch->register_type field was not checked at in the verify_gdbarch function. More than not being checked, the field wasn't mentioned at all. I find this strange, I would expect that every field would at least be mentioned - we already generate comments for some fields saying that this field is _not_ being checked, so the fact that this field isn't being checked looks (to me), like this field is somehow slipping through the cracks. The comment at the top of gdbarch-components.py tries to explain how the validation is done. I didn't understand this comment completely, but, I think this final sentence: "Otherwise, the check is done against 0 (really NULL for function pointers, but same idea)." Means that, if non of the other cases apply, then the field should be checked against 0, with 0 indicating that the field is invalid (was not set by the tdep code). However, this is clearly not being done. Looking in gdbarch.py at the code to generate verify_gdbarch we do find that there is a case that is not handled, the case where the 'invalid' field is set true True, but non of the other cases apply. In this commit I propose two changes: 1. Handle the case where the 'invalid' field of a property is set to True, this should perform a check for the field of gdbarch still being set to 0, and 2. If the if/else series that generates verify_gdbarch doesn't handle a property then we should raise an exception. This means that if a property is added which isn't handled, we should no longer silently ignore it. After doing this, I re-generated the gdbarch files and saw that the following gdbarch fields now had new validation checks: register_type believe_pcc_promotion register_to_value value_to_register frame_red_zone_size displaced_step_restore_all_in_ptid solib_symbols_extension Looking at how these are currently set in the various -tdep.c files, I believe the only one of these that is required to be set for all architectures is the register_type field. And so, for all of the other fields, I've changed the property definition on gdbarch-components.py, setting the 'invalid' field to False. Now, after re-generation, the register_type field is checked against 0, thus an architecture that doesn't set_gdbarch_register_type will now fail during validation. For all the other fields we skip the validation, in which case, it is find for an architecture to not set this field. My expectation is that there should be no user visible changes after this commit. Certainly for all fields except register_type, all I've really done is cause some extra comments to be generated, so I think that's clearly fine. For the register_type field, my claim is that any architecture that didn't provide this would fail when creating its register cache, and I couldn't spot an architecture that doesn't provide this hook. As such, I think this change should be fine too.
2022-03-10 19:18:18 +08:00
else:
gdb/gdbarch: remove the 'invalid=None' state from gdbarch_components.py This commit ensures that the 'invalid' property of all components is either True, False, or a string. Additionally, this commit allows a component to have both a predicate and for the 'invalid' property to be a string. Removing the option for 'invalid' to be None allows us to simplify the algorithms in gdbarch.py a little. Allowing a component to have both a predicate and an 'invalid' string means that we can validate the value that a tdep sets into a field, but also allow a predicate to ensure that the field has changed from the default. This functionality isn't going to be used in this series, but I have tested it locally and believe that it would work, and this might make it easier for others to add new components in the future. In gdbarch_types.py, I've updated the type annotations to show that the 'invalid' field should not be None, and I've changed the default for this field from None to False. The change to using False as the default is temporary. Later in this series I'm going to change the default to True, but we need more fixes before that can be done. Additionally, in gdbarch_types.py I've removed an assert from Component.get_predicate. This assert ensured that we didn't have the predicate field set to True and the invalid field set to a string. However, no component currently uses this configuration, and after this commit, that combination is now supported, so the assert can be removed. As a consequence of the gdbarch_types.py changes we see some additional comments generated in gdbarch.c about verification being skipped due to the invalid field being False. This comment is inline with plenty of other getters that also have a similar comment. Plenty of the getters do have validation, so I think it is reasonable to have a comment noting that the validation has been skipped for a specific reason, rather than due to some bug. In gdbarch_components.py I've had to add 'invalid=True' for two components: gcore_bfd_target and max_insn_length, without this the validation in the gdbarch getter would disappear. And in gdbarch.py I've reworked the logic for generating the verify_gdbarch function, and for generating the getter functions. The logic for generating the getter functions is still not ideal, I ended up having to add this additional logic block: elif c.postdefault is not None and c.predefault is not None: print(" /* Check variable changed from pre-default. */", file=f) print(f" gdb_assert (gdbarch->{c.name} != {c.predefault});", file=f) which was needed to ensure we continued to generate the same code as before, without this the fact that invalid is now False when it would previously have been None, meant that we dropped the gdb_assert in favour of a comment like: print(f" /* Skip verify of {c.name}, invalid_p == 0 */", file=f) which is clearly not a good change. We could potentially look at improving this in a later commit, but I don't plan to do that in this series. Approved-By: Simon Marchi <simon.marchi@efficios.com>
2023-03-06 22:39:28 +08:00
print(f" /* Skip verify of {c.name}, invalid_p == 0 */", file=f)
print(" if (!log.empty ())", file=f)
print(
""" internal_error (_("verify_gdbarch: the following are invalid ...%s"),""",
file=f,
)
print(" log.c_str ());", file=f)
print("}", file=f)
print(file=f)
print(file=f)
#
# Dumping.
#
print("/* Print out the details of the current architecture. */", file=f)
print(file=f)
print("void", file=f)
print("gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)", file=f)
print("{", file=f)
print(""" const char *gdb_nm_file = "<not-defined>";""", file=f)
print(file=f)
print("#if defined (GDB_NM_FILE)", file=f)
print(" gdb_nm_file = GDB_NM_FILE;", file=f)
print("#endif", file=f)
print(" gdb_printf (file,", file=f)
print(""" "gdbarch_dump: GDB_NM_FILE = %s\\n",""", file=f)
print(" gdb_nm_file);", file=f)
for c in components:
if c.predicate:
print(" gdb_printf (file,", file=f)
print(
f""" "gdbarch_dump: gdbarch_{c.name}_p() = %d\\n",""",
file=f,
)
print(f" gdbarch_{c.name}_p (gdbarch));", file=f)
if isinstance(c, Function):
print(" gdb_printf (file,", file=f)
print(f""" "gdbarch_dump: {c.name} = <%s>\\n",""", file=f)
print(
f" host_address_to_string (gdbarch->{c.name}));",
file=f,
)
else:
if c.printer:
printer = c.printer
elif c.type == "CORE_ADDR":
printer = f"core_addr_to_string_nz (gdbarch->{c.name})"
else:
printer = f"plongest (gdbarch->{c.name})"
print(" gdb_printf (file,", file=f)
print(f""" "gdbarch_dump: {c.name} = %s\\n",""", file=f)
print(f" {printer});", file=f)
print(" if (gdbarch->dump_tdep != NULL)", file=f)
print(" gdbarch->dump_tdep (gdbarch, file);", file=f)
print("}", file=f)
print(file=f)
#
# Bodies of setter, accessor, and predicate functions.
#
for c in components:
if c.predicate:
print(file=f)
print("bool", file=f)
print(f"gdbarch_{c.name}_p (struct gdbarch *gdbarch)", file=f)
print("{", file=f)
print(" gdb_assert (gdbarch != NULL);", file=f)
print(f" return {c.get_predicate()};", file=f)
print("}", file=f)
if isinstance(c, Function):
if c.implement:
print(file=f)
print(f"{c.type}", file=f)
print(f"gdbarch_{c.name} ({c.set_list()})", file=f)
print("{", file=f)
print(" gdb_assert (gdbarch != NULL);", file=f)
print(f" gdb_assert (gdbarch->{c.name} != NULL);", file=f)
if c.predicate and c.predefault:
# Allow a call to a function with a predicate.
print(
f" /* Do not check predicate: {c.get_predicate()}, allow call. */",
file=f,
)
if c.param_checks:
for rule in c.param_checks:
print(f" gdb_assert ({rule});", file=f)
print(" if (gdbarch_debug >= 2)", file=f)
print(
f""" gdb_printf (gdb_stdlog, "gdbarch_{c.name} called\\n");""",
file=f,
)
print(" ", file=f, end="")
if c.type != "void":
if c.result_checks:
print("auto result = ", file=f, end="")
else:
print("return ", file=f, end="")
print(f"gdbarch->{c.name} ({c.actuals()});", file=f)
if c.type != "void" and c.result_checks:
for rule in c.result_checks:
print(f" gdb_assert ({rule});", file=f)
print(" return result;", file=f)
print("}", file=f)
print(file=f)
print("void", file=f)
setter_name = f"set_gdbarch_{c.name}"
ftype_name = f"gdbarch_{c.name}_ftype"
print(f"{setter_name} (struct gdbarch *gdbarch,", file=f)
indent_columns = len(f"{setter_name} (")
print(f"{indentation(indent_columns)}{ftype_name} {c.name})", file=f)
print("{", file=f)
print(f" gdbarch->{c.name} = {c.name};", file=f)
print("}", file=f)
elif isinstance(c, Value):
print(file=f)
print(f"{c.type}", file=f)
print(f"gdbarch_{c.name} (struct gdbarch *gdbarch)", file=f)
print("{", file=f)
print(" gdb_assert (gdbarch != NULL);", file=f)
gdb/gdbarch: remove the 'invalid=None' state from gdbarch_components.py This commit ensures that the 'invalid' property of all components is either True, False, or a string. Additionally, this commit allows a component to have both a predicate and for the 'invalid' property to be a string. Removing the option for 'invalid' to be None allows us to simplify the algorithms in gdbarch.py a little. Allowing a component to have both a predicate and an 'invalid' string means that we can validate the value that a tdep sets into a field, but also allow a predicate to ensure that the field has changed from the default. This functionality isn't going to be used in this series, but I have tested it locally and believe that it would work, and this might make it easier for others to add new components in the future. In gdbarch_types.py, I've updated the type annotations to show that the 'invalid' field should not be None, and I've changed the default for this field from None to False. The change to using False as the default is temporary. Later in this series I'm going to change the default to True, but we need more fixes before that can be done. Additionally, in gdbarch_types.py I've removed an assert from Component.get_predicate. This assert ensured that we didn't have the predicate field set to True and the invalid field set to a string. However, no component currently uses this configuration, and after this commit, that combination is now supported, so the assert can be removed. As a consequence of the gdbarch_types.py changes we see some additional comments generated in gdbarch.c about verification being skipped due to the invalid field being False. This comment is inline with plenty of other getters that also have a similar comment. Plenty of the getters do have validation, so I think it is reasonable to have a comment noting that the validation has been skipped for a specific reason, rather than due to some bug. In gdbarch_components.py I've had to add 'invalid=True' for two components: gcore_bfd_target and max_insn_length, without this the validation in the gdbarch getter would disappear. And in gdbarch.py I've reworked the logic for generating the verify_gdbarch function, and for generating the getter functions. The logic for generating the getter functions is still not ideal, I ended up having to add this additional logic block: elif c.postdefault is not None and c.predefault is not None: print(" /* Check variable changed from pre-default. */", file=f) print(f" gdb_assert (gdbarch->{c.name} != {c.predefault});", file=f) which was needed to ensure we continued to generate the same code as before, without this the fact that invalid is now False when it would previously have been None, meant that we dropped the gdb_assert in favour of a comment like: print(f" /* Skip verify of {c.name}, invalid_p == 0 */", file=f) which is clearly not a good change. We could potentially look at improving this in a later commit, but I don't plan to do that in this series. Approved-By: Simon Marchi <simon.marchi@efficios.com>
2023-03-06 22:39:28 +08:00
if isinstance(c.invalid, str):
print(" /* Check variable is valid. */", file=f)
print(f" gdb_assert (!({c.invalid}));", file=f)
elif c.predicate:
print(" /* Check predicate was used. */", file=f)
print(f" gdb_assert (gdbarch_{c.name}_p (gdbarch));", file=f)
elif c.invalid or c.postdefault is not None:
init_value = c.predefault or "0"
print(" /* Check variable changed from its initial value. */", file=f)
print(f" gdb_assert (gdbarch->{c.name} != {init_value});", file=f)
gdb/gdbarch: remove the 'invalid=None' state from gdbarch_components.py This commit ensures that the 'invalid' property of all components is either True, False, or a string. Additionally, this commit allows a component to have both a predicate and for the 'invalid' property to be a string. Removing the option for 'invalid' to be None allows us to simplify the algorithms in gdbarch.py a little. Allowing a component to have both a predicate and an 'invalid' string means that we can validate the value that a tdep sets into a field, but also allow a predicate to ensure that the field has changed from the default. This functionality isn't going to be used in this series, but I have tested it locally and believe that it would work, and this might make it easier for others to add new components in the future. In gdbarch_types.py, I've updated the type annotations to show that the 'invalid' field should not be None, and I've changed the default for this field from None to False. The change to using False as the default is temporary. Later in this series I'm going to change the default to True, but we need more fixes before that can be done. Additionally, in gdbarch_types.py I've removed an assert from Component.get_predicate. This assert ensured that we didn't have the predicate field set to True and the invalid field set to a string. However, no component currently uses this configuration, and after this commit, that combination is now supported, so the assert can be removed. As a consequence of the gdbarch_types.py changes we see some additional comments generated in gdbarch.c about verification being skipped due to the invalid field being False. This comment is inline with plenty of other getters that also have a similar comment. Plenty of the getters do have validation, so I think it is reasonable to have a comment noting that the validation has been skipped for a specific reason, rather than due to some bug. In gdbarch_components.py I've had to add 'invalid=True' for two components: gcore_bfd_target and max_insn_length, without this the validation in the gdbarch getter would disappear. And in gdbarch.py I've reworked the logic for generating the verify_gdbarch function, and for generating the getter functions. The logic for generating the getter functions is still not ideal, I ended up having to add this additional logic block: elif c.postdefault is not None and c.predefault is not None: print(" /* Check variable changed from pre-default. */", file=f) print(f" gdb_assert (gdbarch->{c.name} != {c.predefault});", file=f) which was needed to ensure we continued to generate the same code as before, without this the fact that invalid is now False when it would previously have been None, meant that we dropped the gdb_assert in favour of a comment like: print(f" /* Skip verify of {c.name}, invalid_p == 0 */", file=f) which is clearly not a good change. We could potentially look at improving this in a later commit, but I don't plan to do that in this series. Approved-By: Simon Marchi <simon.marchi@efficios.com>
2023-03-06 22:39:28 +08:00
else:
print(f" /* Skip verify of {c.name}, invalid_p == 0 */", file=f)
print(" if (gdbarch_debug >= 2)", file=f)
print(
f""" gdb_printf (gdb_stdlog, "gdbarch_{c.name} called\\n");""",
file=f,
)
print(f" return gdbarch->{c.name};", file=f)
print("}", file=f)
print(file=f)
print("void", file=f)
setter_name = f"set_gdbarch_{c.name}"
print(f"{setter_name} (struct gdbarch *gdbarch,", file=f)
indent_columns = len(f"{setter_name} (")
print(f"{indentation(indent_columns)}{c.type} {c.name})", file=f)
print("{", file=f)
print(f" gdbarch->{c.name} = {c.name};", file=f)
print("}", file=f)
else:
assert isinstance(c, Info)
print(file=f)
print(f"{c.type}", file=f)
print(f"gdbarch_{c.name} (struct gdbarch *gdbarch)", file=f)
print("{", file=f)
print(" gdb_assert (gdbarch != NULL);", file=f)
print(" if (gdbarch_debug >= 2)", file=f)
print(
f""" gdb_printf (gdb_stdlog, "gdbarch_{c.name} called\\n");""",
file=f,
)
print(f" return gdbarch->{c.name};", file=f)
print("}", file=f)