Compare commits

...

21 Commits

Author SHA1 Message Date
Jan Vrany
02b804035a gdb/python: add section in documentation on implementing JIT interface
This commit adds new section - JIT Interface in Python - outlining how
to use Python APIs introduced in previous commits to implement simple
JIT interface. It also adds new test to make sure the example code is
up-to-date.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2024-11-21 13:52:21 +00:00
Jan Vrany
171a85e261 gdb/python: allow instantiation of gdb.LineTable objects
This commit allows users to instantiate gdb.LineTable objects.
This is a step towards a Python support for dynamically generated code
(JIT) in GDB.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2024-11-21 13:52:21 +00:00
Jan Vrany
b09d63cdcc gdb/python: allow instantiation of gdb.LineTableEntry objects
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2024-11-21 13:52:21 +00:00
Jan Vrany
6b8c17b729 gdb/python: add more attributes to gdb.LinetableEntry objects
This commit adds is_stmt, prologue_end and epilogue_begin attributes
to linetable entry objects.

This prompted change in gdb.Linetable.line() (ltpy_get_pcs_for_line).
In order to fill initialize new attributes we need complete entries
matching the line, not only PCs.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2024-11-21 13:52:21 +00:00
Jan Vrany
917d0f3f6b gdb/python: add add_symbol () method to gdb.Block
This commit adds new method add_symbol () to gdb.Block objects.
A typical use of it is to add previously instantiated gdb.Symbol object
to block when interfacing with JIT compiler.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2024-11-21 13:52:21 +00:00
Jan Vrany
265779df94 gdb/python: allow instantiation of gdb.Symbol from Python
This commit adds code to allow user extension to instantiate
gdb.Symbol.

As of now only "function" symbols can be created (that is: symbols
of FUNCTION_DOMAIN and with address class LOC_BLOCK). This is enough
to be able to implement "JIT reader" equivalent in Python. Future
commits may extend this API to allow creation of other kinds of symbols
(static variables, arguments, locals and so on).

Like previous similar commits, this is a step towards a Python support
for dynamically generated code (JIT) in GDB.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2024-11-21 13:52:21 +00:00
Jan Vrany
3150cb09a4 gdb/python: allow instantiation of gdb.Block from Python
This commit adds code to allow user extension to instantiate
gdb.Block. This is a step towards a Python support for dynamically
generated code (JIT) in GDB.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2024-11-21 13:52:21 +00:00
Jan Vrany
2034719b46 gdb/python: allow instantiation of gdb.Symtab from Python
This commit adds code to allow user extension to instantiate
gdb.Symtab. This is a step towards a Python support for dynamically
generated code (JIT) in GDB.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2024-11-21 13:52:21 +00:00
Jan Vrany
79c9dc182d gdb/python: allow instantiation of gdb.Compunit from Python
This commit adds code to allow user extension to instantiate
gdb.Compunit. This is a step towards a Python support for dynamically
generated code (JIT) in GDB.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2024-11-21 13:52:21 +00:00
Jan Vrany
b0307d2ae4 gdb/python: add unlink () method to gdb.Objfile object
This commit adds method allowing one remove any objfile. This is meant
to be used to remove objfiles for dynamic code when this dynamic code
is discarded. However gdb.Objfile.unlink() makes no attempt to ensure
this - to make it consistent with other Python API to create and modify
objfiles related structures (compunits, symbol tables and so on).

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2024-11-21 13:52:21 +00:00
Jan Vrany
8adaaeb6ad gdb/python: allow instantiation of gdb.Objfile from Python
This commit adds code to allow user extension to instantiate
gdb.Objfile. This is a step towards a Python support for dynamically
generated code (JIT) in GDB.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2024-11-21 13:52:19 +00:00
Jan Vrany
840a8c0eee gdb/python: add gdb.Compunit
This commit introduces gdb.Compunit - a representation of struct
compunit_symtab in Python.

It also adds method gdb.Objfile.compunits() to get a list of compunits
for an objfile and adds compunit attribute to gdb.Block and gdb.Symtab
to access compunit containing given block or symbol table.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2024-11-21 13:49:11 +00:00
Jan Vrany
42373e5864 gdb: use std::vector<> to hold on blocks in struct blockvector
This commit changes internal implementation of struct blockvector to use
std::vector<> rather than flexible array.

The main motivation for this change is to simplify adding blocks to
existing symtab. This feature will be used later by Python API to build
objfiles, compunits and symtabs dynamically (similarly to JIT reader
API).

To do so, this commit

 1. introduces obstack_allocator, an implementation of Allocator
    concept that allocates memory on obstack.
 2. uses std::vector<> with the above allocator to hold on blocks
 3. updates users.

As a side-effect of this change, blockvectors allocated in mdebugread.c
are obstack-allocated rather than xzalloc()ated which seems to be the
correct thing to do. Also, code got simpler.

The downside is higher memory consumption. The size of std::vector with
obstack allocator is 32 bytes (GCC 14) compared to 8 bytes used
currently to store the number of blocks (m_num_blocks). Stopping gdb at
its main(), followed by "maint expand-symtabs" results in 4593
compunit symtabs so in this case the overhead is 24*4593 = 110232 bytes
which I hope is acceptable.

Maybe more concerning is the fact that one may waste obstack memory when
excessively adding blocks. However, blockvectors are not added blocks
after initial allocation at the moment (except in mdebugread.c) so this
is not a problem for existing code. To to mitigate this issue code
allocating may capacity - a number of blocks the blockvector may hold
without reallocating.
2024-11-21 13:49:11 +00:00
Jan Vrany
f44e78463d gdb/python: make gdb.Symtab comparable for equality
Like previous patch, but for gdb.Symtab.
2024-11-21 13:49:11 +00:00
Jan Vrany
329c759de0 gdb/python: make gdb.Symbol comparable for equality
In subsequent patches, it will be useful to be able to compare two
gdb.Symbols. This patch makes gdb.Symbols equal iff both refer to the
same underlying struct symtab.
2024-11-21 13:49:11 +00:00
Jan Vrany
e8f5211fe5 gdb/python: add template function to implement equality comparison
This commit adds gdbpy_richcompare template to implement "default"
equality and non-equality comparison for GDB Python objects.

The "default" behavior is to consider two GDB Python objects as equal if
both refer to the same underlying GDB structure.
2024-11-21 13:49:11 +00:00
Jan Vrany
f350e1aba5 gdb/python: add function () method to gdb.Type object
This commit adds a new method to Python type objects that returns
possibly new function type returning that type. Parameter types can
be specified too.

This will be useful later to create types for function symbols created
using Python extension code.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2024-11-21 13:49:08 +00:00
Jan Vrany
418d86ec68 gdb/python: add void_type () method to gdb.Architecture object
This commit adds a new method to Python architecture objects that
returns a void type for that architecture.

This will be useful later to create types for function symbols created
using Python extension code.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2024-11-21 12:31:20 +00:00
Jan Vrany
6153dea2fb gdb/python: add domain property to gdb.Symbol
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2024-11-21 12:31:20 +00:00
Jan Vrany
d3561c0b69 gdb/python: add subblocks property to gdb.Block
This commit adds new propery "subblocks" to gdb.Block objects. This
allows Python to traverse block tree starting with global block.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2024-11-21 12:31:20 +00:00
Jan Vrany
0d2f828428 gdb: update is_addr_in_objfile to support "dynamic" objfiles
While working with objfiles in Python I noticed that
gdb.Progspace.objfile_for_address () does not return "dynamic" objfile
create by (for example) GDB's JIT reader API.

This is because is_addr_in_objfile() checks if given address falls into
any (mappped) section of that objfile. However objfiles created by JIT
reader API do not have sections.

To solve this issue, this commit updates is_addr_in_objfile() to also
check if address fall into any compunit if that objfile. It does so only
if objfile has no sections.
2024-11-21 12:31:20 +00:00
34 changed files with 2482 additions and 86 deletions

View File

@@ -438,6 +438,7 @@ SUBDIR_PYTHON_SRCS = \
python/py-value.c \
python/py-varobj.c \
python/py-xmethods.c \
python/py-compunit.c \
python/python.c
SUBDIR_PYTHON_OBS = $(patsubst %.c,%.o,$(SUBDIR_PYTHON_SRCS))

View File

@@ -83,6 +83,23 @@
** The 'qualified' argument to gdb.Breakpoint constructor will no
longer accept non-bool types.
** Added gdb.Block.subblocks. Returns a list of blocks contained in that
block.
** Added gdb.Symbol.domain. Contains the domain of the symbol.
** Added gdb.Architecture.void_type. Returns a gdb.Type representing "void"
type for that architecture.
** Added gdb.Type.function. Returns a new gdb.Type representing a function
returning that type. Parameter types can be specified too.
** Added class gdb.Compunit.
** Extended the Python API to allow interfacing with JIT compilers using
Python (as an alternative to JIT reader API). For details, see Section
"JIT Interface in Python" in GDB documentation.
* Debugger Adapter Protocol changes
** The "scopes" request will now return a scope holding global

View File

@@ -17,6 +17,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <cstring>
#include "block.h"
#include "symtab.h"
#include "symfile.h"
@@ -821,6 +822,64 @@ make_blockranges (struct objfile *objfile,
return blr;
}
static bool
block_ordering_predicate(struct block *b1, struct block *b2)
{
CORE_ADDR start1 = b1->start ();
CORE_ADDR start2 = b2->start ();
if (start1 != start2)
return start1 < start2;
return (b2->end () < b1->end ());
}
/* See block.h. */
void
blockvector::add_block (struct block *block)
{
if (num_blocks() <= FIRST_LOCAL_BLOCK)
{
/* No blocks (except global and static block). */
m_blocks.push_back (block);
}
else
{
/* blockvector already contains some blocks. Insert new block
to a correct place. */
auto first = m_blocks.begin () + FIRST_LOCAL_BLOCK;
auto last = m_blocks.end ();
auto insert_before = std::upper_bound (first,
last,
block,
block_ordering_predicate);
m_blocks.insert (insert_before, block);
}
}
/* See block.h. */
void
blockvector::sort ()
{
if (num_blocks() > FIRST_LOCAL_BLOCK)
{
std::sort (blocks ().begin () + FIRST_LOCAL_BLOCK,
blocks ().end (),
block_ordering_predicate);
}
}
/* See block.h. */
struct blockvector *
allocate_blockvector(struct obstack *obstack, int nblocks, int capacity)
{
return new (obstack) blockvector(obstack, nblocks, capacity);
}
/* Implement 'maint info blocks' command. If passed an argument then
print a list of all blocks at the given address. With no arguments
then list all blocks at the current address of the current inferior. */

View File

@@ -20,6 +20,7 @@
#ifndef BLOCK_H
#define BLOCK_H
#include <algorithm>
#include "dictionary.h"
#include "gdbsupport/array-view.h"
#include "gdbsupport/next-iterator.h"
@@ -416,41 +417,60 @@ private:
struct blockvector
{
void* operator new (size_t size, struct obstack *obstack)
{
return obstack_alloc (obstack, size);
}
void* operator new[] (size_t size, struct obstack *obstack)
{
return obstack_alloc (obstack, size);
}
void operator delete (void *memory) {}
void operator delete[] (void *memory) {}
blockvector (struct obstack *obstack, int nblocks, int capacity = 0)
: m_map (nullptr),
m_blocks (0, nullptr, obstack_allocator<struct block *> (obstack))
{
m_blocks.reserve (std::max (nblocks, capacity));
m_blocks.resize (nblocks, nullptr);
}
/* Return a view on the blocks of this blockvector. */
gdb::array_view<struct block *> blocks ()
{
return gdb::array_view<struct block *> (m_blocks, m_num_blocks);
return gdb::array_view<struct block *> (m_blocks.data (), m_blocks.size ());
}
/* Const version of the above. */
gdb::array_view<const struct block *const> blocks () const
{
const struct block **blocks = (const struct block **) m_blocks;
return gdb::array_view<const struct block *const> (blocks, m_num_blocks);
const struct block **blocks = (const struct block **) m_blocks.data ();
return gdb::array_view<const struct block *const> (blocks, m_blocks.size ());
}
/* Return the block at index I. */
struct block *block (size_t i)
{ return this->blocks ()[i]; }
{ return m_blocks[i]; }
/* Const version of the above. */
const struct block *block (size_t i) const
{ return this->blocks ()[i]; }
{ return m_blocks[i]; }
/* Set the block at index I. */
void set_block (int i, struct block *block)
{ m_blocks[i] = block; }
/* Set the number of blocks of this blockvector.
The storage of blocks is done using a flexible array member, so the number
of blocks set here must agree with what was effectively allocated. */
void set_num_blocks (int num_blocks)
{ m_num_blocks = num_blocks; }
/* Add BLOCK, making sure blocks are ordered by code-addresses
as required. Update global and static block start and end
adresses accordingly. */
void add_block(struct block *block);
/* Return the number of blocks in this blockvector. */
int num_blocks () const
{ return m_num_blocks; }
{ return m_blocks.size (); }
/* Return the global block of this blockvector. */
struct global_block *global_block ()
@@ -483,19 +503,23 @@ struct blockvector
void set_map (addrmap_fixed *map)
{ m_map = map; }
void sort ();
private:
/* An address map mapping addresses to blocks in this blockvector.
This pointer is zero if the blocks' start and end addresses are
enough. */
addrmap_fixed *m_map;
/* Number of blocks in the list. */
int m_num_blocks;
/* The blocks themselves. */
struct block *m_blocks[1];
std::vector<struct block *, obstack_allocator<struct block *>> m_blocks;
};
/* Allocate new blockvector for NBLOCKS blocks with enough storage to
hold up to CAPACITY blocks. CAPACITY defaults to NBLOCKS. */
struct blockvector *allocate_blockvector(struct obstack *obstack,
int nblocks, int capacity = 0);
extern const struct blockvector *blockvector_for_pc (CORE_ADDR,
const struct block **);

View File

