[v4,11/11] RFC only: compile: Use also inferior munmap

Message ID 20150503140815.18583.29612.stgit@host1.jankratochvil.net
State New, archived
Headers

Commit Message

Jan Kratochvil May 3, 2015, 2:08 p.m. UTC
  Hi,

currently inferior memory is allocated by inferior mmap() but it is never
deallocated; despite the injected objfile incl. its symbols is freed.  This was
intentional so that one can do for example:
inferior:
	char *str = "foo";
GDB:
	(gdb) compile code str = "bar";

I believe later patches will be needed to introduce full control over keeping
vs. discarding the injected module as being discussed in:
	compile: objfiles lifetime UI
	https://sourceware.org/ml/gdb/2015-04/msg00051.html
	Message-ID: <20150429135735.GA16974@host1.jankratochvil.net>
This patch at least introduces code which will be needed for the part/cases of
really freeing all the resources of an injected module.

It is "RFC only" as given the patch as is it regresses GDB functionality.


Jan


gdb/ChangeLog
2015-04-28  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* arch-utils.c (default_infcall_munmap): New.
	* arch-utils.h (default_infcall_munmap): New declaration.
	* compile/compile-object-load.c (struct munmap_list, munmap_list_add)
	(munmap_list_free, munmap_listp_free_cleanup): New.
	(struct setup_sections_data): Add field munmap_list_headp.
	(setup_sections): Call munmap_list_add.
	(compile_object_load): New variable munmap_list_head, initialize
	setup_sections_data.munmap_list_headp, return munmap_list_head.
	* compile/compile-object-load.h (struct munmap_list): New declaration.
	(struct compile_module): Add field munmap_list_head.
	(munmap_list_free): New declaration.
	* compile/compile-object-run.c (struct do_module_cleanup): Add field
	munmap_list_head.
	(munmap_list_free_regsave): New.
	(do_module_cleanup): Call it.
	(compile_object_run): Pass munmap_list_head to do_module_cleanup.
	* gdbarch.c: Regenerate.
	* gdbarch.h: Regenerate.
	* gdbarch.sh (infcall_munmap): New.
	* linux-tdep.c (linux_infcall_munmap): New.
	(linux_init_abi): Install it.

gdb/testsuite/ChangeLog
2015-04-28  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb.compile/compile.exp (keep jit in memory): Rename to ...
	(do not keep jit in memory): ... this.
	(expect 5): Change it to ...
	(expect no 5): ... this.
---
 gdb/arch-utils.c                      |    6 +++
 gdb/arch-utils.h                      |    1 
 gdb/compile/compile-object-load.c     |   71 +++++++++++++++++++++++++++++++++
 gdb/compile/compile-object-load.h     |    6 +++
 gdb/compile/compile-object-run.c      |   27 +++++++++++++
 gdb/gdbarch.c                         |   23 +++++++++++
 gdb/gdbarch.h                         |    7 +++
 gdb/gdbarch.sh                        |    4 ++
 gdb/linux-tdep.c                      |   30 ++++++++++++++
 gdb/testsuite/gdb.compile/compile.exp |    4 +-
 10 files changed, 176 insertions(+), 3 deletions(-)
  

Comments

Pedro Alves May 6, 2015, 3:21 p.m. UTC | #1
On 05/03/2015 03:08 PM, Jan Kratochvil wrote:
> Hi,
> 
> currently inferior memory is allocated by inferior mmap() but it is never
> deallocated; despite the injected objfile incl. its symbols is freed.  This was
> intentional so that one can do for example:
> inferior:
> 	char *str = "foo";
> GDB:
> 	(gdb) compile code str = "bar";
> 
> I believe later patches will be needed to introduce full control over keeping
> vs. discarding the injected module as being discussed in:
> 	compile: objfiles lifetime UI
> 	https://sourceware.org/ml/gdb/2015-04/msg00051.html
> 	Message-ID: <20150429135735.GA16974@host1.jankratochvil.net>
> This patch at least introduces code which will be needed for the part/cases of
> really freeing all the resources of an injected module.
> 
> It is "RFC only" as given the patch as is it regresses GDB functionality.

