123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543 |
- /* Go language support routines for GDB, the GNU debugger.
- Copyright (C) 2012-2022 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/>. */
- /* TODO:
- - split stacks
- - printing of native types
- - goroutines
- - lots more
- - gccgo mangling needs redoing
- It's too hard, for example, to know whether one is looking at a mangled
- Go symbol or not, and their are ambiguities, e.g., the demangler may
- get passed *any* symbol, including symbols from other languages
- and including symbols that are already demangled.
- One thought is to at least add an _G prefix.
- - 6g mangling isn't supported yet
- */
- #include "defs.h"
- #include "gdbsupport/gdb_obstack.h"
- #include "block.h"
- #include "symtab.h"
- #include "language.h"
- #include "varobj.h"
- #include "go-lang.h"
- #include "c-lang.h"
- #include "parser-defs.h"
- #include "gdbarch.h"
- #include <ctype.h>
- /* The main function in the main package. */
- static const char GO_MAIN_MAIN[] = "main.main";
- /* Function returning the special symbol name used by Go for the main
- procedure in the main program if it is found in minimal symbol list.
- This function tries to find minimal symbols so that it finds them even
- if the program was compiled without debugging information. */
- const char *
- go_main_name (void)
- {
- struct bound_minimal_symbol msym;
- msym = lookup_minimal_symbol (GO_MAIN_MAIN, NULL, NULL);
- if (msym.minsym != NULL)
- return GO_MAIN_MAIN;
- /* No known entry procedure found, the main program is probably not Go. */
- return NULL;
- }
- /* Return non-zero if TYPE is a gccgo string.
- We assume CHECK_TYPEDEF has already been done. */
- static int
- gccgo_string_p (struct type *type)
- {
- /* gccgo strings don't necessarily have a name we can use. */
- if (type->num_fields () == 2)
- {
- struct type *type0 = type->field (0).type ();
- struct type *type1 = type->field (1).type ();
- type0 = check_typedef (type0);
- type1 = check_typedef (type1);
- if (type0->code () == TYPE_CODE_PTR
- && strcmp (type->field (0).name (), "__data") == 0
- && type1->code () == TYPE_CODE_INT
- && strcmp (type->field (1).name (), "__length") == 0)
- {
- struct type *target_type = TYPE_TARGET_TYPE (type0);
- target_type = check_typedef (target_type);
- if (target_type->code () == TYPE_CODE_INT
- && TYPE_LENGTH (target_type) == 1
- && strcmp (target_type->name (), "uint8") == 0)
- return 1;
- }
- }
- return 0;
- }
- /* Return non-zero if TYPE is a 6g string.
- We assume CHECK_TYPEDEF has already been done. */
- static int
- sixg_string_p (struct type *type)
- {
- if (type->num_fields () == 2
- && type->name () != NULL
- && strcmp (type->name (), "string") == 0)
- return 1;
- return 0;
- }
- /* Classify the kind of Go object that TYPE is.
- TYPE is a TYPE_CODE_STRUCT, used to represent a Go object. */
- enum go_type
- go_classify_struct_type (struct type *type)
- {
- type = check_typedef (type);
- /* Recognize strings as they're useful to be able to print without
- pretty-printers. */
- if (gccgo_string_p (type)
- || sixg_string_p (type))
- return GO_TYPE_STRING;
- return GO_TYPE_NONE;
- }
- /* Subroutine of unpack_mangled_go_symbol to simplify it.
- Given "[foo.]bar.baz", store "bar" in *PACKAGEP and "baz" in *OBJECTP.
- We stomp on the last '.' to nul-terminate "bar".
- The caller is responsible for memory management. */
- static void
- unpack_package_and_object (char *buf,
- const char **packagep, const char **objectp)
- {
- char *last_dot;
- last_dot = strrchr (buf, '.');
- gdb_assert (last_dot != NULL);
- *objectp = last_dot + 1;
- *last_dot = '\0';
- last_dot = strrchr (buf, '.');
- if (last_dot != NULL)
- *packagep = last_dot + 1;
- else
- *packagep = buf;
- }
- /* Given a mangled Go symbol, find its package name, object name, and
- method type (if present).
- E.g., for "libgo_net.textproto.String.N33_libgo_net.textproto.ProtocolError"
- *PACKAGEP = "textproto"
- *OBJECTP = "String"
- *METHOD_TYPE_PACKAGEP = "textproto"
- *METHOD_TYPE_OBJECTP = "ProtocolError"
- Space for the resulting strings is malloc'd in one buffer.
- PACKAGEP,OBJECTP,METHOD_TYPE* will (typically) point into this buffer.
- [There are a few exceptions, but the caller is still responsible for
- freeing the resulting pointer.]
- A pointer to this buffer is returned, or NULL if symbol isn't a
- mangled Go symbol.
- The caller is responsible for freeing the result.
- *METHOD_TYPE_IS_POINTERP is set to a boolean indicating if
- the method type is a pointer.
- There may be value in returning the outer container,
- i.e., "net" in the above example, but for now it's not needed.
- Plus it's currently not straightforward to compute,
- it comes from -fgo-prefix, and there's no algorithm to compute it.
- If we ever need to unpack the method type, this routine should work
- for that too. */
- static char *
- unpack_mangled_go_symbol (const char *mangled_name,
- const char **packagep,
- const char **objectp,
- const char **method_type_packagep,
- const char **method_type_objectp,
- int *method_type_is_pointerp)
- {
- char *buf;
- char *p;
- int len = strlen (mangled_name);
- /* Pointer to last digit in "N<digit(s)>_". */
- char *saw_digit;
- /* Pointer to "N" if valid "N<digit(s)>_" found. */
- char *method_type;
- /* Pointer to the first '.'. */
- const char *first_dot;
- /* Pointer to the last '.'. */
- const char *last_dot;
- /* Non-zero if we saw a pointer indicator. */
- int saw_pointer;
- *packagep = *objectp = NULL;
- *method_type_packagep = *method_type_objectp = NULL;
- *method_type_is_pointerp = 0;
- /* main.init is mangled specially. */
- if (strcmp (mangled_name, "__go_init_main") == 0)
- {
- char *package = xstrdup ("main");
- *packagep = package;
- *objectp = "init";
- return package;
- }
- /* main.main is mangled specially (missing prefix). */
- if (strcmp (mangled_name, "main.main") == 0)
- {
- char *package = xstrdup ("main");
- *packagep = package;
- *objectp = "main";
- return package;
- }
- /* We may get passed, e.g., "main.T.Foo", which is *not* mangled.
- Alas it looks exactly like "prefix.package.object."
- To cope for now we only recognize the following prefixes:
- go: the default
- libgo_.*: used by gccgo's runtime
- Thus we don't support -fgo-prefix (except as used by the runtime). */
- if (!startswith (mangled_name, "go.")
- && !startswith (mangled_name, "libgo_"))
- return NULL;
- /* Quick check for whether a search may be fruitful. */
- /* Ignore anything with @plt, etc. in it. */
- if (strchr (mangled_name, '@') != NULL)
- return NULL;
- /* It must have at least two dots. */
- first_dot = strchr (mangled_name, '.');
- if (first_dot == NULL)
- return NULL;
- /* Treat "foo.bar" as unmangled. It can collide with lots of other
- languages and it's not clear what the consequences are.
- And except for main.main, all gccgo symbols are at least
- prefix.package.object. */
- last_dot = strrchr (mangled_name, '.');
- if (last_dot == first_dot)
- return NULL;
- /* More quick checks. */
- if (last_dot[1] == '\0' /* foo. */
- || last_dot[-1] == '.') /* foo..bar */
- return NULL;
- /* At this point we've decided we have a mangled Go symbol. */
- buf = xstrdup (mangled_name);
- /* Search backwards looking for "N<digit(s)>". */
- p = buf + len;
- saw_digit = method_type = NULL;
- saw_pointer = 0;
- while (p > buf)
- {
- int current = *(const unsigned char *) --p;
- int current_is_digit = isdigit (current);
- if (saw_digit)
- {
- if (current_is_digit)
- continue;
- if (current == 'N'
- && ((p > buf && p[-1] == '.')
- || (p > buf + 1 && p[-1] == 'p' && p[-2] == '.')))
- {
- if (atoi (p + 1) == strlen (saw_digit + 2))
- {
- if (p[-1] == '.')
- method_type = p - 1;
- else
- {
- gdb_assert (p[-1] == 'p');
- saw_pointer = 1;
- method_type = p - 2;
- }
- break;
- }
- }
- /* Not what we're looking for, reset and keep looking. */
- saw_digit = NULL;
- saw_pointer = 0;
- continue;
- }
- if (current_is_digit && p[1] == '_')
- {
- /* Possible start of method "this" [sic] type. */
- saw_digit = p;
- continue;
- }
- }
- if (method_type != NULL
- /* Ensure not something like "..foo". */
- && (method_type > buf && method_type[-1] != '.'))
- {
- unpack_package_and_object (saw_digit + 2,
- method_type_packagep, method_type_objectp);
- *method_type = '\0';
- *method_type_is_pointerp = saw_pointer;
- }
- unpack_package_and_object (buf, packagep, objectp);
- return buf;
- }
- /* Implements the la_demangle language_defn routine for language Go.
- N.B. This may get passed *any* symbol, including symbols from other
- languages and including symbols that are already demangled.
- Both of these situations are kinda unfortunate, but that's how things
- are today.
- N.B. This currently only supports gccgo's mangling.
- N.B. gccgo's mangling needs, I think, changing.
- This demangler can't work in all situations,
- thus not too much effort is currently put into it. */
- gdb::unique_xmalloc_ptr<char>
- go_language::demangle_symbol (const char *mangled_name, int options) const
- {
- const char *package_name;
- const char *object_name;
- const char *method_type_package_name;
- const char *method_type_object_name;
- int method_type_is_pointer;
- if (mangled_name == NULL)
- return NULL;
- gdb::unique_xmalloc_ptr<char> name_buf
- (unpack_mangled_go_symbol (mangled_name,
- &package_name, &object_name,
- &method_type_package_name,
- &method_type_object_name,
- &method_type_is_pointer));
- if (name_buf == NULL)
- return NULL;
- auto_obstack tempbuf;
- /* Print methods as they appear in "method expressions". */
- if (method_type_package_name != NULL)
- {
- /* FIXME: Seems like we should include package_name here somewhere. */
- if (method_type_is_pointer)
- obstack_grow_str (&tempbuf, "(*");
- obstack_grow_str (&tempbuf, method_type_package_name);
- obstack_grow_str (&tempbuf, ".");
- obstack_grow_str (&tempbuf, method_type_object_name);
- if (method_type_is_pointer)
- obstack_grow_str (&tempbuf, ")");
- obstack_grow_str (&tempbuf, ".");
- obstack_grow_str (&tempbuf, object_name);
- }
- else
- {
- obstack_grow_str (&tempbuf, package_name);
- obstack_grow_str (&tempbuf, ".");
- obstack_grow_str (&tempbuf, object_name);
- }
- obstack_grow_str0 (&tempbuf, "");
- return make_unique_xstrdup ((const char *) obstack_finish (&tempbuf));
- }
- /* Given a Go symbol, return its package or NULL if unknown.
- Space for the result is malloc'd, caller must free. */
- char *
- go_symbol_package_name (const struct symbol *sym)
- {
- const char *mangled_name = sym->linkage_name ();
- const char *package_name;
- const char *object_name;
- const char *method_type_package_name;
- const char *method_type_object_name;
- int method_type_is_pointer;
- char *name_buf;
- char *result;
- gdb_assert (sym->language () == language_go);
- name_buf = unpack_mangled_go_symbol (mangled_name,
- &package_name, &object_name,
- &method_type_package_name,
- &method_type_object_name,
- &method_type_is_pointer);
- /* Some Go symbols don't have mangled form we interpret (yet). */
- if (name_buf == NULL)
- return NULL;
- result = xstrdup (package_name);
- xfree (name_buf);
- return result;
- }
- /* Return the package that BLOCK is in, or NULL if there isn't one.
- Space for the result is malloc'd, caller must free. */
- char *
- go_block_package_name (const struct block *block)
- {
- while (block != NULL)
- {
- struct symbol *function = BLOCK_FUNCTION (block);
- if (function != NULL)
- {
- char *package_name = go_symbol_package_name (function);
- if (package_name != NULL)
- return package_name;
- /* Stop looking if we find a function without a package name.
- We're most likely outside of Go and thus the concept of the
- "current" package is gone. */
- return NULL;
- }
- block = BLOCK_SUPERBLOCK (block);
- }
- return NULL;
- }
- /* See language.h. */
- void
- go_language::language_arch_info (struct gdbarch *gdbarch,
- struct language_arch_info *lai) const
- {
- const struct builtin_go_type *builtin = builtin_go_type (gdbarch);
- /* Helper function to allow shorter lines below. */
- auto add = [&] (struct type * t) -> struct type *
- {
- lai->add_primitive_type (t);
- return t;
- };
- add (builtin->builtin_void);
- add (builtin->builtin_char);
- add (builtin->builtin_bool);
- add (builtin->builtin_int);
- add (builtin->builtin_uint);
- add (builtin->builtin_uintptr);
- add (builtin->builtin_int8);
- add (builtin->builtin_int16);
- add (builtin->builtin_int32);
- add (builtin->builtin_int64);
- add (builtin->builtin_uint8);
- add (builtin->builtin_uint16);
- add (builtin->builtin_uint32);
- add (builtin->builtin_uint64);
- add (builtin->builtin_float32);
- add (builtin->builtin_float64);
- add (builtin->builtin_complex64);
- add (builtin->builtin_complex128);
- lai->set_string_char_type (builtin->builtin_char);
- lai->set_bool_type (builtin->builtin_bool, "bool");
- }
- /* Single instance of the Go language class. */
- static go_language go_language_defn;
- static void *
- build_go_types (struct gdbarch *gdbarch)
- {
- struct builtin_go_type *builtin_go_type
- = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct builtin_go_type);
- builtin_go_type->builtin_void
- = arch_type (gdbarch, TYPE_CODE_VOID, TARGET_CHAR_BIT, "void");
- builtin_go_type->builtin_char
- = arch_character_type (gdbarch, 8, 1, "char");
- builtin_go_type->builtin_bool
- = arch_boolean_type (gdbarch, 8, 0, "bool");
- builtin_go_type->builtin_int
- = arch_integer_type (gdbarch, gdbarch_int_bit (gdbarch), 0, "int");
- builtin_go_type->builtin_uint
- = arch_integer_type (gdbarch, gdbarch_int_bit (gdbarch), 1, "uint");
- builtin_go_type->builtin_uintptr
- = arch_integer_type (gdbarch, gdbarch_ptr_bit (gdbarch), 1, "uintptr");
- builtin_go_type->builtin_int8
- = arch_integer_type (gdbarch, 8, 0, "int8");
- builtin_go_type->builtin_int16
- = arch_integer_type (gdbarch, 16, 0, "int16");
- builtin_go_type->builtin_int32
- = arch_integer_type (gdbarch, 32, 0, "int32");
- builtin_go_type->builtin_int64
- = arch_integer_type (gdbarch, 64, 0, "int64");
- builtin_go_type->builtin_uint8
- = arch_integer_type (gdbarch, 8, 1, "uint8");
- builtin_go_type->builtin_uint16
- = arch_integer_type (gdbarch, 16, 1, "uint16");
- builtin_go_type->builtin_uint32
- = arch_integer_type (gdbarch, 32, 1, "uint32");
- builtin_go_type->builtin_uint64
- = arch_integer_type (gdbarch, 64, 1, "uint64");
- builtin_go_type->builtin_float32
- = arch_float_type (gdbarch, 32, "float32", floatformats_ieee_single);
- builtin_go_type->builtin_float64
- = arch_float_type (gdbarch, 64, "float64", floatformats_ieee_double);
- builtin_go_type->builtin_complex64
- = init_complex_type ("complex64", builtin_go_type->builtin_float32);
- builtin_go_type->builtin_complex128
- = init_complex_type ("complex128", builtin_go_type->builtin_float64);
- return builtin_go_type;
- }
- static struct gdbarch_data *go_type_data;
- const struct builtin_go_type *
- builtin_go_type (struct gdbarch *gdbarch)
- {
- return (const struct builtin_go_type *) gdbarch_data (gdbarch, go_type_data);
- }
- void _initialize_go_language ();
- void
- _initialize_go_language ()
- {
- go_type_data = gdbarch_data_register_post_init (build_go_types);
- }
|