@@ -429,10 +429,7 @@ buildsym_compunit::make_blockvector ()
{
}
blockvector = (struct blockvector *)
obstack_alloc (&m_objfile->objfile_obstack,
(sizeof (struct blockvector)
+ (i - 1) * sizeof (struct block *)));
blockvector = allocate_blockvector(&m_objfile->objfile_obstack, i);
/* Copy the blocks into the blockvector. This is done in reverse
order, which happens to put the blocks into the proper order
@@ -440,7 +437,6 @@ buildsym_compunit::make_blockvector ()
each block into the list after its subblocks in order to make
sure this is true. */
blockvector->set_num_blocks (i);
for (next = m_pending_blocks; next; next = next->next)
blockvector->set_block (--i, next->block);

View File

@@ -39886,7 +39886,8 @@ If you are using @value{GDBN} to debug a program that uses this interface, then
it should work transparently so long as you have not stripped the binary. If
you are developing a JIT compiler, then the interface is documented in the rest
of this chapter. At this time, the only known client of this interface is the
LLVM JIT.
LLVM JIT. An alternative to interface descrived below is to implement JIT
interface in Python (@pxref{JIT Interface in Python}).
Broadly speaking, the JIT interface mirrors the dynamic loader interface. The
JIT compiler communicates with @value{GDBN} by writing data into a global

View File

@@ -220,6 +220,7 @@ optional arguments while skipping others. Example:
* Blocks In Python:: Accessing blocks from Python.
* Symbols In Python:: Python representation of symbols.
* Symbol Tables In Python:: Python representation of symbol tables.
* Compunits In Python:: Python representation of compunits.
* Line Tables In Python:: Python representation of line tables.
* Breakpoints In Python:: Manipulating breakpoints using Python.
* Finish Breakpoints in Python:: Setting Breakpoints on function return
@@ -232,6 +233,7 @@ optional arguments while skipping others. Example:
* Disassembly In Python:: Instruction Disassembly In Python
* Missing Debug Info In Python:: Handle missing debug info from Python.
* Missing Objfiles In Python:: Handle objfiles from Python.
* JIT Interface in Python:: Writing JIT compilation interface in Python
@end menu
@node Basic Python
@@ -1574,6 +1576,14 @@ Return a new @code{gdb.Type} object which represents a pointer to this
type.
@end defun
@defun Type.function (@r{[}param_type@dots{}@r{]})
Return a new @code{gdb.Type} object which represents a type of function
returning this type. @code{param_type@dots{}} arguments specify parameter
types. Use @code{None} as last parameter type to create a vararg function
type. When invoked with single @code{None} argument or with no arguments at
all it creates a vararg function taking zero or more parameters.
@end defun
@defun Type.strip_typedefs ()
Return a new @code{gdb.Type} that represents the real type,
after removing all layers of typedefs.
@@ -5647,6 +5657,19 @@ Reading symbols from ./hello...
A @code{gdb.Objfile} object has the following methods:
@defun Objfile.__init__ (filename @r{[}, inferior @r{][}, arch @r{]})
Create a new objfile with given @var{filename}.
The optional @var{inferior} argument specifies the inferior to which the newly
created objfile is added. Defaults to currently selected inferior.
@pxref{Inferiors In Python}.
The optional @var{arch} argument specifies the architectore to associate with
the newly created objfile. Defaults to inferior's architecture.
@xref{Architectures In Python}.
@end defun
@defun Objfile.is_valid ()
Returns @code{True} if the @code{gdb.Objfile} object is valid,
@code{False} if not. A @code{gdb.Objfile} object can become invalid
@@ -5683,6 +5706,17 @@ Like @code{Objfile.lookup_global_symbol}, but searches for a global
symbol with static linkage named @var{name} in this objfile.
@end defun
@defun Objfile.compunits ()
Return a sequence of all the compunits associated with this objfile.
@xref{Compunits In Python}.
@end defun
@defun Objfile.unlink ()
Remove this objfile. This should be used only on objfiles created by
Python (see @code{Objfile.__init__} above) but @code{Objfile.unlink} does
not make any checks.
@end defun
@node Frames In Python
@subsubsection Accessing inferior stack frames from Python
@@ -6004,6 +6038,17 @@ historical compatibility.
A @code{gdb.Block} object has the following methods:
@defun Block.__init__ (superblock, start, end)
Create new block in @var{superblock} spanning from @var{start} to @var{end}.
The new block's @var{start}--@var{end} range must be within superblock's
range and must not overlap with any block already contained in superblock.
@end defun
@defun Block.add_symbol (symbol)
Add @var{symbol} to this block. Both the block and the @var{symbol} must
belong to the same compunit (@pxref{Compunits In Python}).
@end defun
@defun Block.is_valid ()
Returns @code{True} if the @code{gdb.Block} object is valid,
@code{False} if not. A block object can become invalid if the block it
@@ -6035,11 +6080,21 @@ have a superblock that is not the static block -- for instance this
happens for an inlined function.
@end defvar
@defvar Block.compunit
The compunit containing this block. @xref{Compunits In Python}.
This attribute is not writable.
@end defvar
@defvar Block.superblock
The block containing this block. If this parent block does not exist,
this attribute holds @code{None}. This attribute is not writable.
@end defvar
@defvar Block.subblocks
A list of blocks nested in this block. If there are no blocks nested,
this attribute holds an empty list. This attribute is not writable.
@end defvar
@defvar Block.global_block
The global block associated with this block. This attribute is not
writable.
@@ -6198,6 +6253,11 @@ of a symbol. Each address class is a constant defined in the
@code{gdb} module and described later in this chapter.
@end defvar
@defvar Symbol.domain
The domain of the symbol. Each domain is a constant defined in the
@code{gdb} module and described later in this chapter.
@end defvar
@defvar Symbol.needs_frame
This is @code{True} if evaluating this symbol's value requires a frame
(@pxref{Frames In Python}) and @code{False} otherwise. Typically,
@@ -6224,6 +6284,32 @@ arguments.
A @code{gdb.Symbol} object has the following methods:
@defun Symbol.__init__ (name, symtab, type, domain, addr_class, value)
Creates new symbol named @var{name} and adds it to symbol table
@var{symtab}.
The @var{type} argument specifies type of the symbol as @var{gdb.Type}
object (@pxref{Types In Python}).
The @var{domain} argument specifies domain of the symbol. Each domain is
a constant defined in the @code{gdb} module and described later in this
chapter.
The @var{addr_class} argument, together with @var{value} argument, specifies
how to find the value of this symbol. Each address class is a constant
defined in the @code{gdb} module and described later in this chapter. As of
now, only @code{gdb.SYMBOL_LOC_BLOCK} address class is supported, but future
versions of @value{GDBN} may support more address classes.
The meaning of @var{value} argument depends on the value of @var{addr_class}:
@vtable @code
@item gdb.SYMBOL_LOC_BLOCK
The @var{value} argument must be a block (a @code{gdb.Block} object). Block
must belong to the same compunit as the
@var{symtab} parameter (@pxref{Compunits In Python}).
@end vtable
@end defun
@defun Symbol.is_valid ()
Returns @code{True} if the @code{gdb.Symbol} object is valid,
@code{False} if not. A @code{gdb.Symbol} object can become invalid if
@@ -6453,8 +6539,17 @@ If no producer information is available then @code{None} is returned.
This attribute is not writable.
@end defvar
@defvar Symtab.compunit
The compunit this symbol table belongs to. @xref{Compunits In Python}.
This attribute is not writable.
@end defvar
A @code{gdb.Symtab} object has the following methods:
@defun Symtab.__init__ (filename, compunit)
Create a new symtab for given @var{filename} in given @var{compunit}.
@end defun
@defun Symtab.is_valid ()
Returns @code{True} if the @code{gdb.Symtab} object is valid,
@code{False} if not. A @code{gdb.Symtab} object can become invalid if
@@ -6482,6 +6577,75 @@ Return the line table associated with the symbol table.
@xref{Line Tables In Python}.
@end defun
@node Compunits In Python
@subsubsection Compunits representation in Python
@cindex compunits in python
@tindex gdb.Compunit
Access to compunits maintained by @value{GDBN} on objfiles
is exposed to Python via @code{gdb.Compunit}. Compunit for a symbol table can
be accessed via @code{compunit} property of @code{gdb.Symtab} object.
@xref{Symbol Tables In Python}. Method @code{compunits} of
@code{gdb.Objfile} can be used to get a list of all compunits belonging to
that objfile. @xref{Objfiles In Python}.
A @code{gdb.Compunit} object has the following attributes:
@defvar Compunit.objfile
The compunit's backing object file. @xref{Objfiles In Python}.
This attribute is not writable.
@end defvar
@defvar Compunit.producer
The name and possibly version number of the program that
compiled the code in the compunit.
The contents of this string is up to the compiler.
If no producer information is available then @code{None} is returned.
This attribute is not writable.
@end defvar
@defvar Compunit.symtabs
The list of symbol tables associated with this compunit.
@xref{Symbol Tables In Python}. This attribute is not writable.
@end defvar
A @code{gdb.Compunit} object has the following methods:
@defun Compunit.__init__ (filename, objfile, start, end @r{[}, capacity @r{]})
Create a new compunit with given @var{filename} in given @var{objfile}
(@pxref{Objfiles In Python}). The newly created compunit has an empty global
block and empty static block (@pxref{Blocks In Python}).
The @var{start} and @var{end} arguments specifies the start and end address
of compunit's global and static blocks. It must not overlap with any existing
compunit belonging to the same program space
(@pxref{Progspaces In Python}).
The optional @var{capacity} argument sets the initial capacity of the
internal block vector. More blocks than @var{capacity} can still be added
to the compunit however. If not specified, defaults to 8 blocks (including
global and static blocks).
@end defun
@defun Compunit.is_valid ()
Returns @code{True} if the @code{gdb.Compunit} object is valid,
@code{False} if not. A @code{gdb.Compunit} object can become invalid if
the compunit it refers to does not exist in @value{GDBN} any
longer. All other @code{gdb.Compunit} methods will throw an exception
if it is invalid at the time the method is called.
@end defun
@defun Compunit.global_block ()
Return the global block of the underlying compunit.
@xref{Blocks In Python}.
@end defun
@defun Compunit.static_block ()
Return the static block of the underlying compunit.
@xref{Blocks In Python}.
@end defun
@node Line Tables In Python
@subsubsection Manipulating line tables using Python
@@ -6511,6 +6675,31 @@ executable code for that source line resides in memory. This
attribute is not writable.
@end defvar
@defvar LineTableEntry.is_stmt
True if pc (associated with this entry) is a good location to place
a breakpoint for line (associated with this entry). This attribute is not
writable.
@end defvar
@defvar LineTableEntry.prologue_end
True if pc (associated with this entry) is a good location to place
a breakpoint after a function prologue. This attribute is not
writable.
@end defvar
@defvar LineTableEntry.epilogue_begin
True if pc (associated with this entry) marks the start of the epilogue.
This attribute is not writable.
@end defvar
@code{LineTableEntry} objects have the following methods:
@defun LineTableEntry.__init__ (line, pc@r{[}, is_stmt@r{][}, prologue_end@r{][}, epilogue_begin@r{]})
Create new line table entry. Arguments correspond to @code{LineTableEntry}
attributes described above. Optional arguments @var{is_stmt},
@var{prologue_end} and @var{epilogue_begin} default to @code{False}.
@end defun
As there can be multiple addresses for a single source line, you may
receive multiple @code{LineTableEntry} objects with matching
@code{line} attributes, but with different @code{pc} attributes. The
@@ -6540,6 +6729,14 @@ Line: 45 Address: 0x400615L
In addition to being able to iterate over a @code{LineTable}, it also
has the following direct access methods:
@defun LineTable.__init__ (symtab, entries)
Creates a new @code{LineTable} object and associate it with given
@var{symtab}. Old linetable that might already be associated with @var{symtab}
is discarded. The @var{entries} argument is a list of @code{LineTableEntry}
objects that constitute the newly created line table. Line table entries
do not need to be sorted.
@end defun
@defun LineTable.line (line)
Return a Python @code{Tuple} of @code{LineTableEntry} objects for any
entries in the line table for the given @var{line}, which specifies
@@ -7080,6 +7277,10 @@ If the indicated type cannot be found, this function will throw a
@code{ValueError} exception.
@end defun
@defun Architecture.void_type ()
This function returns a void type.
@end defun
@anchor{gdbpy_architecture_registers}
@defun Architecture.registers (@r{[} reggroup @r{]})
Return a @code{gdb.RegisterDescriptorIterator} (@pxref{Registers In
@@ -8354,6 +8555,127 @@ handlers, all of the matching handlers are enabled. The
@code{enabled} field of each matching handler is set to @code{True}.
@end table
@node JIT Interface in Python
@subsubsection Writing JIT compilation interface in Python
@cindex python, just-in-time compilation, JIT compilation interface
This section provides a high-level overview how to implement a JIT compiler
interface entirely in Python. For alternative way of interfacing a JIT
@pxref{JIT Interface}.
A JIT compiler interface usually needs to implement three elements:
@enumerate
@item
A way how to get notified when the JIT compiler compiles (and installs) new
code and when existing code is discarded. Typical solution is to put a Python
breakpoint (@pxref{Breakpoints In Python}) on some function known to be
called by the JIT compiler once code is installed or discarded.
@item
When a new code is installed the JIT interface needs to extract (debug)
information for newly installed code from the JIT compiler
(@pxref{Values From Inferior}) and build @value{GDBN}'s internal structures.
@xref{Objfiles In Python}, @ref{Compunits In Python},
@ref{Blocks In Python}, @ref{Symbol Tables In Python},
@ref{Symbols In Python}, @ref{Line Tables In Python}).
@item
Finally, when (previously installed) code is discarded the JIT interface
needs to discard @value{GDBN}'s internal structures built in previous step.
This is done by calling @code{unlink} on an objfile for that code
(which was created in previous step).
@end enumerate
Here's an example showing how to write a simple JIT interface in Python:
@c The copy of the code below is also in testsuite/gdb.python/py-jit.py
@c and used by py-jit.exp to make sure it is up to date. If changed the
@c test and py-jit.py should be checked and update accordingly if needed.
@smallexample
import gdb
class JITRegisterCode(gdb.Breakpoint):
def stop(self):
# Extract new code's address, size, name, linetable (and possibly
# other useful information). How exactly to do so depends on JIT
# compiler in question.
#
# In this example address, size and name get passed as parameters
# to registration function.
frame = gdb.newest_frame()
addr = int(frame.read_var('code'))
size = int(frame.read_var('size'))
name = frame.read_var('name').string()
linetable_entries = get_linetable_entries(addr)
# Create objfile and compunit for allocated "jitted" code
objfile = gdb.Objfile(name)
compunit = gdb.Compunit(name, objfile, addr, addr + size)
# Mark the objfile as "jitted" code. This will be used later when
# unregistering discarded code to check the objfile was indeed
# created for "jitted" code.
setattr(objfile, "is_jit_code", True)
# Create block for jitted function
block = gdb.Block(compunit.static_block(), addr, addr + size)
# Create symbol table holding info about jitted function, ...
symtab = gdb.Symtab("py-jit.c", compunit)
linetable = gdb.LineTable(symtab, linetable_entries)
# ...type of the jitted function...
void_t = gdb.selected_inferior().architecture().void_type()
func_t = void_t.function(None)
# ...and symbol representing jitted function.
symbol = gdb.Symbol(name, symtab, func_t,
gdb.SYMBOL_FUNCTION_DOMAIN, gdb.SYMBOL_LOC_BLOCK,
block)
# Finally, register the symbol in static block...
compunit.static_block().add_symbol(symbol)
# ..and continue execution
return False
# Create breakpoint to register new code
JITRegisterCode("jit_register_code", internal=True)
class JITUnregisterCode(gdb.Breakpoint):
def stop(self):
# Find out which code has been discarded. Again, how exactly to
# do so depends on JIT compiler in question.
#
# In this example address of discarded code is passed as a
# parameter.
frame = gdb.newest_frame()
addr = int(frame.read_var('code'))
# Find objfile which was created in JITRegisterCode.stop() for
# given jitted code.
objfile = gdb.current_progspace().objfile_for_address(addr)
if objfile is None:
# No objfile for given addr (this should not normally happen)
return False # Continue execution
if not getattr(objfile, "is_jit_code", False):
# Not a jitted code (this should not happen either)
return False # Continue execution
# Remove the objfile and all debug info associated with it...
objfile.unlink()
# ..and continue execution
return False # Continue execution
# Create breakpoint to discard old code
JITUnregisterCode("jit_unregister_code", internal=True)
@end smallexample
@node Python Auto-loading
@subsection Python Auto-loading
@cindex Python auto-loading

View File

@@ -517,7 +517,6 @@ static void
finalize_symtab (struct gdb_symtab *stab, struct objfile *objfile)
{
struct compunit_symtab *cust;
size_t blockvector_size;
CORE_ADDR begin, end;
struct blockvector *bv;
@@ -552,18 +551,13 @@ finalize_symtab (struct gdb_symtab *stab, struct objfile *objfile)
filetab->set_linetable (new_table);
}
blockvector_size = (sizeof (struct blockvector)
+ (actual_nblocks - 1) * sizeof (struct block *));
bv = (struct blockvector *) obstack_alloc (&objfile->objfile_obstack,
blockvector_size);
bv = allocate_blockvector(&objfile->objfile_obstack, actual_nblocks);
cust->set_blockvector (bv);
/* At the end of this function, (begin, end) will contain the PC range this
entire blockvector spans. */
bv->set_map (nullptr);
begin = stab->blocks.front ().begin;
end = stab->blocks.front ().end;
bv->set_num_blocks (actual_nblocks);
/* First run over all the gdb_block objects, creating a real block
object for each. Simultaneously, keep setting the real_block

View File

@@ -242,8 +242,6 @@ static struct compunit_symtab *new_symtab (const char *, int, struct objfile *);
static struct linetable *new_linetable (int);
static struct blockvector *new_bvect (int);
static struct type *parse_type (int, union aux_ext *, unsigned int, int *,
int, const char *);
@@ -4499,17 +4497,8 @@ add_block (struct block *b, struct symtab *s)
/* Cast away "const", but that's ok because we're building the
symtab and blockvector here. */
struct blockvector *bv
= (struct blockvector *) s->compunit ()->blockvector ();
bv = (struct blockvector *) xrealloc ((void *) bv,
(sizeof (struct blockvector)
+ bv->num_blocks ()
* sizeof (struct block)));
if (bv != s->compunit ()->blockvector ())
s->compunit ()->set_blockvector (bv);
bv->set_block (bv->num_blocks (), b);
bv->set_num_blocks (bv->num_blocks () + 1);
= const_cast<struct blockvector*> (s->compunit ()->blockvector ());
bv->add_block (b);
}
/* Add a new linenumber entry (LINENO,ADR) to a linevector LT.
@@ -4632,7 +4621,7 @@ new_symtab (const char *name, int maxlines, struct objfile *objfile)
lang = cust->language ();
/* All symtabs must have at least two blocks. */
bv = new_bvect (2);
bv = allocate_blockvector(&objfile->objfile_obstack, 2);
bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang));
bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang));
bv->static_block ()->set_superblock (bv->global_block ());
@@ -4700,21 +4689,6 @@ shrink_linetable (struct linetable *lt)
* sizeof (lt->item))));
}
/* Allocate and zero a new blockvector of NBLOCKS blocks. */
static struct blockvector *
new_bvect (int nblocks)
{
struct blockvector *bv;
int size;
size = sizeof (struct blockvector) + nblocks * sizeof (struct block *);
bv = (struct blockvector *) xzalloc (size);
bv->set_num_blocks (nblocks);
return bv;
}
/* Allocate and zero a new block of language LANGUAGE, and set its
BLOCK_MULTIDICT. If function is non-zero, assume the block is
associated to a function, and make sure that the symbols are stored

View File

@@ -1194,6 +1194,22 @@ is_addr_in_objfile (CORE_ADDR addr, const struct objfile *objfile)
if (osect->contains (addr))
return true;
}
/* Objfiles created dynamically by JIT reader API (and possibly by
other means too) do not have sections and therefore the above
check never succeeds.
For such "dynamic" objfiles walk over all compunits and check
if given ADDR falls into compunit's global block. */
if (! objfile->sections_start)
{
for (compunit_symtab *cu
: const_cast<struct objfile *>(objfile)->compunits ())
{
global_block *gb = cu->blockvector ()->global_block ();
if (gb->start () <= addr && addr <= gb->end ())
return true;
}
}
return false;
}
@@ -1296,3 +1312,23 @@ objfile_int_type (struct objfile *of, int size_in_bytes, bool unsigned_p)
gdb_assert_not_reached ("unable to find suitable integer type");
}
/* See objfiles.h. */
int
objfile::find_section_index (CORE_ADDR start, CORE_ADDR end)
{
obj_section *sect;
int sect_index;
for (sect = this->sections_start, sect_index = 0;
sect < this->sections_end;
sect++, sect_index++)
{
if (sect->the_bfd_section == nullptr)
continue;
if (sect->addr () <= start && end <= sect->endaddr ())
return sect_index;
}
return -1;
}