I don't which parts of this would cause regressions; it's helpful
when an RFC points out what is known to be hacky or patch bits that are known
to need to change.  It's not obvious, at least to me.

That said, I skimmed it and it overall looks good.  The stop_registers
bits looked surprising.  My reaction was that I'd think that would be
something handled around save_infcall_suspend_state/restore_infcall_suspend_state,
though it's not clear to me.

Thanks,
Pedro Alves
  
Jan Kratochvil May 6, 2015, 7:32 p.m. UTC | #2
On Wed, 06 May 2015 17:21:37 +0200, Pedro Alves wrote:
> On 05/03/2015 03:08 PM, Jan Kratochvil wrote:
> > I believe later patches will be needed to introduce full control over keeping
> > vs. discarding the injected module as being discussed in:
> > 	compile: objfiles lifetime UI
> > 	https://sourceware.org/ml/gdb/2015-04/msg00051.html
> > 	Message-ID: <20150429135735.GA16974@host1.jankratochvil.net>
> > This patch at least introduces code which will be needed for the part/cases of
> > really freeing all the resources of an injected module.
> > 
> > It is "RFC only" as given the patch as is it regresses GDB functionality.
> 
> I don't which parts of this would cause regressions;

For example the existing testsuite case - without its change it would
PASS->FAIL.


> it's helpful
> when an RFC points out what is known to be hacky or patch bits that are known
> to need to change.  It's not obvious, at least to me.

One can imagine many cases when the inferior can later reference the injected
object's memory; although I agree for most of the 'print' expressions it will
not happen.  Maybe only the C 'char *' case
	https://sourceware.org/ml/gdb/2015-04/msg00054.html
is the most visible one, C++ has better memory ownership/management in its
objects.


> That said, I skimmed it and it overall looks good.  The stop_registers
> bits looked surprising.  My reaction was that I'd think that would be
> something handled around save_infcall_suspend_state/restore_infcall_suspend_state,
> though it's not clear to me.

This was a minimal change how to make this patch working.  I agree
'stop_registers' (also) looks as a hack to me, I will therefore try to make
some pre/post cleanup patch if we can get rid of 'stop_registers', I have no
idea.


Thanks,
Jan
  
Jan Kratochvil May 8, 2015, 8:23 p.m. UTC | #3
On Wed, 06 May 2015 21:32:08 +0200, Jan Kratochvil wrote:
> This was a minimal change how to make this patch working.  I agree
> 'stop_registers' (also) looks as a hack to me, I will therefore try to make
> some pre/post cleanup patch if we can get rid of 'stop_registers', I have no
> idea.

Addressed by:
	[PATCH 4/6] infcall: stop_registers -> register_dummy_frame_dtor
	https://sourceware.org/ml/gdb-patches/2015-05/msg00221.html


Jan
  

Patch

diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c
index e1c8ab0..362c71b 100644
--- a/gdb/arch-utils.c
+++ b/gdb/arch-utils.c
@@ -864,6 +864,12 @@  default_infcall_mmap (CORE_ADDR size, unsigned prot)
   error (_("This target does not support inferior memory allocation by mmap."));
 }
 
+void
+default_infcall_munmap (CORE_ADDR addr, CORE_ADDR size)
+{
+  /* Memory reserved by inferior mmap is kept leaked.  */
+}
+
 /* -mcmodel=large is used so that no GOT (Global Offset Table) is needed to be
    created in inferior memory by GDB (normally it is set by ld.so).  */
 
diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h
index ed3bec9..f40e076 100644
--- a/gdb/arch-utils.h
+++ b/gdb/arch-utils.h
@@ -200,6 +200,7 @@  extern void default_skip_permanent_breakpoint (struct regcache *regcache);
 #define GDB_MMAP_PROT_EXEC	0x4	/* Page can be executed.  */
 
 extern CORE_ADDR default_infcall_mmap (CORE_ADDR size, unsigned prot);
+extern void default_infcall_munmap (CORE_ADDR addr, CORE_ADDR size);
 extern char *default_gcc_target_options (struct gdbarch *gdbarch);
 extern const char *default_gnu_triplet_regexp (struct gdbarch *gdbarch);
 
