Silence GCC "uninitialized" warning on minsyms.c:lookup_minimal_symbol_by_pc_section

Message ID 73d51f47-0f68-178f-3a5d-e842a2fa240b@redhat.com
State New, archived
Headers

Commit Message

Pedro Alves June 19, 2018, 3:21 p.m. UTC
  On 06/18/2018 09:26 PM, Sergio Durigan Junior wrote:
> Commit 20944a6e20324cd897bf6c4c5fd20ef7224dacaa ("Fix stepping past
> GNU ifunc resolvers (introduce lookup_msym_prefer)") introduced a new
> way to determine the 'want_type' variable on
> minsyms.c:lookup_minimal_symbol_by_pc_section.  Because the
> 'lookup_msym_prefer' has a default value, we know that 'want_type'
> will always be initialized.  

Sorry, but that doesn't follow.  It's the 'prefer' parameter
that will always be initialized to something with the default
value, not the 'want_type' local.

struct bound_minimal_symbol lookup_minimal_symbol_by_pc_section
  (CORE_ADDR,
   struct obj_section *,
   lookup_msym_prefer prefer = lookup_msym_prefer::TEXT);

vs:

  enum minimal_symbol_type want_type;


But even that is not necessarily true, since the caller may well
pass down an explicit argument, which in turn was uninitialized,
like:

 lookup_msym_prefer bogus;
 lookup_minimal_symbol_by_pc_section (addr, NULL, bogus);

> However, GCC is complaining that the
> variable can be used uninitialized in the function:
> 
>   ../../gdb/minsyms.c: In function 'bound_minimal_symbol lookup_minimal_symbol_by_pc_section(CORE_ADDR, obj_section*, lookup_msym_prefer)':
>   ../../gdb/minsyms.c:825:40: warning: 'want_type' may be used uninitialized in this function [-Wmaybe-uninitialized]
> 	   && MSYMBOL_TYPE (&msymbol[hi]) != want_type
> 
> (This is with gcc-8.1.1-1.fc29.x86_64).
> 
> This patch fixes it by initializing 'want_type' with 'mst_text', which
> is the same default value that is passed in the 'lookup_msym_prefer'
> variable.

But it's not, as explained above.

The reason this warning is a false positive is that we know that 'want_type'
is always going to be initialized because the switch to converts
enum lookup_msym_prefer values to enum minimal_symbol_type values:

  switch (prefer)
   {
    case lookup_msym_prefer::TEXT:
      want_type = mst_text;
      break;
    case lookup_msym_prefer::TRAMPOLINE:
      want_type = mst_solib_trampoline;
      break;
    case lookup_msym_prefer::GNU_IFUNC:
      want_type = mst_text_gnu_ifunc;
      break;
    }

has a case for every possible enumerator.

The actual problem is that GCC assumes that enum variables may hold
values other than the named enumerators, like e.g.,
"lookup_msym_prefer prefer = (lookup_msym_prefer) 10;".
We know that this isn't something we want to support with these
enum types, so it's better to assert that we never see a value
not covered by the enumerators.

The simplest is to add a "default:" case with a gdb_assert, but
when I wrote that code, I had not done that on purpose,
thinking that I'd prefer it if we enabled "-Wswitch" in gdb, which
helps find switch/cases where we need to handle a new enumerator,
whenever we add one, like this:

  CXX    minsyms.o
src/gdb/minsyms.c: In function ‘bound_minimal_symbol lookup_minimal_symbol_by_pc_section(CORE_ADDR, obj_section*, lookup_msym_prefer)’:
src/gdb/minsyms.c:695:10: error: enumeration value ‘NEW_KIND’ not handled in switch [-Werror=switch]
   switch (prefer)
          ^

I think that it's preferable, if reasonable, to rework the code a bit to
make it more explicit to the compiler that a variable is always
initialized instead of initializing variables to quiet -Wmaybe-uninitialized,
which is documented as spewing false positives.  (IMO it'd be reasonable
if GCC moved that warning from -Wall to -Wextra.)

Thus I'd prefer this instead:

From d66e808818ece92d6d79bfdb4e0a421b30662dd3 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Mon, 18 Jun 2018 22:36:43 +0100
Subject: [PATCH] Silence -Wmaybe-uninitialized warning in
 minsyms.c:lookup_minimal_symbol_by_pc_section

Compiling with GCC 8.1 shows this warning:

  ../../gdb/minsyms.c: In function 'bound_minimal_symbol lookup_minimal_symbol_by_pc_section(CORE_ADDR, obj_section*, lookup_msym_prefer)':
  ../../gdb/minsyms.c:825:40: warning: 'want_type' may be used uninitialized in this function [-Wmaybe-uninitialized]
	   && MSYMBOL_TYPE (&msymbol[hi]) != want_type

That warning is a false positive, because the switch that converts
enum lookup_msym_prefer values to enum enum minimal_symbol_type values
has a case for every lookup_msym_prefer enumerator:

  switch (prefer)
   {
    case lookup_msym_prefer::TEXT:
      want_type = mst_text;
      break;
    case lookup_msym_prefer::TRAMPOLINE:
      want_type = mst_solib_trampoline;
      break;
    case lookup_msym_prefer::GNU_IFUNC:
      want_type = mst_text_gnu_ifunc;
      break;
    }

The problem is that GCC assumes that enum variables may hold values
other than the named enumerators (like e.g., "lookup_msym_prefer
prefer = (lookup_msym_prefer) 10;").

Rework the code a bit, adding a gdb_assert to make it explicit to the
compiler that want_type is initialized in all normal-return paths.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* minsyms.c (msym_prefer_to_msym_type): New, factored out from ...
	(lookup_minimal_symbol_by_pc_section)... here.
---
 gdb/minsyms.c | 35 ++++++++++++++++++++++-------------
 1 file changed, 22 insertions(+), 13 deletions(-)
  

Comments

Sergio Durigan Junior June 19, 2018, 4:55 p.m. UTC | #1
On Tuesday, June 19 2018, Pedro Alves wrote:

> On 06/18/2018 09:26 PM, Sergio Durigan Junior wrote:
>> Commit 20944a6e20324cd897bf6c4c5fd20ef7224dacaa ("Fix stepping past
>> GNU ifunc resolvers (introduce lookup_msym_prefer)") introduced a new
>> way to determine the 'want_type' variable on
>> minsyms.c:lookup_minimal_symbol_by_pc_section.  Because the
>> 'lookup_msym_prefer' has a default value, we know that 'want_type'
>> will always be initialized.  
>
> Sorry, but that doesn't follow.  It's the 'prefer' parameter
> that will always be initialized to something with the default
> value, not the 'want_type' local.
>
> struct bound_minimal_symbol lookup_minimal_symbol_by_pc_section
>   (CORE_ADDR,
>    struct obj_section *,
>    lookup_msym_prefer prefer = lookup_msym_prefer::TEXT);
>
> vs:
>
>   enum minimal_symbol_type want_type;

That's correct, when I wrote "will always be initialized" I was thinking
that it will always have a value assigned to it, because of the switch.

> But even that is not necessarily true, since the caller may well
> pass down an explicit argument, which in turn was uninitialized,
> like:
>
>  lookup_msym_prefer bogus;
>  lookup_minimal_symbol_by_pc_section (addr, NULL, bogus);

That's true as well.  And as you said below, we also have to account for
the fact that we may add more entries in the enum, and somehow forget to
update the switch statement.  So saying that "it will always have a
value assigned to it" is also not good.

>> However, GCC is complaining that the
>> variable can be used uninitialized in the function:
>> 
>>   ../../gdb/minsyms.c: In function 'bound_minimal_symbol lookup_minimal_symbol_by_pc_section(CORE_ADDR, obj_section*, lookup_msym_prefer)':
>>   ../../gdb/minsyms.c:825:40: warning: 'want_type' may be used uninitialized in this function [-Wmaybe-uninitialized]
>> 	   && MSYMBOL_TYPE (&msymbol[hi]) != want_type
>> 
>> (This is with gcc-8.1.1-1.fc29.x86_64).
>> 
>> This patch fixes it by initializing 'want_type' with 'mst_text', which
>> is the same default value that is passed in the 'lookup_msym_prefer'
>> variable.
>
> But it's not, as explained above.
>
> The reason this warning is a false positive is that we know that 'want_type'
> is always going to be initialized because the switch to converts
> enum lookup_msym_prefer values to enum minimal_symbol_type values:
>
>   switch (prefer)
>    {
>     case lookup_msym_prefer::TEXT:
>       want_type = mst_text;
>       break;
>     case lookup_msym_prefer::TRAMPOLINE:
>       want_type = mst_solib_trampoline;
>       break;
>     case lookup_msym_prefer::GNU_IFUNC:
>       want_type = mst_text_gnu_ifunc;
>       break;
>     }
>
> has a case for every possible enumerator.
>
> The actual problem is that GCC assumes that enum variables may hold
> values other than the named enumerators, like e.g.,
> "lookup_msym_prefer prefer = (lookup_msym_prefer) 10;".
> We know that this isn't something we want to support with these
> enum types, so it's better to assert that we never see a value
> not covered by the enumerators.
>
> The simplest is to add a "default:" case with a gdb_assert, but
> when I wrote that code, I had not done that on purpose,
> thinking that I'd prefer it if we enabled "-Wswitch" in gdb, which
> helps find switch/cases where we need to handle a new enumerator,
> whenever we add one, like this:
>
>   CXX    minsyms.o
> src/gdb/minsyms.c: In function ‘bound_minimal_symbol lookup_minimal_symbol_by_pc_section(CORE_ADDR, obj_section*, lookup_msym_prefer)’:
> src/gdb/minsyms.c:695:10: error: enumeration value ‘NEW_KIND’ not handled in switch [-Werror=switch]
>    switch (prefer)
>           ^
>
> I think that it's preferable, if reasonable, to rework the code a bit to
> make it more explicit to the compiler that a variable is always
> initialized instead of initializing variables to quiet -Wmaybe-uninitialized,
> which is documented as spewing false positives.  (IMO it'd be reasonable
> if GCC moved that warning from -Wall to -Wextra.)
>
> Thus I'd prefer this instead:

Fair enough.  Thanks for taking a look at this.

> From d66e808818ece92d6d79bfdb4e0a421b30662dd3 Mon Sep 17 00:00:00 2001
> From: Pedro Alves <palves@redhat.com>
> Date: Mon, 18 Jun 2018 22:36:43 +0100
> Subject: [PATCH] Silence -Wmaybe-uninitialized warning in
>  minsyms.c:lookup_minimal_symbol_by_pc_section
>
> Compiling with GCC 8.1 shows this warning:
>
>   ../../gdb/minsyms.c: In function 'bound_minimal_symbol lookup_minimal_symbol_by_pc_section(CORE_ADDR, obj_section*, lookup_msym_prefer)':
>   ../../gdb/minsyms.c:825:40: warning: 'want_type' may be used uninitialized in this function [-Wmaybe-uninitialized]
> 	   && MSYMBOL_TYPE (&msymbol[hi]) != want_type
>
> That warning is a false positive, because the switch that converts
> enum lookup_msym_prefer values to enum enum minimal_symbol_type values
> has a case for every lookup_msym_prefer enumerator:
>
>   switch (prefer)
>    {
>     case lookup_msym_prefer::TEXT:
>       want_type = mst_text;
>       break;
>     case lookup_msym_prefer::TRAMPOLINE:
>       want_type = mst_solib_trampoline;
>       break;
>     case lookup_msym_prefer::GNU_IFUNC:
>       want_type = mst_text_gnu_ifunc;
>       break;
>     }
>
> The problem is that GCC assumes that enum variables may hold values
> other than the named enumerators (like e.g., "lookup_msym_prefer
> prefer = (lookup_msym_prefer) 10;").
>
> Rework the code a bit, adding a gdb_assert to make it explicit to the
> compiler that want_type is initialized in all normal-return paths.
>
> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>
> 	* minsyms.c (msym_prefer_to_msym_type): New, factored out from ...
> 	(lookup_minimal_symbol_by_pc_section)... here.
> ---
>  gdb/minsyms.c | 35 ++++++++++++++++++++++-------------
>  1 file changed, 22 insertions(+), 13 deletions(-)
>
> diff --git a/gdb/minsyms.c b/gdb/minsyms.c
> index 4882e58ee4..4409e6f8b3 100644
> --- a/gdb/minsyms.c
> +++ b/gdb/minsyms.c
> @@ -656,6 +656,27 @@ frob_address (struct objfile *objfile, CORE_ADDR *pc)
>    return 0;
>  }
>  
> +/* Helper for lookup_minimal_symbol_by_pc_section.  Convert a
> +   lookup_msym_prefer to a minimal_symbol_type.  */
> +
> +static minimal_symbol_type
> +msym_prefer_to_msym_type (lookup_msym_prefer prefer)
> +{
> +  switch (prefer)
> +    {
> +    case lookup_msym_prefer::TEXT:
> +      return mst_text;
> +    case lookup_msym_prefer::TRAMPOLINE:
> +      return mst_solib_trampoline;
> +    case lookup_msym_prefer::GNU_IFUNC:
> +      return mst_text_gnu_ifunc;
> +    }
> +
> +  /* Assert here instead of in a default switch case above so that
> +     -Wswitch warns if a new enumerator is added.  */
> +  gdb_assert_not_reached ("unhandled lookup_msym_prefer");
> +}
> +
>  /* Search through the minimal symbol table for each objfile and find
>     the symbol whose address is the largest address that is still less
>     than or equal to PC, and matches SECTION (which is not NULL).
> @@ -683,7 +704,6 @@ lookup_minimal_symbol_by_pc_section (CORE_ADDR pc_in, struct obj_section *sectio
>    struct minimal_symbol *best_symbol = NULL;
>    struct objfile *best_objfile = NULL;
>    struct bound_minimal_symbol result;
> -  enum minimal_symbol_type want_type;
>  
>    if (section == NULL)
>      {
> @@ -692,18 +712,7 @@ lookup_minimal_symbol_by_pc_section (CORE_ADDR pc_in, struct obj_section *sectio
>  	return {};
>      }
>  
> -  switch (prefer)
> -    {
> -    case lookup_msym_prefer::TEXT:
> -      want_type = mst_text;
> -      break;
> -    case lookup_msym_prefer::TRAMPOLINE:
> -      want_type = mst_solib_trampoline;
> -      break;
> -    case lookup_msym_prefer::GNU_IFUNC:
> -      want_type = mst_text_gnu_ifunc;
> -      break;
> -    }
> +  minimal_symbol_type want_type = msym_prefer_to_msym_type (prefer);
>  
>    /* We can not require the symbol found to be in section, because
>       e.g. IRIX 6.5 mdebug relies on this code returning an absolute
> -- 
> 2.14.3
  
Tom Tromey June 19, 2018, 6:47 p.m. UTC | #2
>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:

Pedro> The simplest is to add a "default:" case with a gdb_assert, but
Pedro> when I wrote that code, I had not done that on purpose,
Pedro> thinking that I'd prefer it if we enabled "-Wswitch" in gdb, which
Pedro> helps find switch/cases where we need to handle a new enumerator,
Pedro> whenever we add one, like this:

I looked at adding -Wswitch or -Wswitch-enum once.  All I really
remember is thinking that there were cases where gdb would not want
this.

However, I think maybe it can be done on a case-by-case basis with
"#pragma GCC diagnostic push".

Tom
  

Patch

diff --git a/gdb/minsyms.c b/gdb/minsyms.c
index 4882e58ee4..4409e6f8b3 100644
--- a/gdb/minsyms.c
+++ b/gdb/minsyms.c
@@ -656,6 +656,27 @@  frob_address (struct objfile *objfile, CORE_ADDR *pc)
   return 0;
 }
 
+/* Helper for lookup_minimal_symbol_by_pc_section.  Convert a
+   lookup_msym_prefer to a minimal_symbol_type.  */
+
+static minimal_symbol_type
+msym_prefer_to_msym_type (lookup_msym_prefer prefer)
+{
+  switch (prefer)
+    {
+    case lookup_msym_prefer::TEXT:
+      return mst_text;
+    case lookup_msym_prefer::TRAMPOLINE:
+      return mst_solib_trampoline;
+    case lookup_msym_prefer::GNU_IFUNC:
+      return mst_text_gnu_ifunc;
+    }
+
+  /* Assert here instead of in a default switch case above so that
+     -Wswitch warns if a new enumerator is added.  */
+  gdb_assert_not_reached ("unhandled lookup_msym_prefer");
+}
+
 /* Search through the minimal symbol table for each objfile and find
    the symbol whose address is the largest address that is still less
    than or equal to PC, and matches SECTION (which is not NULL).
@@ -683,7 +704,6 @@  lookup_minimal_symbol_by_pc_section (CORE_ADDR pc_in, struct obj_section *sectio
   struct minimal_symbol *best_symbol = NULL;
   struct objfile *best_objfile = NULL;
   struct bound_minimal_symbol result;
-  enum minimal_symbol_type want_type;
 
   if (section == NULL)
     {
@@ -692,18 +712,7 @@  lookup_minimal_symbol_by_pc_section (CORE_ADDR pc_in, struct obj_section *sectio
 	return {};
     }
 
-  switch (prefer)
-    {
-    case lookup_msym_prefer::TEXT:
-      want_type = mst_text;
-      break;
-    case lookup_msym_prefer::TRAMPOLINE:
-      want_type = mst_solib_trampoline;
-      break;
-    case lookup_msym_prefer::GNU_IFUNC:
-      want_type = mst_text_gnu_ifunc;
-      break;
-    }
+  minimal_symbol_type want_type = msym_prefer_to_msym_type (prefer);
 
   /* We can not require the symbol found to be in section, because
      e.g. IRIX 6.5 mdebug relies on this code returning an absolute