View File

@@ -644,6 +644,10 @@ public:
this->section_offsets[idx] = offset;
}
/* Return the section index for section mapped at memory range
[START, END]. If there's no such section, return -1. */
int find_section_index (CORE_ADDR start, CORE_ADDR end);
class section_iterator
{
public:

View File

@@ -318,6 +318,18 @@ archpy_integer_type (PyObject *self, PyObject *args, PyObject *kw)
return type_to_type_object (type);
}
/* Implementation of gdb.void_type. */
static PyObject *
archpy_void_type (PyObject *self, PyObject *args)
{
struct gdbarch *gdbarch;
ARCHPY_REQUIRE_VALID (self, gdbarch);
builtin_type (gdbarch);
return type_to_type_object (builtin_type (gdbarch)->builtin_void);
}
/* __repr__ implementation for gdb.Architecture. */
static PyObject *
@@ -383,6 +395,10 @@ END_PC." },
"integer_type (size [, signed]) -> type\n\
Return an integer Type corresponding to the given bitsize and signed-ness.\n\
If not specified, the type defaults to signed." },
{ "void_type", (PyCFunction) archpy_void_type,
METH_NOARGS,
"void_type () -> type\n\
Return an void Type." },
{ "registers", (PyCFunction) archpy_registers,
METH_VARARGS | METH_KEYWORDS,
"registers ([ group-name ]) -> Iterator.\n\

View File

@@ -149,6 +149,34 @@ blpy_get_superblock (PyObject *self, void *closure)
Py_RETURN_NONE;
}
static PyObject *
blpy_get_subblocks (PyObject *self, void *closure)
{
const struct block *block;
BLPY_REQUIRE_VALID (self, block);
gdbpy_ref<> list (PyList_New (0));
if (list == nullptr)
return nullptr;
compunit_symtab *cu = block->global_block ()->compunit ();
for (const struct block *each : cu->blockvector ()->blocks ())
{
if (each->superblock () == block)
{
gdbpy_ref<> item (block_to_block_object (each, cu->objfile ()));
if (item.get () == nullptr
|| PyList_Append (list.get (), item.get ()) == -1)
return nullptr;
}
}
return list.release ();
}
/* Return the global block associated to this block. */
static PyObject *
@@ -187,6 +215,19 @@ blpy_get_static_block (PyObject *self, void *closure)
return block_to_block_object (static_block, self_obj->objfile);
}
/* Getter function for Block.compunit. */
static PyObject *
blpy_get_compunit (PyObject *self, void *closure)
{
const struct block *block;
BLPY_REQUIRE_VALID (self, block);
return compunit_to_compunit_object (
block->global_block ()->compunit ()).release ();
}
/* Implementation of gdb.Block.is_global (self) -> Boolean.
Returns True if this block object is a global block. */
@@ -220,6 +261,43 @@ blpy_is_static (PyObject *self, void *closure)
Py_RETURN_FALSE;
}
/* Implementation of gdb.Block.add_symbol (self, symbol).
Adds SYMBOL to this block. */
static PyObject *
blpy_add_symbol (PyObject *self, PyObject *symbol_obj)
{
const struct block *block;
BLPY_REQUIRE_VALID (self, block);
struct symbol *symbol = symbol_object_to_symbol (symbol_obj);
if (symbol == nullptr)
{
return PyErr_Format (PyExc_TypeError,
_("The symbol argument is not valid gdb.Symbol"));
}
if (symbol->symtab ()->compunit() != block->global_block ()->compunit ())
{
return PyErr_Format (PyExc_TypeError,
_("The symbol argument belongs to different "
"compunit than block"));
}
multidictionary *dict = block->multidict ();
if (dict == nullptr)
{
auto_obstack *obstack =
&(block->global_block ()->compunit ()->objfile ()->objfile_obstack);
dict = mdict_create_linear (obstack, nullptr);
const_cast<struct block *>(block)->set_multidict (dict);
}
mdict_add_symbol (dict, symbol);
Py_RETURN_NONE;
}
/* Given a string, returns the gdb.Symbol representing that symbol in this
block. If such a symbol does not exist, returns NULL with a Python
exception. */
@@ -301,6 +379,106 @@ blpy_dealloc (PyObject *obj)
Py_TYPE (obj)->tp_free (obj);
}
/* Object initializer; creates new block.
Use: __init__(SUPERBLOCK, START, END). */
static int
blpy_init (PyObject *zelf, PyObject *args, PyObject *kw)
{
struct block_object *self = (struct block_object*) zelf;
if (self->block)
{
PyErr_Format (PyExc_RuntimeError,
_("Block object already initialized."));
return -1;
}
static const char *keywords[] = { "superblock", "start", "end", nullptr };
PyObject *superblock_obj;
uint64_t start;
uint64_t end;
if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "OKK", keywords,
&superblock_obj, &start, &end))
return -1;
auto superblock = block_object_to_block (superblock_obj);
if (superblock == nullptr)
{
PyErr_Format (PyExc_TypeError,
_("The superblock argument is not valid gdb.Block "
"object"));
return -1;
}
/* Check that start-end range is valid. */
if (! (start <= end))
{
PyErr_Format (PyExc_ValueError,
_("The start argument must be less or equal to the end "
"argument"));
return -1;
}
/* Check that start-end range is within superblocks' range. */
if (! (superblock-> start() <= start && end <= superblock->end ()))
{
PyErr_Format (PyExc_ValueError,
_("The start-end range must be within superblocks' "
"range"));
return -1;
}
/* Check that start-end range does not overlap with any
"sibling" blocks' range. */
compunit_symtab *cu = superblock->global_block ()->compunit ();
for (const struct block *each : cu->blockvector ()->blocks ())
{
if (each->superblock () == superblock)
{
/* each is a "sibling" block. */
if (std::max (start, each->start ()) < std::min(end, each->end ()))
{
PyErr_Format (PyExc_ValueError,
_("The start-end range overlaps with one of the "
"sibling blocks"));
return -1;
}
}
}
auto_obstack *obstack = &(cu->objfile ()->objfile_obstack);
struct block *blk = new (obstack) block ();
blk->set_superblock (superblock);
blk->set_multidict (mdict_create_linear (obstack, nullptr));
blk->set_start ((CORE_ADDR) start);
blk->set_end ((CORE_ADDR) end);
cu->blockvector ()->add_block (blk);
self->block = blk;
self->objfile = cu->objfile ();
htab_t table = blpy_objfile_data_key.get (self->objfile);
if (table == nullptr)
{
table = htab_create_alloc (10, block_object_hash, block_object_eq,
block_object_del, xcalloc, xfree);
blpy_objfile_data_key.set (self->objfile, table);
}
hashval_t hash = htab_hash_pointer (blk);
void **slot = htab_find_slot_with_hash (table, blk, hash, INSERT);
*slot = self;
return 0;
}
/* Create a new block object (gdb.Block) that encapsulates the struct
block object from GDB. */
PyObject *
@@ -492,7 +670,6 @@ blpy_richcompare (PyObject *self, PyObject *other, int op)
static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
gdbpy_initialize_blocks (void)
{
block_object_type.tp_new = PyType_GenericNew;
if (gdbpy_type_ready (&block_object_type) < 0)
return -1;
@@ -511,6 +688,9 @@ static PyMethodDef block_object_methods[] = {
{ "is_valid", blpy_is_valid, METH_NOARGS,
"is_valid () -> Boolean.\n\
Return true if this block is valid, false if not." },
{ "add_symbol", blpy_add_symbol, METH_O,
"add_symbol (symbol) -> None.\n\
Add given symbol to the block." },
{NULL} /* Sentinel */
};
@@ -525,10 +705,14 @@ static gdb_PyGetSetDef block_object_getset[] = {
"Block containing the global block.", NULL },
{ "static_block", blpy_get_static_block, NULL,
"Block containing the static block.", NULL },
{ "compunit", blpy_get_compunit, nullptr,
"Compunit containing this block.", nullptr },
{ "is_static", blpy_is_static, NULL,
"Whether this block is a static block.", NULL },
{ "is_global", blpy_is_global, NULL,
"Whether this block is a global block.", NULL },
{ "subblocks", blpy_get_subblocks, nullptr,
"List of blocks contained in this block.", nullptr },
{ NULL } /* Sentinel */
};
@@ -568,7 +752,15 @@ PyTypeObject block_object_type = {
0, /* tp_iternext */
block_object_methods, /* tp_methods */
0, /* tp_members */
block_object_getset /* tp_getset */
block_object_getset, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
blpy_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew /* tp_new */
};
static PyMethodDef block_iterator_object_methods[] = {

414
gdb/python/py-compunit.c Normal file
View File

@@ -0,0 +1,414 @@
/* Python interface to compunits.
Copyright (C) 2008-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/>. */
#include <algorithm>
#include "charset.h"
#include "symtab.h"
#include "source.h"
#include "python-internal.h"
#include "objfiles.h"
#include "block.h"
struct compunit_object {
PyObject_HEAD
/* The GDB compunit structure. */
struct compunit_symtab *compunit;
/* A compunit object is associated with an objfile, so keep track with
a doubly-linked list, rooted in the objfile. This allows
invalidation of the underlying struct compunit_symtab when the objfile is
deleted. */
compunit_object *prev;
compunit_object *next;
};
/* This function is called when an objfile is about to be freed.
Invalidate the compunit as further actions on the compunit
would result in bad data. All access to obj->compunit should be
gated by CUPY_REQUIRE_VALID which will raise an exception on
compunits. */
struct cupy_deleter
{
void operator() (compunit_object *obj)
{
while (obj)
{
compunit_object *next = obj->next;
obj->compunit = nullptr;
obj->next = nullptr;
obj->prev = nullptr;
obj = next;
}
}
};
extern PyTypeObject compunit_object_type
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("compunit_object");
static const registry<objfile>::key<compunit_object, cupy_deleter>
cupy_objfile_data_key;
/* Require a valid compunit. All access to compunit_object->compunit
should be gated by this call. */
#define CUPY_REQUIRE_VALID(compunit_obj, compunit) \
do { \
compunit = compunit_object_to_compunit (compunit_obj); \
if (compunit == nullptr) \
{ \
PyErr_SetString (PyExc_RuntimeError, \
_("Compunit object is invalid.")); \
return nullptr; \
} \
} while (0)
/* Getter function for gdb.Compunit.objfile. */
static PyObject *
cupy_get_objfile (PyObject *self, void *closure)
{
struct compunit_symtab *compunit = nullptr;
CUPY_REQUIRE_VALID (self, compunit);
return objfile_to_objfile_object (compunit->objfile ()).release ();
}
/* Getter function for gdb.Compunit.producer. */
static PyObject *
cupy_get_producer (PyObject *self, void *closure)
{
struct compunit_symtab *compunit = nullptr;
CUPY_REQUIRE_VALID (self, compunit);
if (compunit->producer () != nullptr)
{
const char *producer = compunit->producer ();
return host_string_to_python_string (producer).release ();
}
Py_RETURN_NONE;
}
/* Implementation of gdb.Compunit.is_valid (self) -> Boolean.
Returns True if this Symbol table still exists in GDB. */
static PyObject *
cupy_is_valid (PyObject *self, PyObject *args)
{
struct compunit_symtab *compunit = nullptr;
compunit = compunit_object_to_compunit (self);
if (compunit == nullptr)
Py_RETURN_FALSE;
Py_RETURN_TRUE;
}
/* Return the GLOBAL_BLOCK of the underlying compunit. */
static PyObject *
cupy_global_block (PyObject *self, PyObject *args)
{
struct compunit_symtab *compunit = nullptr;
const struct blockvector *blockvector;
CUPY_REQUIRE_VALID (self, compunit);
blockvector = compunit->blockvector ();
const struct block *block = blockvector->global_block ();
return block_to_block_object (block, compunit->objfile ());
}
/* Return the STATIC_BLOCK of the underlying compunit. */
static PyObject *
cupy_static_block (PyObject *self, PyObject *args)
{
struct compunit_symtab *compunit = nullptr;
const struct blockvector *blockvector;
CUPY_REQUIRE_VALID (self, compunit);
blockvector = compunit->blockvector ();
const struct block *block = blockvector->static_block ();
return block_to_block_object (block, compunit->objfile ());
}
static PyObject *
cupy_get_symtabs (PyObject *self, void *closure)
{
struct compunit_symtab *compunit = nullptr;
CUPY_REQUIRE_VALID (self, compunit);
gdbpy_ref<> list (PyList_New (0));
if (list == nullptr)
return nullptr;
for (struct symtab *each : compunit->filetabs ())
{
{
gdbpy_ref<> item (symtab_to_symtab_object (each));
if (item.get () == nullptr
|| PyList_Append (list.get (), item.get ()) == -1)
return nullptr;
}
}
return list.release ();
}
static void
cupy_dealloc (PyObject *obj)
{
compunit_object *compunit = (compunit_object *) obj;
if (compunit->prev)
compunit->prev->next = compunit->next;
else if (compunit->compunit)
cupy_objfile_data_key.set (compunit->compunit->objfile (),
compunit->next);
if (compunit->next)
compunit->next->prev = compunit->prev;
compunit->compunit = nullptr;
Py_TYPE (obj)->tp_free (obj);
}
/* Given a compunit, and a compunit_object that has previously been
allocated and initialized, populate the compunit_object with the
struct compunit_symtab data. Also, register the compunit_object life-cycle
with the life-cycle of the object file associated with this
compunit, if needed. */
static void
set_compunit (compunit_object *obj, struct compunit_symtab *compunit)
{
obj->compunit = compunit;
obj->prev = nullptr;
if (compunit)
{
obj->next = cupy_objfile_data_key.get (compunit->objfile ());
if (obj->next)
obj->next->prev = obj;
cupy_objfile_data_key.set (compunit->objfile (), obj);
}
else
obj->next = nullptr;
}
/* Object initializer; creates a new compunit.
Use: __init__(FILENAME, OBJFILE, START, END [, CAPACITY]). */
static int
cupy_init (PyObject *zelf, PyObject *args, PyObject *kw)
{
struct compunit_object *self = (struct compunit_object*) zelf;
if (self->compunit)
{
PyErr_Format (PyExc_RuntimeError,
_("Compunit object already initialized."));
return -1;
}
static const char *keywords[] = { "filename", "objfile", "start", "end",
"capacity", nullptr };
const char *filename;
PyObject *objf_obj = nullptr;
uint64_t start = 0;
uint64_t end = 0;
uint64_t capacity = 8;
if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "sOKK|K", keywords,
&filename, &objf_obj, &start, &end,
&capacity))
return -1;
auto objf = objfile_object_to_objfile (objf_obj);
if (! objf)
{
PyErr_Format (PyExc_TypeError,
_("The objfile argument is not valid gdb.Objfile object"));
return -1;
}
/* Check that start-end range is valid. */
if (! (start <= end))
{
PyErr_Format (PyExc_ValueError,
_("The start argument must be less or equal to the end "
"argument"));
return -1;
}
/* Check that to-be created compunits' global block does not overlap
with existing compunit. */
for (struct objfile *of : objf->pspace ()->objfiles_safe ())
{
for (compunit_symtab *cu : of->compunits ())
{
global_block *gb = cu->blockvector ()->global_block ();
if (std::max (start, gb->start ()) < std::min(end, gb->end ()))
{
PyErr_Format (PyExc_ValueError,
_("The start-end range overlaps with existing compunit"));
return -1;
}
}
}
blockvector *bv = allocate_blockvector (&objf->objfile_obstack,
FIRST_LOCAL_BLOCK, capacity);
compunit_symtab *cu = allocate_compunit_symtab (objf, filename);
cu->set_dirname (nullptr);
cu->set_blockvector (bv);
/* Allocate global block. */
global_block *gb = new (&objf->objfile_obstack) global_block ();
gb->set_multidict (mdict_create_linear_expandable (language_minimal));
gb->set_start ((CORE_ADDR) start);
gb->set_end ((CORE_ADDR) end);
gb->set_compunit (cu);
bv->set_block (GLOBAL_BLOCK, gb);
/* Allocate static block. */
struct block *sb = new (&objf->objfile_obstack) block ();
sb->set_multidict (mdict_create_linear_expandable (language_minimal));
sb->set_start ((CORE_ADDR) start);
sb->set_end ((CORE_ADDR) end);
sb->set_superblock (gb);
bv->set_block (STATIC_BLOCK, sb);
add_compunit_symtab_to_objfile (cu);
set_compunit(self, cu);
return 0;
}
/* Return a new reference to gdb.Compunit Python object representing
COMPUNIT. Return NULL and set the Python error on failure. */
gdbpy_ref<>
compunit_to_compunit_object (struct compunit_symtab *compunit)
{
compunit_object *compunit_obj;
compunit_obj = PyObject_NEW(compunit_object, &compunit_object_type);
if (compunit_obj)
set_compunit(compunit_obj, compunit);
return gdbpy_ref<>::new_reference ((PyObject * )compunit_obj);
}
/* Return struct compunit_symtab reference that is wrapped by this object. */
struct compunit_symtab *
compunit_object_to_compunit (PyObject *obj)
{
if (! PyObject_TypeCheck (obj, &compunit_object_type))
return nullptr;
return ((compunit_object *) obj)->compunit;
}
static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
gdbpy_initialize_compunits (void)
{
if (gdbpy_type_ready (&compunit_object_type) < 0)
return -1;
return 0;
}
GDBPY_INITIALIZE_FILE (gdbpy_initialize_compunits);
static gdb_PyGetSetDef compunit_object_getset[] = {
{ "objfile", cupy_get_objfile, nullptr, "The compunit's objfile.",
nullptr },
{ "producer", cupy_get_producer, nullptr,
"The name/version of the program that compiled this compunit.", nullptr },
{ "symtabs", cupy_get_symtabs, nullptr,
"List of symbol tables associated with this compunit", nullptr },
{nullptr} /* Sentinel */
};
static PyMethodDef compunit_object_methods[] = {
{ "is_valid", cupy_is_valid, METH_NOARGS,
"is_valid () -> Boolean.\n\
Return true if this compunit is valid, false if not." },
{ "global_block", cupy_global_block, METH_NOARGS,
"global_block () -> gdb.Block.\n\
Return the global block of the compunit." },
{ "static_block", cupy_static_block, METH_NOARGS,
"static_block () -> gdb.Block.\n\
Return the static block of the compunit." },
{nullptr} /* Sentinel */
};
PyTypeObject compunit_object_type = {
PyVarObject_HEAD_INIT (nullptr, 0)
"gdb.Compunit", /*tp_name*/
sizeof (compunit_object), /*tp_basicsize*/
0, /*tp_itemsize*/
cupy_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"GDB compunit object", /*tp_doc */
0, /*tp_traverse */
0, /*tp_clear */
gdbpy_richcompare<compunit_object, compunit_symtab,
&compunit_object::compunit>,/*tp_richcompare */
0, /*tp_weaklistoffset */
0, /*tp_iter */
0, /*tp_iternext */
compunit_object_methods, /*tp_methods */
0, /*tp_members */
compunit_object_getset, /*tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
cupy_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew /* tp_new */
};