diff --git a/gdb/compile/compile-object-load.c b/gdb/compile/compile-object-load.c
index 0be47ca..d9e88a6 100644
--- a/gdb/compile/compile-object-load.c
+++ b/gdb/compile/compile-object-load.c
@@ -32,6 +32,58 @@ 
 #include "block.h"
 #include "arch-utils.h"
 
+/* Track inferior memory reserved by inferior mmap.  */
+
+struct munmap_list
+{
+  struct munmap_list *next;
+  CORE_ADDR addr, size;
+};
+
+/* Add inferior mmap memory range ADDR..ADDR+SIZE (exclusive) to list
+   HEADP.  *HEADP needs to be initialized to NULL.  */
+
+static void
+munmap_list_add (struct munmap_list **headp, CORE_ADDR addr, CORE_ADDR size)
+{
+  struct munmap_list *head_new = xmalloc (sizeof (*head_new));
+
+  head_new->next = *headp;
+  *headp = head_new;
+  head_new->addr = addr;
+  head_new->size = size;
+}
+
+/* Free list of inferior mmap memory ranges HEAD.  HEAD is the first
+   element of the list, it can be NULL.  After calling this function
+   HEAD pointer is invalid and the possible list needs to be
+   reinitialized by caller to NULL.  */
+
+void
+munmap_list_free (struct munmap_list *head)
+{
+  while (head)
+    {
+      struct munmap_list *todo = head;
+
+      head = todo->next;
+      gdbarch_infcall_munmap (target_gdbarch (), todo->addr, todo->size);
+      xfree (todo);
+    }
+}
+
+/* Stub for munmap_list_free suitable for make_cleanup.  Contrary to
+   munmap_list_free this function's parameter is a pointer to the first
+   list element pointer.  */
+
+static void
+munmap_listp_free_cleanup (void *headp_voidp)
+{
+  struct munmap_list **headp = headp_voidp;
+
+  munmap_list_free (*headp);
+}
+
 /* Helper data for setup_sections.  */
 
 struct setup_sections_data
@@ -48,6 +100,10 @@  struct setup_sections_data
   /* Maximum of alignments of all sections matching LAST_PROT.
      This value is always at least 1.  This value is always a power of 2.  */
   CORE_ADDR last_max_alignment;
+
+  /* List of inferior mmap ranges where setup_sections should add its
+     next range.  */
+  struct munmap_list **munmap_list_headp;
 };
 
 /* Place all ABFD sections next to each other obeying all constraints.  */
