| /* debug.c -- Handle generic debugging information. |
| Copyright (C) 1995-2021 Free Software Foundation, Inc. |
| Written by Ian Lance Taylor <ian@cygnus.com>. |
| |
| This file is part of GNU Binutils. |
| |
| 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, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA |
| 02110-1301, USA. */ |
| |
| |
| /* This file implements a generic debugging format. We may eventually |
| have readers which convert different formats into this generic |
| format, and writers which write it out. The initial impetus for |
| this was writing a converter from stabs to HP IEEE-695 debugging |
| format. */ |
| |
| #include "sysdep.h" |
| #include <assert.h> |
| #include "bfd.h" |
| #include "libiberty.h" |
| #include "filenames.h" |
| #include "debug.h" |
| |
| /* Global information we keep for debugging. A pointer to this |
| structure is the debugging handle passed to all the routines. */ |
| |
| struct debug_handle |
| { |
| /* A linked list of compilation units. */ |
| struct debug_unit *units; |
| /* The current compilation unit. */ |
| struct debug_unit *current_unit; |
| /* The current source file. */ |
| struct debug_file *current_file; |
| /* The current function. */ |
| struct debug_function *current_function; |
| /* The current block. */ |
| struct debug_block *current_block; |
| /* The current line number information for the current unit. */ |
| struct debug_lineno *current_lineno; |
| /* Mark. This is used by debug_write. */ |
| unsigned int mark; |
| /* A struct/class ID used by debug_write. */ |
| unsigned int class_id; |
| /* The base for class_id for this call to debug_write. */ |
| unsigned int base_id; |
| /* The current line number in debug_write. */ |
| struct debug_lineno *current_write_lineno; |
| unsigned int current_write_lineno_index; |
| /* A list of classes which have assigned ID's during debug_write. |
| This is linked through the next_id field of debug_class_type. */ |
| struct debug_class_id *id_list; |
| /* A list used to avoid recursion during debug_type_samep. */ |
| struct debug_type_compare_list *compare_list; |
| }; |
| |
| /* Information we keep for a single compilation unit. */ |
| |
| struct debug_unit |
| { |
| /* The next compilation unit. */ |
| struct debug_unit *next; |
| /* A list of files included in this compilation unit. The first |
| file is always the main one, and that is where the main file name |
| is stored. */ |
| struct debug_file *files; |
| /* Line number information for this compilation unit. This is not |
| stored by function, because assembler code may have line number |
| information without function information. */ |
| struct debug_lineno *linenos; |
| }; |
| |
| /* Information kept for a single source file. */ |
| |
| struct debug_file |
| { |
| /* The next source file in this compilation unit. */ |
| struct debug_file *next; |
| /* The name of the source file. */ |
| const char *filename; |
| /* Global functions, variables, types, etc. */ |
| struct debug_namespace *globals; |
| }; |
| |
| /* A type. */ |
| |
| struct debug_type_s |
| { |
| /* Kind of type. */ |
| enum debug_type_kind kind; |
| /* Size of type (0 if not known). */ |
| unsigned int size; |
| /* Type which is a pointer to this type. */ |
| debug_type pointer; |
| /* Tagged union with additional information about the type. */ |
| union |
| { |
| /* DEBUG_KIND_INDIRECT. */ |
| struct debug_indirect_type *kindirect; |
| /* DEBUG_KIND_INT. */ |
| /* Whether the integer is unsigned. */ |
| bool kint; |
| /* DEBUG_KIND_STRUCT, DEBUG_KIND_UNION, DEBUG_KIND_CLASS, |
| DEBUG_KIND_UNION_CLASS. */ |
| struct debug_class_type *kclass; |
| /* DEBUG_KIND_ENUM. */ |
| struct debug_enum_type *kenum; |
| /* DEBUG_KIND_POINTER. */ |
| struct debug_type_s *kpointer; |
| /* DEBUG_KIND_FUNCTION. */ |
| struct debug_function_type *kfunction; |
| /* DEBUG_KIND_REFERENCE. */ |
| struct debug_type_s *kreference; |
| /* DEBUG_KIND_RANGE. */ |
| struct debug_range_type *krange; |
| /* DEBUG_KIND_ARRAY. */ |
| struct debug_array_type *karray; |
| /* DEBUG_KIND_SET. */ |
| struct debug_set_type *kset; |
| /* DEBUG_KIND_OFFSET. */ |
| struct debug_offset_type *koffset; |
| /* DEBUG_KIND_METHOD. */ |
| struct debug_method_type *kmethod; |
| /* DEBUG_KIND_CONST. */ |
| struct debug_type_s *kconst; |
| /* DEBUG_KIND_VOLATILE. */ |
| struct debug_type_s *kvolatile; |
| /* DEBUG_KIND_NAMED, DEBUG_KIND_TAGGED. */ |
| struct debug_named_type *knamed; |
| } u; |
| }; |
| |
| /* Information kept for an indirect type. */ |
| |
| struct debug_indirect_type |
| { |
| /* Slot where the final type will appear. */ |
| debug_type *slot; |
| /* Tag. */ |
| const char *tag; |
| }; |
| |
| /* Information kept for a struct, union, or class. */ |
| |
| struct debug_class_type |
| { |
| /* NULL terminated array of fields. */ |
| debug_field *fields; |
| /* A mark field which indicates whether the struct has already been |
| printed. */ |
| unsigned int mark; |
| /* This is used to uniquely identify unnamed structs when printing. */ |
| unsigned int id; |
| /* The remaining fields are only used for DEBUG_KIND_CLASS and |
| DEBUG_KIND_UNION_CLASS. */ |
| /* NULL terminated array of base classes. */ |
| debug_baseclass *baseclasses; |
| /* NULL terminated array of methods. */ |
| debug_method *methods; |
| /* The type of the class providing the virtual function table for |
| this class. This may point to the type itself. */ |
| debug_type vptrbase; |
| }; |
| |
| /* Information kept for an enum. */ |
| |
| struct debug_enum_type |
| { |
| /* NULL terminated array of names. */ |
| const char **names; |
| /* Array of corresponding values. */ |
| bfd_signed_vma *values; |
| }; |
| |
| /* Information kept for a function. FIXME: We should be able to |
| record the parameter types. */ |
| |
| struct debug_function_type |
| { |
| /* Return type. */ |
| debug_type return_type; |
| /* NULL terminated array of argument types. */ |
| debug_type *arg_types; |
| /* Whether the function takes a variable number of arguments. */ |
| bool varargs; |
| }; |
| |
| /* Information kept for a range. */ |
| |
| struct debug_range_type |
| { |
| /* Range base type. */ |
| debug_type type; |
| /* Lower bound. */ |
| bfd_signed_vma lower; |
| /* Upper bound. */ |
| bfd_signed_vma upper; |
| }; |
| |
| /* Information kept for an array. */ |
| |
| struct debug_array_type |
| { |
| /* Element type. */ |
| debug_type element_type; |
| /* Range type. */ |
| debug_type range_type; |
| /* Lower bound. */ |
| bfd_signed_vma lower; |
| /* Upper bound. */ |
| bfd_signed_vma upper; |
| /* Whether this array is really a string. */ |
| bool stringp; |
| }; |
| |
| /* Information kept for a set. */ |
| |
| struct debug_set_type |
| { |
| /* Base type. */ |
| debug_type type; |
| /* Whether this set is really a bitstring. */ |
| bool bitstringp; |
| }; |
| |
| /* Information kept for an offset type (a based pointer). */ |
| |
| struct debug_offset_type |
| { |
| /* The type the pointer is an offset from. */ |
| debug_type base_type; |
| /* The type the pointer points to. */ |
| debug_type target_type; |
| }; |
| |
| /* Information kept for a method type. */ |
| |
| struct debug_method_type |
| { |
| /* The return type. */ |
| debug_type return_type; |
| /* The object type which this method is for. */ |
| debug_type domain_type; |
| /* A NULL terminated array of argument types. */ |
| debug_type *arg_types; |
| /* Whether the method takes a variable number of arguments. */ |
| bool varargs; |
| }; |
| |
| /* Information kept for a named type. */ |
| |
| struct debug_named_type |
| { |
| /* Name. */ |
| struct debug_name *name; |
| /* Real type. */ |
| debug_type type; |
| }; |
| |
| /* A field in a struct or union. */ |
| |
| struct debug_field_s |
| { |
| /* Name of the field. */ |
| const char *name; |
| /* Type of the field. */ |
| struct debug_type_s *type; |
| /* Visibility of the field. */ |
| enum debug_visibility visibility; |
| /* Whether this is a static member. */ |
| bool static_member; |
| union |
| { |
| /* If static_member is false. */ |
| struct |
| { |
| /* Bit position of the field in the struct. */ |
| unsigned int bitpos; |
| /* Size of the field in bits. */ |
| unsigned int bitsize; |
| } f; |
| /* If static_member is true. */ |
| struct |
| { |
| const char *physname; |
| } s; |
| } u; |
| }; |
| |
| /* A base class for an object. */ |
| |
| struct debug_baseclass_s |
| { |
| /* Type of the base class. */ |
| struct debug_type_s *type; |
| /* Bit position of the base class in the object. */ |
| unsigned int bitpos; |
| /* Whether the base class is virtual. */ |
| bool is_virtual; |
| /* Visibility of the base class. */ |
| enum debug_visibility visibility; |
| }; |
| |
| /* A method of an object. */ |
| |
| struct debug_method_s |
| { |
| /* The name of the method. */ |
| const char *name; |
| /* A NULL terminated array of different types of variants. */ |
| struct debug_method_variant_s **variants; |
| }; |
| |
| /* The variants of a method function of an object. These indicate |
| which method to run. */ |
| |
| struct debug_method_variant_s |
| { |
| /* The physical name of the function. */ |
| const char *physname; |
| /* The type of the function. */ |
| struct debug_type_s *type; |
| /* The visibility of the function. */ |
| enum debug_visibility visibility; |
| /* Whether the function is const. */ |
| bool constp; |
| /* Whether the function is volatile. */ |
| bool volatilep; |
| /* The offset to the function in the virtual function table. */ |
| bfd_vma voffset; |
| /* If voffset is VOFFSET_STATIC_METHOD, this is a static method. */ |
| #define VOFFSET_STATIC_METHOD ((bfd_vma) -1) |
| /* Context of a virtual method function. */ |
| struct debug_type_s *context; |
| }; |
| |
| /* A variable. This is the information we keep for a variable object. |
| This has no name; a name is associated with a variable in a |
| debug_name structure. */ |
| |
| struct debug_variable |
| { |
| /* Kind of variable. */ |
| enum debug_var_kind kind; |
| /* Type. */ |
| debug_type type; |
| /* Value. The interpretation of the value depends upon kind. */ |
| bfd_vma val; |
| }; |
| |
| /* A function. This has no name; a name is associated with a function |
| in a debug_name structure. */ |
| |
| struct debug_function |
| { |
| /* Return type. */ |
| debug_type return_type; |
| /* Parameter information. */ |
| struct debug_parameter *parameters; |
| /* Block information. The first structure on the list is the main |
| block of the function, and describes function local variables. */ |
| struct debug_block *blocks; |
| }; |
| |
| /* A function parameter. */ |
| |
| struct debug_parameter |
| { |
| /* Next parameter. */ |
| struct debug_parameter *next; |
| /* Name. */ |
| const char *name; |
| /* Type. */ |
| debug_type type; |
| /* Kind. */ |
| enum debug_parm_kind kind; |
| /* Value (meaning depends upon kind). */ |
| bfd_vma val; |
| }; |
| |
| /* A typed constant. */ |
| |
| struct debug_typed_constant |
| { |
| /* Type. */ |
| debug_type type; |
| /* Value. FIXME: We may eventually need to support non-integral |
| values. */ |
| bfd_vma val; |
| }; |
| |
| /* Information about a block within a function. */ |
| |
| struct debug_block |
| { |
| /* Next block with the same parent. */ |
| struct debug_block *next; |
| /* Parent block. */ |
| struct debug_block *parent; |
| /* List of child blocks. */ |
| struct debug_block *children; |
| /* Start address of the block. */ |
| bfd_vma start; |
| /* End address of the block. */ |
| bfd_vma end; |
| /* Local variables. */ |
| struct debug_namespace *locals; |
| }; |
| |
| /* Line number information we keep for a compilation unit. FIXME: |
| This structure is easy to create, but can be very space |
| inefficient. */ |
| |
| struct debug_lineno |
| { |
| /* More line number information for this block. */ |
| struct debug_lineno *next; |
| /* Source file. */ |
| struct debug_file *file; |
| /* Line numbers, terminated by a -1 or the end of the array. */ |
| #define DEBUG_LINENO_COUNT 10 |
| unsigned long linenos[DEBUG_LINENO_COUNT]; |
| /* Addresses for the line numbers. */ |
| bfd_vma addrs[DEBUG_LINENO_COUNT]; |
| }; |
| |
| /* A namespace. This is a mapping from names to objects. FIXME: This |
| should be implemented as a hash table. */ |
| |
| struct debug_namespace |
| { |
| /* List of items in this namespace. */ |
| struct debug_name *list; |
| /* Pointer to where the next item in this namespace should go. */ |
| struct debug_name **tail; |
| }; |
| |
| /* Kinds of objects that appear in a namespace. */ |
| |
| enum debug_object_kind |
| { |
| /* A type. */ |
| DEBUG_OBJECT_TYPE, |
| /* A tagged type (really a different sort of namespace). */ |
| DEBUG_OBJECT_TAG, |
| /* A variable. */ |
| DEBUG_OBJECT_VARIABLE, |
| /* A function. */ |
| DEBUG_OBJECT_FUNCTION, |
| /* An integer constant. */ |
| DEBUG_OBJECT_INT_CONSTANT, |
| /* A floating point constant. */ |
| DEBUG_OBJECT_FLOAT_CONSTANT, |
| /* A typed constant. */ |
| DEBUG_OBJECT_TYPED_CONSTANT |
| }; |
| |
| /* Linkage of an object that appears in a namespace. */ |
| |
| enum debug_object_linkage |
| { |
| /* Local variable. */ |
| DEBUG_LINKAGE_AUTOMATIC, |
| /* Static--either file static or function static, depending upon the |
| namespace is. */ |
| DEBUG_LINKAGE_STATIC, |
| /* Global. */ |
| DEBUG_LINKAGE_GLOBAL, |
| /* No linkage. */ |
| DEBUG_LINKAGE_NONE |
| }; |
| |
| /* A name in a namespace. */ |
| |
| struct debug_name |
| { |
| /* Next name in this namespace. */ |
| struct debug_name *next; |
| /* Name. */ |
| const char *name; |
| /* Mark. This is used by debug_write. */ |
| unsigned int mark; |
| /* Kind of object. */ |
| enum debug_object_kind kind; |
| /* Linkage of object. */ |
| enum debug_object_linkage linkage; |
| /* Tagged union with additional information about the object. */ |
| union |
| { |
| /* DEBUG_OBJECT_TYPE. */ |
| struct debug_type_s *type; |
| /* DEBUG_OBJECT_TAG. */ |
| struct debug_type_s *tag; |
| /* DEBUG_OBJECT_VARIABLE. */ |
| struct debug_variable *variable; |
| /* DEBUG_OBJECT_FUNCTION. */ |
| struct debug_function *function; |
| /* DEBUG_OBJECT_INT_CONSTANT. */ |
| bfd_vma int_constant; |
| /* DEBUG_OBJECT_FLOAT_CONSTANT. */ |
| double float_constant; |
| /* DEBUG_OBJECT_TYPED_CONSTANT. */ |
| struct debug_typed_constant *typed_constant; |
| } u; |
| }; |
| |
| /* During debug_write, a linked list of these structures is used to |
| keep track of ID numbers that have been assigned to classes. */ |
| |
| struct debug_class_id |
| { |
| /* Next ID number. */ |
| struct debug_class_id *next; |
| /* The type with the ID. */ |
| struct debug_type_s *type; |
| /* The tag; NULL if no tag. */ |
| const char *tag; |
| }; |
| |
| /* During debug_type_samep, a linked list of these structures is kept |
| on the stack to avoid infinite recursion. */ |
| |
| struct debug_type_compare_list |
| { |
| /* Next type on list. */ |
| struct debug_type_compare_list *next; |
| /* The types we are comparing. */ |
| struct debug_type_s *t1; |
| struct debug_type_s *t2; |
| }; |
| |
| /* During debug_get_real_type, a linked list of these structures is |
| kept on the stack to avoid infinite recursion. */ |
| |
| struct debug_type_real_list |
| { |
| /* Next type on list. */ |
| struct debug_type_real_list *next; |
| /* The type we are checking. */ |
| struct debug_type_s *t; |
| }; |
| |
| /* Local functions. */ |
| |
| static void debug_error (const char *); |
| static struct debug_name *debug_add_to_namespace |
| (struct debug_handle *, struct debug_namespace **, const char *, |
| enum debug_object_kind, enum debug_object_linkage); |
| static struct debug_name *debug_add_to_current_namespace |
| (struct debug_handle *, const char *, enum debug_object_kind, |
| enum debug_object_linkage); |
| static struct debug_type_s *debug_make_type |
| (struct debug_handle *, enum debug_type_kind, unsigned int); |
| static struct debug_type_s *debug_get_real_type |
| (void *, debug_type, struct debug_type_real_list *); |
| static bool debug_write_name |
| (struct debug_handle *, const struct debug_write_fns *, void *, |
| struct debug_name *); |
| static bool debug_write_type |
| (struct debug_handle *, const struct debug_write_fns *, void *, |
| struct debug_type_s *, struct debug_name *); |
| static bool debug_write_class_type |
| (struct debug_handle *, const struct debug_write_fns *, void *, |
| struct debug_type_s *, const char *); |
| static bool debug_write_function |
| (struct debug_handle *, const struct debug_write_fns *, void *, |
| const char *, enum debug_object_linkage, struct debug_function *); |
| static bool debug_write_block |
| (struct debug_handle *, const struct debug_write_fns *, void *, |
| struct debug_block *); |
| static bool debug_write_linenos |
| (struct debug_handle *, const struct debug_write_fns *, void *, bfd_vma); |
| static bool debug_set_class_id |
| (struct debug_handle *, const char *, struct debug_type_s *); |
| static bool debug_type_samep |
| (struct debug_handle *, struct debug_type_s *, struct debug_type_s *); |
| static bool debug_class_type_samep |
| (struct debug_handle *, struct debug_type_s *, struct debug_type_s *); |
| |
| /* Issue an error message. */ |
| |
| static void |
| debug_error (const char *message) |
| { |
| fprintf (stderr, "%s\n", message); |
| } |
| |
| /* Add an object to a namespace. */ |
| |
| static struct debug_name * |
| debug_add_to_namespace (struct debug_handle *info ATTRIBUTE_UNUSED, |
| struct debug_namespace **nsp, const char *name, |
| enum debug_object_kind kind, |
| enum debug_object_linkage linkage) |
| { |
| struct debug_name *n; |
| struct debug_namespace *ns; |
| |
| n = (struct debug_name *) xmalloc (sizeof *n); |
| memset (n, 0, sizeof *n); |
| |
| n->name = name; |
| n->kind = kind; |
| n->linkage = linkage; |
| |
| ns = *nsp; |
| if (ns == NULL) |
| { |
| ns = (struct debug_namespace *) xmalloc (sizeof *ns); |
| memset (ns, 0, sizeof *ns); |
| |
| ns->tail = &ns->list; |
| |
| *nsp = ns; |
| } |
| |
| *ns->tail = n; |
| ns->tail = &n->next; |
| |
| return n; |
| } |
| |
| /* Add an object to the current namespace. */ |
| |
| static struct debug_name * |
| debug_add_to_current_namespace (struct debug_handle *info, const char *name, |
| enum debug_object_kind kind, |
| enum debug_object_linkage linkage) |
| { |
| struct debug_namespace **nsp; |
| |
| if (info->current_unit == NULL |
| || info->current_file == NULL) |
| { |
| debug_error (_("debug_add_to_current_namespace: no current file")); |
| return NULL; |
| } |
| |
| if (info->current_block != NULL) |
| nsp = &info->current_block->locals; |
| else |
| nsp = &info->current_file->globals; |
| |
| return debug_add_to_namespace (info, nsp, name, kind, linkage); |
| } |
| |
| /* Return a handle for debugging information. */ |
| |
| void * |
| debug_init (void) |
| { |
| struct debug_handle *ret; |
| |
| ret = (struct debug_handle *) xmalloc (sizeof *ret); |
| memset (ret, 0, sizeof *ret); |
| return (void *) ret; |
| } |
| |
| /* Set the source filename. This implicitly starts a new compilation |
| unit. */ |
| |
| bool |
| debug_set_filename (void *handle, const char *name) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_file *nfile; |
| struct debug_unit *nunit; |
| |
| if (name == NULL) |
| name = ""; |
| |
| nfile = (struct debug_file *) xmalloc (sizeof *nfile); |
| memset (nfile, 0, sizeof *nfile); |
| |
| nfile->filename = name; |
| |
| nunit = (struct debug_unit *) xmalloc (sizeof *nunit); |
| memset (nunit, 0, sizeof *nunit); |
| |
| nunit->files = nfile; |
| info->current_file = nfile; |
| |
| if (info->current_unit != NULL) |
| info->current_unit->next = nunit; |
| else |
| { |
| assert (info->units == NULL); |
| info->units = nunit; |
| } |
| |
| info->current_unit = nunit; |
| |
| info->current_function = NULL; |
| info->current_block = NULL; |
| info->current_lineno = NULL; |
| |
| return true; |
| } |
| |
| /* Change source files to the given file name. This is used for |
| include files in a single compilation unit. */ |
| |
| bool |
| debug_start_source (void *handle, const char *name) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_file *f, **pf; |
| |
| if (name == NULL) |
| name = ""; |
| |
| if (info->current_unit == NULL) |
| { |
| debug_error (_("debug_start_source: no debug_set_filename call")); |
| return false; |
| } |
| |
| for (f = info->current_unit->files; f != NULL; f = f->next) |
| { |
| if (filename_cmp (f->filename, name) == 0) |
| { |
| info->current_file = f; |
| return true; |
| } |
| } |
| |
| f = (struct debug_file *) xmalloc (sizeof *f); |
| memset (f, 0, sizeof *f); |
| |
| f->filename = name; |
| |
| for (pf = &info->current_file->next; |
| *pf != NULL; |
| pf = &(*pf)->next) |
| ; |
| *pf = f; |
| |
| info->current_file = f; |
| |
| return true; |
| } |
| |
| /* Record a function definition. This implicitly starts a function |
| block. The debug_type argument is the type of the return value. |
| The boolean indicates whether the function is globally visible. |
| The bfd_vma is the address of the start of the function. Currently |
| the parameter types are specified by calls to |
| debug_record_parameter. FIXME: There is no way to specify nested |
| functions. */ |
| |
| bool |
| debug_record_function (void *handle, const char *name, |
| debug_type return_type, bool global, |
| bfd_vma addr) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_function *f; |
| struct debug_block *b; |
| struct debug_name *n; |
| |
| if (name == NULL) |
| name = ""; |
| if (return_type == NULL) |
| return false; |
| |
| if (info->current_unit == NULL) |
| { |
| debug_error (_("debug_record_function: no debug_set_filename call")); |
| return false; |
| } |
| |
| f = (struct debug_function *) xmalloc (sizeof *f); |
| memset (f, 0, sizeof *f); |
| |
| f->return_type = return_type; |
| |
| b = (struct debug_block *) xmalloc (sizeof *b); |
| memset (b, 0, sizeof *b); |
| |
| b->start = addr; |
| b->end = (bfd_vma) -1; |
| |
| f->blocks = b; |
| |
| info->current_function = f; |
| info->current_block = b; |
| |
| /* FIXME: If we could handle nested functions, this would be the |
| place: we would want to use a different namespace. */ |
| n = debug_add_to_namespace (info, |
| &info->current_file->globals, |
| name, |
| DEBUG_OBJECT_FUNCTION, |
| (global |
| ? DEBUG_LINKAGE_GLOBAL |
| : DEBUG_LINKAGE_STATIC)); |
| if (n == NULL) |
| return false; |
| |
| n->u.function = f; |
| |
| return true; |
| } |
| |
| /* Record a parameter for the current function. */ |
| |
| bool |
| debug_record_parameter (void *handle, const char *name, debug_type type, |
| enum debug_parm_kind kind, bfd_vma val) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_parameter *p, **pp; |
| |
| if (name == NULL || type == NULL) |
| return false; |
| |
| if (info->current_unit == NULL |
| || info->current_function == NULL) |
| { |
| debug_error (_("debug_record_parameter: no current function")); |
| return false; |
| } |
| |
| p = (struct debug_parameter *) xmalloc (sizeof *p); |
| memset (p, 0, sizeof *p); |
| |
| p->name = name; |
| p->type = type; |
| p->kind = kind; |
| p->val = val; |
| |
| for (pp = &info->current_function->parameters; |
| *pp != NULL; |
| pp = &(*pp)->next) |
| ; |
| *pp = p; |
| |
| return true; |
| } |
| |
| /* End a function. FIXME: This should handle function nesting. */ |
| |
| bool |
| debug_end_function (void *handle, bfd_vma addr) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| |
| if (info->current_unit == NULL |
| || info->current_block == NULL |
| || info->current_function == NULL) |
| { |
| debug_error (_("debug_end_function: no current function")); |
| return false; |
| } |
| |
| if (info->current_block->parent != NULL) |
| { |
| debug_error (_("debug_end_function: some blocks were not closed")); |
| return false; |
| } |
| |
| info->current_block->end = addr; |
| |
| info->current_function = NULL; |
| info->current_block = NULL; |
| |
| return true; |
| } |
| |
| /* Start a block in a function. All local information will be |
| recorded in this block, until the matching call to debug_end_block. |
| debug_start_block and debug_end_block may be nested. The bfd_vma |
| argument is the address at which this block starts. */ |
| |
| bool |
| debug_start_block (void *handle, bfd_vma addr) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_block *b, **pb; |
| |
| /* We must always have a current block: debug_record_function sets |
| one up. */ |
| if (info->current_unit == NULL |
| || info->current_block == NULL) |
| { |
| debug_error (_("debug_start_block: no current block")); |
| return false; |
| } |
| |
| b = (struct debug_block *) xmalloc (sizeof *b); |
| memset (b, 0, sizeof *b); |
| |
| b->parent = info->current_block; |
| b->start = addr; |
| b->end = (bfd_vma) -1; |
| |
| /* This new block is a child of the current block. */ |
| for (pb = &info->current_block->children; |
| *pb != NULL; |
| pb = &(*pb)->next) |
| ; |
| *pb = b; |
| |
| info->current_block = b; |
| |
| return true; |
| } |
| |
| /* Finish a block in a function. This matches the call to |
| debug_start_block. The argument is the address at which this block |
| ends. */ |
| |
| bool |
| debug_end_block (void *handle, bfd_vma addr) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_block *parent; |
| |
| if (info->current_unit == NULL |
| || info->current_block == NULL) |
| { |
| debug_error (_("debug_end_block: no current block")); |
| return false; |
| } |
| |
| parent = info->current_block->parent; |
| if (parent == NULL) |
| { |
| debug_error (_("debug_end_block: attempt to close top level block")); |
| return false; |
| } |
| |
| info->current_block->end = addr; |
| |
| info->current_block = parent; |
| |
| return true; |
| } |
| |
| /* Associate a line number in the current source file and function |
| with a given address. */ |
| |
| bool |
| debug_record_line (void *handle, unsigned long lineno, bfd_vma addr) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_lineno *l; |
| unsigned int i; |
| |
| if (info->current_unit == NULL) |
| { |
| debug_error (_("debug_record_line: no current unit")); |
| return false; |
| } |
| |
| l = info->current_lineno; |
| if (l != NULL && l->file == info->current_file) |
| { |
| for (i = 0; i < DEBUG_LINENO_COUNT; i++) |
| { |
| if (l->linenos[i] == (unsigned long) -1) |
| { |
| l->linenos[i] = lineno; |
| l->addrs[i] = addr; |
| return true; |
| } |
| } |
| } |
| |
| /* If we get here, then either 1) there is no current_lineno |
| structure, which means this is the first line number in this |
| compilation unit, 2) the current_lineno structure is for a |
| different file, or 3) the current_lineno structure is full. |
| Regardless, we want to allocate a new debug_lineno structure, put |
| it in the right place, and make it the new current_lineno |
| structure. */ |
| |
| l = (struct debug_lineno *) xmalloc (sizeof *l); |
| memset (l, 0, sizeof *l); |
| |
| l->file = info->current_file; |
| l->linenos[0] = lineno; |
| l->addrs[0] = addr; |
| for (i = 1; i < DEBUG_LINENO_COUNT; i++) |
| l->linenos[i] = (unsigned long) -1; |
| |
| if (info->current_lineno != NULL) |
| info->current_lineno->next = l; |
| else |
| info->current_unit->linenos = l; |
| |
| info->current_lineno = l; |
| |
| return true; |
| } |
| |
| /* Start a named common block. This is a block of variables that may |
| move in memory. */ |
| |
| bool |
| debug_start_common_block (void *handle ATTRIBUTE_UNUSED, |
| const char *name ATTRIBUTE_UNUSED) |
| { |
| /* FIXME */ |
| debug_error (_("debug_start_common_block: not implemented")); |
| return false; |
| } |
| |
| /* End a named common block. */ |
| |
| bool |
| debug_end_common_block (void *handle ATTRIBUTE_UNUSED, |
| const char *name ATTRIBUTE_UNUSED) |
| { |
| /* FIXME */ |
| debug_error (_("debug_end_common_block: not implemented")); |
| return false; |
| } |
| |
| /* Record a named integer constant. */ |
| |
| bool |
| debug_record_int_const (void *handle, const char *name, bfd_vma val) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_name *n; |
| |
| if (name == NULL) |
| return false; |
| |
| n = debug_add_to_current_namespace (info, name, DEBUG_OBJECT_INT_CONSTANT, |
| DEBUG_LINKAGE_NONE); |
| if (n == NULL) |
| return false; |
| |
| n->u.int_constant = val; |
| |
| return true; |
| } |
| |
| /* Record a named floating point constant. */ |
| |
| bool |
| debug_record_float_const (void *handle, const char *name, double val) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_name *n; |
| |
| if (name == NULL) |
| return false; |
| |
| n = debug_add_to_current_namespace (info, name, DEBUG_OBJECT_FLOAT_CONSTANT, |
| DEBUG_LINKAGE_NONE); |
| if (n == NULL) |
| return false; |
| |
| n->u.float_constant = val; |
| |
| return true; |
| } |
| |
| /* Record a typed constant with an integral value. */ |
| |
| bool |
| debug_record_typed_const (void *handle, const char *name, debug_type type, |
| bfd_vma val) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_name *n; |
| struct debug_typed_constant *tc; |
| |
| if (name == NULL || type == NULL) |
| return false; |
| |
| n = debug_add_to_current_namespace (info, name, DEBUG_OBJECT_TYPED_CONSTANT, |
| DEBUG_LINKAGE_NONE); |
| if (n == NULL) |
| return false; |
| |
| tc = (struct debug_typed_constant *) xmalloc (sizeof *tc); |
| memset (tc, 0, sizeof *tc); |
| |
| tc->type = type; |
| tc->val = val; |
| |
| n->u.typed_constant = tc; |
| |
| return true; |
| } |
| |
| /* Record a label. */ |
| |
| bool |
| debug_record_label (void *handle ATTRIBUTE_UNUSED, |
| const char *name ATTRIBUTE_UNUSED, |
| debug_type type ATTRIBUTE_UNUSED, |
| bfd_vma addr ATTRIBUTE_UNUSED) |
| { |
| /* FIXME. */ |
| debug_error (_("debug_record_label: not implemented")); |
| return false; |
| } |
| |
| /* Record a variable. */ |
| |
| bool |
| debug_record_variable (void *handle, const char *name, debug_type type, |
| enum debug_var_kind kind, bfd_vma val) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_namespace **nsp; |
| enum debug_object_linkage linkage; |
| struct debug_name *n; |
| struct debug_variable *v; |
| |
| if (name == NULL || type == NULL) |
| return false; |
| |
| if (info->current_unit == NULL |
| || info->current_file == NULL) |
| { |
| debug_error (_("debug_record_variable: no current file")); |
| return false; |
| } |
| |
| if (kind == DEBUG_GLOBAL || kind == DEBUG_STATIC) |
| { |
| nsp = &info->current_file->globals; |
| if (kind == DEBUG_GLOBAL) |
| linkage = DEBUG_LINKAGE_GLOBAL; |
| else |
| linkage = DEBUG_LINKAGE_STATIC; |
| } |
| else |
| { |
| if (info->current_block == NULL) |
| nsp = &info->current_file->globals; |
| else |
| nsp = &info->current_block->locals; |
| linkage = DEBUG_LINKAGE_AUTOMATIC; |
| } |
| |
| n = debug_add_to_namespace (info, nsp, name, DEBUG_OBJECT_VARIABLE, linkage); |
| if (n == NULL) |
| return false; |
| |
| v = (struct debug_variable *) xmalloc (sizeof *v); |
| memset (v, 0, sizeof *v); |
| |
| v->kind = kind; |
| v->type = type; |
| v->val = val; |
| |
| n->u.variable = v; |
| |
| return true; |
| } |
| |
| /* Make a type with a given kind and size. */ |
| |
| static struct debug_type_s * |
| debug_make_type (struct debug_handle *info ATTRIBUTE_UNUSED, |
| enum debug_type_kind kind, unsigned int size) |
| { |
| struct debug_type_s *t; |
| |
| t = (struct debug_type_s *) xmalloc (sizeof *t); |
| memset (t, 0, sizeof *t); |
| |
| t->kind = kind; |
| t->size = size; |
| |
| return t; |
| } |
| |
| /* Make an indirect type which may be used as a placeholder for a type |
| which is referenced before it is defined. */ |
| |
| debug_type |
| debug_make_indirect_type (void *handle, debug_type *slot, const char *tag) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| struct debug_indirect_type *i; |
| |
| t = debug_make_type (info, DEBUG_KIND_INDIRECT, 0); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| i = (struct debug_indirect_type *) xmalloc (sizeof *i); |
| memset (i, 0, sizeof *i); |
| |
| i->slot = slot; |
| i->tag = tag; |
| |
| t->u.kindirect = i; |
| |
| return t; |
| } |
| |
| /* Make a void type. There is only one of these. */ |
| |
| debug_type |
| debug_make_void_type (void *handle) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| |
| return debug_make_type (info, DEBUG_KIND_VOID, 0); |
| } |
| |
| /* Make an integer type of a given size. The boolean argument is true |
| if the integer is unsigned. */ |
| |
| debug_type |
| debug_make_int_type (void *handle, unsigned int size, bool unsignedp) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| |
| t = debug_make_type (info, DEBUG_KIND_INT, size); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| t->u.kint = unsignedp; |
| |
| return t; |
| } |
| |
| /* Make a floating point type of a given size. FIXME: On some |
| platforms, like an Alpha, you probably need to be able to specify |
| the format. */ |
| |
| debug_type |
| debug_make_float_type (void *handle, unsigned int size) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| |
| return debug_make_type (info, DEBUG_KIND_FLOAT, size); |
| } |
| |
| /* Make a boolean type of a given size. */ |
| |
| debug_type |
| debug_make_bool_type (void *handle, unsigned int size) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| |
| return debug_make_type (info, DEBUG_KIND_BOOL, size); |
| } |
| |
| /* Make a complex type of a given size. */ |
| |
| debug_type |
| debug_make_complex_type (void *handle, unsigned int size) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| |
| return debug_make_type (info, DEBUG_KIND_COMPLEX, size); |
| } |
| |
| /* Make a structure type. The second argument is true for a struct, |
| false for a union. The third argument is the size of the struct. |
| The fourth argument is a NULL terminated array of fields. */ |
| |
| debug_type |
| debug_make_struct_type (void *handle, bool structp, bfd_vma size, |
| debug_field *fields) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| struct debug_class_type *c; |
| |
| t = debug_make_type (info, |
| structp ? DEBUG_KIND_STRUCT : DEBUG_KIND_UNION, |
| size); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| c = (struct debug_class_type *) xmalloc (sizeof *c); |
| memset (c, 0, sizeof *c); |
| |
| c->fields = fields; |
| |
| t->u.kclass = c; |
| |
| return t; |
| } |
| |
| /* Make an object type. The first three arguments after the handle |
| are the same as for debug_make_struct_type. The next arguments are |
| a NULL terminated array of base classes, a NULL terminated array of |
| methods, the type of the object holding the virtual function table |
| if it is not this object, and a boolean which is true if this |
| object has its own virtual function table. */ |
| |
| debug_type |
| debug_make_object_type (void *handle, bool structp, bfd_vma size, |
| debug_field *fields, debug_baseclass *baseclasses, |
| debug_method *methods, debug_type vptrbase, |
| bool ownvptr) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| struct debug_class_type *c; |
| |
| t = debug_make_type (info, |
| structp ? DEBUG_KIND_CLASS : DEBUG_KIND_UNION_CLASS, |
| size); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| c = (struct debug_class_type *) xmalloc (sizeof *c); |
| memset (c, 0, sizeof *c); |
| |
| c->fields = fields; |
| c->baseclasses = baseclasses; |
| c->methods = methods; |
| if (ownvptr) |
| c->vptrbase = t; |
| else |
| c->vptrbase = vptrbase; |
| |
| t->u.kclass = c; |
| |
| return t; |
| } |
| |
| /* Make an enumeration type. The arguments are a null terminated |
| array of strings, and an array of corresponding values. */ |
| |
| debug_type |
| debug_make_enum_type (void *handle, const char **names, |
| bfd_signed_vma *values) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| struct debug_enum_type *e; |
| |
| t = debug_make_type (info, DEBUG_KIND_ENUM, 0); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| e = (struct debug_enum_type *) xmalloc (sizeof *e); |
| memset (e, 0, sizeof *e); |
| |
| e->names = names; |
| e->values = values; |
| |
| t->u.kenum = e; |
| |
| return t; |
| } |
| |
| /* Make a pointer to a given type. */ |
| |
| debug_type |
| debug_make_pointer_type (void *handle, debug_type type) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| |
| if (type == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| if (type->pointer != DEBUG_TYPE_NULL) |
| return type->pointer; |
| |
| t = debug_make_type (info, DEBUG_KIND_POINTER, 0); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| t->u.kpointer = type; |
| |
| type->pointer = t; |
| |
| return t; |
| } |
| |
| /* Make a function returning a given type. FIXME: We should be able |
| to record the parameter types. */ |
| |
| debug_type |
| debug_make_function_type (void *handle, debug_type type, |
| debug_type *arg_types, bool varargs) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| struct debug_function_type *f; |
| |
| if (type == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| t = debug_make_type (info, DEBUG_KIND_FUNCTION, 0); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| f = (struct debug_function_type *) xmalloc (sizeof *f); |
| memset (f, 0, sizeof *f); |
| |
| f->return_type = type; |
| f->arg_types = arg_types; |
| f->varargs = varargs; |
| |
| t->u.kfunction = f; |
| |
| return t; |
| } |
| |
| /* Make a reference to a given type. */ |
| |
| debug_type |
| debug_make_reference_type (void *handle, debug_type type) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| |
| if (type == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| t = debug_make_type (info, DEBUG_KIND_REFERENCE, 0); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| t->u.kreference = type; |
| |
| return t; |
| } |
| |
| /* Make a range of a given type from a lower to an upper bound. */ |
| |
| debug_type |
| debug_make_range_type (void *handle, debug_type type, bfd_signed_vma lower, |
| bfd_signed_vma upper) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| struct debug_range_type *r; |
| |
| if (type == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| t = debug_make_type (info, DEBUG_KIND_RANGE, 0); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| r = (struct debug_range_type *) xmalloc (sizeof *r); |
| memset (r, 0, sizeof *r); |
| |
| r->type = type; |
| r->lower = lower; |
| r->upper = upper; |
| |
| t->u.krange = r; |
| |
| return t; |
| } |
| |
| /* Make an array type. The second argument is the type of an element |
| of the array. The third argument is the type of a range of the |
| array. The fourth and fifth argument are the lower and upper |
| bounds, respectively. The sixth argument is true if this array is |
| actually a string, as in C. */ |
| |
| debug_type |
| debug_make_array_type (void *handle, debug_type element_type, |
| debug_type range_type, bfd_signed_vma lower, |
| bfd_signed_vma upper, bool stringp) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| struct debug_array_type *a; |
| |
| if (element_type == NULL || range_type == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| t = debug_make_type (info, DEBUG_KIND_ARRAY, 0); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| a = (struct debug_array_type *) xmalloc (sizeof *a); |
| memset (a, 0, sizeof *a); |
| |
| a->element_type = element_type; |
| a->range_type = range_type; |
| a->lower = lower; |
| a->upper = upper; |
| a->stringp = stringp; |
| |
| t->u.karray = a; |
| |
| return t; |
| } |
| |
| /* Make a set of a given type. For example, a Pascal set type. The |
| boolean argument is true if this set is actually a bitstring, as in |
| CHILL. */ |
| |
| debug_type |
| debug_make_set_type (void *handle, debug_type type, bool bitstringp) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| struct debug_set_type *s; |
| |
| if (type == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| t = debug_make_type (info, DEBUG_KIND_SET, 0); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| s = (struct debug_set_type *) xmalloc (sizeof *s); |
| memset (s, 0, sizeof *s); |
| |
| s->type = type; |
| s->bitstringp = bitstringp; |
| |
| t->u.kset = s; |
| |
| return t; |
| } |
| |
| /* Make a type for a pointer which is relative to an object. The |
| second argument is the type of the object to which the pointer is |
| relative. The third argument is the type that the pointer points |
| to. */ |
| |
| debug_type |
| debug_make_offset_type (void *handle, debug_type base_type, |
| debug_type target_type) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| struct debug_offset_type *o; |
| |
| if (base_type == NULL || target_type == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| t = debug_make_type (info, DEBUG_KIND_OFFSET, 0); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| o = (struct debug_offset_type *) xmalloc (sizeof *o); |
| memset (o, 0, sizeof *o); |
| |
| o->base_type = base_type; |
| o->target_type = target_type; |
| |
| t->u.koffset = o; |
| |
| return t; |
| } |
| |
| /* Make a type for a method function. The second argument is the |
| return type, the third argument is the domain, and the fourth |
| argument is a NULL terminated array of argument types. */ |
| |
| debug_type |
| debug_make_method_type (void *handle, debug_type return_type, |
| debug_type domain_type, debug_type *arg_types, |
| bool varargs) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| struct debug_method_type *m; |
| |
| if (return_type == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| t = debug_make_type (info, DEBUG_KIND_METHOD, 0); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| m = (struct debug_method_type *) xmalloc (sizeof *m); |
| memset (m, 0, sizeof *m); |
| |
| m->return_type = return_type; |
| m->domain_type = domain_type; |
| m->arg_types = arg_types; |
| m->varargs = varargs; |
| |
| t->u.kmethod = m; |
| |
| return t; |
| } |
| |
| /* Make a const qualified version of a given type. */ |
| |
| debug_type |
| debug_make_const_type (void *handle, debug_type type) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| |
| if (type == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| t = debug_make_type (info, DEBUG_KIND_CONST, 0); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| t->u.kconst = type; |
| |
| return t; |
| } |
| |
| /* Make a volatile qualified version of a given type. */ |
| |
| debug_type |
| debug_make_volatile_type (void *handle, debug_type type) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| |
| if (type == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| t = debug_make_type (info, DEBUG_KIND_VOLATILE, 0); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| t->u.kvolatile = type; |
| |
| return t; |
| } |
| |
| /* Make an undefined tagged type. For example, a struct which has |
| been mentioned, but not defined. */ |
| |
| debug_type |
| debug_make_undefined_tagged_type (void *handle, const char *name, |
| enum debug_type_kind kind) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| |
| if (name == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| switch (kind) |
| { |
| case DEBUG_KIND_STRUCT: |
| case DEBUG_KIND_UNION: |
| case DEBUG_KIND_CLASS: |
| case DEBUG_KIND_UNION_CLASS: |
| case DEBUG_KIND_ENUM: |
| break; |
| |
| default: |
| debug_error (_("debug_make_undefined_type: unsupported kind")); |
| return DEBUG_TYPE_NULL; |
| } |
| |
| t = debug_make_type (info, kind, 0); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| return debug_tag_type (handle, name, t); |
| } |
| |
| /* Make a base class for an object. The second argument is the base |
| class type. The third argument is the bit position of this base |
| class in the object (always 0 unless doing multiple inheritance). |
| The fourth argument is whether this is a virtual class. The fifth |
| argument is the visibility of the base class. */ |
| |
| debug_baseclass |
| debug_make_baseclass (void *handle ATTRIBUTE_UNUSED, debug_type type, |
| bfd_vma bitpos, bool is_virtual, |
| enum debug_visibility visibility) |
| { |
| struct debug_baseclass_s *b; |
| |
| b = (struct debug_baseclass_s *) xmalloc (sizeof *b); |
| memset (b, 0, sizeof *b); |
| |
| b->type = type; |
| b->bitpos = bitpos; |
| b->is_virtual = is_virtual; |
| b->visibility = visibility; |
| |
| return b; |
| } |
| |
| /* Make a field for a struct. The second argument is the name. The |
| third argument is the type of the field. The fourth argument is |
| the bit position of the field. The fifth argument is the size of |
| the field (it may be zero). The sixth argument is the visibility |
| of the field. */ |
| |
| debug_field |
| debug_make_field (void *handle ATTRIBUTE_UNUSED, const char *name, |
| debug_type type, bfd_vma bitpos, bfd_vma bitsize, |
| enum debug_visibility visibility) |
| { |
| struct debug_field_s *f; |
| |
| f = (struct debug_field_s *) xmalloc (sizeof *f); |
| memset (f, 0, sizeof *f); |
| |
| f->name = name; |
| f->type = type; |
| f->static_member = false; |
| f->u.f.bitpos = bitpos; |
| f->u.f.bitsize = bitsize; |
| f->visibility = visibility; |
| |
| return f; |
| } |
| |
| /* Make a static member of an object. The second argument is the |
| name. The third argument is the type of the member. The fourth |
| argument is the physical name of the member (i.e., the name as a |
| global variable). The fifth argument is the visibility of the |
| member. */ |
| |
| debug_field |
| debug_make_static_member (void *handle ATTRIBUTE_UNUSED, const char *name, |
| debug_type type, const char *physname, |
| enum debug_visibility visibility) |
| { |
| struct debug_field_s *f; |
| |
| f = (struct debug_field_s *) xmalloc (sizeof *f); |
| memset (f, 0, sizeof *f); |
| |
| f->name = name; |
| f->type = type; |
| f->static_member = true; |
| f->u.s.physname = physname; |
| f->visibility = visibility; |
| |
| return f; |
| } |
| |
| /* Make a method. The second argument is the name, and the third |
| argument is a NULL terminated array of method variants. */ |
| |
| debug_method |
| debug_make_method (void *handle ATTRIBUTE_UNUSED, const char *name, |
| debug_method_variant *variants) |
| { |
| struct debug_method_s *m; |
| |
| m = (struct debug_method_s *) xmalloc (sizeof *m); |
| memset (m, 0, sizeof *m); |
| |
| m->name = name; |
| m->variants = variants; |
| |
| return m; |
| } |
| |
| /* Make a method argument. The second argument is the real name of |
| the function. The third argument is the type of the function. The |
| fourth argument is the visibility. The fifth argument is whether |
| this is a const function. The sixth argument is whether this is a |
| volatile function. The seventh argument is the offset in the |
| virtual function table, if any. The eighth argument is the virtual |
| function context. FIXME: Are the const and volatile arguments |
| necessary? Could we just use debug_make_const_type? */ |
| |
| debug_method_variant |
| debug_make_method_variant (void *handle ATTRIBUTE_UNUSED, |
| const char *physname, debug_type type, |
| enum debug_visibility visibility, |
| bool constp, bool volatilep, |
| bfd_vma voffset, debug_type context) |
| { |
| struct debug_method_variant_s *m; |
| |
| m = (struct debug_method_variant_s *) xmalloc (sizeof *m); |
| memset (m, 0, sizeof *m); |
| |
| m->physname = physname; |
| m->type = type; |
| m->visibility = visibility; |
| m->constp = constp; |
| m->volatilep = volatilep; |
| m->voffset = voffset; |
| m->context = context; |
| |
| return m; |
| } |
| |
| /* Make a static method argument. The arguments are the same as for |
| debug_make_method_variant, except that the last two are omitted |
| since a static method can not also be virtual. */ |
| |
| debug_method_variant |
| debug_make_static_method_variant (void *handle ATTRIBUTE_UNUSED, |
| const char *physname, debug_type type, |
| enum debug_visibility visibility, |
| bool constp, bool volatilep) |
| { |
| struct debug_method_variant_s *m; |
| |
| m = (struct debug_method_variant_s *) xmalloc (sizeof *m); |
| memset (m, 0, sizeof *m); |
| |
| m->physname = physname; |
| m->type = type; |
| m->visibility = visibility; |
| m->constp = constp; |
| m->volatilep = volatilep; |
| m->voffset = VOFFSET_STATIC_METHOD; |
| |
| return m; |
| } |
| |
| /* Name a type. */ |
| |
| debug_type |
| debug_name_type (void *handle, const char *name, debug_type type) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| struct debug_named_type *n; |
| struct debug_name *nm; |
| |
| if (name == NULL || type == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| if (info->current_unit == NULL |
| || info->current_file == NULL) |
| { |
| debug_error (_("debug_name_type: no current file")); |
| return DEBUG_TYPE_NULL; |
| } |
| |
| t = debug_make_type (info, DEBUG_KIND_NAMED, 0); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| n = (struct debug_named_type *) xmalloc (sizeof *n); |
| memset (n, 0, sizeof *n); |
| |
| n->type = type; |
| |
| t->u.knamed = n; |
| |
| /* We always add the name to the global namespace. This is probably |
| wrong in some cases, but it seems to be right for stabs. FIXME. */ |
| |
| nm = debug_add_to_namespace (info, &info->current_file->globals, name, |
| DEBUG_OBJECT_TYPE, DEBUG_LINKAGE_NONE); |
| if (nm == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| nm->u.type = t; |
| |
| n->name = nm; |
| |
| return t; |
| } |
| |
| /* Tag a type. */ |
| |
| debug_type |
| debug_tag_type (void *handle, const char *name, debug_type type) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_type_s *t; |
| struct debug_named_type *n; |
| struct debug_name *nm; |
| |
| if (name == NULL || type == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| if (info->current_file == NULL) |
| { |
| debug_error (_("debug_tag_type: no current file")); |
| return DEBUG_TYPE_NULL; |
| } |
| |
| if (type->kind == DEBUG_KIND_TAGGED) |
| { |
| if (strcmp (type->u.knamed->name->name, name) == 0) |
| return type; |
| debug_error (_("debug_tag_type: extra tag attempted")); |
| return DEBUG_TYPE_NULL; |
| } |
| |
| t = debug_make_type (info, DEBUG_KIND_TAGGED, 0); |
| if (t == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| n = (struct debug_named_type *) xmalloc (sizeof *n); |
| memset (n, 0, sizeof *n); |
| |
| n->type = type; |
| |
| t->u.knamed = n; |
| |
| /* We keep a global namespace of tags for each compilation unit. I |
| don't know if that is the right thing to do. */ |
| |
| nm = debug_add_to_namespace (info, &info->current_file->globals, name, |
| DEBUG_OBJECT_TAG, DEBUG_LINKAGE_NONE); |
| if (nm == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| nm->u.tag = t; |
| |
| n->name = nm; |
| |
| return t; |
| } |
| |
| /* Record the size of a given type. */ |
| |
| bool |
| debug_record_type_size (void *handle ATTRIBUTE_UNUSED, debug_type type, |
| unsigned int size) |
| { |
| if (type->size != 0 && type->size != size) |
| fprintf (stderr, _("Warning: changing type size from %d to %d\n"), |
| type->size, size); |
| |
| type->size = size; |
| |
| return true; |
| } |
| |
| /* Find a named type. */ |
| |
| debug_type |
| debug_find_named_type (void *handle, const char *name) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_block *b; |
| struct debug_file *f; |
| |
| /* We only search the current compilation unit. I don't know if |
| this is right or not. */ |
| |
| if (info->current_unit == NULL) |
| { |
| debug_error (_("debug_find_named_type: no current compilation unit")); |
| return DEBUG_TYPE_NULL; |
| } |
| |
| for (b = info->current_block; b != NULL; b = b->parent) |
| { |
| if (b->locals != NULL) |
| { |
| struct debug_name *n; |
| |
| for (n = b->locals->list; n != NULL; n = n->next) |
| { |
| if (n->kind == DEBUG_OBJECT_TYPE |
| && n->name[0] == name[0] |
| && strcmp (n->name, name) == 0) |
| return n->u.type; |
| } |
| } |
| } |
| |
| for (f = info->current_unit->files; f != NULL; f = f->next) |
| { |
| if (f->globals != NULL) |
| { |
| struct debug_name *n; |
| |
| for (n = f->globals->list; n != NULL; n = n->next) |
| { |
| if (n->kind == DEBUG_OBJECT_TYPE |
| && n->name[0] == name[0] |
| && strcmp (n->name, name) == 0) |
| return n->u.type; |
| } |
| } |
| } |
| |
| return DEBUG_TYPE_NULL; |
| } |
| |
| /* Find a tagged type. */ |
| |
| debug_type |
| debug_find_tagged_type (void *handle, const char *name, |
| enum debug_type_kind kind) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_unit *u; |
| |
| /* We search the globals of all the compilation units. I don't know |
| if this is correct or not. It would be easy to change. */ |
| |
| for (u = info->units; u != NULL; u = u->next) |
| { |
| struct debug_file *f; |
| |
| for (f = u->files; f != NULL; f = f->next) |
| { |
| struct debug_name *n; |
| |
| if (f->globals != NULL) |
| { |
| for (n = f->globals->list; n != NULL; n = n->next) |
| { |
| if (n->kind == DEBUG_OBJECT_TAG |
| && (kind == DEBUG_KIND_ILLEGAL |
| || n->u.tag->kind == kind) |
| && n->name[0] == name[0] |
| && strcmp (n->name, name) == 0) |
| return n->u.tag; |
| } |
| } |
| } |
| } |
| |
| return DEBUG_TYPE_NULL; |
| } |
| |
| /* Get a base type. We build a linked list on the stack to avoid |
| crashing if the type is defined circularly. */ |
| |
| static struct debug_type_s * |
| debug_get_real_type (void *handle, debug_type type, |
| struct debug_type_real_list *list) |
| { |
| struct debug_type_real_list *l; |
| struct debug_type_real_list rl; |
| |
| switch (type->kind) |
| { |
| default: |
| return type; |
| |
| case DEBUG_KIND_INDIRECT: |
| case DEBUG_KIND_NAMED: |
| case DEBUG_KIND_TAGGED: |
| break; |
| } |
| |
| for (l = list; l != NULL; l = l->next) |
| { |
| if (l->t == type || l == l->next) |
| { |
| fprintf (stderr, |
| _("debug_get_real_type: circular debug information for %s\n"), |
| debug_get_type_name (handle, type)); |
| return NULL; |
| } |
| } |
| |
| rl.next = list; |
| rl.t = type; |
| |
| switch (type->kind) |
| { |
| /* The default case is just here to avoid warnings. */ |
| default: |
| case DEBUG_KIND_INDIRECT: |
| if (*type->u.kindirect->slot != NULL) |
| return debug_get_real_type (handle, *type->u.kindirect->slot, &rl); |
| return type; |
| case DEBUG_KIND_NAMED: |
| case DEBUG_KIND_TAGGED: |
| return debug_get_real_type (handle, type->u.knamed->type, &rl); |
| } |
| /*NOTREACHED*/ |
| } |
| |
| /* Get the kind of a type. */ |
| |
| enum debug_type_kind |
| debug_get_type_kind (void *handle, debug_type type) |
| { |
| if (type == NULL) |
| return DEBUG_KIND_ILLEGAL; |
| type = debug_get_real_type (handle, type, NULL); |
| if (type == NULL) |
| return DEBUG_KIND_ILLEGAL; |
| return type->kind; |
| } |
| |
| /* Get the name of a type. */ |
| |
| const char * |
| debug_get_type_name (void *handle, debug_type type) |
| { |
| if (type->kind == DEBUG_KIND_INDIRECT) |
| { |
| if (*type->u.kindirect->slot != NULL) |
| return debug_get_type_name (handle, *type->u.kindirect->slot); |
| return type->u.kindirect->tag; |
| } |
| if (type->kind == DEBUG_KIND_NAMED |
| || type->kind == DEBUG_KIND_TAGGED) |
| return type->u.knamed->name->name; |
| return NULL; |
| } |
| |
| /* Get the size of a type. */ |
| |
| bfd_vma |
| debug_get_type_size (void *handle, debug_type type) |
| { |
| if (type == NULL) |
| return 0; |
| |
| /* We don't call debug_get_real_type, because somebody might have |
| called debug_record_type_size on a named or indirect type. */ |
| |
| if (type->size != 0) |
| return type->size; |
| |
| switch (type->kind) |
| { |
| default: |
| return 0; |
| case DEBUG_KIND_INDIRECT: |
| if (*type->u.kindirect->slot != NULL) |
| return debug_get_type_size (handle, *type->u.kindirect->slot); |
| return 0; |
| case DEBUG_KIND_NAMED: |
| case DEBUG_KIND_TAGGED: |
| return debug_get_type_size (handle, type->u.knamed->type); |
| } |
| /*NOTREACHED*/ |
| } |
| |
| /* Get the return type of a function or method type. */ |
| |
| debug_type |
| debug_get_return_type (void *handle, debug_type type) |
| { |
| if (type == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| type = debug_get_real_type (handle, type, NULL); |
| if (type == NULL) |
| return DEBUG_TYPE_NULL; |
| |
| switch (type->kind) |
| { |
| default: |
| return DEBUG_TYPE_NULL; |
| case DEBUG_KIND_FUNCTION: |
| return type->u.kfunction->return_type; |
| case DEBUG_KIND_METHOD: |
| return type->u.kmethod->return_type; |
| } |
| /*NOTREACHED*/ |
| } |
| |
| /* Get the parameter types of a function or method type (except that |
| we don't currently store the parameter types of a function). */ |
| |
| const debug_type * |
| debug_get_parameter_types (void *handle, debug_type type, |
| bool *pvarargs) |
| { |
| if (type == NULL) |
| return NULL; |
| |
| type = debug_get_real_type (handle, type, NULL); |
| if (type == NULL) |
| return NULL; |
| |
| switch (type->kind) |
| { |
| default: |
| return NULL; |
| case DEBUG_KIND_FUNCTION: |
| *pvarargs = type->u.kfunction->varargs; |
| return type->u.kfunction->arg_types; |
| case DEBUG_KIND_METHOD: |
| *pvarargs = type->u.kmethod->varargs; |
| return type->u.kmethod->arg_types; |
| } |
| /*NOTREACHED*/ |
| } |
| |
| /* Get the target type of a type. */ |
| |
| debug_type |
| debug_get_target_type (void *handle, debug_type type) |
| { |
| if (type == NULL) |
| return NULL; |
| |
| type = debug_get_real_type (handle, type, NULL); |
| if (type == NULL) |
| return NULL; |
| |
| switch (type->kind) |
| { |
| default: |
| return NULL; |
| case DEBUG_KIND_POINTER: |
| return type->u.kpointer; |
| case DEBUG_KIND_REFERENCE: |
| return type->u.kreference; |
| case DEBUG_KIND_CONST: |
| return type->u.kconst; |
| case DEBUG_KIND_VOLATILE: |
| return type->u.kvolatile; |
| } |
| /*NOTREACHED*/ |
| } |
| |
| /* Get the NULL terminated array of fields for a struct, union, or |
| class. */ |
| |
| const debug_field * |
| debug_get_fields (void *handle, debug_type type) |
| { |
| if (type == NULL) |
| return NULL; |
| |
| type = debug_get_real_type (handle, type, NULL); |
| if (type == NULL) |
| return NULL; |
| |
| switch (type->kind) |
| { |
| default: |
| return NULL; |
| case DEBUG_KIND_STRUCT: |
| case DEBUG_KIND_UNION: |
| case DEBUG_KIND_CLASS: |
| case DEBUG_KIND_UNION_CLASS: |
| return type->u.kclass->fields; |
| } |
| /*NOTREACHED*/ |
| } |
| |
| /* Get the type of a field. */ |
| |
| debug_type |
| debug_get_field_type (void *handle ATTRIBUTE_UNUSED, debug_field field) |
| { |
| if (field == NULL) |
| return NULL; |
| return field->type; |
| } |
| |
| /* Get the name of a field. */ |
| |
| const char * |
| debug_get_field_name (void *handle ATTRIBUTE_UNUSED, debug_field field) |
| { |
| if (field == NULL) |
| return NULL; |
| return field->name; |
| } |
| |
| /* Get the bit position of a field. */ |
| |
| bfd_vma |
| debug_get_field_bitpos (void *handle ATTRIBUTE_UNUSED, debug_field field) |
| { |
| if (field == NULL || field->static_member) |
| return (bfd_vma) -1; |
| return field->u.f.bitpos; |
| } |
| |
| /* Get the bit size of a field. */ |
| |
| bfd_vma |
| debug_get_field_bitsize (void *handle ATTRIBUTE_UNUSED, debug_field field) |
| { |
| if (field == NULL || field->static_member) |
| return (bfd_vma) -1; |
| return field->u.f.bitsize; |
| } |
| |
| /* Get the visibility of a field. */ |
| |
| enum debug_visibility |
| debug_get_field_visibility (void *handle ATTRIBUTE_UNUSED, debug_field field) |
| { |
| if (field == NULL) |
| return DEBUG_VISIBILITY_IGNORE; |
| return field->visibility; |
| } |
| |
| /* Get the physical name of a field. */ |
| |
| const char * |
| debug_get_field_physname (void *handle ATTRIBUTE_UNUSED, debug_field field) |
| { |
| if (field == NULL || ! field->static_member) |
| return NULL; |
| return field->u.s.physname; |
| } |
| |
| /* Write out the debugging information. This is given a handle to |
| debugging information, and a set of function pointers to call. */ |
| |
| bool |
| debug_write (void *handle, const struct debug_write_fns *fns, void *fhandle) |
| { |
| struct debug_handle *info = (struct debug_handle *) handle; |
| struct debug_unit *u; |
| |
| /* We use a mark to tell whether we have already written out a |
| particular name. We use an integer, so that we don't have to |
| clear the mark fields if we happen to write out the same |
| information more than once. */ |
| ++info->mark; |
| |
| /* The base_id field holds an ID value which will never be used, so |
| that we can tell whether we have assigned an ID during this call |
| to debug_write. */ |
| info->base_id = info->class_id; |
| |
| /* We keep a linked list of classes for which was have assigned ID's |
| during this call to debug_write. */ |
| info->id_list = NULL; |
| |
| for (u = info->units; u != NULL; u = u->next) |
| { |
| struct debug_file *f; |
| bool first_file; |
| |
| info->current_write_lineno = u->linenos; |
| info->current_write_lineno_index = 0; |
| |
| if (! (*fns->start_compilation_unit) (fhandle, u->files->filename)) |
| return false; |
| |
| first_file = true; |
| for (f = u->files; f != NULL; f = f->next) |
| { |
| struct debug_name *n; |
| |
| if (first_file) |
| first_file = false; |
| else if (! (*fns->start_source) (fhandle, f->filename)) |
| return false; |
| |
| if (f->globals != NULL) |
| for (n = f->globals->list; n != NULL; n = n->next) |
| if (! debug_write_name (info, fns, fhandle, n)) |
| return false; |
| } |
| |
| /* Output any line number information which hasn't already been |
| handled. */ |
| if (! debug_write_linenos (info, fns, fhandle, (bfd_vma) -1)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* Write out an element in a namespace. */ |
| |
| static bool |
| debug_write_name (struct debug_handle *info, |
| const struct debug_write_fns *fns, void *fhandle, |
| struct debug_name *n) |
| { |
| switch (n->kind) |
| { |
| case DEBUG_OBJECT_TYPE: |
| if (! debug_write_type (info, fns, fhandle, n->u.type, n) |
| || ! (*fns->typdef) (fhandle, n->name)) |
| return false; |
| return true; |
| case DEBUG_OBJECT_TAG: |
| if (! debug_write_type (info, fns, fhandle, n->u.tag, n)) |
| return false; |
| return (*fns->tag) (fhandle, n->name); |
| case DEBUG_OBJECT_VARIABLE: |
| if (! debug_write_type (info, fns, fhandle, n->u.variable->type, |
| (struct debug_name *) NULL)) |
| return false; |
| return (*fns->variable) (fhandle, n->name, n->u.variable->kind, |
| n->u.variable->val); |
| case DEBUG_OBJECT_FUNCTION: |
| return debug_write_function (info, fns, fhandle, n->name, |
| n->linkage, n->u.function); |
| case DEBUG_OBJECT_INT_CONSTANT: |
| return (*fns->int_constant) (fhandle, n->name, n->u.int_constant); |
| case DEBUG_OBJECT_FLOAT_CONSTANT: |
| return (*fns->float_constant) (fhandle, n->name, n->u.float_constant); |
| case DEBUG_OBJECT_TYPED_CONSTANT: |
| if (! debug_write_type (info, fns, fhandle, n->u.typed_constant->type, |
| (struct debug_name *) NULL)) |
| return false; |
| return (*fns->typed_constant) (fhandle, n->name, |
| n->u.typed_constant->val); |
| default: |
| abort (); |
| return false; |
| } |
| /*NOTREACHED*/ |
| } |
| |
| /* Write out a type. If the type is DEBUG_KIND_NAMED or |
| DEBUG_KIND_TAGGED, then the name argument is the name for which we |
| are about to call typedef or tag. If the type is anything else, |
| then the name argument is a tag from a DEBUG_KIND_TAGGED type which |
| points to this one. */ |
| |
| static bool |
| debug_write_type (struct debug_handle *info, |
| const struct debug_write_fns *fns, void *fhandle, |
| struct debug_type_s *type, struct debug_name *name) |
| { |
| unsigned int i; |
| int is; |
| const char *tag = NULL; |
| |
| if (type == DEBUG_TYPE_NULL) |
| return (*fns->empty_type) (fhandle); |
| |
| /* If we have a name for this type, just output it. We only output |
| typedef names after they have been defined. We output type tags |
| whenever we are not actually defining them. */ |
| if ((type->kind == DEBUG_KIND_NAMED |
| || type->kind == DEBUG_KIND_TAGGED) |
| && (type->u.knamed->name->mark == info->mark |
| || (type->kind == DEBUG_KIND_TAGGED |
| && type->u.knamed->name != name))) |
| { |
| if (type->kind == DEBUG_KIND_NAMED) |
| return (*fns->typedef_type) (fhandle, type->u.knamed->name->name); |
| else |
| { |
| struct debug_type_s *real; |
| unsigned int id; |
| |
| real = debug_get_real_type ((void *) info, type, NULL); |
| if (real == NULL) |
| return (*fns->empty_type) (fhandle); |
| id = 0; |
| if ((real->kind == DEBUG_KIND_STRUCT |
| || real->kind == DEBUG_KIND_UNION |
| || real->kind == DEBUG_KIND_CLASS |
| || real->kind == DEBUG_KIND_UNION_CLASS) |
| && real->u.kclass != NULL) |
| { |
| if (real->u.kclass->id <= info->base_id) |
| { |
| if (! debug_set_class_id (info, |
| type->u.knamed->name->name, |
| real)) |
| return false; |
| } |
| id = real->u.kclass->id; |
| } |
| |
| return (*fns->tag_type) (fhandle, type->u.knamed->name->name, id, |
| real->kind); |
| } |
| } |
| |
| /* Mark the name after we have already looked for a known name, so |
| that we don't just define a type in terms of itself. We need to |
| mark the name here so that a struct containing a pointer to |
| itself will work. */ |
| if (name != NULL) |
| name->mark = info->mark; |
| |
| if (name != NULL |
| && type->kind != DEBUG_KIND_NAMED |
| && type->kind != DEBUG_KIND_TAGGED) |
| { |
| assert (name->kind == DEBUG_OBJECT_TAG); |
| tag = name->name; |
| } |
| |
| switch (type->kind) |
| { |
| case DEBUG_KIND_ILLEGAL: |
| debug_error (_("debug_write_type: illegal type encountered")); |
| return false; |
| case DEBUG_KIND_INDIRECT: |
| return debug_write_type (info, fns, fhandle, *type->u.kindirect->slot, |
| name); |
| case DEBUG_KIND_VOID: |
| return (*fns->void_type) (fhandle); |
| case DEBUG_KIND_INT: |
| return (*fns->int_type) (fhandle, type->size, type->u.kint); |
| case DEBUG_KIND_FLOAT: |
| return (*fns->float_type) (fhandle, type->size); |
| case DEBUG_KIND_COMPLEX: |
| return (*fns->complex_type) (fhandle, type->size); |
| case DEBUG_KIND_BOOL: |
| return (*fns->bool_type) (fhandle, type->size); |
| case DEBUG_KIND_STRUCT: |
| case DEBUG_KIND_UNION: |
| if (type->u.kclass != NULL) |
| { |
| if (type->u.kclass->id <= info->base_id) |
| { |
| if (! debug_set_class_id (info, tag, type)) |
| return false; |
| } |
| |
| if (info->mark == type->u.kclass->mark) |
| { |
| /* We are currently outputting this struct, or we have |
| already output it. I don't know if this can happen, |
| but it can happen for a class. */ |
| assert (type->u.kclass->id > info->base_id); |
| return (*fns->tag_type) (fhandle, tag, type->u.kclass->id, |
| type->kind); |
| } |
| type->u.kclass->mark = info->mark; |
| } |
| |
| if (! (*fns->start_struct_type) (fhandle, tag, |
| (type->u.kclass != NULL |
| ? type->u.kclass->id |
| : 0), |
| type->kind == DEBUG_KIND_STRUCT, |
| type->size)) |
| return false; |
| if (type->u.kclass != NULL |
| && type->u.kclass->fields != NULL) |
| { |
| for (i = 0; type->u.kclass->fields[i] != NULL; i++) |
| { |
| struct debug_field_s *f; |
| |
| f = type->u.kclass->fields[i]; |
| if (! debug_write_type (info, fns, fhandle, f->type, |
| (struct debug_name *) NULL) |
| || ! (*fns->struct_field) (fhandle, f->name, f->u.f.bitpos, |
| f->u.f.bitsize, f->visibility)) |
| return false; |
| } |
| } |
| return (*fns->end_struct_type) (fhandle); |
| case DEBUG_KIND_CLASS: |
| case DEBUG_KIND_UNION_CLASS: |
| return debug_write_class_type (info, fns, fhandle, type, tag); |
| case DEBUG_KIND_ENUM: |
| if (type->u.kenum == NULL) |
| return (*fns->enum_type) (fhandle, tag, (const char **) NULL, |
| (bfd_signed_vma *) NULL); |
| return (*fns->enum_type) (fhandle, tag, type->u.kenum->names, |
| type->u.kenum->values); |
| case DEBUG_KIND_POINTER: |
| if (! debug_write_type (info, fns, fhandle, type->u.kpointer, |
| (struct debug_name *) NULL)) |
| return false; |
| return (*fns->pointer_type) (fhandle); |
| case DEBUG_KIND_FUNCTION: |
| if (! debug_write_type (info, fns, fhandle, |
| type->u.kfunction->return_type, |
| (struct debug_name *) NULL)) |
| return false; |
| if (type->u.kfunction->arg_types == NULL) |
| is = -1; |
| else |
| { |
| for (is = 0; type->u.kfunction->arg_types[is] != NULL; is++) |
| if (! debug_write_type (info, fns, fhandle, |
| type->u.kfunction->arg_types[is], |
| (struct debug_name *) NULL)) |
| return false; |
| } |
| return (*fns->function_type) (fhandle, is, |
| type->u.kfunction->varargs); |
| case DEBUG_KIND_REFERENCE: |
| if (! debug_write_type (info, fns, fhandle, type->u.kreference, |
| (struct debug_name *) NULL)) |
| return false; |
| return (*fns->reference_type) (fhandle); |
| case DEBUG_KIND_RANGE: |
| if (! debug_write_type (info, fns, fhandle, type->u.krange->type, |
| (struct debug_name *) NULL)) |
| return false; |
| return (*fns->range_type) (fhandle, type->u.krange->lower, |
| type->u.krange->upper); |
| case DEBUG_KIND_ARRAY: |
| if (! debug_write_type (info, fns, fhandle, type->u.karray->element_type, |
| (struct debug_name *) NULL) |
| || ! debug_write_type (info, fns, fhandle, |
| type->u.karray->range_type, |
| (struct debug_name *) NULL)) |
| return false; |
| return (*fns->array_type) (fhandle, type->u.karray->lower, |
| type->u.karray->upper, |
| type->u.karray->stringp); |
| case DEBUG_KIND_SET: |
| if (! debug_write_type (info, fns, fhandle, type->u.kset->type, |
| (struct debug_name *) NULL)) |
| return false; |
| return (*fns->set_type) (fhandle, type->u.kset->bitstringp); |
| case DEBUG_KIND_OFFSET: |
| if (! debug_write_type (info, fns, fhandle, type->u.koffset->base_type, |
| (struct debug_name *) NULL) |
| || ! debug_write_type (info, fns, fhandle, |
| type->u.koffset->target_type, |
| (struct debug_name *) NULL)) |
| return false; |
| return (*fns->offset_type) (fhandle); |
| case DEBUG_KIND_METHOD: |
| if (! debug_write_type (info, fns, fhandle, |
| type->u.kmethod->return_type, |
| (struct debug_name *) NULL)) |
| return false; |
| if (type->u.kmethod->arg_types == NULL) |
| is = -1; |
| else |
| { |
| for (is = 0; type->u.kmethod->arg_types[is] != NULL; is++) |
| if (! debug_write_type (info, fns, fhandle, |
| type->u.kmethod->arg_types[is], |
| (struct debug_name *) NULL)) |
| return false; |
| } |
| if (type->u.kmethod->domain_type != NULL) |
| { |
| if (! debug_write_type (info, fns, fhandle, |
| type->u.kmethod->domain_type, |
| (struct debug_name *) NULL)) |
| return false; |
| } |
| return (*fns->method_type) (fhandle, |
| type->u.kmethod->domain_type != NULL, |
| is, |
| type->u.kmethod->varargs); |
| case DEBUG_KIND_CONST: |
| if (! debug_write_type (info, fns, fhandle, type->u.kconst, |
| (struct debug_name *) NULL)) |
| return false; |
| return (*fns->const_type) (fhandle); |
| case DEBUG_KIND_VOLATILE: |
| if (! debug_write_type (info, fns, fhandle, type->u.kvolatile, |
| (struct debug_name *) NULL)) |
| return false; |
| return (*fns->volatile_type) (fhandle); |
| case DEBUG_KIND_NAMED: |
| return debug_write_type (info, fns, fhandle, type->u.knamed->type, |
| (struct debug_name *) NULL); |
| case DEBUG_KIND_TAGGED: |
| return debug_write_type (info, fns, fhandle, type->u.knamed->type, |
| type->u.knamed->name); |
| default: |
| abort (); |
| return false; |
| } |
| } |
| |
| /* Write out a class type. */ |
| |
| static bool |
| debug_write_class_type (struct debug_handle *info, |
| const struct debug_write_fns *fns, void *fhandle, |
| struct debug_type_s *type, const char *tag) |
| { |
| unsigned int i; |
| unsigned int id; |
| struct debug_type_s *vptrbase; |
| |
| if (type->u.kclass == NULL) |
| { |
| id = 0; |
| vptrbase = NULL; |
| } |
| else |
| { |
| if (type->u.kclass->id <= info->base_id) |
| { |
| if (! debug_set_class_id (info, tag, type)) |
| return false; |
| } |
| |
| if (info->mark == type->u.kclass->mark) |
| { |
| /* We are currently outputting this class, or we have |
| already output it. This can happen when there are |
| methods for an anonymous class. */ |
| assert (type->u.kclass->id > info->base_id); |
| return (*fns->tag_type) (fhandle, tag, type->u.kclass->id, |
| type->kind); |
| } |
| type->u.kclass->mark = info->mark; |
| id = type->u.kclass->id; |
| |
| vptrbase = type->u.kclass->vptrbase; |
| if (vptrbase != NULL && vptrbase != type) |
| { |
| if (! debug_write_type (info, fns, fhandle, vptrbase, |
| (struct debug_name *) NULL)) |
| return false; |
| } |
| } |
| |
| if (! (*fns->start_class_type) (fhandle, tag, id, |
| type->kind == DEBUG_KIND_CLASS, |
| type->size, |
| vptrbase != NULL, |
| vptrbase == type)) |
| return false; |
| |
| if (type->u.kclass != NULL) |
| { |
| if (type->u.kclass->fields != NULL) |
| { |
| for (i = 0; type->u.kclass->fields[i] != NULL; i++) |
| { |
| struct debug_field_s *f; |
| |
| f = type->u.kclass->fields[i]; |
| if (! debug_write_type (info, fns, fhandle, f->type, |
| (struct debug_name *) NULL)) |
| return false; |
| if (f->static_member) |
| { |
| if (! (*fns->class_static_member) (fhandle, f->name, |
| f->u.s.physname, |
| f->visibility)) |
| return false; |
| } |
| else |
| { |
| if (! (*fns->struct_field) (fhandle, f->name, f->u.f.bitpos, |
| f->u.f.bitsize, f->visibility)) |
| return false; |
| } |
| } |
| } |
| |
| if (type->u.kclass->baseclasses != NULL) |
| { |
| for (i = 0; type->u.kclass->baseclasses[i] != NULL; i++) |
| { |
| struct debug_baseclass_s *b; |
| |
| b = type->u.kclass->baseclasses[i]; |
| if (! debug_write_type (info, fns, fhandle, b->type, |
| (struct debug_name *) NULL)) |
| return false; |
| if (! (*fns->class_baseclass) (fhandle, b->bitpos, b->is_virtual, |
| b->visibility)) |
| return false; |
| } |
| } |
| |
| if (type->u.kclass->methods != NULL) |
| { |
| for (i = 0; type->u.kclass->methods[i] != NULL; i++) |
| { |
| struct debug_method_s *m; |
| unsigned int j; |
| |
| m = type->u.kclass->methods[i]; |
| if (! (*fns->class_start_method) (fhandle, m->name)) |
| return false; |
| for (j = 0; m->variants[j] != NULL; j++) |
| { |
| struct debug_method_variant_s *v; |
| |
| v = m->variants[j]; |
| if (v->context != NULL) |
| { |
| if (! debug_write_type (info, fns, fhandle, v->context, |
| (struct debug_name *) NULL)) |
| return false; |
| } |
| if (! debug_write_type (info, fns, fhandle, v->type, |
| (struct debug_name *) NULL)) |
| return false; |
| if (v->voffset != VOFFSET_STATIC_METHOD) |
| { |
| if (! (*fns->class_method_variant) (fhandle, v->physname, |
| v->visibility, |
| v->constp, |
| v->volatilep, |
| v->voffset, |
| v->context != NULL)) |
| return false; |
| } |
| else |
| { |
| if (! (*fns->class_static_method_variant) (fhandle, |
| v->physname, |
| v->visibility, |
| v->constp, |
| v->volatilep)) |
| return false; |
| } |
| } |
| if (! (*fns->class_end_method) (fhandle)) |
| return false; |
| } |
| } |
| } |
| |
| return (*fns->end_class_type) (fhandle); |
| } |
| |
| /* Write out information for a function. */ |
| |
| static bool |
| debug_write_function (struct debug_handle *info, |
| const struct debug_write_fns *fns, void *fhandle, |
| const char *name, enum debug_object_linkage linkage, |
| struct debug_function *function) |
| { |
| struct debug_parameter *p; |
| struct debug_block *b; |
| |
| if (! debug_write_linenos (info, fns, fhandle, function->blocks->start)) |
| return false; |
| |
| if (! debug_write_type (info, fns, fhandle, function->return_type, |
| (struct debug_name *) NULL)) |
| return false; |
| |
| if (! (*fns->start_function) (fhandle, name, |
| linkage == DEBUG_LINKAGE_GLOBAL)) |
| return false; |
| |
| for (p = function->parameters; p != NULL; p = p->next) |
| { |
| if (! debug_write_type (info, fns, fhandle, p->type, |
| (struct debug_name *) NULL) |
| || ! (*fns->function_parameter) (fhandle, p->name, p->kind, p->val)) |
| return false; |
| } |
| |
| for (b = function->blocks; b != NULL; b = b->next) |
| { |
| if (! debug_write_block (info, fns, fhandle, b)) |
| return false; |
| } |
| |
| return (*fns->end_function) (fhandle); |
| } |
| |
| /* Write out information for a block. */ |
| |
| static bool |
| debug_write_block (struct debug_handle *info, |
| const struct debug_write_fns *fns, void *fhandle, |
| struct debug_block *block) |
| { |
| struct debug_name *n; |
| struct debug_block *b; |
| |
| if (! debug_write_linenos (info, fns, fhandle, block->start)) |
| return false; |
| |
| /* I can't see any point to writing out a block with no local |
| variables, so we don't bother, except for the top level block. */ |
| if (block->locals != NULL || block->parent == NULL) |
| { |
| if (! (*fns->start_block) (fhandle, block->start)) |
| return false; |
| } |
| |
| if (block->locals != NULL) |
| { |
| for (n = block->locals->list; n != NULL; n = n->next) |
| { |
| if (! debug_write_name (info, fns, fhandle, n)) |
| return false; |
| } |
| } |
| |
| for (b = block->children; b != NULL; b = b->next) |
| { |
| if (! debug_write_block (info, fns, fhandle, b)) |
| return false; |
| } |
| |
| if (! debug_write_linenos (info, fns, fhandle, block->end)) |
| return false; |
| |
| if (block->locals != NULL || block->parent == NULL) |
| { |
| if (! (*fns->end_block) (fhandle, block->end)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* Write out line number information up to ADDRESS. */ |
| |
| static bool |
| debug_write_linenos (struct debug_handle *info, |
| const struct debug_write_fns *fns, void *fhandle, |
| bfd_vma address) |
| { |
| while (info->current_write_lineno != NULL) |
| { |
| struct debug_lineno *l; |
| |
| l = info->current_write_lineno; |
| |
| while (info->current_write_lineno_index < DEBUG_LINENO_COUNT) |
| { |
| if (l->linenos[info->current_write_lineno_index] |
| == (unsigned long) -1) |
| break; |
| |
| if (l->addrs[info->current_write_lineno_index] >= address) |
| return true; |
| |
| if (! (*fns->lineno) (fhandle, l->file->filename, |
| l->linenos[info->current_write_lineno_index], |
| l->addrs[info->current_write_lineno_index])) |
| return false; |
| |
| ++info->current_write_lineno_index; |
| } |
| |
| info->current_write_lineno = l->next; |
| info->current_write_lineno_index = 0; |
| } |
| |
| return true; |
| } |
| |
| /* Get the ID number for a class. If during the same call to |
| debug_write we find a struct with the same definition with the same |
| name, we use the same ID. This type of things happens because the |
| same struct will be defined by multiple compilation units. */ |
| |
| static bool |
| debug_set_class_id (struct debug_handle *info, const char *tag, |
| struct debug_type_s *type) |
| { |
| struct debug_class_type *c; |
| struct debug_class_id *l; |
| |
| assert (type->kind == DEBUG_KIND_STRUCT |
| || type->kind == DEBUG_KIND_UNION |
| || type->kind == DEBUG_KIND_CLASS |
| || type->kind == DEBUG_KIND_UNION_CLASS); |
| |
| c = type->u.kclass; |
| |
| if (c->id > info->base_id) |
| return true; |
| |
| for (l = info->id_list; l != NULL; l = l->next) |
| { |
| if (l->type->kind != type->kind) |
| continue; |
| |
| if (tag == NULL) |
| { |
| if (l->tag != NULL) |
| continue; |
| } |
| else |
| { |
| if (l->tag == NULL |
| || l->tag[0] != tag[0] |
| || strcmp (l->tag, tag) != 0) |
| continue; |
| } |
| |
| if (debug_type_samep (info, l->type, type)) |
| { |
| c->id = l->type->u.kclass->id; |
| return true; |
| } |
| } |
| |
| /* There are no identical types. Use a new ID, and add it to the |
| list. */ |
| ++info->class_id; |
| c->id = info->class_id; |
| |
| l = (struct debug_class_id *) xmalloc (sizeof *l); |
| memset (l, 0, sizeof *l); |
| |
| l->type = type; |
| l->tag = tag; |
| |
| l->next = info->id_list; |
| info->id_list = l; |
| |
| return true; |
| } |
| |
| /* See if two types are the same. At this point, we don't care about |
| tags and the like. */ |
| |
| static bool |
| debug_type_samep (struct debug_handle *info, struct debug_type_s *t1, |
| struct debug_type_s *t2) |
| { |
| struct debug_type_compare_list *l; |
| struct debug_type_compare_list top; |
| bool ret; |
| |
| if (t1 == NULL) |
| return t2 == NULL; |
| if (t2 == NULL) |
| return false; |
| |
| while (t1->kind == DEBUG_KIND_INDIRECT) |
| { |
| t1 = *t1->u.kindirect->slot; |
| if (t1 == NULL) |
| return false; |
| } |
| while (t2->kind == DEBUG_KIND_INDIRECT) |
| { |
| t2 = *t2->u.kindirect->slot; |
| if (t2 == NULL) |
| return false; |
| } |
| |
| if (t1 == t2) |
| return true; |
| |
| /* As a special case, permit a typedef to match a tag, since C++ |
| debugging output will sometimes add a typedef where C debugging |
| output will not. */ |
| if (t1->kind == DEBUG_KIND_NAMED |
| && t2->kind == DEBUG_KIND_TAGGED) |
| return debug_type_samep (info, t1->u.knamed->type, t2); |
| else if (t1->kind == DEBUG_KIND_TAGGED |
| && t2->kind == DEBUG_KIND_NAMED) |
| return debug_type_samep (info, t1, t2->u.knamed->type); |
| |
| if (t1->kind != t2->kind |
| || t1->size != t2->size) |
| return false; |
| |
| /* Get rid of the trivial cases first. */ |
| switch (t1->kind) |
| { |
| default: |
| break; |
| case DEBUG_KIND_VOID: |
| case DEBUG_KIND_FLOAT: |
| case DEBUG_KIND_COMPLEX: |
| case DEBUG_KIND_BOOL: |
| return true; |
| case DEBUG_KIND_INT: |
| return t1->u.kint == t2->u.kint; |
| } |
| |
| /* We have to avoid an infinite recursion. We do this by keeping a |
| list of types which we are comparing. We just keep the list on |
| the stack. If we encounter a pair of types we are currently |
| comparing, we just assume that they are equal. */ |
| for (l = info->compare_list; l != NULL; l = l->next) |
| { |
| if (l->t1 == t1 && l->t2 == t2) |
| return true; |
| } |
| |
| top.t1 = t1; |
| top.t2 = t2; |
| top.next = info->compare_list; |
| info->compare_list = ⊤ |
| |
| switch (t1->kind) |
| { |
| default: |
| abort (); |
| ret = false; |
| break; |
| |
| case DEBUG_KIND_STRUCT: |
| case DEBUG_KIND_UNION: |
| case DEBUG_KIND_CLASS: |
| case DEBUG_KIND_UNION_CLASS: |
| if (t1->u.kclass == NULL) |
| ret = t2->u.kclass == NULL; |
| else if (t2->u.kclass == NULL) |
| ret = false; |
| else if (t1->u.kclass->id > info->base_id |
| && t1->u.kclass->id == t2->u.kclass->id) |
| ret = true; |
| else |
| ret = debug_class_type_samep (info, t1, t2); |
| break; |
| |
| case DEBUG_KIND_ENUM: |
| if (t1->u.kenum == NULL) |
| ret = t2->u.kenum == NULL; |
| else if (t2->u.kenum == NULL) |
| ret = false; |
| else |
| { |
| const char **pn1, **pn2; |
| bfd_signed_vma *pv1, *pv2; |
| |
| pn1 = t1->u.kenum->names; |
| pn2 = t2->u.kenum->names; |
| pv1 = t1->u.kenum->values; |
| pv2 = t2->u.kenum->values; |
| while (*pn1 != NULL && *pn2 != NULL) |
| { |
| if (**pn1 != **pn2 |
| || *pv1 != *pv2 |
| || strcmp (*pn1, *pn2) != 0) |
| break; |
| ++pn1; |
| ++pn2; |
| ++pv1; |
| ++pv2; |
| } |
| ret = *pn1 == NULL && *pn2 == NULL; |
| } |
| break; |
| |
| case DEBUG_KIND_POINTER: |
| ret = debug_type_samep (info, t1->u.kpointer, t2->u.kpointer); |
| break; |
| |
| case DEBUG_KIND_FUNCTION: |
| if (t1->u.kfunction->varargs != t2->u.kfunction->varargs |
| || ! debug_type_samep (info, t1->u.kfunction->return_type, |
| t2->u.kfunction->return_type) |
| || ((t1->u.kfunction->arg_types == NULL) |
| != (t2->u.kfunction->arg_types == NULL))) |
| ret = false; |
| else if (t1->u.kfunction->arg_types == NULL) |
| ret = true; |
| else |
| { |
| struct debug_type_s **a1, **a2; |
| |
| a1 = t1->u.kfunction->arg_types; |
| a2 = t2->u.kfunction->arg_types; |
| while (*a1 != NULL && *a2 != NULL) |
| { |
| if (! debug_type_samep (info, *a1, *a2)) |
| break; |
| ++a1; |
| ++a2; |
| } |
| ret = *a1 == NULL && *a2 == NULL; |
| } |
| break; |
| |
| case DEBUG_KIND_REFERENCE: |
| ret = debug_type_samep (info, t1->u.kreference, t2->u.kreference); |
| break; |
| |
| case DEBUG_KIND_RANGE: |
| ret = (t1->u.krange->lower == t2->u.krange->lower |
| && t1->u.krange->upper == t2->u.krange->upper |
| &&
|