View File

@@ -225,6 +225,16 @@ python_free_objfile (struct objfile *objfile)
gdbpy_print_stack ();
}
/* Return inferior reference that is wrapped by this object. */
inferior *
inferior_object_to_inferior (PyObject *obj)
{
if (! PyObject_TypeCheck (obj, &inferior_object_type))
return nullptr;
return ((inferior_object *) obj)->inferior;
}
/* Return a reference to the Python object of type Inferior
representing INFERIOR. If the object has already been created,
return it and increment the reference count, otherwise, create it.

View File

@@ -17,7 +17,9 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <algorithm>
#include "python-internal.h"
#include "objfiles.h"
struct linetable_entry_object {
PyObject_HEAD
@@ -25,6 +27,12 @@ struct linetable_entry_object {
int line;
/* The pc associated with the source line. */
CORE_ADDR pc;
/* See is_stmt in stuct linetable_entry. */
bool is_stmt : 1;
/* See prologue_end in stuct linetable_entry. */
bool prologue_end : 1;
/* See epilogue_begin in struct linetable_entry. */
bool epilogue_begin : 1;
};
extern PyTypeObject linetable_entry_object_type
@@ -98,7 +106,8 @@ symtab_to_linetable_object (PyObject *symtab)
and an address. */
static PyObject *
build_linetable_entry (int line, CORE_ADDR address)
build_linetable_entry (int line, CORE_ADDR address, bool is_stmt,
bool prologue_end, bool epilogue_begin)
{
linetable_entry_object *obj;
@@ -108,6 +117,9 @@ build_linetable_entry (int line, CORE_ADDR address)
{
obj->line = line;
obj->pc = address;
obj->is_stmt = is_stmt;
obj->prologue_end = prologue_end;
obj->epilogue_begin = epilogue_begin;
}
return (PyObject *) obj;
@@ -120,22 +132,26 @@ build_linetable_entry (int line, CORE_ADDR address)
address. */
static PyObject *
build_line_table_tuple_from_pcs (int line, const std::vector<CORE_ADDR> &pcs)
build_line_table_tuple_from_entries (
const struct objfile *objfile,
const std::vector<const linetable_entry *> &entries)
{
int i;
if (pcs.size () < 1)
if (entries.size () < 1)
Py_RETURN_NONE;
gdbpy_ref<> tuple (PyTuple_New (pcs.size ()));
gdbpy_ref<> tuple (PyTuple_New (entries.size ()));
if (tuple == NULL)
return NULL;
for (i = 0; i < pcs.size (); ++i)
for (i = 0; i < entries.size (); ++i)
{
CORE_ADDR pc = pcs[i];
gdbpy_ref<> obj (build_linetable_entry (line, pc));
const linetable_entry *entry = entries[i];
gdbpy_ref<> obj (build_linetable_entry (
entry->line, entry->pc (objfile), entry->is_stmt,
entry->prologue_end, entry->epilogue_begin));
if (obj == NULL)
return NULL;
@@ -155,24 +171,35 @@ ltpy_get_pcs_for_line (PyObject *self, PyObject *args)
{
struct symtab *symtab;
gdb_py_longest py_line;
const linetable_entry *best_entry = nullptr;
std::vector<CORE_ADDR> pcs;
std::vector<const linetable_entry*> entries;
LTPY_REQUIRE_VALID (self, symtab);
if (! PyArg_ParseTuple (args, GDB_PY_LL_ARG, &py_line))
return NULL;
if (! symtab->linetable ())
Py_RETURN_NONE;
try
{
pcs = find_pcs_for_symtab_line (symtab, py_line, &best_entry);
const linetable_entry *entry;
int i;
for (entry = symtab->linetable ()->item, i = 0;
i < symtab->linetable ()->nitems;
entry++, i++)
{
if (entry->line == py_line)
entries.push_back (entry);
}
}
catch (const gdb_exception &except)
{
return gdbpy_handle_gdb_exception (nullptr, except);
}
return build_line_table_tuple_from_pcs (py_line, pcs);
struct objfile *objfile = symtab->compunit ()->objfile ();
return build_line_table_tuple_from_entries (objfile, entries);
}
/* Implementation of gdb.LineTable.has_line (self, line) -> Boolean.
@@ -269,6 +296,102 @@ ltpy_is_valid (PyObject *self, PyObject *args)
Py_RETURN_TRUE;
}
/* Object initializer; creates new linetable.
Use: __init__(SYMTAB, ENTRIES). */
static int
ltpy_init (PyObject *zelf, PyObject *args, PyObject *kw)
{
struct linetable_object *self = (struct linetable_object*) zelf;
static const char *keywords[] = { "symtab", "entries", nullptr };
PyObject *symtab_obj;
PyObject *entries;
if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "OO", keywords,
&symtab_obj, &entries))
return -1;
struct symtab *symtab = symtab_object_to_symtab (symtab_obj);
if (symtab == nullptr)
{
PyErr_Format (PyExc_TypeError,
_("The symtab argument is not valid gdb.Symtab."));
return -1;
}
if (!PyList_Check (entries))
{
PyErr_Format (PyExc_TypeError,
_("The entries parameter is not a list."));
return -1;
}
struct objfile *objfile = symtab->compunit ()->objfile ();
/* Commit 1acc9dca "Change linetables to be objfile-independent"
changed linetables so that entries contain relative of objfile's
text section offset. Since the objfile has been created dynamically
and may not have "text" section offset initialized, we do it here.
Note that here no section is added to objfile (since that requires
having bfd_section first), only text offset. */
if (objfile->sect_index_text == -1)
{
objfile->section_offsets.push_back (0);
objfile->sect_index_text = objfile->section_offsets.size () - 1;
}
CORE_ADDR text_section_offset = objfile->text_section_offset ();
long nentries = PyList_Size (entries);
long linetable_size
= sizeof (struct linetable)
+ std::max(nentries - 1, 0L) * sizeof (struct linetable_entry);
struct linetable *linetable
= (struct linetable *)obstack_alloc (&(objfile->objfile_obstack),
linetable_size);
linetable->nitems = nentries;
for (int i = 0; i < nentries; i++)
{
linetable_entry_object *entry_obj
= (linetable_entry_object *)PyList_GetItem (entries, i);
;
if (! PyObject_TypeCheck (entry_obj , &linetable_entry_object_type))
{
PyErr_Format (PyExc_TypeError,
_("Element at %d of entries argument is not a "
"gdb.LineTableEntry object"), i);
return -1;
}
/* Since PC of entries passed to this function are "unrelocated",
we compensate here. */
CORE_ADDR pc ((CORE_ADDR)entry_obj->pc - text_section_offset);
linetable->item[i].line = entry_obj->line;
linetable->item[i].set_unrelocated_pc (unrelocated_addr (pc));
linetable->item[i].is_stmt = entry_obj->is_stmt;
linetable->item[i].prologue_end = entry_obj->prologue_end;
linetable->item[i].epilogue_begin = entry_obj->epilogue_begin;
}
/* Now sort the entries in increasing PC order. */
if (nentries > 0)
{
auto linetable_entry_ordering = [] (const struct linetable_entry &e1,
const struct linetable_entry &e2)
{
return e1.unrelocated_pc () < e2.unrelocated_pc ();
};
std::sort (&(linetable->item[0]), &(linetable->item[nentries]),
linetable_entry_ordering);
}
symtab->set_linetable (linetable);
self->symtab = symtab_obj;
Py_INCREF (symtab_obj);
return 0;
}
/* Deconstructor for the line table object. Decrement the reference
to the symbol table object before calling the default free. */
@@ -277,7 +400,8 @@ ltpy_dealloc (PyObject *self)
{
linetable_object *obj = (linetable_object *) self;
Py_DECREF (obj->symtab);
if (obj->symtab)
Py_DECREF (obj->symtab);
Py_TYPE (self)->tp_free (self);
}
@@ -321,6 +445,85 @@ ltpy_entry_get_pc (PyObject *self, void *closure)
return gdb_py_object_from_ulongest (obj->pc).release ();
}
/* Implementation of gdb.LineTableEntry.is_stmt (self) -> bool. Returns
True if associated PC is a good location to place a breakpoint for
associatated LINE. */
static PyObject *
ltpy_entry_get_is_stmt (PyObject *self, void *closure)
{
linetable_entry_object *obj = (linetable_entry_object *) self;
if (obj->is_stmt != 0)
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
}
/* Implementation of gdb.LineTableEntry.prologue_end (self) -> bool. Returns
True if associated PC is a good location to place a breakpoint after a
function prologue. */
static PyObject *
ltpy_entry_get_prologue_end (PyObject *self, void *closure)
{
linetable_entry_object *obj = (linetable_entry_object *) self;
if (obj->prologue_end)
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
}
/* Implementation of gdb.LineTableEntry.prologue_end (self) -> bool. Returns
True if this location marks the start of the epilogue. */
static PyObject *
ltpy_entry_get_epilogue_begin (PyObject *self, void *closure)
{
linetable_entry_object *obj = (linetable_entry_object *) self;
if (obj->epilogue_begin)
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
}
/* Object initializer; creates new linetable entry.
Use: __init__(LINE, PC, IS_STMT, PROLOGUE_END, EPILOGUE_BEGIN). */
static int
ltpy_entry_init (PyObject *zelf, PyObject *args, PyObject *kw)
{
linetable_entry_object *self = (linetable_entry_object *) zelf;
static const char *keywords[] = { "line", "pc", "is_stmt", "prologue_end",
"epilogue_begin", nullptr };
int line = 0;
CORE_ADDR pc = 0;
int is_stmt = 0;
int prologue_end = 0;
int epilogue_begin = 0;
if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "iK|ppp",
keywords,
&line,
&pc,
&is_stmt,
&prologue_end,
&epilogue_begin))
return -1;
self->line = line;
self->pc = pc;
self->is_stmt = is_stmt == 1 ? true : false;
self->prologue_end = prologue_end == 1 ? true : false;
self->epilogue_begin = epilogue_begin == 1 ? true : false;
return 0;
}
/* LineTable iterator functions. */
/* Return a new line table iterator. */
@@ -406,7 +609,8 @@ ltpy_iternext (PyObject *self)
}
struct objfile *objfile = symtab->compunit ()->objfile ();
obj = build_linetable_entry (item->line, item->pc (objfile));
obj = build_linetable_entry (item->line, item->pc (objfile), item->is_stmt,
item->prologue_end, item->epilogue_begin );
iter_obj->current_index++;
return obj;
@@ -486,8 +690,9 @@ PyTypeObject linetable_object_type = {
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
ltpy_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew /* tp_new */
};
static PyMethodDef ltpy_iterator_methods[] = {
@@ -534,9 +739,16 @@ static gdb_PyGetSetDef linetable_entry_object_getset[] = {
"The line number in the source file.", NULL },
{ "pc", ltpy_entry_get_pc, NULL,
"The memory address for this line number.", NULL },
{ "is_stmt", ltpy_entry_get_is_stmt, nullptr,
"Whether this is a good location to place a breakpoint for associated LINE.", nullptr },
{ "prologue_end", ltpy_entry_get_prologue_end, nullptr,
"Whether this is a good location to place a breakpoint after method prologue.", nullptr },
{ "epilogue_begin", ltpy_entry_get_epilogue_begin, nullptr,
"True if this location marks the start of the epilogue.", nullptr },
{ NULL } /* Sentinel */
};
PyTypeObject linetable_entry_object_type = {
PyVarObject_HEAD_INIT (NULL, 0)
"gdb.LineTableEntry", /*tp_name*/
@@ -573,6 +785,7 @@ PyTypeObject linetable_entry_object_type = {
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
ltpy_entry_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
};

View File

@@ -25,6 +25,7 @@
#include "symtab.h"
#include "python.h"
#include "inferior.h"
#include "observable.h"
struct objfile_object
{
@@ -251,6 +252,84 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
return (PyObject *) self.release ();
}
/* Object initializer; creates new a objfile.
Use: __init__(FILENAME [, INFERIOR [,ARCH]]). */
static int
objfpy_init (PyObject *zelf, PyObject *args, PyObject *kw)
{
struct objfile_object *self = (struct objfile_object*) zelf;
if (self->objfile)
{
PyErr_Format (PyExc_RuntimeError,
_("Objfile object already initialized."));
return -1;
}
static const char *keywords[] = { "filename", "inferior", "arch", nullptr };
const char *filename;
PyObject* inf_obj = nullptr;
PyObject* arch_obj = nullptr;
if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|OO", keywords,
&filename, &inf_obj, &arch_obj))
return -1;
inferior *inf = nullptr;
if (inf_obj)
{
inf = inferior_object_to_inferior (inf_obj);
if (! inf)
{
PyErr_Format (PyExc_TypeError,
_("The inferior argument is not gdb.Inferior object"));
return -1;
}
}
else
{
inf = current_inferior ();
}
gdbarch *arch = nullptr;
if (arch_obj)
{
if (! gdbpy_is_architecture (arch_obj))
{
PyErr_Format (PyExc_TypeError,
_("The arch argument is not gdb.Architecture object"));
return -1;
}
arch = arch_object_to_gdbarch (arch_obj);
}
else
{
arch = inf->arch ();
}
if (!objfpy_initialize (self))
{
PyErr_Format (PyExc_RuntimeError,
_("Failed to initialize Objfile object."));
return -1;
}
struct objfile *objfile;
objfile = objfile::make (nullptr, inf->pspace, filename, OBJF_NOT_FILENAME | OBJF_READNOW);
objfile->per_bfd->gdbarch = arch;
self->objfile = objfile;
objfpy_objfile_data_key.set(objfile, self);
/* Increment refcount on self as it is now referenced from objfile! */
Py_INCREF (self);
return 0;
}
PyObject *
objfpy_get_printers (PyObject *o, void *ignore)
{
@@ -529,6 +608,20 @@ objfpy_lookup_static_symbol (PyObject *self, PyObject *args, PyObject *kw)
Py_RETURN_NONE;
}
/* Implementation of gdb.Objfile.unlink (). */
static PyObject *
objfpy_unlink (PyObject *self, PyObject *args)
{
objfile_object *obj = (objfile_object *) self;
OBJFPY_REQUIRE_VALID (obj);
obj->objfile->unlink();
Py_RETURN_NONE;
}
/* Implement repr() for gdb.Objfile. */
static PyObject *
@@ -544,6 +637,31 @@ objfpy_repr (PyObject *self_)
objfile_name (obj));
}
/* Implementation of gdb.Objfile.compunits() -> List */
static PyObject *
objfpy_compunits (PyObject *self_, PyObject *args)
{
objfile_object *self = (objfile_object *) self_;
OBJFPY_REQUIRE_VALID (self);
gdbpy_ref<> list (PyList_New (0));
if (list == nullptr)
return nullptr;
for (struct compunit_symtab *compunit : self->objfile->compunits ())
{
gdbpy_ref<> item = compunit_to_compunit_object (compunit);
if (item.get () == nullptr
|| PyList_Append (list.get (), item.get ()) == -1)
return nullptr;
}
return list.release ();
}
/* Subroutine of gdbpy_lookup_objfile_by_build_id to simplify it.
Return non-zero if STRING is a potentially valid build id. */
@@ -706,6 +824,44 @@ objfile_to_objfile_object (struct objfile *objfile)
return gdbpy_ref<>::new_reference (result);
}
/* Returns the struct objfile value corresponding to the given Python
objfile object OBJ. Returns NULL if OBJ is not an objfile object. */
struct objfile *
objfile_object_to_objfile (PyObject *obj)
{
if (! PyObject_TypeCheck (obj, &objfile_object_type))
return nullptr;
return ((objfile_object *)obj)->objfile;
}
/* This function remove any dynamic objfiles left over when the
inferior exits. */
static void
objfpy_inferior_exit_hook (struct inferior *inf)
{
for (objfile *objf : current_program_space->objfiles_safe ())
{
if (objf->obfd == nullptr)
{
/* Following check is to only unlink dynamic objfiles created by
Python code. Dynamic objfiles created by JIT reader API are
unlinked in jit_inferior_exit_hook (). */
if (objf->jited_data == nullptr || objf->jited_data->addr != 0)
objf->unlink ();
}
}
}
void _initialize_py_objfile ();
void
_initialize_py_objfile ()
{
gdb::observers::inferior_exit.attach (objfpy_inferior_exit_hook, "py-objfile");
}
static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
gdbpy_initialize_objfile (void)
{
@@ -737,6 +893,14 @@ Look up a global symbol in this objfile and return it." },
"lookup_static_symbol (name [, domain]).\n\
Look up a static-linkage global symbol in this objfile and return it." },
{ "compunits", objfpy_compunits, METH_NOARGS,
"compunits () -> List.\n\
Return a sequence of compunits associated to this objfile." },
{ "unlink", objfpy_unlink, METH_NOARGS,
"unlink ().\n\
Remove this objfile." },
{ NULL }
};
@@ -806,8 +970,8 @@ PyTypeObject objfile_object_type =
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
offsetof (objfile_object, dict), /* tp_dictoffset */
0, /* tp_init */
offsetof (objfile_object, dict),/* tp_dictoffset */
objfpy_init, /* tp_init */
0, /* tp_alloc */
objfpy_new, /* tp_new */
objfpy_new, /* tp_new */
};