@@ -97,6 +153,7 @@  setup_sections (bfd *abfd, asection *sect, void *data_voidp)
 	{
 	  addr = gdbarch_infcall_mmap (target_gdbarch (), data->last_size,
 				       data->last_prot);
+	  munmap_list_add (data->munmap_list_headp, addr, data->last_size);
 	  if (compile_debug)
 	    fprintf_unfiltered (gdb_stdout,
 				"allocated %s bytes at %s prot %u\n",
@@ -588,6 +645,7 @@  compile_object_load (const char *object_file, const char *source_file,
   struct objfile *objfile;
   int expect_parameters;
   struct type *expect_return_type;
+  struct munmap_list *munmap_list_head = NULL;
 
   filename = tilde_expand (object_file);
   cleanups = make_cleanup (xfree, filename);
@@ -609,6 +667,8 @@  compile_object_load (const char *object_file, const char *source_file,
   setup_sections_data.last_section_first = abfd->sections;
   setup_sections_data.last_prot = -1;
   setup_sections_data.last_max_alignment = 1;
+  setup_sections_data.munmap_list_headp = &munmap_list_head;
+  make_cleanup (munmap_listp_free_cleanup, &munmap_list_head);
   bfd_map_over_sections (abfd, setup_sections, &setup_sections_data);
   setup_sections (abfd, NULL, &setup_sections_data);
 
@@ -728,6 +788,7 @@  compile_object_load (const char *object_file, const char *source_file,
 					TYPE_LENGTH (regs_type),
 					GDB_MMAP_PROT_READ);
       gdb_assert (regs_addr != 0);
+      munmap_list_add (&munmap_list_head, regs_addr, TYPE_LENGTH (regs_type));
       if (compile_debug)
 	fprintf_unfiltered (gdb_stdout,
 			    "allocated %s bytes at %s for registers\n",
@@ -752,6 +813,8 @@  compile_object_load (const char *object_file, const char *source_file,
 					     (GDB_MMAP_PROT_READ
 					      | GDB_MMAP_PROT_WRITE));
       gdb_assert (out_value_addr != 0);
+      munmap_list_add (&munmap_list_head, out_value_addr,
+		       TYPE_LENGTH (out_value_type));
       if (compile_debug)
 	fprintf_unfiltered (gdb_stdout,
 			    "allocated %s bytes at %s for printed value\n",
@@ -761,7 +824,6 @@  compile_object_load (const char *object_file, const char *source_file,
     }
 
   discard_cleanups (cleanups_free_objfile);
-  do_cleanups (cleanups);
 
   retval = xmalloc (sizeof (*retval));
   retval->objfile = objfile;
@@ -772,5 +834,12 @@  compile_object_load (const char *object_file, const char *source_file,
   retval->scope_data = scope_data;
   retval->out_value_addr = out_value_addr;
   retval->out_value_type = out_value_type;
+
+  /* CLEANUPS will free MUNMAP_LIST_HEAD.  */
+  retval->munmap_list_head = munmap_list_head;
+  munmap_list_head = NULL;
+
+  do_cleanups (cleanups);
+
   return retval;
 }
diff --git a/gdb/compile/compile-object-load.h b/gdb/compile/compile-object-load.h
index 3e0e45b..61d8563 100644
--- a/gdb/compile/compile-object-load.h
+++ b/gdb/compile/compile-object-load.h
@@ -17,6 +17,8 @@ 
 #ifndef GDB_COMPILE_OBJECT_LOAD_H
 #define GDB_COMPILE_OBJECT_LOAD_H
 
+struct munmap_list;
+
 struct compile_module
 {
   /* objfile for the compiled module.  */
@@ -45,10 +47,14 @@  struct compile_module
   /* Inferior parameter out value type or NULL if the inferior function does not
      have one.  */
   struct type *out_value_type;
+
+  /* Track inferior memory reserved by inferior mmap.  */
+  struct munmap_list *munmap_list_head;
 };
 
 extern struct compile_module *compile_object_load
   (const char *object_file, const char *source_file,
    enum compile_i_scope_types scope, void *scope_data);
+extern void munmap_list_free (struct munmap_list *head);
 
 #endif /* GDB_COMPILE_OBJECT_LOAD_H */
diff --git a/gdb/compile/compile-object-run.c b/gdb/compile/compile-object-run.c
index 067f549..fe2d923 100644
--- a/gdb/compile/compile-object-run.c
+++ b/gdb/compile/compile-object-run.c
@@ -48,10 +48,34 @@  struct do_module_cleanup
   CORE_ADDR out_value_addr;
   struct type *out_value_type;
 
+  /* Copy from struct compile_module.  */
+  struct munmap_list *munmap_list_head;
+
   /* objfile_name of our objfile.  */
   char objfile_name_string[1];
 };
 
+/* Wrapper for munmap_list_free which can be used from
+   dummy_frame_dtor_ftype.  call_function_by_hand_dummy expects
+   stop_registers will not change by calling dummy_frame_dtor_ftype.  */
+
+static void
+munmap_list_free_regsave (struct munmap_list *head)
+{
+  struct regcache *stop_registers_saved;
+  
+  if (stop_registers == NULL)
+    stop_registers_saved = NULL;
+  else
+    stop_registers_saved = regcache_dup (stop_registers);
+
+  munmap_list_free (head);
+
+  if (stop_registers != NULL)
+    regcache_xfree (stop_registers);
+  stop_registers = stop_registers_saved;
+}
+
 /* Cleanup everything after the inferior function dummy frame gets
    discarded.  */
 
@@ -97,6 +121,8 @@  do_module_cleanup (void *arg)
   unlink (data->source_file);
   xfree (data->source_file);
 
+  munmap_list_free_regsave (data->munmap_list_head);
+
   /* Delete the .o file.  */
   unlink (data->objfile_name_string);
   xfree (data);
@@ -144,6 +170,7 @@  compile_object_run (struct compile_module *module)
   data->scope_data = module->scope_data;
   data->out_value_addr = module->out_value_addr;
   data->out_value_type = module->out_value_type;
+  data->munmap_list_head = module->munmap_list_head;
 
   xfree (module->source_file);
   xfree (module);
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index 97874c9..aa2789e 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -326,6 +326,7 @@  struct gdbarch
   gdbarch_auxv_parse_ftype *auxv_parse;
   gdbarch_vsyscall_range_ftype *vsyscall_range;
   gdbarch_infcall_mmap_ftype *infcall_mmap;
+  gdbarch_infcall_munmap_ftype *infcall_munmap;
   gdbarch_gcc_target_options_ftype *gcc_target_options;
   gdbarch_gnu_triplet_regexp_ftype *gnu_triplet_regexp;
 };
@@ -426,6 +427,7 @@  gdbarch_alloc (const struct gdbarch_info *info,
   gdbarch->insn_is_jump = default_insn_is_jump;
   gdbarch->vsyscall_range = default_vsyscall_range;
   gdbarch->infcall_mmap = default_infcall_mmap;
+  gdbarch->infcall_munmap = default_infcall_munmap;
   gdbarch->gcc_target_options = default_gcc_target_options;
   gdbarch->gnu_triplet_regexp = default_gnu_triplet_regexp;
   /* gdbarch_alloc() */
@@ -658,6 +660,7 @@  verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of auxv_parse, has predicate.  */
   /* Skip verify of vsyscall_range, invalid_p == 0 */
   /* Skip verify of infcall_mmap, invalid_p == 0 */
+  /* Skip verify of infcall_munmap, invalid_p == 0 */
   /* Skip verify of gcc_target_options, invalid_p == 0 */
   /* Skip verify of gnu_triplet_regexp, invalid_p == 0 */
   buf = ui_file_xstrdup (log, &length);
@@ -1029,6 +1032,9 @@  gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
                       "gdbarch_dump: infcall_mmap = <%s>\n",
                       host_address_to_string (gdbarch->infcall_mmap));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: infcall_munmap = <%s>\n",
+                      host_address_to_string (gdbarch->infcall_munmap));
+  fprintf_unfiltered (file,
                       "gdbarch_dump: gdbarch_info_proc_p() = %d\n",
                       gdbarch_info_proc_p (gdbarch));
   fprintf_unfiltered (file,
@@ -4673,6 +4679,23 @@  set_gdbarch_infcall_mmap (struct gdbarch *gdbarch,
   gdbarch->infcall_mmap = infcall_mmap;
 }
 
+void
+gdbarch_infcall_munmap (struct gdbarch *gdbarch, CORE_ADDR addr, CORE_ADDR size)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->infcall_munmap != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_infcall_munmap called\n");
+  gdbarch->infcall_munmap (addr, size);
+}
+
+void
+set_gdbarch_infcall_munmap (struct gdbarch *gdbarch,
+                            gdbarch_infcall_munmap_ftype infcall_munmap)
+{
+  gdbarch->infcall_munmap = infcall_munmap;
+}
+
 char *
 gdbarch_gcc_target_options (struct gdbarch *gdbarch)
 {
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index c94c19c..59882df 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -1440,6 +1440,13 @@  typedef CORE_ADDR (gdbarch_infcall_mmap_ftype) (CORE_ADDR size, unsigned prot);
 extern CORE_ADDR gdbarch_infcall_mmap (struct gdbarch *gdbarch, CORE_ADDR size, unsigned prot);
 extern void set_gdbarch_infcall_mmap (struct gdbarch *gdbarch, gdbarch_infcall_mmap_ftype *infcall_mmap);
 
+/* Deallocate SIZE bytes of memory at ADDR in inferior from gdbarch_infcall_mmap.
+   Print a warning if it is not possible. */
+
+typedef void (gdbarch_infcall_munmap_ftype) (CORE_ADDR addr, CORE_ADDR size);
+extern void gdbarch_infcall_munmap (struct gdbarch *gdbarch, CORE_ADDR addr, CORE_ADDR size);
+extern void set_gdbarch_infcall_munmap (struct gdbarch *gdbarch, gdbarch_infcall_munmap_ftype *infcall_munmap);
+
 /* Return string (caller has to use xfree for it) with options for GCC
    to produce code for this target, typically "-m64", "-m32" or "-m31".
    These options are put before CU's DW_AT_producer compilation options so that
diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
index 0f303a4..f719ec1 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -1097,6 +1097,10 @@  m:int:vsyscall_range:struct mem_range *range:range::default_vsyscall_range::0
 # Throw an error if it is not possible.  Returned address is always valid.
 f:CORE_ADDR:infcall_mmap:CORE_ADDR size, unsigned prot:size, prot::default_infcall_mmap::0
 
+# Deallocate SIZE bytes of memory at ADDR in inferior from gdbarch_infcall_mmap.
+# Print a warning if it is not possible.
+f:void:infcall_munmap:CORE_ADDR addr, CORE_ADDR size:addr, size::default_infcall_munmap::0
+
 # Return string (caller has to use xfree for it) with options for GCC
 # to produce code for this target, typically "-m64", "-m32" or "-m31".
 # These options are put before CU's DW_AT_producer compilation options so that
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 9d75b66..8cb91c9 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -2348,6 +2348,35 @@  linux_infcall_mmap (CORE_ADDR size, unsigned prot)
   return retval;
 }
 
+/* See gdbarch.sh 'infcall_munmap'.  */
+
+static void
+linux_infcall_munmap (CORE_ADDR addr, CORE_ADDR size)
+{
+  struct objfile *objf;
+  struct value *munmap_val = find_function_in_inferior ("munmap", &objf);
+  struct value *retval_val;
+  struct gdbarch *gdbarch = get_objfile_arch (objf);
+  LONGEST retval;
+  enum
+    {
+      ARG_ADDR, ARG_LENGTH, ARG_LAST
+    };
+  struct value *arg[ARG_LAST];
+
+  arg[ARG_ADDR] = value_from_pointer (builtin_type (gdbarch)->builtin_data_ptr,
+				      addr);
+  /* Assuming sizeof (unsigned long) == sizeof (size_t).  */
+  arg[ARG_LENGTH] = value_from_ulongest
+		    (builtin_type (gdbarch)->builtin_unsigned_long, size);
+  retval_val = call_function_by_hand (munmap_val, ARG_LAST, arg);
+  retval = value_as_long (retval_val);
+  if (retval != 0)
+    warning (_("Failed inferior munmap call at %s for %s bytes, "
+	       "errno is changed."),
+	     hex_string (addr), pulongest (size));
+}
+
 /* See linux-tdep.h.  */
 
 CORE_ADDR
@@ -2409,6 +2438,7 @@  linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 				    linux_gdb_signal_to_target);
   set_gdbarch_vsyscall_range (gdbarch, linux_vsyscall_range);
   set_gdbarch_infcall_mmap (gdbarch, linux_infcall_mmap);
+  set_gdbarch_infcall_munmap (gdbarch, linux_infcall_munmap);
 }
 
 /* Provide a prototype to silence -Wmissing-prototypes.  */
diff --git a/gdb/testsuite/gdb.compile/compile.exp b/gdb/testsuite/gdb.compile/compile.exp
index 07276bd..dd46a5f 100644
--- a/gdb/testsuite/gdb.compile/compile.exp
+++ b/gdb/testsuite/gdb.compile/compile.exp
@@ -131,8 +131,8 @@  gdb_test_no_output "compile code globalvar = static_local"
 gdb_test "p globalvar" " = 77000" "check static_local"
 
 gdb_test_no_output "compile code static int staticvar = 5; intptr = &staticvar" \
-    "keep jit in memory"
-gdb_test "p *intptr" " = 5" "expect 5"
+    "do not keep jit in memory"
+gdb_test "p *intptr" "Cannot access memory at address 0x\[0-9a-f\]+" "expect no 5"
 
 gdb_test "compile code func_doesnotexist ();" "warning: Could not find symbol \"func_doesnotexist\" for .*"