123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604 |
- /* Auxiliary vector support for GDB, the GNU debugger.
- Copyright (C) 2004-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/>. */
- #include "defs.h"
- #include "target.h"
- #include "gdbtypes.h"
- #include "command.h"
- #include "inferior.h"
- #include "valprint.h"
- #include "gdbcore.h"
- #include "observable.h"
- #include "gdbsupport/filestuff.h"
- #include "objfiles.h"
- #include "auxv.h"
- #include "elf/common.h"
- #include <unistd.h>
- #include <fcntl.h>
- /* Implement the to_xfer_partial target_ops method. This function
- handles access via /proc/PID/auxv, which is a common method for
- native targets. */
- static enum target_xfer_status
- procfs_xfer_auxv (gdb_byte *readbuf,
- const gdb_byte *writebuf,
- ULONGEST offset,
- ULONGEST len,
- ULONGEST *xfered_len)
- {
- ssize_t l;
- std::string pathname = string_printf ("/proc/%d/auxv", inferior_ptid.pid ());
- scoped_fd fd
- = gdb_open_cloexec (pathname, writebuf != NULL ? O_WRONLY : O_RDONLY, 0);
- if (fd.get () < 0)
- return TARGET_XFER_E_IO;
- if (offset != (ULONGEST) 0
- && lseek (fd.get (), (off_t) offset, SEEK_SET) != (off_t) offset)
- l = -1;
- else if (readbuf != NULL)
- l = read (fd.get (), readbuf, (size_t) len);
- else
- l = write (fd.get (), writebuf, (size_t) len);
- if (l < 0)
- return TARGET_XFER_E_IO;
- else if (l == 0)
- return TARGET_XFER_EOF;
- else
- {
- *xfered_len = (ULONGEST) l;
- return TARGET_XFER_OK;
- }
- }
- /* This function handles access via ld.so's symbol `_dl_auxv'. */
- static enum target_xfer_status
- ld_so_xfer_auxv (gdb_byte *readbuf,
- const gdb_byte *writebuf,
- ULONGEST offset,
- ULONGEST len, ULONGEST *xfered_len)
- {
- struct bound_minimal_symbol msym;
- CORE_ADDR data_address, pointer_address;
- struct type *ptr_type = builtin_type (target_gdbarch ())->builtin_data_ptr;
- size_t ptr_size = TYPE_LENGTH (ptr_type);
- size_t auxv_pair_size = 2 * ptr_size;
- gdb_byte *ptr_buf = (gdb_byte *) alloca (ptr_size);
- LONGEST retval;
- size_t block;
- msym = lookup_minimal_symbol ("_dl_auxv", NULL, NULL);
- if (msym.minsym == NULL)
- return TARGET_XFER_E_IO;
- if (MSYMBOL_SIZE (msym.minsym) != ptr_size)
- return TARGET_XFER_E_IO;
- /* POINTER_ADDRESS is a location where the `_dl_auxv' variable
- resides. DATA_ADDRESS is the inferior value present in
- `_dl_auxv', therefore the real inferior AUXV address. */
- pointer_address = BMSYMBOL_VALUE_ADDRESS (msym);
- /* The location of the _dl_auxv symbol may no longer be correct if
- ld.so runs at a different address than the one present in the
- file. This is very common case - for unprelinked ld.so or with a
- PIE executable. PIE executable forces random address even for
- libraries already being prelinked to some address. PIE
- executables themselves are never prelinked even on prelinked
- systems. Prelinking of a PIE executable would block their
- purpose of randomizing load of everything including the
- executable.
- If the memory read fails, return -1 to fallback on another
- mechanism for retrieving the AUXV.
- In most cases of a PIE running under valgrind there is no way to
- find out the base addresses of any of ld.so, executable or AUXV
- as everything is randomized and /proc information is not relevant
- for the virtual executable running under valgrind. We think that
- we might need a valgrind extension to make it work. This is PR
- 11440. */
- if (target_read_memory (pointer_address, ptr_buf, ptr_size) != 0)
- return TARGET_XFER_E_IO;
- data_address = extract_typed_address (ptr_buf, ptr_type);
- /* Possibly still not initialized such as during an inferior
- startup. */
- if (data_address == 0)
- return TARGET_XFER_E_IO;
- data_address += offset;
- if (writebuf != NULL)
- {
- if (target_write_memory (data_address, writebuf, len) == 0)
- {
- *xfered_len = (ULONGEST) len;
- return TARGET_XFER_OK;
- }
- else
- return TARGET_XFER_E_IO;
- }
- /* Stop if trying to read past the existing AUXV block. The final
- AT_NULL was already returned before. */
- if (offset >= auxv_pair_size)
- {
- if (target_read_memory (data_address - auxv_pair_size, ptr_buf,
- ptr_size) != 0)
- return TARGET_XFER_E_IO;
- if (extract_typed_address (ptr_buf, ptr_type) == AT_NULL)
- return TARGET_XFER_EOF;
- }
- retval = 0;
- block = 0x400;
- gdb_assert (block % auxv_pair_size == 0);
- while (len > 0)
- {
- if (block > len)
- block = len;
- /* Reading sizes smaller than AUXV_PAIR_SIZE is not supported.
- Tails unaligned to AUXV_PAIR_SIZE will not be read during a
- call (they should be completed during next read with
- new/extended buffer). */
- block &= -auxv_pair_size;
- if (block == 0)
- break;
- if (target_read_memory (data_address, readbuf, block) != 0)
- {
- if (block <= auxv_pair_size)
- break;
- block = auxv_pair_size;
- continue;
- }
- data_address += block;
- len -= block;
- /* Check terminal AT_NULL. This function is being called
- indefinitely being extended its READBUF until it returns EOF
- (0). */
- while (block >= auxv_pair_size)
- {
- retval += auxv_pair_size;
- if (extract_typed_address (readbuf, ptr_type) == AT_NULL)
- {
- *xfered_len = (ULONGEST) retval;
- return TARGET_XFER_OK;
- }
- readbuf += auxv_pair_size;
- block -= auxv_pair_size;
- }
- }
- *xfered_len = (ULONGEST) retval;
- return TARGET_XFER_OK;
- }
- /* Implement the to_xfer_partial target_ops method for
- TARGET_OBJECT_AUXV. It handles access to AUXV. */
- enum target_xfer_status
- memory_xfer_auxv (struct target_ops *ops,
- enum target_object object,
- const char *annex,
- gdb_byte *readbuf,
- const gdb_byte *writebuf,
- ULONGEST offset,
- ULONGEST len, ULONGEST *xfered_len)
- {
- gdb_assert (object == TARGET_OBJECT_AUXV);
- gdb_assert (readbuf || writebuf);
- /* ld_so_xfer_auxv is the only function safe for virtual
- executables being executed by valgrind's memcheck. Using
- ld_so_xfer_auxv during inferior startup is problematic, because
- ld.so symbol tables have not yet been relocated. So GDB uses
- this function only when attaching to a process.
- */
- if (current_inferior ()->attach_flag != 0)
- {
- enum target_xfer_status ret;
- ret = ld_so_xfer_auxv (readbuf, writebuf, offset, len, xfered_len);
- if (ret != TARGET_XFER_E_IO)
- return ret;
- }
- return procfs_xfer_auxv (readbuf, writebuf, offset, len, xfered_len);
- }
- /* This function compared to other auxv_parse functions: it takes the size of
- the auxv type field as a parameter. */
- static int
- generic_auxv_parse (struct gdbarch *gdbarch, gdb_byte **readptr,
- gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp,
- int sizeof_auxv_type)
- {
- struct type *ptr_type = builtin_type (gdbarch)->builtin_data_ptr;
- const int sizeof_auxv_val = TYPE_LENGTH (ptr_type);
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- gdb_byte *ptr = *readptr;
- if (endptr == ptr)
- return 0;
- if (endptr - ptr < 2 * sizeof_auxv_val)
- return -1;
- *typep = extract_unsigned_integer (ptr, sizeof_auxv_type, byte_order);
- /* Even if the auxv type takes less space than an auxv value, there is
- padding after the type such that the value is aligned on a multiple of
- its size (and this is why we advance by `sizeof_auxv_val` and not
- `sizeof_auxv_type`). */
- ptr += sizeof_auxv_val;
- *valp = extract_unsigned_integer (ptr, sizeof_auxv_val, byte_order);
- ptr += sizeof_auxv_val;
- *readptr = ptr;
- return 1;
- }
- /* See auxv.h. */
- int
- default_auxv_parse (struct target_ops *ops, gdb_byte **readptr,
- gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp)
- {
- struct gdbarch *gdbarch = target_gdbarch ();
- struct type *ptr_type = builtin_type (gdbarch)->builtin_data_ptr;
- const int sizeof_auxv_type = TYPE_LENGTH (ptr_type);
- return generic_auxv_parse (gdbarch, readptr, endptr, typep, valp,
- sizeof_auxv_type);
- }
- /* See auxv.h. */
- int
- svr4_auxv_parse (struct gdbarch *gdbarch, gdb_byte **readptr,
- gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp)
- {
- struct type *int_type = builtin_type (gdbarch)->builtin_int;
- const int sizeof_auxv_type = TYPE_LENGTH (int_type);
- return generic_auxv_parse (gdbarch, readptr, endptr, typep, valp,
- sizeof_auxv_type);
- }
- /* Read one auxv entry from *READPTR, not reading locations >= ENDPTR.
- Return 0 if *READPTR is already at the end of the buffer.
- Return -1 if there is insufficient buffer for a whole entry.
- Return 1 if an entry was read into *TYPEP and *VALP. */
- int
- target_auxv_parse (gdb_byte **readptr,
- gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp)
- {
- struct gdbarch *gdbarch = target_gdbarch();
- if (gdbarch_auxv_parse_p (gdbarch))
- return gdbarch_auxv_parse (gdbarch, readptr, endptr, typep, valp);
- return current_inferior ()->top_target ()->auxv_parse (readptr, endptr,
- typep, valp);
- }
- /* Auxiliary Vector information structure. This is used by GDB
- for caching purposes for each inferior. This helps reduce the
- overhead of transfering data from a remote target to the local host. */
- struct auxv_info
- {
- gdb::optional<gdb::byte_vector> data;
- };
- /* Per-inferior data key for auxv. */
- static const struct inferior_key<auxv_info> auxv_inferior_data;
- /* Invalidate INF's auxv cache. */
- static void
- invalidate_auxv_cache_inf (struct inferior *inf)
- {
- auxv_inferior_data.clear (inf);
- }
- /* Invalidate current inferior's auxv cache. */
- static void
- invalidate_auxv_cache (void)
- {
- invalidate_auxv_cache_inf (current_inferior ());
- }
- /* Fetch the auxv object from inferior INF. If auxv is cached already,
- return a pointer to the cache. If not, fetch the auxv object from the
- target and cache it. This function always returns a valid INFO pointer. */
- static struct auxv_info *
- get_auxv_inferior_data (struct target_ops *ops)
- {
- struct auxv_info *info;
- struct inferior *inf = current_inferior ();
- info = auxv_inferior_data.get (inf);
- if (info == NULL)
- {
- info = auxv_inferior_data.emplace (inf);
- info->data = target_read_alloc (ops, TARGET_OBJECT_AUXV, NULL);
- }
- return info;
- }
- /* Extract the auxiliary vector entry with a_type matching MATCH.
- Return zero if no such entry was found, or -1 if there was
- an error getting the information. On success, return 1 after
- storing the entry's value field in *VALP. */
- int
- target_auxv_search (struct target_ops *ops, CORE_ADDR match, CORE_ADDR *valp)
- {
- CORE_ADDR type, val;
- auxv_info *info = get_auxv_inferior_data (ops);
- if (!info->data)
- return -1;
- gdb_byte *data = info->data->data ();
- gdb_byte *ptr = data;
- size_t len = info->data->size ();
- while (1)
- switch (target_auxv_parse (&ptr, data + len, &type, &val))
- {
- case 1: /* Here's an entry, check it. */
- if (type == match)
- {
- *valp = val;
- return 1;
- }
- break;
- case 0: /* End of the vector. */
- return 0;
- default: /* Bogosity. */
- return -1;
- }
- /*NOTREACHED*/
- }
- /* Print the description of a single AUXV entry on the specified file. */
- void
- fprint_auxv_entry (struct ui_file *file, const char *name,
- const char *description, enum auxv_format format,
- CORE_ADDR type, CORE_ADDR val)
- {
- gdb_printf (file, ("%-4s %-20s %-30s "),
- plongest (type), name, description);
- switch (format)
- {
- case AUXV_FORMAT_DEC:
- gdb_printf (file, ("%s\n"), plongest (val));
- break;
- case AUXV_FORMAT_HEX:
- gdb_printf (file, ("%s\n"), paddress (target_gdbarch (), val));
- break;
- case AUXV_FORMAT_STR:
- {
- struct value_print_options opts;
- get_user_print_options (&opts);
- if (opts.addressprint)
- gdb_printf (file, ("%s "), paddress (target_gdbarch (), val));
- val_print_string (builtin_type (target_gdbarch ())->builtin_char,
- NULL, val, -1, file, &opts);
- gdb_printf (file, ("\n"));
- }
- break;
- }
- }
- /* The default implementation of gdbarch_print_auxv_entry. */
- void
- default_print_auxv_entry (struct gdbarch *gdbarch, struct ui_file *file,
- CORE_ADDR type, CORE_ADDR val)
- {
- const char *name = "???";
- const char *description = "";
- enum auxv_format format = AUXV_FORMAT_HEX;
- switch (type)
- {
- #define TAG(tag, text, kind) \
- case tag: name = #tag; description = text; format = kind; break
- TAG (AT_NULL, _("End of vector"), AUXV_FORMAT_HEX);
- TAG (AT_IGNORE, _("Entry should be ignored"), AUXV_FORMAT_HEX);
- TAG (AT_EXECFD, _("File descriptor of program"), AUXV_FORMAT_DEC);
- TAG (AT_PHDR, _("Program headers for program"), AUXV_FORMAT_HEX);
- TAG (AT_PHENT, _("Size of program header entry"), AUXV_FORMAT_DEC);
- TAG (AT_PHNUM, _("Number of program headers"), AUXV_FORMAT_DEC);
- TAG (AT_PAGESZ, _("System page size"), AUXV_FORMAT_DEC);
- TAG (AT_BASE, _("Base address of interpreter"), AUXV_FORMAT_HEX);
- TAG (AT_FLAGS, _("Flags"), AUXV_FORMAT_HEX);
- TAG (AT_ENTRY, _("Entry point of program"), AUXV_FORMAT_HEX);
- TAG (AT_NOTELF, _("Program is not ELF"), AUXV_FORMAT_DEC);
- TAG (AT_UID, _("Real user ID"), AUXV_FORMAT_DEC);
- TAG (AT_EUID, _("Effective user ID"), AUXV_FORMAT_DEC);
- TAG (AT_GID, _("Real group ID"), AUXV_FORMAT_DEC);
- TAG (AT_EGID, _("Effective group ID"), AUXV_FORMAT_DEC);
- TAG (AT_CLKTCK, _("Frequency of times()"), AUXV_FORMAT_DEC);
- TAG (AT_PLATFORM, _("String identifying platform"), AUXV_FORMAT_STR);
- TAG (AT_HWCAP, _("Machine-dependent CPU capability hints"),
- AUXV_FORMAT_HEX);
- TAG (AT_FPUCW, _("Used FPU control word"), AUXV_FORMAT_DEC);
- TAG (AT_DCACHEBSIZE, _("Data cache block size"), AUXV_FORMAT_DEC);
- TAG (AT_ICACHEBSIZE, _("Instruction cache block size"), AUXV_FORMAT_DEC);
- TAG (AT_UCACHEBSIZE, _("Unified cache block size"), AUXV_FORMAT_DEC);
- TAG (AT_IGNOREPPC, _("Entry should be ignored"), AUXV_FORMAT_DEC);
- TAG (AT_BASE_PLATFORM, _("String identifying base platform"),
- AUXV_FORMAT_STR);
- TAG (AT_RANDOM, _("Address of 16 random bytes"), AUXV_FORMAT_HEX);
- TAG (AT_HWCAP2, _("Extension of AT_HWCAP"), AUXV_FORMAT_HEX);
- TAG (AT_EXECFN, _("File name of executable"), AUXV_FORMAT_STR);
- TAG (AT_SECURE, _("Boolean, was exec setuid-like?"), AUXV_FORMAT_DEC);
- TAG (AT_SYSINFO, _("Special system info/entry points"), AUXV_FORMAT_HEX);
- TAG (AT_SYSINFO_EHDR, _("System-supplied DSO's ELF header"),
- AUXV_FORMAT_HEX);
- TAG (AT_L1I_CACHESHAPE, _("L1 Instruction cache information"),
- AUXV_FORMAT_HEX);
- TAG (AT_L1I_CACHESIZE, _("L1 Instruction cache size"), AUXV_FORMAT_HEX);
- TAG (AT_L1I_CACHEGEOMETRY, _("L1 Instruction cache geometry"),
- AUXV_FORMAT_HEX);
- TAG (AT_L1D_CACHESHAPE, _("L1 Data cache information"), AUXV_FORMAT_HEX);
- TAG (AT_L1D_CACHESIZE, _("L1 Data cache size"), AUXV_FORMAT_HEX);
- TAG (AT_L1D_CACHEGEOMETRY, _("L1 Data cache geometry"),
- AUXV_FORMAT_HEX);
- TAG (AT_L2_CACHESHAPE, _("L2 cache information"), AUXV_FORMAT_HEX);
- TAG (AT_L2_CACHESIZE, _("L2 cache size"), AUXV_FORMAT_HEX);
- TAG (AT_L2_CACHEGEOMETRY, _("L2 cache geometry"), AUXV_FORMAT_HEX);
- TAG (AT_L3_CACHESHAPE, _("L3 cache information"), AUXV_FORMAT_HEX);
- TAG (AT_L3_CACHESIZE, _("L3 cache size"), AUXV_FORMAT_HEX);
- TAG (AT_L3_CACHEGEOMETRY, _("L3 cache geometry"), AUXV_FORMAT_HEX);
- TAG (AT_MINSIGSTKSZ, _("Minimum stack size for signal delivery"),
- AUXV_FORMAT_HEX);
- TAG (AT_SUN_UID, _("Effective user ID"), AUXV_FORMAT_DEC);
- TAG (AT_SUN_RUID, _("Real user ID"), AUXV_FORMAT_DEC);
- TAG (AT_SUN_GID, _("Effective group ID"), AUXV_FORMAT_DEC);
- TAG (AT_SUN_RGID, _("Real group ID"), AUXV_FORMAT_DEC);
- TAG (AT_SUN_LDELF, _("Dynamic linker's ELF header"), AUXV_FORMAT_HEX);
- TAG (AT_SUN_LDSHDR, _("Dynamic linker's section headers"),
- AUXV_FORMAT_HEX);
- TAG (AT_SUN_LDNAME, _("String giving name of dynamic linker"),
- AUXV_FORMAT_STR);
- TAG (AT_SUN_LPAGESZ, _("Large pagesize"), AUXV_FORMAT_DEC);
- TAG (AT_SUN_PLATFORM, _("Platform name string"), AUXV_FORMAT_STR);
- TAG (AT_SUN_CAP_HW1, _("Machine-dependent CPU capability hints"),
- AUXV_FORMAT_HEX);
- TAG (AT_SUN_IFLUSH, _("Should flush icache?"), AUXV_FORMAT_DEC);
- TAG (AT_SUN_CPU, _("CPU name string"), AUXV_FORMAT_STR);
- TAG (AT_SUN_EMUL_ENTRY, _("COFF entry point address"), AUXV_FORMAT_HEX);
- TAG (AT_SUN_EMUL_EXECFD, _("COFF executable file descriptor"),
- AUXV_FORMAT_DEC);
- TAG (AT_SUN_EXECNAME,
- _("Canonicalized file name given to execve"), AUXV_FORMAT_STR);
- TAG (AT_SUN_MMU, _("String for name of MMU module"), AUXV_FORMAT_STR);
- TAG (AT_SUN_LDDATA, _("Dynamic linker's data segment address"),
- AUXV_FORMAT_HEX);
- TAG (AT_SUN_AUXFLAGS,
- _("AF_SUN_ flags passed from the kernel"), AUXV_FORMAT_HEX);
- TAG (AT_SUN_EMULATOR, _("Name of emulation binary for runtime linker"),
- AUXV_FORMAT_STR);
- TAG (AT_SUN_BRANDNAME, _("Name of brand library"), AUXV_FORMAT_STR);
- TAG (AT_SUN_BRAND_AUX1, _("Aux vector for brand modules 1"),
- AUXV_FORMAT_HEX);
- TAG (AT_SUN_BRAND_AUX2, _("Aux vector for brand modules 2"),
- AUXV_FORMAT_HEX);
- TAG (AT_SUN_BRAND_AUX3, _("Aux vector for brand modules 3"),
- AUXV_FORMAT_HEX);
- TAG (AT_SUN_CAP_HW2, _("Machine-dependent CPU capability hints 2"),
- AUXV_FORMAT_HEX);
- }
- fprint_auxv_entry (file, name, description, format, type, val);
- }
- /* Print the contents of the target's AUXV on the specified file. */
- int
- fprint_target_auxv (struct ui_file *file, struct target_ops *ops)
- {
- struct gdbarch *gdbarch = target_gdbarch ();
- CORE_ADDR type, val;
- int ents = 0;
- auxv_info *info = get_auxv_inferior_data (ops);
- if (!info->data)
- return -1;
- gdb_byte *data = info->data->data ();
- gdb_byte *ptr = data;
- size_t len = info->data->size ();
- while (target_auxv_parse (&ptr, data + len, &type, &val) > 0)
- {
- gdbarch_print_auxv_entry (gdbarch, file, type, val);
- ++ents;
- if (type == AT_NULL)
- break;
- }
- return ents;
- }
- static void
- info_auxv_command (const char *cmd, int from_tty)
- {
- if (! target_has_stack ())
- error (_("The program has no auxiliary information now."));
- else
- {
- int ents = fprint_target_auxv (gdb_stdout,
- current_inferior ()->top_target ());
- if (ents < 0)
- error (_("No auxiliary vector found, or failed reading it."));
- else if (ents == 0)
- error (_("Auxiliary vector is empty."));
- }
- }
- void _initialize_auxv ();
- void
- _initialize_auxv ()
- {
- add_info ("auxv", info_auxv_command,
- _("Display the inferior's auxiliary vector.\n\
- This is information provided by the operating system at program startup."));
- /* Observers used to invalidate the auxv cache when needed. */
- gdb::observers::inferior_exit.attach (invalidate_auxv_cache_inf, "auxv");
- gdb::observers::inferior_appeared.attach (invalidate_auxv_cache_inf, "auxv");
- gdb::observers::executable_changed.attach (invalidate_auxv_cache, "auxv");
- }
|