View File

@@ -153,6 +153,16 @@ sympy_get_addr_class (PyObject *self, void *closure)
return gdb_py_object_from_longest (symbol->aclass ()).release ();
}
static PyObject *
sympy_get_domain (PyObject *self, void *closure)
{
struct symbol *symbol = nullptr;
SYMPY_REQUIRE_VALID (self, symbol);
return gdb_py_object_from_longest (symbol->domain ()).release ();
}
static PyObject *
sympy_is_argument (PyObject *self, void *closure)
{
@@ -390,6 +400,135 @@ sympy_repr (PyObject *self)
symbol->print_name ());
}
/* Object initializer; creates new symbol.
Use: __init__(NAME, SYMTAB, TYPE, DOMAIN, ADDR_CLASS, VALUE). */
static int
sympy_init (PyObject *zelf, PyObject *args, PyObject *kw)
{
struct symbol_object *self = (struct symbol_object*) zelf;
if (self->symbol)
{
PyErr_Format (PyExc_RuntimeError,
_("Symbol object already initialized."));
return -1;
}
static const char *keywords[] = { "name", "symtab", "type",
"domain", "addr_class", "value",
nullptr };
const char *name;
PyObject *symtab_obj = nullptr;
PyObject *type_obj = nullptr;
domain_enum domain;
unsigned int addr_class;
PyObject *value_obj = nullptr;
if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "sOOIIO", keywords,
&name, &symtab_obj, &type_obj,
&domain, &addr_class, &value_obj))
return -1;
struct symtab *symtab = symtab_object_to_symtab (symtab_obj);
if (symtab == nullptr)
{
PyErr_Format (PyExc_TypeError,
_("The symtab argument is not valid gdb.Symtab object"));
return -1;
}
struct type *type = type_object_to_type (type_obj);
if (type == nullptr)
{
PyErr_Format (PyExc_TypeError,
_("The type argument is not valid gdb.Type object"));
return -1;
}
if (type->objfile_owner () != nullptr &&
type->objfile_owner () != symtab->compunit ()->objfile ())
{
PyErr_Format (PyExc_ValueError,
_("The type argument's owning objfile differs from "
"symtab's objfile."));
return -1;
}
union _value {
const struct block *block;
} value;
switch (addr_class)
{
default:
PyErr_Format (PyExc_ValueError,
_("The value of addr_class argument is not supported"));
return -1;
case LOC_BLOCK:
if ((value.block = block_object_to_block (value_obj)) == nullptr)
{
PyErr_Format (PyExc_TypeError,
_("The addr_class argument is SYMBOL_LOC_BLOCK but "
"the value argument is not a valid gdb.Block."));
return -1;
}
if (type->code () != TYPE_CODE_FUNC)
{
PyErr_Format (PyExc_ValueError,
_("The addr_class argument is SYMBOL_LOC_BLOCK but "
"the type argument is not a function type."));
return -1;
}
break;
}
struct objfile *objfile = symtab->compunit ()->objfile ();
auto_obstack *obstack = &(objfile->objfile_obstack);
struct symbol *sym = new (obstack) symbol();
sym->m_name = obstack_strdup (obstack, name);
sym->set_symtab (symtab);
sym->set_type (type);
sym->set_domain (domain);
sym->set_aclass_index (addr_class);
switch (addr_class)
{
case LOC_BLOCK:
{
sym->set_value_block (value.block);
if (domain == FUNCTION_DOMAIN)
const_cast<struct block*> (value.block)->set_function (sym);
/* Set symbol's section index. This needed in somewhat unusual
usecase where dynamic code is generated into a special section
(defined in custom linker script or otherwise). Otherwise,
find_pc_sect_compunit_symtab () would not find the compunit
symtab and commands like "disassemble function_name" would
resort to disassemble complete section.
Note that in usual case where new objfile is created for
dynamic code, the objfile has no sections at all and
objfile::find_section_index () returns -1.
*/
CORE_ADDR start = value.block->start ();
CORE_ADDR end = value.block->end ();
sym->set_section_index (objfile->find_section_index (start, end));
}
break;
default:
gdb_assert_not_reached("unreachable");
break;
}
set_symbol (self, sym);
return 0;
}
/* Implementation of
gdb.lookup_symbol (name [, block] [, domain]) -> (symbol, is_field_of_this)
A tuple with 2 elements is always returned. The first is the symbol
@@ -707,6 +846,7 @@ static gdb_PyGetSetDef symbol_object_getset[] = {
This is either name or linkage_name, depending on whether the user asked GDB\n\
to display demangled or mangled names.", NULL },
{ "addr_class", sympy_get_addr_class, NULL, "Address class of the symbol." },
{ "domain", sympy_get_domain, nullptr, "Domain of the symbol." },
{ "is_argument", sympy_is_argument, NULL,
"True if the symbol is an argument of a function." },
{ "is_constant", sympy_is_constant, NULL,
@@ -756,11 +896,20 @@ PyTypeObject symbol_object_type = {
"GDB symbol object", /*tp_doc */
0, /*tp_traverse */
0, /*tp_clear */
0, /*tp_richcompare */
gdbpy_richcompare<symbol_object, symbol, &symbol_object::symbol>,
/*tp_richcompare */
0, /*tp_weaklistoffset */
0, /*tp_iter */
0, /*tp_iternext */
symbol_object_methods, /*tp_methods */
0, /*tp_members */
symbol_object_getset /*tp_getset */
symbol_object_getset, /*tp_getset */
0, /*tp_base */
0, /*tp_dict */
0, /*tp_descr_get */
0, /*tp_descr_set */
0, /*tp_dictoffset */
sympy_init, /*tp_init */
0, /*tp_alloc */
PyType_GenericNew, /*tp_new */
};

