Skip to content
Snippets Groups Projects
  1. Mar 03, 2023
  2. Jan 20, 2023
    • Simon Marchi's avatar
      gdb: remove language.h include from frame.h · 83b6e1f1
      Simon Marchi authored
      
      This helps resolve some cyclic include problem later in the series.
      The only language-related thing frame.h needs is enum language, and that
      is in defs.h.
      
      Doing so reveals that a bunch of files were relying on frame.h to
      include language.h, so fix the fallouts here and there.
      
      Change-Id: I178a7efec1953c2d088adb58483bade1f349b705
      Reviewed-By: default avatarBruno Larsen <blarsen@redhat.com>
      83b6e1f1
  3. Jan 01, 2023
  4. Nov 28, 2022
    • Andrew Burgess's avatar
      gdb/disasm: mark functions passed to the disassembler noexcept · 8eb7d135
      Andrew Burgess authored
      While working on another patch, Simon pointed out that GDB could be
      improved by marking the functions passed to the disassembler as
      noexcept.
      
        https://sourceware.org/pipermail/gdb-patches/2022-October/193084.html
      
      The reason this is important is the on some hosts, libopcodes, being C
      code, will not be compiled with support for handling exceptions.  As
      such, an attempt to throw an exception over libopcodes code will cause
      GDB to terminate.
      
      See bug gdb/29712 for an example of when this happened.
      
      In this commit all the functions that are passed to the disassembler,
      and which might be used as callbacks by libopcodes are marked
      noexcept.
      
      Ideally, I would have liked to change these typedefs:
      
        using read_memory_ftype = decltype (disassemble_info::read_memory_func);
        using memory_error_ftype = decltype (disassemble_info::memory_error_func);
        using print_address_ftype = decltype (disassemble_info::print_address_func);
        using fprintf_ftype = decltype (disassemble_info::fprintf_func);
        using fprintf_styled_ftype = decltype (disassemble_info::fprintf_styled_func);
      
      which are declared in disasm.h, as including the noexcept keyword.
      However, when I tried this, I ran into this warning/error:
      
        In file included from ../../src/gdb/disasm.c:25:
        ../../src/gdb/disasm.h: In constructor ‘gdb_printing_disassembler::gdb_printing_disassembler(gdbarch*, ui_file*, gdb_disassemble_info::read_memory_ftype, gdb_disassemble_info::memory_error_ftype, gdb_disassemble_info::print_address_ftype)’:
        ../../src/gdb/disasm.h:116:3: error: mangled name for ‘gdb_printing_disassembler::gdb_printing_disassembler(gdbarch*, ui_file*, gdb_disassemble_info::read_memory_ftype, gdb_disassemble_info::memory_error_ftype, gdb_disassemble_info::print_address_ftype)’ will change in C++17 because the exception specification is part of a function type [-Werror=noexcept-type]
          116 |   gdb_printing_disassembler (struct gdbarch *gdbarch,
              |   ^~~~~~~~~~~~~~~~~~~~~~~~~
      
      So I've left that change out.  This does mean that if somebody adds a
      new use of the disassembler classes in the future, and forgets to mark
      the callbacks as noexcept, this will compile fine.  We'll just have to
      manually check for that during review.
      8eb7d135
    • Andrew Burgess's avatar
      gdb/python: avoid throwing an exception over libopcodes code · 65639fcc
      Andrew Burgess authored
      Bug gdb/29712 identifies a problem with the Python disassembler API.
      In some cases GDB will try to throw an exception through the
      libopcodes disassembler code, however, not all targets include
      exception unwind information when compiling C code, for targets that
      don't include this information GDB will terminate when trying to pass
      the exception through libopcodes.
      
      To explain what GDB is trying to do, consider the following trivial
      use of the Python disassembler API:
      
        class ExampleDisassembler(gdb.disassembler.Disassembler):
      
            class MyInfo(gdb.disassembler.DisassembleInfo):
                def __init__(self, info):
                    super().__init__(info)
      
                def read_memory(self, length, offset):
                    return super().read_memory(length, offset)
      
            def __init__(self):
                super().__init__("ExampleDisassembler")
      
            def __call__(self, info):
                info = self.MyInfo(info)
                return gdb.disassembler.builtin_disassemble(info)
      
      This disassembler doesn't add any value, it defers back to GDB to do
      all the actual work, but it serves to allow us to discuss the problem.
      
      The problem occurs when a Python exception is raised by the
      MyInfo.read_memory method.  The MyInfo.read_memory method is called
      from the C++ function gdbpy_disassembler::read_memory_func.  The C++
      stack at the point this function is called looks like this:
      
        #0  gdbpy_disassembler::read_memory_func (memaddr=4198805, buff=0x7fff9ab9d2a8 "\220ӹ\232\377\177", len=1, info=0x7fff9ab9d558) at ../../src/gdb/python/py-disasm.c:510
        #1  0x000000000104ba06 in fetch_data (info=0x7fff9ab9d558, addr=0x7fff9ab9d2a9 "ӹ\232\377\177") at ../../src/opcodes/i386-dis.c:305
        #2  0x000000000104badb in ckprefix (ins=0x7fff9ab9d100) at ../../src/opcodes/i386-dis.c:8571
        #3  0x000000000104e28e in print_insn (pc=4198805, info=0x7fff9ab9d558, intel_syntax=-1) at ../../src/opcodes/i386-dis.c:9548
        #4  0x000000000104f4d4 in print_insn_i386 (pc=4198805, info=0x7fff9ab9d558) at ../../src/opcodes/i386-dis.c:9949
        #5  0x00000000004fa7ea in default_print_insn (memaddr=4198805, info=0x7fff9ab9d558) at ../../src/gdb/arch-utils.c:1033
        #6  0x000000000094fe5e in i386_print_insn (pc=4198805, info=0x7fff9ab9d558) at ../../src/gdb/i386-tdep.c:4072
        #7  0x0000000000503d49 in gdbarch_print_insn (gdbarch=0x5335560, vma=4198805, info=0x7fff9ab9d558) at ../../src/gdb/gdbarch.c:3351
        #8  0x0000000000bcc8c6 in disasmpy_builtin_disassemble (self=0x7f2ab07f54d0, args=0x7f2ab0789790, kw=0x0) at ../../src/gdb/python/py-disasm.c:324
      
        ### ... snip lots of frames as we pass through Python itself ...
      
        #22 0x0000000000bcd860 in gdbpy_print_insn (gdbarch=0x5335560, memaddr=0x401195, info=0x7fff9ab9e3c8) at ../../src/gdb/python/py-disasm.c:783
        #23 0x00000000008995a5 in ext_lang_print_insn (gdbarch=0x5335560, address=0x401195, info=0x7fff9ab9e3c8) at ../../src/gdb/extension.c:939
        #24 0x0000000000741aaa in gdb_print_insn_1 (gdbarch=0x5335560, vma=0x401195, info=0x7fff9ab9e3c8) at ../../src/gdb/disasm.c:1078
        #25 0x0000000000741bab in gdb_disassembler::print_insn (this=0x7fff9ab9e3c0, memaddr=0x401195, branch_delay_insns=0x0) at ../../src/gdb/disasm.c:1101
      
      So gdbpy_disassembler::read_memory_func is called from the libopcodes
      disassembler to read memory, this C++ function then calls into user
      supplied Python code to do the work.
      
      If the user supplied Python code raises an gdb.MemoryError exception
      indicating the memory read failed, this is fine.  The C++ code
      converts this exception back into a return value that libopcodes can
      understand, and returns to libopcodes.
      
      However, if the user supplied Python code raises some other exception,
      what we want is for this exception to propagate through GDB and appear
      as if raised by the call to gdb.disassembler.builtin_disassemble.  To
      achieve this, when gdbpy_disassembler::read_memory_func spots an
      unknown Python exception, we must pass the information about this
      exception from frame #0 to frame #8 in the above backtrace.  Frame #8
      is the C++ implementation of gdb.disassembler.builtin_disassemble, and
      so it is this function that we want to re-raise the unknown Python
      exception, so the user can, if they want, catch the exception in their
      code.
      
      The previous mechanism by which the exception was passed was to pack
      the details of the Python exception into a C++ exception, then throw
      the exception from frame #0, and catch the exception in frame #8,
      unpack the details of the Python exception, and re-raise it.
      
      However, this relies on the exception passing through frames #1 to #7,
      some of which are in libopcodes, which is C code, and so, might not be
      compiled with exception support.
      
      This commit proposes an alternative solution that does not rely on
      throwing a C++ exception.
      
      When we spot an unhandled Python exception in frame #0, we will store
      the details of this exception within the gdbpy_disassembler object
      currently in use.  Then we return to libopcodes a value indicating
      that the memory_read failed.
      
      libopcodes will now continue to disassemble as though that memory read
      failed (with one special case described below), then, when we
      eventually return to disasmpy_builtin_disassemble we check to see if
      there is an exception stored in the gdbpy_disassembler object.  If
      there is then this exception can immediately be installed, and then we
      return back to Python, when the user will be able to catch the
      exception.
      
      There is one extra change in gdbpy_disassembler::read_memory_func.
      After the first call that results in an exception being stored on the
      gdbpy_disassembler object, any future calls to the ::read_memory_func
      function will immediately return as if the read failed.  This avoids
      any additional calls into user supplied Python code.
      
      My thinking here is that should the first call fail with some unknown
      error, GDB should not keep trying with any additional calls.  This
      maintains the illusion that the exception raised from
      MyInfo.read_memory is immediately raised by
      gdb.disassembler.builtin_disassemble.  I have no tests for this change
      though - to trigger this issue would rely on a libopcodes disassembler
      that will try to read further memory even after the first failed
      read.  I'm not aware of any such disassembler that currently does
      this, but that doesn't mean such a disassembler couldn't exist in the
      future.
      
      With this change in place the gdb.python/py-disasm.exp test should now
      pass on AArch64.
      
      Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=29712
      
      
      
      Approved-By: default avatarSimon Marchi <simon.marchi@efficios.com>
      65639fcc
  5. Jul 25, 2022
    • Andrew Burgess's avatar
      gdb/python: fix invalid use disassemble_info::stream · 23aa2bef
      Andrew Burgess authored
      After this commit:
      
        commit 81384924
        Date:   Tue Apr 5 11:06:16 2022 +0100
      
            gdb: have gdb_disassemble_info carry 'this' in its stream pointer
      
      The disassemble_info::stream field will no longer be a ui_file*.  That
      commit failed to update one location in py-disasm.c though.
      
      While running some tests using the Python disassembler API, I
      triggered a call to gdbpy_disassembler::print_address_func, and, as I
      had compiled GDB with the undefined behaviour sanitizer, GDB crashed
      as the code currently (incorrectly) casts the stream field to be a
      ui_file*.
      
      In this commit I fix this error.
      
      In order to test this case I had to tweak the existing test case a
      little.  I also spotted some debug printf statements in py-disasm.py,
      which I have removed.
      23aa2bef
  6. Jun 15, 2022
    • Andrew Burgess's avatar
      gdb/python: implement the print_insn extension language hook · 15e15b2d
      Andrew Burgess authored
      This commit extends the Python API to include disassembler support.
      
      The motivation for this commit was to provide an API by which the user
      could write Python scripts that would augment the output of the
      disassembler.
      
      To achieve this I have followed the model of the existing libopcodes
      disassembler, that is, instructions are disassembled one by one.  This
      does restrict the type of things that it is possible to do from a
      Python script, i.e. all additional output has to fit on a single line,
      but this was all I needed, and creating something more complex would,
      I think, require greater changes to how GDB's internal disassembler
      operates.
      
      The disassembler API is contained in the new gdb.disassembler module,
      which defines the following classes:
      
        DisassembleInfo
      
            Similar to libopcodes disassemble_info structure, has read-only
        properties: address, architecture, and progspace.  And has methods:
        __init__, read_memory, and is_valid.
      
            Each time GDB wants an instruction disassembled, an instance of
        this class is passed to a user written disassembler function, by
        reading the properties, and calling the methods (and other support
        methods in the gdb.disassembler module) the user can perform and
        return the disassembly.
      
        Disassembler
      
            This is a base-class which user written disassemblers should
        inherit from.  This base class provides base implementations of
        __init__ and __call__ which the user written disassembler should
        override.
      
        DisassemblerResult
      
            This class can be used to hold the result of a call to the
        disassembler, it's really just a wrapper around a string (the text
        of the disassembled instruction) and a length (in bytes).  The user
        can return an instance of this class from Disassembler.__call__ to
        represent the newly disassembled instruction.
      
      The gdb.disassembler module also provides the following functions:
      
        register_disassembler
      
            This function registers an instance of a Disassembler sub-class
        as a disassembler, either for one specific architecture, or, as a
        global disassembler for all architectures.
      
        builtin_disassemble
      
            This provides access to GDB's builtin disassembler.  A common
        use case that I see is augmenting the existing disassembler output.
        The user code can call this function to have GDB disassemble the
        instruction in the normal way.  The user gets back a
        DisassemblerResult object, which they can then read in order to
        augment the disassembler output in any way they wish.
      
            This function also provides a mechanism to intercept the
        disassemblers reads of memory, thus the user can adjust what GDB
        sees when it is disassembling.
      
      The included documentation provides a more detailed description of the
      API.
      
      There is also a new CLI command added:
      
        maint info python-disassemblers
      
      This command is defined in the Python gdb.disassemblers module, and
      can be used to list the currently registered Python disassemblers.
      15e15b2d
Loading