View File

@@ -136,6 +136,8 @@ static const registry<objfile>::key<sal_object, salpy_deleter>
} \
} while (0)
static void set_symtab (symtab_object *obj, struct symtab *symtab);
static PyObject *
stpy_str (PyObject *self)
{
@@ -193,6 +195,18 @@ stpy_get_producer (PyObject *self, void *closure)
Py_RETURN_NONE;
}
/* Getter function for Symtab.compunit. */
static PyObject *
stpy_get_compunit (PyObject *self, void *closure)
{
struct symtab *symtab = nullptr;
STPY_REQUIRE_VALID (self, symtab);
return compunit_to_compunit_object (symtab->compunit ()).release ();
}
static PyObject *
stpy_fullname (PyObject *self, PyObject *args)
{
@@ -267,6 +281,46 @@ stpy_get_linetable (PyObject *self, PyObject *args)
return symtab_to_linetable_object (self);
}
/* Object initializer; creates new symtab.
Use: __init__(FILENAME, COMPUNIT). */
static int
stpy_init (PyObject *zelf, PyObject *args, PyObject *kw)
{
struct symtab_object *self = (struct symtab_object*) zelf;
if (self->symtab)
{
PyErr_Format (PyExc_RuntimeError,
_("Symtab object already initialized."));
return -1;
}
static const char *keywords[] = { "filename", "compunit", nullptr };
const char *filename;
PyObject *cu_obj;
if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "sO", keywords,
&filename, &cu_obj))
return -1;
compunit_symtab *cu = compunit_object_to_compunit (cu_obj);
if (cu == nullptr)
{
PyErr_Format (PyExc_TypeError,
_("The compunit argument is not valid gdb.Compunit "
"object"));
return -1;
}
struct symtab *symtab = allocate_symtab (cu, filename);
set_symtab (self, symtab);
return 0;
}
static PyObject *
salpy_str (PyObject *self)
{
@@ -511,7 +565,6 @@ symtab_object_to_symtab (PyObject *obj)
static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
gdbpy_initialize_symtabs (void)
{
symtab_object_type.tp_new = PyType_GenericNew;
if (gdbpy_type_ready (&symtab_object_type) < 0)
return -1;
@@ -533,6 +586,8 @@ static gdb_PyGetSetDef symtab_object_getset[] = {
NULL },
{ "producer", stpy_get_producer, NULL,
"The name/version of the program that compiled this symtab.", NULL },
{ "compunit", stpy_get_compunit, nullptr,
"The compunit this symtab belongs to.", nullptr },
{NULL} /* Sentinel */
};
@@ -579,13 +634,22 @@ PyTypeObject symtab_object_type = {
"GDB symtab object", /*tp_doc */
0, /*tp_traverse */
0, /*tp_clear */
0, /*tp_richcompare */
gdbpy_richcompare<symtab_object, symtab, &symtab_object::symtab>,
/*tp_richcompare */
0, /*tp_weaklistoffset */
0, /*tp_iter */
0, /*tp_iternext */
symtab_object_methods, /*tp_methods */
0, /*tp_members */
symtab_object_getset /*tp_getset */
symtab_object_getset, /*tp_getset */
0, /*tp_base */
0, /*tp_dict */
0, /*tp_descr_get */
0, /*tp_descr_set */
0, /*tp_dictoffset */
stpy_init, /*tp_init */
0, /*tp_alloc */
PyType_GenericNew, /*tp_new */
};
static gdb_PyGetSetDef sal_object_getset[] = {

View File

@@ -774,6 +774,57 @@ typy_unqualified (PyObject *self, PyObject *args)
return type_to_type_object (type);
}
/* Return a function type. */
static PyObject *
typy_function (PyObject *self, PyObject *args)
{
struct type *type = ((type_object *) self)->type;
gdb_assert (PySequence_Check (args));
std::vector<struct type *> param_types (PySequence_Length (args));
for (int i = 0; i < PySequence_Length (args); i++)
{
PyObject *param_type_obj = PySequence_GetItem (args, i);
if (param_type_obj == Py_None)
{
param_types[i] = nullptr;
if (i != (PySequence_Length (args) - 1))
{
PyErr_Format (PyExc_ValueError,
_("Argument at index %d is None but None can "
"only be the last type."), i);
return nullptr;
}
}
else
{
param_types[i] = type_object_to_type (param_type_obj);
if (!param_types[i])
{
PyErr_Format (PyExc_TypeError,
_("Argument at index %d is not a gdb.Type "
"object."), i);
return nullptr;
}
}
}
try
{
type = lookup_function_type_with_arguments (
type, param_types.size (), param_types.data ());
}
catch (const gdb_exception &except)
{
return gdbpy_handle_gdb_exception (nullptr, except);
}
return type_to_type_object (type);
}
/* Return the size of the type represented by SELF, in bytes. */
static PyObject *
typy_get_sizeof (PyObject *self, void *closure)
@@ -1641,6 +1692,9 @@ Return the type of a template argument." },
{ "unqualified", typy_unqualified, METH_NOARGS,
"unqualified () -> Type\n\
Return a variant of this type without const or volatile attributes." },
{ "function", typy_function, METH_VARARGS,
"function () -> Type\n\
Return a function type returning value of this type." },
{ "values", typy_values, METH_NOARGS,
"values () -> list\n\
Return a list holding all the fields of this type.\n\

View File

@@ -555,6 +555,8 @@ gdbpy_ref<thread_object> create_thread_object (struct thread_info *tp);
gdbpy_ref<> thread_to_thread_object (thread_info *thr);;
gdbpy_ref<inferior_object> inferior_to_inferior_object (inferior *inf);
gdbpy_ref<> compunit_to_compunit_object (struct compunit_symtab *compunit);
PyObject *gdbpy_buffer_to_membuf (gdb::unique_xmalloc_ptr<gdb_byte> buffer,
CORE_ADDR address, ULONGEST length);
@@ -571,6 +573,9 @@ struct symtab *symtab_object_to_symtab (PyObject *obj);
struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
frame_info_ptr frame_object_to_frame_info (PyObject *frame_obj);
struct gdbarch *arch_object_to_gdbarch (PyObject *obj);
struct compunit_symtab *compunit_object_to_compunit (PyObject *obj);
inferior *inferior_object_to_inferior(PyObject *obj);
struct objfile *objfile_object_to_objfile(PyObject *obj);
extern PyObject *gdbpy_execute_mi_command (PyObject *self, PyObject *args,
PyObject *kw);
@@ -1145,4 +1150,37 @@ gdbpy_type_ready (PyTypeObject *type, PyObject *mod = nullptr)
# define PyType_Ready POISONED_PyType_Ready
#endif
/* Implements default equality and non-equality comparisons for GDB
Python objects.
All other comparison operators will throw a TypeError Python exception.
Two Python objects of type P are considered equal if both point to the
same underlying GBB structure of type D. The last template parameter
specifies the member of Python object holding reference to underlying
GBB structure. */
template <typename P, typename D, D* P::*data>
PyObject *
gdbpy_richcompare (PyObject *self, PyObject *other, int op)
{
int result;
if (!PyObject_TypeCheck (other, self->ob_type)
|| (op != Py_EQ && op != Py_NE))
{
Py_INCREF (Py_NotImplemented);
return Py_NotImplemented;
}
if ( (P *)self->*data == (P *)other->*data)
result = Py_EQ;
else
result = Py_NE;
if (op == result)
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
#endif /* PYTHON_PYTHON_INTERNAL_H */

View File

@@ -229,6 +229,15 @@ proc jit_reader_test {} {
gdb_test "python print( \[o for o in gdb.objfiles() if o.filename.startswith('<< JIT compiled code')\]\[0\].build_id )" \
"None" \
"python gdb.Objfile.build_id"
# Check that Progspace.objfile_for_address () finds "jitted"
# objfile
gdb_test "frame 0" \
"#0 $hex in jit_function_stack_mangle ()$any" \
"select frame 0"
gdb_test "python print( gdb.current_progspace().objfile_for_address(gdb.parse_and_eval('\$pc')) )" \
"<gdb.Objfile filename=<< JIT compiled code at $hex >>>" \
"python gdb.Progspace.objfile_for_address"
}
}
}

View File

@@ -104,6 +104,10 @@ foreach_with_prefix test_data { {None None} \
"check 'signed' argument can handle non-bool type $bad_type"
}
gdb_test "python print(arch.void_type())" \
"void" \
"get void type"
# Test for gdb.architecture_names(). First we're going to grab the
# complete list of architecture names using the 'complete' command.
set arch_names []

View File

@@ -61,6 +61,10 @@ gdb_py_test_silent_cmd "python sblock = block.static_block" \
"Get block, static_block" 1
gdb_test "python print (gblock.is_global)" "True" "is the global block"
gdb_test "python print (sblock.is_static)" "True" "is the static block"
gdb_test "python print (len(gblock.subblocks) > 0)" "True" \
"global block contains at least one block"
gdb_test "python print (sblock in gblock.subblocks)" "True" \
"global block contains at static block"
# Move up superblock(s) until we reach function block_func.
gdb_test_no_output "python block = block.superblock" "get superblock"
@@ -100,6 +104,8 @@ gdb_test "python print (repr (block))" \
"Check block in many_locals_func"
gdb_test "python print (block.function)" "many_locals_func" \
"many_locals_func block"
gdb_test "python print(block.compunit)" "<gdb\.Compunit object at .*>" \
"test compunit property"
# Switch frames, then test for main block.
gdb_test "up" ".*"
@@ -109,6 +115,49 @@ gdb_test "python print (repr (block))" "<gdb.Block main \{.*\}>" \
"Check Frame 2's block not None"
gdb_test "python print (block.function)" "main" "main block"
# Test creation of blocks. For that we create a new compunit to make sure
# there's space for new blocks to fit in.
gdb_py_test_silent_cmd "python cu = gdb.Compunit(\"dynamic\", gdb.current_progspace().objfiles()\[0\], 100, 200)" \
"Create new compunit" 1
gdb_test "python print ( gdb.Block(cu.static_block(), 100, 150))" \
"<gdb.Block <anonymous> \{.*\}>" \
"Create new block"
gdb_test "python print ( gdb.Block(\"xxx\", 160, 170))" \
"TypeError.*:.*" \
"Try create new block with non-block superblock"
gdb_test "python print ( gdb.Block(cu.static_block(), 170, 160))" \
"ValueError.*:.*" \
"Try create new block with start > end"
gdb_test "python print ( gdb.Block(cu.static_block(), 70, 160))" \
"ValueError.*:.*" \
"Try create new block with outside superblock"
gdb_test "python print ( gdb.Block(cu.static_block(), 140, 160))" \
"ValueError.*:.*" \
"Try create new block overlaping with sibling"
gdb_test "python print ( gdb.Block(cu.static_block(), 160, 170))" \
"<gdb.Block <anonymous> \{.*\}>" \
"Create sibling block"
# Test adding symbols to a block.
gdb_py_test_silent_cmd "python symtab = gdb.Symtab(\"some_file.txt\", cu)" \
"Create new symtab" 1
gdb_py_test_silent_cmd "python typ = gdb.selected_inferior().architecture().integer_type(0).function()" \
"Create type of new symbol" 1
gdb_py_test_silent_cmd "python sym = gdb.Symbol(\"static_block\", symtab, typ, gdb.SYMBOL_FUNCTION_DOMAIN, gdb.SYMBOL_LOC_BLOCK, cu.static_block() )" \
"Create new symbol" 1
gdb_test "python print ( sym in list(cu.global_block()) )" \
"False" \
"Symbol is not in global block"
gdb_py_test_silent_cmd "python cu.global_block().add_symbol(sym)" \
"Add new symbol to block" 1
gdb_test "python print ( sym in list(cu.global_block()) )" \
"True" \
"Symbol is in global block"
gdb_test "python print ( cu.global_block().add_symbol(cu))" \
"TypeError.*:.*" \
"Add non-symbol to block"
# Test Block is_valid. This must always be the last test in this
# testcase as it unloads the object file.
delete_breakpoints

View File

@@ -0,0 +1,102 @@
# Copyright (C) 2024-2024 Free Software Foundation, Inc.
# 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/>.
# This file is part of the GDB testsuite. It tests the compunit
# support in Python.
load_lib gdb-python.exp
require allow_python_tests
standard_testfile py-objfile.c
if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
return -1
}
if {![runto_main]} {
return 0
}
set python_error_text "Error occurred in Python.*"
gdb_py_test_silent_cmd "python sym = gdb.lookup_symbol(\"some_var\")" \
"Find a symbol in objfile" 1
gdb_py_test_silent_cmd "python objfile = sym\[0\].symtab.objfile" \
"Get backing object file" 1
gdb_test "python print (len(objfile.compunits()) > 0)" \
"True" \
"Get objfile compunits"
gdb_test "python print (objfile.compunits())" \
"\\\[<gdb\.Compunit object at .*>\\\]" \
"Objfile compunits return a sequence of gdb.Compunit"
gdb_test "python print (objfile.compunits()\[0\] == objfile.compunits()\[0\])" \
"True" \
"Compunits are comparable"
gdb_test "python print (len(objfile.compunits()\[0\].symtabs) > 0)" \
"True" \
"Get compunit symtabs"
gdb_test "python print (objfile.compunits()\[0\].symtabs)" \
"\\\[<gdb\.Symtab.*>\\\]" \
"Compunit symtabs return a sequence of gdb.Symtab"
# Test creation of compunits
gdb_py_test_silent_cmd "python cu = gdb.Compunit(\"compunit1\", objfile, 200, 300)" \
"Create compunit1" 1
gdb_test "python print(cu)" \
"<gdb\.Compunit object at .*>" \
"Print created compunit1"
gdb_test "python print(cu in objfile.compunits())" \
"True" \
"Test compunit1 is in objfile.compunits()"
gdb_test "python print(cu.global_block().start)" \
"0" \
"Test compunit1.global_block().start is 0"
gdb_test "python print(cu.global_block().end)" \
"0" \
"Test compunit1.global_block().end is 0"
gdb_test "python print(cu.static_block().start)" \
"0" \
"Test compunit1.static_block().start is 0"
gdb_test "python print(cu.static_block().end)" \
"0" \
"Test compunit1.static_block().end is 0"
gdb_py_test_silent_cmd "python cu2 = gdb.Compunit(\"dynamic2\", objfile, 400, 500, 24)" \
"Create compunit2 with capacity fo 24 blocks" 1
gdb_test "python cu3 = gdb.Compunit(\"dynamic3\", gdb, 600, 700)" \
"TypeError.*:.*" \
"Create compunit3 passing non-objfile"
gdb_test "python cu4 = gdb.Compunit(\"dynamic4\", objfile, 900, 800)" \
"ValueError.*:.*" \
"Create compunit4 passing invalid global block range"
gdb_test "python cu5 = gdb.Compunit(\"dynamic5\", objfile, 225, 325)" \
"ValueError.*:.*" \
"Create compunit4 passing overlapping global block range"
gdb_unload "unload 1"
gdb_test "python print (objfile.is_valid())" "False" \
"Get objfile validity after unload"
gdb_test "python print (objfile.compunits())" "RuntimeError.*: Objfile no longer exists.*" \
"Get objfile compunits after unload"
gdb_py_test_silent_cmd "python cu6 = gdb.Compunit(\"dynamic6\", objfile)" \
"Create compunit4 passing invalid objfile" 1

View File

@@ -0,0 +1,61 @@
/* Copyright (C) 2024-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/>. */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
/* "JIT-ed" function, with the prototype `long (long, long)`. */
static const unsigned char jit_function_add_code[] = {
0x48, 0x01, 0xfe, /* add %rdi,%rsi */
0x48, 0x89, 0xf0, /* mov %rsi,%rax */
0xc3, /* retq */
};
/* Dummy function to inform the debugger a new code has been installed. */
void jit_register_code (char * name, uintptr_t code, size_t size)
{}
/* Dummy function to inform the debugger that code has been installed. */
void jit_unregister_code (uintptr_t code)
{}
int
main (int argc, char **argv)
{
void *code = mmap (NULL, getpagesize (), PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
assert (code != MAP_FAILED);
/* "Compile" jit_function_add. */
memcpy (code, jit_function_add_code,
sizeof (jit_function_add_code));
jit_register_code ("jit_function_add", (uintptr_t)code, sizeof (jit_function_add_code));
((long (*)(long, long))code)(1,5); /* breakpoint 1 line */
/* "Discard" jit_function_add. */
memset(code, 0, sizeof(jit_function_add_code));
jit_unregister_code ((uintptr_t)code);
return 0; /* breakpoint 2 line */
}

View File

@@ -0,0 +1,57 @@
# Copyright (C) 2024-2024 Free Software Foundation, Inc.
# 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/>.
# This file is part of the GDB testsuite. It test the Python API to
# create symbol tables for dynamic (JIT) code and follows the example
# code given in documentation (see section JIT Interface in Python)
load_lib gdb-python.exp
require allow_python_tests
require is_x86_64_m64_target
standard_testfile
if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
return -1
}
if ![runto_main] {
return 0
}
set remote_python_file [gdb_remote_download host \
${srcdir}/${subdir}/${testfile}.py]
gdb_test_no_output "source ${remote_python_file}" "load python file"
gdb_breakpoint [gdb_get_line_number "breakpoint 1 line" ${testfile}.c]
gdb_continue_to_breakpoint "continue to breakpoint 1 line"
gdb_test "disassemble /s jit_function_add" \
"Dump of assembler code for function jit_function_add:.*End of assembler dump." \
"disassemble jit_function_add"
gdb_breakpoint "jit_function_add"
gdb_continue_to_breakpoint "continue to jit_function_add"
gdb_test "bt 1" \
"#0 jit_function_add \\(\\) at py-jit.c:.*" \
"bt 1"
gdb_breakpoint [gdb_get_line_number "breakpoint 2 line" ${testfile}.c]
gdb_continue_to_breakpoint "continue to breakpoint 2 line"
gdb_test "disassemble jit_function_add" \
"No symbol \"jit_function_add\" in current context." \
"disassemble jit_function_add after code has been unregistered"

View File

@@ -0,0 +1,110 @@
# Copyright (C) 2024-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/>. */
# This code is same (modulo small tweaks) as the code in documentation,
# section "JIT Interface in Python". If changed the documentation should
# be checked and updated accordingly if necessary.
import gdb
objfile = None
compunit = None
block = None
symtab = None
symbol = None
def get_linetable_entries(addr):
# Entries are not in increasing order to test that
# gdb.LineTable.__init__() sorts them properly.
return [
gdb.LineTableEntry(31, addr+6, True),
gdb.LineTableEntry(29, addr, True),
gdb.LineTableEntry(30, addr+3, True)
]
class JITRegisterCode(gdb.Breakpoint):
def stop(self):
global objfile
global compunit
global block
global symtab
global symbol
frame = gdb.newest_frame()
name = frame.read_var('name').string()
addr = int(frame.read_var('code'))
size = int(frame.read_var('size'))
linetable_entries = get_linetable_entries(addr)
# Create objfile and compunit for allocated "jit" code
objfile = gdb.Objfile(name)
compunit = gdb.Compunit(name, objfile, addr, addr + size)
# Mark the objfile as "jitted code". This will be used later when
# unregistering discarded code to check the objfile was indeed
# created for jitted code.
setattr(objfile, "is_jit_code", True)
# Create block for jitted function
block = gdb.Block(compunit.static_block(), addr, addr + size)
# Create symbol table holding info about jitted function, ...
symtab = gdb.Symtab("py-jit.c", compunit)
linetable = gdb.LineTable(symtab, linetable_entries)
# ...type of the jitted function...
int64_t = gdb.selected_inferior().architecture().integer_type(64)
func_t = int64_t.function(int64_t, int64_t)
# ...and symbol representing jitted function.
symbol = gdb.Symbol(name, symtab, func_t,
gdb.SYMBOL_FUNCTION_DOMAIN, gdb.SYMBOL_LOC_BLOCK,
block)
# Finally, register the symbol in static block
compunit.static_block().add_symbol(symbol)
return False # Continue execution
# Create breakpoint to register new code
JITRegisterCode("jit_register_code", internal=True)
class JITUnregisterCode(gdb.Breakpoint):
def stop(self):
frame = gdb.newest_frame()
addr = int(frame.read_var('code'))
objfile = gdb.current_progspace().objfile_for_address(addr)
if objfile is None:
# No objfile for given addr - bail out (this should not happen)
assert False
return False # Continue execution
if not getattr(objfile, "is_jit_code", False):
# Not a jitted addr - bail out (this should not happen either)
assert False
return False # Continue execution
# Remove the objfile and all debug info associated with it.
objfile.unlink()
return False # Continue execution
# Create breakpoint to discard old code
JITUnregisterCode("jit_unregister_code", internal=True)

View File

@@ -39,12 +39,12 @@ gdb_py_test_silent_cmd "python lt = gdb.selected_frame().find_sal().symtab.linet
gdb_test_multiline "input simple command" \
"python" "" \
"def list_lines():" "" \
"def list_lines(lt):" "" \
" for l in lt:" "" \
" print ('L' + str(l.line) + ' A ' + hex(l.pc))" "" \
"end" ""
gdb_test "python list_lines()" \
gdb_test "python list_lines(lt)" \
"L20 A $hex.*L21 A $hex.*L22 A $hex.*L24 A $hex.*L25 A $hex.*L40 A $hex.*L42 A $hex.*L44 A $hex.*L42 A $hex.*L46 A $hex.*" \
"test linetable iterator addr"
gdb_test "python print(len(lt.line(42)))" "2" \
@@ -53,6 +53,16 @@ gdb_test "python print(len(lt.line(20)))" "1" \
"Test length of a single pc line"
gdb_test "python print(lt.line(1))" "None" \
"Test None returned for line with no pc"
gdb_test "python print(next(iter(lt)).is_stmt)" \
"(True|False)" \
"Test is_stmt"
gdb_test "python print(next(iter(lt)).prologue_end)" \
"(True|False)" \
"Test prologue_end"
gdb_test "python print(next(iter(lt)).epilogue_begin)" \
"(True|False)" \
"Test epilogue_begin"
# Test gdb.Linetable.sourcelines ()
gdb_py_test_silent_cmd "python fset = lt.source_lines()" \
@@ -71,3 +81,56 @@ gdb_test "python print(lt.has_line(44))" \
gdb_test "python print(lt.has_line(10))" \
"False.*" \
"test has_pcs at line 10"
# Test gdb.LineTableEntry.__init__ ()
gdb_test "python print( gdb.LineTableEntry(10, 0xcafe0000).line)" \
"10" \
"test new LineTableEntry line"
gdb_test "python print( gdb.LineTableEntry(10, 123456).pc)" \
"123456" \
"test new LineTableEntry pc"
gdb_test "python print( gdb.LineTableEntry(10, 123456).is_stmt)" \
"False" \
"test new LineTableEntry is_stmt"
gdb_test "python print( gdb.LineTableEntry(10, 123456).prologue_end)" \
"False" \
"test new LineTableEntry prologue_end"
gdb_test "python print( gdb.LineTableEntry(10, 123456).epilogue_begin)" \
"False" \
"test new LineTableEntry epilogue_begin"
gdb_test "python print( gdb.LineTableEntry('xx', 123456).pc)" \
"TypeError.*:.*" \
"test creating invalid gdb.LineTableEntry"
gdb_test "python print( gdb.LineTableEntry(10, 123456, prologue_end = True).prologue_end)" \
"True" \
"test prologue_end keyword argument"
# Test gdb.LineTable.__init__(). To do so, we create new compunit and new
# symtab.
gdb_py_test_silent_cmd "python cu = gdb.Compunit(\"dynamic_cu\", gdb.Objfile(\"dynamic_objf\"), 0x100, 0x200)" \
"create new compunit" 1
gdb_py_test_silent_cmd "python st = gdb.Symtab(\"dynamic_st\", cu)" \
"create new symtab" 1
gdb_test "python lt2 = gdb.LineTable(st, \[gdb.LineTableEntry(10, 0x123)\]); print(lt)" \
"<gdb.LineTable object at $hex.*" \
"create new linetable"
gdb_test "python print(lt2.is_valid())" \
"True" \
"test new linetable is valid"
gdb_test "python list_lines(lt2)" \
"L10 A 0x123.*" \
"test new linetable iterator"
gdb_test "python print(len(list(gdb.LineTable(st, \[\]))))" \
"0" \
"test creating empty linetable"
gdb_test "python gdb.LineTable(123, \[gdb.LineTableEntry(10, 0x123)\])" \
"TypeError.*:.*" \
"test creating linetable with invalid symtab"
gdb_test "python gdb.LineTable(st, gdb.LineTableEntry(10, 0x123))" \
"TypeError.*:.*" \
"test creating linetable with invalid entries"
gdb_test "python gdb.LineTable(st, \[gdb.LineTableEntry(10, 0x123), \"xxx\"\])" \
"TypeError.*:.*" \
"test creating linetable with invalid element in entries"

View File

@@ -172,4 +172,52 @@ if ![ishost *-*-mingw*] {
gdb_py_test_silent_cmd "python objfile = gdb.objfiles()\[0\]" \
"get first objfile" 1
gdb_file_cmd ${binfile}
gdb_test "python print(objfile)" "<gdb.Objfile \\\(invalid\\\)>"
gdb_test "python print(objfile)" "<gdb.Objfile \\\(invalid\\\)>" "print invalid objfile"
# Test creating objfile dynamically from Python
gdb_py_test_silent_cmd "python objfile = gdb.Objfile(\"Test objfile\")" \
"create objfile" 1
gdb_test "python print(objfile)" \
"<gdb.Objfile filename=Test objfile>" \
"print dynamic objfile"
gdb_test "python print(objfile.is_file)" \
"False" \
"(dynamic) objfile.is_file"
gdb_test "python print(objfile.is_valid())" \
"True" \
"(dynamic) objfile.is_valid()"
gdb_test "python print(objfile in gdb.objfiles())" \
"True" \
"(dynamic) objfile in gdb.objfiles()"
gdb_test "python print( gdb.Objfile(\"Test objfile 2\", gdb.selected_inferior()))" \
"<gdb.Objfile filename=Test objfile 2>" \
"create objfile with inferior"
gdb_test "python print( gdb.Objfile(\"Test objfile 3\", gdb.selected_inferior(), gdb.selected_inferior().architecture()))" \
"<gdb.Objfile filename=Test objfile 3>" \
"create objfile with inferior and arch"
gdb_test "python print( gdb.Objfile(\"Test objfile 4\", gdb))" \
"TypeError.*:.*" \
"create objfile with invalid inferior"
gdb_test "python print( gdb.Objfile(\"Test objfile 5\", gdb.selected_inferior(), gdb.selected_inferior()))" \
"TypeError.*:.*" \
"create objfile with valid inferior but invalid arch"
gdb_test "python print(objfile.unlink())" \
"None" \
"remove (dynamic) objfile"
gdb_test "python print(objfile in gdb.objfiles())" \
"False" \
"removed (dynamic) objfile no longer in gdb.objfiles()"
gdb_test "python print(objfile.is_valid())" \
"False" \
"removes (dynamic) objfile is no longer valid"

View File

@@ -135,6 +135,7 @@ gdb_test "python print (func.name)" "func" "test func.name"
gdb_test "python print (func.print_name)" "func" "test func.print_name"
gdb_test "python print (func.linkage_name)" "func" "test func.linkage_name"
gdb_test "python print (func.addr_class == gdb.SYMBOL_LOC_BLOCK)" "True" "test func.addr_class"
gdb_test "python print (func.domain == gdb.SYMBOL_FUNCTION_DOMAIN)" "True" "test func.domain"
# Stop in a second file and ensure we find its local static symbol.
gdb_breakpoint "function_in_other_file"
@@ -200,6 +201,40 @@ if { [is_remote host] } {
}
gdb_test "python print (t\[0\].symtab)" "${py_symbol_c}" "get symtab"
# Test comparison for equality and non-equality
gdb_test "python print (t\[0\] == t\[0\] )"\
"True" \
"test symbol equality with itself"
gdb_test "python print (t\[0\] == a\[0\] )"\
"False" \
"test symbol equality with other symbol"
gdb_test "python print (t\[0\] == 123 )"\
"False" \
"test symbol equality with non-symbol"
gdb_test "python print (t\[0\] != t\[0\] )"\
"False" \
"test symbol non-equality with itself"
gdb_test "python print (t\[0\] != a\[0\] )"\
"True" \
"test symbol non-equality with other symbol"
gdb_test "python print (t\[0\] != 123 )"\
"True" \
"test symbol non-equality with non-symbol"
# Test creation of new symbols
gdb_py_test_silent_cmd "python s = gdb.Symbol(\"ns1\", t\[0\].symtab, t\[0\].type.function(), gdb.SYMBOL_FUNCTION_DOMAIN, gdb.SYMBOL_LOC_BLOCK, t\[0\].symtab.static_block() )" \
"create symbol" 0
gdb_test "python print (s)" \
"ns1" \
"test new symbol's __str__"
gdb_test "python print (s.symtab == t\[0\].symtab)" \
"True" \
"test new symbol's symtab"
gdb_test "python print (s.type == t\[0\].type.function())" \
"True" \
"test new symbol's type"
# C++ tests
# Recompile binary.
lappend opts c++

View File

@@ -68,6 +68,8 @@ gdb_test "python print (sal.is_valid())" "True" "test sal.is_valid"
gdb_test "python print (symtab.filename)" ".*${py_symbol_c}" "test symtab.filename"
gdb_test "python print (symtab.objfile)" \
"<gdb.Objfile filename=.*${testfile}.*>" "test symtab.objfile"
gdb_test "python print (symtab.compunit)" \
"<gdb.Compunit .*>" "test symtab.compunit"
gdb_test "python print (symtab.fullname())" ".*${full_py_symbol_c}" "test symtab.fullname"
gdb_test "python print (symtab.is_valid())" "True" "test symtab.is_valid()"
gdb_test "python print (\"qq\" in global_symbols)" "True" "test qq in global symbols"
@@ -89,6 +91,44 @@ gdb_test_multiple "python print (\"simple_struct\" in static_symbols)" \
}
}
}
# Test comparison for equality and non-equality
gdb_test "python print (symtab == symtab)"\
"True" \
"test symtab equality with itself"
gdb_test "python print (symtab == sal.symtab)"\
"True" \
"test symtab equality with other symtab object referring to the same symtab"
gdb_test "python print (symtab == 123 )"\
"False" \
"test symtab equality with non-symtab"
gdb_test "python print (symtab != symtab)"\
"False" \
"test symtab non-equality with itself"
gdb_test "python print (symtab != sal.symtab)"\
"False" \
"test symtab non-equality with other symtab object referring to the same symtab"
gdb_test "python print (symtab != 123 )"\
"True" \
"test symtab non-equality with non-symtab"
gdb_test "python print (symtab.compunit in symtab.objfile.compunits())" \
"True" "Test symtab.compunit in symtab.objfile.compunits()"
gdb_test "python print (symtab.compunit.global_block() is symtab.global_block())" \
"True" "Test symtab.compunit.global_block() is symtab.global_block()"
gdb_test "python print (symtab.compunit.static_block() is symtab.static_block())" \
"True" "Test symtab.compunit.static_block() is symtab.static_block()"
# Test creating new symtab in existing compunit
gdb_py_test_silent_cmd "python cu = symtab.compunit" "remember compunit" 1
gdb_test "python print(repr( gdb.Symtab(\"some_file.txt\", cu) ))" \
"<gdb.Symtab object at .*>" \
"test creation of symtab"
gdb_test "python print(repr( gdb.Symtab(\"some_file.txt\", (1,2,3)) ))" \
"TypeError.*:.*" \
"test creation of symtab passing non-compunit object"
# Test is_valid when the objfile is unloaded. This must be the last
# test as it unloads the object file in GDB.
@@ -100,3 +140,7 @@ gdb_test "python print (symtab.is_valid())" "False" \
gdb_test_no_output "python sal = None" "test sal destructor"
gdb_test_no_output "python symtab = None" "test symtab destructor"
gdb_test "python print(repr( gdb.Symtab(\"some_file2.txt\", cu) ))" \
"TypeError.*:.*" \
"test creation of symtab passing invalid compunit"

View File

@@ -365,6 +365,26 @@ if { [build_inferior "${binfile}" "c"] == 0 } {
gdb_test "python print(gdb.lookup_type('int').optimized_out())" \
"<optimized out>"
gdb_test_no_output "python int_t = gdb.lookup_type('int')"
gdb_test "python print(repr(int_t.function()))" \
"<gdb.Type code=TYPE_CODE_FUNC name=int \\(\\)>"
gdb_test "python print(repr(int_t.function(int_t, int_t, int_t)))" \
"<gdb.Type code=TYPE_CODE_FUNC name=int \\(int, int, int\\)>"
gdb_test "python print(repr(int_t.function(int_t, None)))" \
"<gdb.Type code=TYPE_CODE_FUNC name=int \\(int, ...\\)>"
gdb_test "python print(repr(int_t.function(None)))" \
"<gdb.Type code=TYPE_CODE_FUNC name=int \\(\\)>"
gdb_test "python print(repr(int_t.function(123)))" \
"TypeError.*:.*"
gdb_test "python print(repr(int_t.function(int_t, None, int_t)))" \
"ValueError.*:.*"
set sint [get_sizeof int 0]
gdb_test "python print(gdb.parse_and_eval('aligncheck').type.alignof)" \
$sint

View File

@@ -20,6 +20,8 @@
#if !defined (GDB_OBSTACK_H)
#define GDB_OBSTACK_H 1
#include <limits>
#include <type_traits>
#include "obstack.h"
/* Utility macros - wrap obstack alloc into something more robust. */
@@ -157,4 +159,54 @@ struct allocate_on_obstack
void operator delete[] (void *memory) {}
};
/* Implementation of Allocator concept using obstack to
allocate memory. This allows standard containers to be
used with obstack. */
template <typename T>
class obstack_allocator
{
public:
typedef T value_type;
obstack_allocator (struct obstack *obstack)
: m_obstack(obstack)
{}
template <typename U> constexpr obstack_allocator (const obstack_allocator<U>& allocator) noexcept
: m_obstack(allocator.m_obstack)
{}
T* allocate (std::size_t n)
{
if (n > std::numeric_limits<std::size_t>::max () / sizeof (T))
throw std::bad_array_new_length ();
if (auto p = static_cast<T*> (obstack_alloc (m_obstack, n * sizeof (T))))
{
return p;
}
throw std::bad_alloc ();
}
void deallocate(T* p, std::size_t n) noexcept
{}
private:
struct obstack *m_obstack;
};
template <class T, class U>
bool operator==(const obstack_allocator<T> &t, const obstack_allocator<U> &u)
{
return (std::is_same<T, U>::value_type) && (t.m_obstack == u.m_obstack);
}
template <class T, class U>
bool operator!=(const obstack_allocator<T> &t, const obstack_allocator<U> &u)
{
return ! (t == u);
}
#endif