RFC: come up with startswith function.

Message ID 7fed2112-05a0-8d07-8346-6c0cf70f834e@suse.cz
State New
Headers show
Series
  • RFC: come up with startswith function.
Related show

Commit Message

Martin Liška March 18, 2021, 10:46 a.m.
Hey.

Recently, I noticed a cumbersome construct we use for string startswith function
(most notably in a situation when the prefix is a string literal).

Commonly used patterns are:
1) strncmp (arg, "--sysroot=", 10) == 0
2) strncmp (name, "not found", sizeof ("not found") - 1) == 0
3) strncmp (varname, "__builtin_", strlen ("__builtin_")) == 0
4) #define STR "-foffload-abi="
    if (strncmp (argv[i], STR, strlen (STR)) == 0)

I see all these quite error prone for the following reasons:
1) one needs to correctly calculate string length (in their head)
2) sizeof ("foo") - 1 == strlen ("foo")
3) one needs to undefine a temporary macros

Moreover, there are helper functions that already do the same:

gcc/ada/adadecode.c:
static int
has_prefix (const char *name, const char *prefix)
{
   return strncmp (name, prefix, strlen (prefix)) == 0;
}

gcc/fortran/gfortran.h:
#define gfc_str_startswith(str, pref) \
        (strncmp ((str), (pref), strlen (pref)) == 0)

That said, I'm suggesting a new function 'startswith' in system.h.
I prepared a patch that utilizes the function in gcc/ subfolder
(excluding all target code for now). I can prepare similar mechanical
patch for the rest of the compiler (and run-time libraries).

Thoughts?
Thanks,
Martin

---
  gcc/ada/adadecode.c                 | 14 +++--------
  gcc/ada/init.c                      |  8 +++----
  gcc/analyzer/sm-file.cc             |  5 ++--
  gcc/builtins.c                      | 10 +++-----
  gcc/c-family/c-ada-spec.c           |  2 +-
  gcc/c-family/c-common.c             |  2 +-
  gcc/c-family/c-format.c             |  2 +-
  gcc/collect2.c                      | 36 ++++++++++++++---------------
  gcc/cp/decl.c                       |  6 ++---
  gcc/cp/mangle.c                     |  2 +-
  gcc/d/dmd/dmangle.c                 |  2 +-
  gcc/d/dmd/hdrgen.c                  |  2 +-
  gcc/d/dmd/identifier.c              |  6 ++---
  gcc/dwarf2out.c                     | 14 +++++------
  gcc/fortran/decl.c                  |  4 ++--
  gcc/fortran/gfortran.h              |  4 ----
  gcc/fortran/module.c                | 10 ++++----
  gcc/fortran/options.c               |  2 +-
  gcc/fortran/primary.c               |  6 ++---
  gcc/fortran/trans-decl.c            |  2 +-
  gcc/fortran/trans-expr.c            |  2 +-
  gcc/fortran/trans-intrinsic.c       | 22 +++++++++---------
  gcc/gcc.c                           |  4 ++--
  gcc/gencfn-macros.c                 |  2 +-
  gcc/gengtype.c                      |  6 ++---
  gcc/genmatch.c                      |  8 +++----
  gcc/genoutput.c                     |  2 +-
  gcc/go/gofrontend/runtime.cc        |  2 +-
  gcc/incpath.c                       |  2 +-
  gcc/langhooks.c                     |  8 +++----
  gcc/objc/objc-next-runtime-abi-02.c |  2 +-
  gcc/opts-common.c                   |  2 +-
  gcc/opts.c                          |  2 +-
  gcc/read-rtl-function.c             |  2 +-
  gcc/selftest.c                      |  3 +--
  gcc/system.h                        |  8 +++++++
  gcc/timevar.c                       |  2 +-
  gcc/tree.c                          |  2 +-
  gcc/ubsan.c                         |  2 +-
  gcc/varasm.c                        | 22 +++++++++---------
  40 files changed, 117 insertions(+), 127 deletions(-)

-- 
2.30.2

Comments

H.J. Lu via Gcc-patches March 18, 2021, 3:30 p.m. | #1
On 3/18/21 4:46 AM, Martin Liška wrote:
> Hey.

> 

> Recently, I noticed a cumbersome construct we use for string startswith 

> function

> (most notably in a situation when the prefix is a string literal).

> 

> Commonly used patterns are:

> 1) strncmp (arg, "--sysroot=", 10) == 0

> 2) strncmp (name, "not found", sizeof ("not found") - 1) == 0

> 3) strncmp (varname, "__builtin_", strlen ("__builtin_")) == 0

> 4) #define STR "-foffload-abi="

>     if (strncmp (argv[i], STR, strlen (STR)) == 0)

> 

> I see all these quite error prone for the following reasons:

> 1) one needs to correctly calculate string length (in their head)

> 2) sizeof ("foo") - 1 == strlen ("foo")


Right.  They could be alleviated by either developing a new warning
or extending -Wstring-compare to complain when the length of a string
literal argument isn't the same as the bound in these cases.

> 3) one needs to undefine a temporary macros

> 

> Moreover, there are helper functions that already do the same:

> 

> gcc/ada/adadecode.c:

> static int

> has_prefix (const char *name, const char *prefix)

> {

>    return strncmp (name, prefix, strlen (prefix)) == 0;

> }

> 

> gcc/fortran/gfortran.h:

> #define gfc_str_startswith(str, pref) \

>         (strncmp ((str), (pref), strlen (pref)) == 0)

> 

> That said, I'm suggesting a new function 'startswith' in system.h.

> I prepared a patch that utilizes the function in gcc/ subfolder

> (excluding all target code for now). I can prepare similar mechanical

> patch for the rest of the compiler (and run-time libraries).

> 

> Thoughts?

> Thanks,

> Martin


I like it.

...
> diff --git a/gcc/system.h b/gcc/system.h

> index a3f5948aaee..3e384616d3a 100644

> --- a/gcc/system.h

> +++ b/gcc/system.h

> @@ -1291,4 +1291,12 @@ void gcc_stablesort (void *, size_t, size_t,

>   #define NULL nullptr

>   #endif

> 

> +/* Return 1 if STR string starts with PREFIX.  */

> +

> +static inline int

> +startswith (const char *str, const char *prefix)

> +{

> +  return strncmp (str, prefix, strlen (prefix)) == 0;

> +}


The return type of the function should be bool rather than int.

Martin

Patch

diff --git a/gcc/ada/adadecode.c b/gcc/ada/adadecode.c
index 43a378f9058..1154e628311 100644
--- a/gcc/ada/adadecode.c
+++ b/gcc/ada/adadecode.c
@@ -29,8 +29,9 @@ 
   *                                                                          *
   ****************************************************************************/
  
+#include "config.h"
+#include "system.h"
  #include "runtime.h"
-#include <string.h>
  #include <stdio.h>
  #include <ctype.h>
  
@@ -47,7 +48,6 @@ 
  #include "adadecode.h"
  
  static void add_verbose (const char *, char *);
-static int has_prefix (const char *, const char *);
  static int has_suffix (const char *, const char *);
  
  /* This is a safe version of strcpy that can be used with overlapped
@@ -68,14 +68,6 @@  static void add_verbose (const char *text, char *ada_name)
    verbose_info = 1;
  }
  
-/* Returns 1 if NAME starts with PREFIX.  */
-
-static int
-has_prefix (const char *name, const char *prefix)
-{
-  return strncmp (name, prefix, strlen (prefix)) == 0;
-}
-
  /* Returns 1 if NAME ends with SUFFIX.  */
  
  static int
@@ -167,7 +159,7 @@  __gnat_decode (const char *coded_name, char *ada_name, int verbose)
      }
  
    /* Check for library level subprogram.  */
-  else if (has_prefix (coded_name, "_ada_"))
+  else if (startswith (coded_name, "_ada_"))
      {
        strcpy (ada_name, coded_name + 5);
        lib_subprog = 1;
diff --git a/gcc/ada/init.c b/gcc/ada/init.c
index 3ceb1a31b02..24eb67bbf0d 100644
--- a/gcc/ada/init.c
+++ b/gcc/ada/init.c
@@ -2111,10 +2111,10 @@  __gnat_install_handler (void)
        prefix for vxsim when running on Linux and Windows.  */
    {
      char *model = sysModel ();
-    if ((strncmp (model, "Linux", 5) == 0)
-        || (strncmp (model, "Windows", 7) == 0)
-        || (strncmp (model, "SIMLINUX", 8) == 0) /* vx7 */
-        || (strncmp (model, "SIMNT", 5) == 0)) /* ditto */
+    if ((startswith(model, "Linux")
+        || startswith (model, "Windows")
+        || startswith (model, "SIMLINUX") /* vx7 */
+	|| startswith (model, "SIMNT")) /* ditto */
        __gnat_set_is_vxsim (TRUE);
    }
  #endif
diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc
index 7a81c8ff632..1e19ddbec7a 100644
--- a/gcc/analyzer/sm-file.cc
+++ b/gcc/analyzer/sm-file.cc
@@ -312,9 +312,8 @@  is_file_using_fn_p (tree fndecl)
  
    /* Also support variants of these names prefixed with "_IO_".  */
    const char *name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
-  if (strncmp (name, "_IO_", 4) == 0)
-    if (fs.contains_name_p (name + 4))
-      return true;
+  if (startswith (name, "_IO_") && fs.contains_name_p (name + 4))
+    return true;
  
    return false;
  }
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 196dda3fa5e..af71caf2f08 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -740,13 +740,9 @@  pointer_query::flush_cache ()
  static bool
  is_builtin_name (const char *name)
  {
-  if (strncmp (name, "__builtin_", 10) == 0)
-    return true;
-  if (strncmp (name, "__sync_", 7) == 0)
-    return true;
-  if (strncmp (name, "__atomic_", 9) == 0)
-    return true;
-  return false;
+  return (startswith (name, "__builtin_")
+	  || startswith (name, "__sync_")
+	  || startswith (name, "__atomic_"));
  }
  
  /* Return true if NODE should be considered for inline expansion regardless
diff --git a/gcc/c-family/c-ada-spec.c b/gcc/c-family/c-ada-spec.c
index dd8e8bbd90a..9b95c9d1440 100644
--- a/gcc/c-family/c-ada-spec.c
+++ b/gcc/c-family/c-ada-spec.c
@@ -2693,7 +2693,7 @@  print_destructor (pretty_printer *buffer, tree t, tree type)
    tree decl_name = DECL_NAME (TYPE_NAME (type));
  
    pp_string (buffer, "Delete_");
-  if (strncmp (IDENTIFIER_POINTER (DECL_NAME (t)), "__dt_del", 8) == 0)
+  if (startswith (IDENTIFIER_POINTER (DECL_NAME (t)), "__dt_del"))
      pp_string (buffer, "And_Free_");
    pp_ada_tree_identifier (buffer, decl_name, t, false);
  }
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index d227686a030..071f5089361 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -4670,7 +4670,7 @@  static bool builtin_function_disabled_p (const char *);
  void
  disable_builtin_function (const char *name)
  {
-  if (strncmp (name, "__builtin_", strlen ("__builtin_")) == 0)
+  if (startswith (name, "__builtin_"))
      error ("cannot disable built-in function %qs", name);
    else
      {
diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
index 0a63cacb0d9..73cc4edd42c 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -5104,7 +5104,7 @@  convert_format_name_to_system_name (const char *attr_name)
    int i;
  
    if (attr_name == NULL || *attr_name == 0
-      || strncmp (attr_name, "gcc_", 4) == 0)
+      || startswith (attr_name, "gcc_"))
      return attr_name;
  #ifdef TARGET_OVERRIDES_FORMAT_INIT
    TARGET_OVERRIDES_FORMAT_INIT ();
diff --git a/gcc/collect2.c b/gcc/collect2.c
index bd371430ab7..6c94a98c748 100644
--- a/gcc/collect2.c
+++ b/gcc/collect2.c
@@ -970,7 +970,7 @@  main (int argc, char **argv)
  	  selected_linker = USE_GOLD_LD;
  	else if (strcmp (argv[i], "-fuse-ld=lld") == 0)
  	  selected_linker = USE_LLD_LD;
-	else if (strncmp (argv[i], "-o", 2) == 0)
+	else if (startswith (argv[i], "-o"))
  	  {
  	    /* Parse the output filename if it's given so that we can make
  	       meaningful temp filenames.  */
@@ -984,19 +984,19 @@  main (int argc, char **argv)
  	/* These flags are position independent, although their order
  	   is important - subsequent flags override earlier ones. */
  	else if (strcmp (argv[i], "-b64") == 0)
-	    aix64_flag = 1;
+	  aix64_flag = 1;
  	/* -bexport:filename always needs the :filename */
-	else if (strncmp (argv[i], "-bE:", 4) == 0
-	      || strncmp (argv[i], "-bexport:", 9) == 0)
-	    export_flag = 1;
+	else if (startswith (argv[i], "-bE:")
+		 || startswith (argv[i], "-bexport:"))
+	  export_flag = 1;
  	else if (strcmp (argv[i], "-brtl") == 0
  	      || strcmp (argv[i], "-bsvr4") == 0
  	      || strcmp (argv[i], "-G") == 0)
-	    aixrtl_flag = 1;
+	  aixrtl_flag = 1;
  	else if (strcmp (argv[i], "-bnortl") == 0)
-	    aixrtl_flag = 0;
+	  aixrtl_flag = 0;
  	else if (strcmp (argv[i], "-blazy") == 0)
-	    aixlazy_flag = 1;
+	  aixlazy_flag = 1;
  #endif
        }
  
@@ -1016,11 +1016,11 @@  main (int argc, char **argv)
  	const char *q = extract_string (&p);
  	if (*q == '-' && (q[1] == 'm' || q[1] == 'f'))
  	  num_c_args++;
-	if (strncmp (q, "-flto-partition=none", 20) == 0)
+	if (startswith (q, "-flto-partition=none"))
  	  no_partition = true;
-	else if (strncmp (q, "-fno-lto", 8) == 0)
+	else if (startswith (q, "-fno-lto"))
  	  lto_mode = LTO_MODE_NONE;
-	else if (strncmp (q, "-save-temps", 11) == 0)
+	else if (startswith (q, "-save-temps"))
  	  /* FIXME: Honour =obj.  */
  	  save_temps = true;
  	else if (strcmp (q, "-dumpdir") == 0)
@@ -1254,7 +1254,7 @@  main (int argc, char **argv)
  	(void) extract_string (&p);
  #ifdef COLLECT_EXPORT_LIST
        /* Detect any invocation with -fvisibility.  */
-      if (strncmp (q, "-fvisibility", 12) == 0)
+      if (startswith (q, "-fvisibility")
  	visibility_flag = 1;
  #endif
      }
@@ -1303,7 +1303,7 @@  main (int argc, char **argv)
  	      break;
  
              case 'f':
-	      if (strncmp (arg, "-flto", 5) == 0)
+	      if (startswith (arg, "-flto"))
  		{
  #ifdef ENABLE_LTO
  		  /* Do not pass LTO flag to the linker. */
@@ -1315,13 +1315,13 @@  main (int argc, char **argv)
  #endif
  		}
  	      else if (!use_collect_ld
-		       && strncmp (arg, "-fuse-ld=", 9) == 0)
+		       && startswith (arg, "-fuse-ld="))
  		{
  		  /* Do not pass -fuse-ld={bfd|gold|lld} to the linker. */
  		  ld1--;
  		  ld2--;
  		}
-	      else if (strncmp (arg, "-fno-lto", 8) == 0)
+	      else if (startswith (arg, "-fno-lto"))
  		{
  		  /* Do not pass -fno-lto to the linker. */
  		  ld1--;
@@ -1462,7 +1462,7 @@  main (int argc, char **argv)
  		  ld2--;
  #endif
  		}
-	      else if (strncmp (arg, "--demangle", 10) == 0)
+	      else if (startswith (arg, "--demangle"))
  		{
  #ifndef HAVE_LD_DEMANGLE
  		  no_demangle = 0;
@@ -1479,7 +1479,7 @@  main (int argc, char **argv)
  		  ld2--;
  #endif
  		}
-	      else if (strncmp (arg, "--sysroot=", 10) == 0)
+	      else if (startswith (arg, "--sysroot="))
  		target_system_root = arg + 10;
  	      else if (strcmp (arg, "--version") == 0)
  		verbose = true;
@@ -2619,7 +2619,7 @@  scan_libraries (const char *prog_name)
  	continue;
  
        name = p;
-      if (strncmp (name, "not found", sizeof ("not found") - 1) == 0)
+      if (startswith (name, "not found"))
  	fatal_error (input_location, "dynamic dependency %s not found", buf);
  
        /* Find the end of the symbol name.  */
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 8e8f37d060e..e7479e8e8a1 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -7569,9 +7569,9 @@  omp_declare_variant_finalize_one (tree decl, tree attr)
  	  return true;
  	}
        if (fndecl_built_in_p (variant)
-	  && (strncmp (varname, "__builtin_", strlen ("__builtin_")) == 0
-	      || strncmp (varname, "__sync_", strlen ("__sync_")) == 0
-	      || strncmp (varname, "__atomic_", strlen ("__atomic_")) == 0))
+	  && (startswith (varname, "__builtin_")
+	      || startswith (varname, "__sync_")
+	      || startswith (varname, "__atomic_")))
  	{
  	  error_at (varid_loc, "variant %qD is a built-in", variant);
  	  return true;
diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c
index 0a9e5aa79a0..5ec7bf7af5a 100644
--- a/gcc/cp/mangle.c
+++ b/gcc/cp/mangle.c
@@ -4419,7 +4419,7 @@  static void
  write_guarded_var_name (const tree variable)
  {
    if (DECL_NAME (variable)
-      && strncmp (IDENTIFIER_POINTER (DECL_NAME (variable)), "_ZGR", 4) == 0)
+      && startswith (IDENTIFIER_POINTER (DECL_NAME (variable)), "_ZGR"))
      /* The name of a guard variable for a reference temporary should refer
         to the reference, not the temporary.  */
      write_string (IDENTIFIER_POINTER (DECL_NAME (variable)) + 4);
diff --git a/gcc/d/dmd/dmangle.c b/gcc/d/dmd/dmangle.c
index 303ae617874..5a73b3e3a65 100644
--- a/gcc/d/dmd/dmangle.c
+++ b/gcc/d/dmd/dmangle.c
@@ -666,7 +666,7 @@  public:
                  cd == ClassDeclaration::object ||
                  cd == Type::typeinfoclass ||
                  cd == Module::moduleinfo ||
-                strncmp(cd->ident->toChars(), "TypeInfo_", 9) == 0)
+                startswith (cd->ident->toChars(), "TypeInfo_"))
              {
                  // Don't mangle parent
                  ad->parent = NULL;
diff --git a/gcc/d/dmd/hdrgen.c b/gcc/d/dmd/hdrgen.c
index a11c9c353d9..4959b480fe5 100644
--- a/gcc/d/dmd/hdrgen.c
+++ b/gcc/d/dmd/hdrgen.c
@@ -3189,7 +3189,7 @@  public:
          }
          else if (p->type->ty == Tident &&
                   strlen(((TypeIdentifier *)p->type)->ident->toChars()) > 3 &&
-                 strncmp(((TypeIdentifier *)p->type)->ident->toChars(), "__T", 3) == 0)
+                 startswith (((TypeIdentifier *)p->type)->ident->toChars(), "__T"))
          {
              // print parameter name, instead of undetermined type parameter
              buf->writestring(p->ident->toChars());
diff --git a/gcc/d/dmd/identifier.c b/gcc/d/dmd/identifier.c
index 197d288e532..dd2c58fd657 100644
--- a/gcc/d/dmd/identifier.c
+++ b/gcc/d/dmd/identifier.c
@@ -73,11 +73,11 @@  const char *Identifier::toHChars2()
      {   p = toChars();
          if (*p == '_')
          {
-            if (strncmp(p, "_staticCtor", 11) == 0)
+            if (startswith(p, "_staticCtor"))
                  p = "static this";
-            else if (strncmp(p, "_staticDtor", 11) == 0)
+            else if (startswith(p, "_staticDtor"))
                  p = "static ~this";
-            else if (strncmp(p, "__invariant", 11) == 0)
+            else if (startswith(p, "__invariant"))
                  p = "invariant";
          }
      }
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index b3ca159c3a8..760714c47e5 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -5465,7 +5465,7 @@  is_cxx (const_tree decl)
      {
        const_tree context = get_ultimate_context (decl);
        if (context && TRANSLATION_UNIT_LANGUAGE (context))
-	return strncmp (TRANSLATION_UNIT_LANGUAGE (context), "GNU C++", 7) == 0;
+	return startswith (TRANSLATION_UNIT_LANGUAGE (context), "GNU C++");
      }
    return is_cxx ();
  }
@@ -24572,8 +24572,8 @@  gen_compile_unit_die (const char *filename)
  	    common_lang = TRANSLATION_UNIT_LANGUAGE (t);
  	  else if (strcmp (common_lang, TRANSLATION_UNIT_LANGUAGE (t)) == 0)
  	    ;
-	  else if (strncmp (common_lang, "GNU C", 5) == 0
-		    && strncmp (TRANSLATION_UNIT_LANGUAGE (t), "GNU C", 5) == 0)
+	  else if (startswith (common_lang, "GNU C")
+		    && startswith (TRANSLATION_UNIT_LANGUAGE (t), "GNU C"))
  	    /* Mixing C and C++ is ok, use C++ in that case.  */
  	    common_lang = highest_c_language (common_lang,
  					      TRANSLATION_UNIT_LANGUAGE (t));
@@ -24590,7 +24590,7 @@  gen_compile_unit_die (const char *filename)
      }
  
    language = DW_LANG_C;
-  if (strncmp (language_string, "GNU C", 5) == 0
+  if (startswith (language_string, "GNU C")
        && ISDIGIT (language_string[5]))
      {
        language = DW_LANG_C89;
@@ -24606,7 +24606,7 @@  gen_compile_unit_die (const char *filename)
  	      language = DW_LANG_C11;
  	}
      }
-  else if (strncmp (language_string, "GNU C++", 7) == 0)
+  else if (startswith (language_string, "GNU C++"))
      {
        language = DW_LANG_C_plus_plus;
        if (dwarf_version >= 5 /* || !dwarf_strict */)
@@ -24628,7 +24628,7 @@  gen_compile_unit_die (const char *filename)
      {
        if (strcmp (language_string, "GNU Ada") == 0)
  	language = DW_LANG_Ada95;
-      else if (strncmp (language_string, "GNU Fortran", 11) == 0)
+      else if (startswith (language_string, "GNU Fortran"))
  	{
  	  language = DW_LANG_Fortran95;
  	  if (dwarf_version >= 5 /* || !dwarf_strict */)
@@ -24652,7 +24652,7 @@  gen_compile_unit_die (const char *filename)
  	}
      }
    /* Use a degraded Fortran setting in strict DWARF2 so is_fortran works.  */
-  else if (strncmp (language_string, "GNU Fortran", 11) == 0)
+  else if (startswith (language_string, "GNU Fortran"))
      language = DW_LANG_Fortran90;
    /* Likewise for Ada.  */
    else if (strcmp (language_string, "GNU Ada") == 0)
diff --git a/gcc/fortran/decl.c b/gcc/fortran/decl.c
index 947e4f868a1..413c7a75e0c 100644
--- a/gcc/fortran/decl.c
+++ b/gcc/fortran/decl.c
@@ -2721,7 +2721,7 @@  variable_decl (int elem)
      }
  
    /* %FILL components may not have initializers.  */
-  if (gfc_str_startswith (name, "%FILL") && gfc_match_eos () != MATCH_YES)
+  if (startswith (name, "%FILL") && gfc_match_eos () != MATCH_YES)
      {
        gfc_error ("%qs entity cannot have an initializer at %C", "%FILL");
        m = MATCH_ERROR;
@@ -8221,7 +8221,7 @@  gfc_match_end (gfc_statement *st)
      {
      case COMP_ASSOCIATE:
      case COMP_BLOCK:
-      if (gfc_str_startswith (block_name, "block@"))
+      if (startswith (block_name, "block@"))
  	block_name = NULL;
        break;
  
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 7935aca23db..b8e67bcc03e 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -3514,10 +3514,6 @@  bool gfc_is_compile_time_shape (gfc_array_spec *);
  
  bool gfc_ref_dimen_size (gfc_array_ref *, int dimen, mpz_t *, mpz_t *);
  
-
-#define gfc_str_startswith(str, pref) \
-	(strncmp ((str), (pref), strlen (pref)) == 0)
-
  /* interface.c -- FIXME: some of these should be in symbol.c */
  void gfc_free_interface (gfc_interface *);
  bool gfc_compare_derived_types (gfc_symbol *, gfc_symbol *);
diff --git a/gcc/fortran/module.c b/gcc/fortran/module.c
index 4db0a3ac76d..a55855548b4 100644
--- a/gcc/fortran/module.c
+++ b/gcc/fortran/module.c
@@ -5029,7 +5029,7 @@  load_omp_udrs (void)
        mio_pool_string (&name);
        gfc_clear_ts (&ts);
        mio_typespec (&ts);
-      if (gfc_str_startswith (name, "operator "))
+      if (startswith (name, "operator "))
  	{
  	  const char *p = name + sizeof ("operator ") - 1;
  	  if (strcmp (p, "+") == 0)
@@ -5477,8 +5477,8 @@  read_module (void)
  
  	  /* Exception: Always import vtabs & vtypes.  */
  	  if (p == NULL && name[0] == '_'
-	      && (gfc_str_startswith (name, "__vtab_")
-		  || gfc_str_startswith (name, "__vtype_")))
+	      && (startswith (name, "__vtab_")
+		  || startswith (name, "__vtype_")))
  	    p = name;
  
  	  /* Skip symtree nodes not in an ONLY clause, unless there
@@ -5563,8 +5563,8 @@  read_module (void)
  		sym->attr.use_rename = 1;
  
  	      if (name[0] != '_'
-		  || (!gfc_str_startswith (name, "__vtab_")
-		      && !gfc_str_startswith (name, "__vtype_")))
+		  || (!startswith (name, "__vtab_")
+		      && !startswith (name, "__vtype_")))
  		sym->attr.use_only = only_flag;
  
  	      /* Store the symtree pointing to this symbol.  */
diff --git a/gcc/fortran/options.c b/gcc/fortran/options.c
index 3a0b98bf1ec..1723f689a57 100644
--- a/gcc/fortran/options.c
+++ b/gcc/fortran/options.c
@@ -615,7 +615,7 @@  gfc_handle_runtime_check_option (const char *arg)
  	      result = 1;
  	      break;
  	    }
-	  else if (optname[n] && pos > 3 && gfc_str_startswith (arg, "no-")
+	  else if (optname[n] && pos > 3 && startswith (arg, "no-")
  		   && strncmp (optname[n], arg+3, pos-3) == 0)
  	    {
  	      gfc_option.rtcheck &= ~optmask[n];
diff --git a/gcc/fortran/primary.c b/gcc/fortran/primary.c
index a6df885c80c..9fe8d1ee20c 100644
--- a/gcc/fortran/primary.c
+++ b/gcc/fortran/primary.c
@@ -1786,21 +1786,21 @@  match_arg_list_function (gfc_actual_arglist *result)
        switch (name[0])
  	{
  	case 'l':
-	  if (gfc_str_startswith (name, "loc"))
+	  if (startswith (name, "loc"))
  	    {
  	      result->name = "%LOC";
  	      break;
  	    }
  	  /* FALLTHRU */
  	case 'r':
-	  if (gfc_str_startswith (name, "ref"))
+	  if (startswith (name, "ref"))
  	    {
  	      result->name = "%REF";
  	      break;
  	    }
  	  /* FALLTHRU */
  	case 'v':
-	  if (gfc_str_startswith (name, "val"))
+	  if (startswith (name, "val"))
  	    {
  	      result->name = "%VAL";
  	      break;
diff --git a/gcc/fortran/trans-decl.c b/gcc/fortran/trans-decl.c
index 6a4ed9bbfdd..fc8ccca9d63 100644
--- a/gcc/fortran/trans-decl.c
+++ b/gcc/fortran/trans-decl.c
@@ -1940,7 +1940,7 @@  gfc_get_symbol_decl (gfc_symbol * sym)
       Marking this as artificial means that OpenMP will treat this as
       predetermined shared.  */
  
-  bool def_init = gfc_str_startswith (sym->name, "__def_init");
+  bool def_init = startswith (sym->name, "__def_init");
  
    if (sym->attr.vtab || def_init)
      {
diff --git a/gcc/fortran/trans-expr.c b/gcc/fortran/trans-expr.c
index bffe0808dff..82698b411dd 100644
--- a/gcc/fortran/trans-expr.c
+++ b/gcc/fortran/trans-expr.c
@@ -6791,7 +6791,7 @@  gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
        /* When calling __copy for character expressions to unlimited
  	 polymorphic entities, the dst argument needs a string length.  */
        if (sym->name[0] == '_' && e && e->ts.type == BT_CHARACTER
-	  && gfc_str_startswith (sym->name, "__vtab_CHARACTER")
+	  && startswith (sym->name, "__vtab_CHARACTER")
  	  && arg->next && arg->next->expr
  	  && (arg->next->expr->ts.type == BT_DERIVED
  	      || arg->next->expr->ts.type == BT_CLASS)
diff --git a/gcc/fortran/trans-intrinsic.c b/gcc/fortran/trans-intrinsic.c
index 5e53d1162fa..1369d2f76b9 100644
--- a/gcc/fortran/trans-intrinsic.c
+++ b/gcc/fortran/trans-intrinsic.c
@@ -10072,27 +10072,27 @@  gfc_conv_ieee_arithmetic_function (gfc_se * se, gfc_expr * expr)
  {
    const char *name = expr->value.function.name;
  
-  if (gfc_str_startswith (name, "_gfortran_ieee_is_nan"))
+  if (startswith (name, "_gfortran_ieee_is_nan"))
      conv_intrinsic_ieee_builtin (se, expr, BUILT_IN_ISNAN, 1);
-  else if (gfc_str_startswith (name, "_gfortran_ieee_is_finite"))
+  else if (startswith (name, "_gfortran_ieee_is_finite"))
      conv_intrinsic_ieee_builtin (se, expr, BUILT_IN_ISFINITE, 1);
-  else if (gfc_str_startswith (name, "_gfortran_ieee_unordered"))
+  else if (startswith (name, "_gfortran_ieee_unordered"))
      conv_intrinsic_ieee_builtin (se, expr, BUILT_IN_ISUNORDERED, 2);
-  else if (gfc_str_startswith (name, "_gfortran_ieee_is_normal"))
+  else if (startswith (name, "_gfortran_ieee_is_normal"))
      conv_intrinsic_ieee_is_normal (se, expr);
-  else if (gfc_str_startswith (name, "_gfortran_ieee_is_negative"))
+  else if (startswith (name, "_gfortran_ieee_is_negative"))
      conv_intrinsic_ieee_is_negative (se, expr);
-  else if (gfc_str_startswith (name, "_gfortran_ieee_copy_sign"))
+  else if (startswith (name, "_gfortran_ieee_copy_sign"))
      conv_intrinsic_ieee_copy_sign (se, expr);
-  else if (gfc_str_startswith (name, "_gfortran_ieee_scalb"))
+  else if (startswith (name, "_gfortran_ieee_scalb"))
      conv_intrinsic_ieee_scalb (se, expr);
-  else if (gfc_str_startswith (name, "_gfortran_ieee_next_after"))
+  else if (startswith (name, "_gfortran_ieee_next_after"))
      conv_intrinsic_ieee_next_after (se, expr);
-  else if (gfc_str_startswith (name, "_gfortran_ieee_rem"))
+  else if (startswith (name, "_gfortran_ieee_rem"))
      conv_intrinsic_ieee_rem (se, expr);
-  else if (gfc_str_startswith (name, "_gfortran_ieee_logb"))
+  else if (startswith (name, "_gfortran_ieee_logb"))
      conv_intrinsic_ieee_logb_rint (se, expr, BUILT_IN_LOGB);
-  else if (gfc_str_startswith (name, "_gfortran_ieee_rint"))
+  else if (startswith (name, "_gfortran_ieee_rint"))
      conv_intrinsic_ieee_logb_rint (se, expr, BUILT_IN_RINT);
    else
      /* It is not among the functions we translate directly.  We return
diff --git a/gcc/gcc.c b/gcc/gcc.c
index 7837553958b..3a870c6144a 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -1900,7 +1900,7 @@  init_spec (void)
         when given the proper command line arguments.  */
      while (*p)
        {
-	if (in_sep && *p == '-' && strncmp (p, "-lgcc", 5) == 0)
+	if (in_sep && *p == '-' && startswith (p, "-lgcc"))
  	  {
  	    init_gcc_specs (&obstack,
  			    "-lgcc_s"
@@ -1923,7 +1923,7 @@  init_spec (void)
  	    p += 5;
  	    in_sep = 0;
  	  }
-	else if (in_sep && *p == 'l' && strncmp (p, "libgcc.a%s", 10) == 0)
+	else if (in_sep && *p == 'l' && startswith (p, "libgcc.a%s"))
  	  {
  	    /* Ug.  We don't know shared library extensions.  Hope that
  	       systems that use this form don't do shared libraries.  */
diff --git a/gcc/gencfn-macros.c b/gcc/gencfn-macros.c
index b620fe6aebc..fd9ae14fc04 100644
--- a/gcc/gencfn-macros.c
+++ b/gcc/gencfn-macros.c
@@ -208,7 +208,7 @@  main (int argc, char **argv)
    for (unsigned int i = 0; builtin_names[i]; ++i)
      {
        const char *name = builtin_names[i];
-      if (strncmp (name, "BUILT_IN_", 9) == 0)
+      if (startswith (name, "BUILT_IN_"))
  	{
  	  const char *root = name + 9;
  	  for (unsigned int j = 0; suffix_lists[j]; ++j)
diff --git a/gcc/gengtype.c b/gcc/gengtype.c
index 5f50242e857..64ea6348a68 100644
--- a/gcc/gengtype.c
+++ b/gcc/gengtype.c
@@ -671,7 +671,7 @@  type_for_name (const char *s)
           extern GTY(()) gcc::some_type *some_ptr;
       where the autogenerated functions will refer to simply "some_type",
       where they can be resolved into their namespace.  */
-  if (strncmp (s, "gcc::", 5) == 0)
+  if (startswith (s, "gcc::"))
      s += 5;
  
    for (p = typedefs; p != NULL; p = p->next)
@@ -1102,7 +1102,7 @@  gen_rtx_next (void)
        int k;
  
        rtx_next_new[i] = -1;
-      if (strncmp (rtx_format[i], "uu", 2) == 0)
+      if (startswith (rtx_format[i], "uu"))
  	rtx_next_new[i] = 1;
        else if (i == COND_EXEC || i == SET || i == EXPR_LIST || i == INSN_LIST)
  	rtx_next_new[i] = 1;
@@ -1828,7 +1828,7 @@  get_file_langdir (const input_file *inpf)
      return NULL;
  
    lang_index = get_prefix_langdir_index (srcdir_relative_path);
-  if (lang_index < 0 && strncmp (srcdir_relative_path, "c-family", 8) == 0)
+  if (lang_index < 0 && startswith (srcdir_relative_path, "c-family"))
      r = "c-family";
    else if (lang_index >= 0)
      r = lang_dir_names[lang_index];
diff --git a/gcc/genmatch.c b/gcc/genmatch.c
index 8311f5d768a..332afc842b7 100644
--- a/gcc/genmatch.c
+++ b/gcc/genmatch.c
@@ -605,10 +605,10 @@  get_operator (const char *id, bool allow_null = false)
        for (unsigned int i = 0; id2[i]; ++i)
  	id2[i] = TOUPPER (id2[i]);
      }
-  else if (all_upper && strncmp (id, "IFN_", 4) == 0)
+  else if (all_upper && startswith (id, "IFN_"))
      /* Try CFN_ instead of IFN_.  */
      id2 = ACONCAT (("CFN_", id + 4, NULL));
-  else if (all_upper && strncmp (id, "BUILT_IN_", 9) == 0)
+  else if (all_upper && startswith (id, "BUILT_IN_"))
      /* Try prepending CFN_.  */
      id2 = ACONCAT (("CFN_", id, NULL));
    else
@@ -2391,7 +2391,7 @@  get_operand_type (id_base *op, unsigned pos,
    else if (*op == COND_EXPR
  	   && pos == 0)
      return "boolean_type_node";
-  else if (strncmp (op->id, "CFN_COND_", 9) == 0)
+  else if (startswith (op->id, "CFN_COND_"))
      {
        /* IFN_COND_* operands 1 and later by default have the same type
  	 as the result.  The type of operand 0 needs to be specified
@@ -2464,7 +2464,7 @@  expr::gen_transform (FILE *f, int indent, const char *dest, bool gimple,
      }
    else if (*opr == COND_EXPR
  	   || *opr == VEC_COND_EXPR
-	   || strncmp (opr->id, "CFN_COND_", 9) == 0)
+	   || startswith (opr->id, "CFN_COND_"))
      {
        /* Conditions are of the same type as their first alternative.  */
        snprintf (optype, sizeof (optype), "TREE_TYPE (_o%d[1])", depth);
diff --git a/gcc/genoutput.c b/gcc/genoutput.c
index b88e13b90b6..bd5a007c6da 100644
--- a/gcc/genoutput.c
+++ b/gcc/genoutput.c
@@ -841,7 +841,7 @@  validate_optab_operands (class data *d)
      return;
  
    /* Miscellaneous tests.  */
-  if (strncmp (d->name, "cstore", 6) == 0
+  if (startswith (d->name, "cstore")
        && d->name[strlen (d->name) - 1] == '4'
        && d->operand[0].mode == VOIDmode)
      {
diff --git a/gcc/go/gofrontend/runtime.cc b/gcc/go/gofrontend/runtime.cc
index 3cc5ded3617..514c045a68f 100644
--- a/gcc/go/gofrontend/runtime.cc
+++ b/gcc/go/gofrontend/runtime.cc
@@ -463,7 +463,7 @@  Runtime::name_to_code(const std::string& name)
        // The names in the table have "runtime." prefix. We may be
        // called with a name without the prefix. Try matching
        // without the prefix as well.
-      if (strncmp(runtime_function_name, "runtime.", 8) == 0
+      if (startswith(runtime_function_name, "runtime.")
            && strcmp(runtime_function_name + 8, name.c_str()) == 0)
          code = static_cast<Runtime::Function>(i);
      }
diff --git a/gcc/incpath.c b/gcc/incpath.c
index 446d280321d..52dbb806b1b 100644
--- a/gcc/incpath.c
+++ b/gcc/incpath.c
@@ -330,7 +330,7 @@  add_sysroot_to_chain (const char *sysroot, int chain)
  	{
  	  if (p->name[0] == '=')
  	    p->name = concat (sysroot, p->name + 1, NULL);
-	  if (strncmp (p->name, "$SYSROOT", strlen ("$SYSROOT")) == 0)
+	  if (startswith (p->name, "$SYSROOT"))
  	    p->name = concat (sysroot, p->name + strlen ("$SYSROOT"), NULL);
  	}
      }
diff --git a/gcc/langhooks.c b/gcc/langhooks.c
index 2354386f7b4..12c99888e6c 100644
--- a/gcc/langhooks.c
+++ b/gcc/langhooks.c
@@ -901,7 +901,7 @@  lhd_finalize_early_debug (void)
  bool
  lang_GNU_C (void)
  {
-  return (strncmp (lang_hooks.name, "GNU C", 5) == 0
+  return (startswith (lang_hooks.name, "GNU C")
  	  && (lang_hooks.name[5] == '\0' || ISDIGIT (lang_hooks.name[5])));
  }
  
@@ -910,7 +910,7 @@  lang_GNU_C (void)
  bool
  lang_GNU_CXX (void)
  {
-  return strncmp (lang_hooks.name, "GNU C++", 7) == 0;
+  return startswith (lang_hooks.name, "GNU C++");
  }
  
  /* Returns true if the current lang_hooks represents the GNU Fortran frontend.  */
@@ -918,7 +918,7 @@  lang_GNU_CXX (void)
  bool
  lang_GNU_Fortran (void)
  {
-  return strncmp (lang_hooks.name, "GNU Fortran", 11) == 0;
+  return startswith (lang_hooks.name, "GNU Fortran");
  }
  
  /* Returns true if the current lang_hooks represents the GNU Objective-C
@@ -927,5 +927,5 @@  lang_GNU_Fortran (void)
  bool
  lang_GNU_OBJC (void)
  {
-  return strncmp (lang_hooks.name, "GNU Objective-C", 15) == 0;
+  return startswith (lang_hooks.name, "GNU Objective-C");
  }
diff --git a/gcc/objc/objc-next-runtime-abi-02.c b/gcc/objc/objc-next-runtime-abi-02.c
index af68c1c84a3..3cfcd0b1a57 100644
--- a/gcc/objc/objc-next-runtime-abi-02.c
+++ b/gcc/objc/objc-next-runtime-abi-02.c
@@ -2209,7 +2209,7 @@  has_load_impl (tree clsmeth)
      {
        tree id = METHOD_SEL_NAME (clsmeth);
        if (IDENTIFIER_LENGTH (id) == 4
-	  && strncmp (IDENTIFIER_POINTER (id), "load", 4) == 0)
+	  && startswith (IDENTIFIER_POINTER (id), "load"))
          return true;
        clsmeth = DECL_CHAIN (clsmeth);
      }
diff --git a/gcc/opts-common.c b/gcc/opts-common.c
index 5e10edeb4cf..9d1914ff2ff 100644
--- a/gcc/opts-common.c
+++ b/gcc/opts-common.c
@@ -1805,7 +1805,7 @@  parse_options_from_collect_gcc_options (const char *collect_gcc_options,
  	      if (argv_storage[j] == '\0')
  		fatal_error (input_location,
  			     "malformed %<COLLECT_GCC_OPTIONS%>");
-	      else if (strncmp (&argv_storage[j], "'\\''", 4) == 0)
+	      else if (startswith (&argv_storage[j], "'\\''"))
  		{
  		  argv_storage[k++] = '\'';
  		  j += 4;
diff --git a/gcc/opts.c b/gcc/opts.c
index 24bb64198c8..8422e56faa7 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -47,7 +47,7 @@  const char *const debug_type_names[] =
     and set the flag variables. */
  
  #define MATCH( prefix, string ) \
-  ((strncmp (prefix, string, sizeof prefix - 1) == 0) \
+  ((startswith (prefix, string)) \
     ? ((string += sizeof prefix - 1), 1) : 0)
  
  void
diff --git a/gcc/read-rtl-function.c b/gcc/read-rtl-function.c
index 2a1fb4bac9c..eaf380dd466 100644
--- a/gcc/read-rtl-function.c
+++ b/gcc/read-rtl-function.c
@@ -1082,7 +1082,7 @@  function_reader::read_rtx_operand_r (rtx x)
  	 "orig:%i", ORIGINAL_REGNO (rtx).
  	 Consume it, we don't set ORIGINAL_REGNO, since we can
  	 get that from the 2nd copy later.  */
-      if (strncmp (desc, "orig:", 5) == 0)
+      if (startswith (desc, "orig:"))
  	{
  	  expect_original_regno = true;
  	  desc_start += 5;
diff --git a/gcc/selftest.c b/gcc/selftest.c
index 6e26ea553f2..8f1cde0cc19 100644
--- a/gcc/selftest.c
+++ b/gcc/selftest.c
@@ -150,8 +150,7 @@  assert_str_startswith (const location &loc,
  		    "ASSERT_STR_STARTSWITH (%s, %s) str=\"%s\" prefix=NULL",
  		    desc_str, desc_prefix, val_str);
  
-  const char *test = strstr (val_str, val_prefix);
-  if (test == val_str)
+  if (startswith (val_str, val_prefix))
      pass (loc, "ASSERT_STR_STARTSWITH");
    else
      fail_formatted
diff --git a/gcc/system.h b/gcc/system.h
index a3f5948aaee..3e384616d3a 100644
--- a/gcc/system.h
+++ b/gcc/system.h
@@ -1291,4 +1291,12 @@  void gcc_stablesort (void *, size_t, size_t,
  #define NULL nullptr
  #endif
  
+/* Return 1 if STR string starts with PREFIX.  */
+
+static inline int
+startswith (const char *str, const char *prefix)
+{
+  return strncmp (str, prefix, strlen (prefix)) == 0;
+}
+
  #endif /* ! GCC_SYSTEM_H */
diff --git a/gcc/timevar.c b/gcc/timevar.c
index 5d4e1597f23..8fc122ba9fe 100644
--- a/gcc/timevar.c
+++ b/gcc/timevar.c
@@ -600,7 +600,7 @@  timer::validate_phases (FILE *fp) const
        if (!tv->used)
  	continue;
  
-      if (strncmp (tv->name, phase_prefix, sizeof phase_prefix - 1) == 0)
+      if (startswith (tv->name, phase_prefix))
  	{
  	  phase_user += tv->elapsed.user;
  	  phase_sys += tv->elapsed.sys;
diff --git a/gcc/tree.c b/gcc/tree.c
index 7c44c226a33..0763e3337f6 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -9725,7 +9725,7 @@  get_file_function_name (const char *type)
       We also assign sub_I and sub_D sufixes to constructors called from
       the global static constructors.  These are always local.  */
    else if (((type[0] == 'I' || type[0] == 'D') && targetm.have_ctors_dtors)
-	   || (strncmp (type, "sub_", 4) == 0
+	   || (startswith (type, "sub_")
  	       && (type[4] == 'I' || type[4] == 'D')))
      {
        const char *file = main_input_filename;
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index d752b897d64..767114ef1f6 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -1783,7 +1783,7 @@  ubsan_use_new_style_p (location_t loc)
      return false;
  
    expanded_location xloc = expand_location (loc);
-  if (xloc.file == NULL || strncmp (xloc.file, "\1", 2) == 0
+  if (xloc.file == NULL || startswith (xloc.file, "\1")
        || xloc.file[0] == '\0' || xloc.file[0] == '\xff'
        || xloc.file[1] == '\xff')
      return false;
diff --git a/gcc/varasm.c b/gcc/varasm.c
index 811212244a5..4656a37dc09 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -792,7 +792,7 @@  default_function_rodata_section (tree decl, bool relocatable)
        /* For .gnu.linkonce.t.foo we want to use .gnu.linkonce.r.foo or
  	 .gnu.linkonce.d.rel.ro.local.foo if the jump table is relocatable.  */
        else if (DECL_COMDAT_GROUP (decl)
-	       && strncmp (name, ".gnu.linkonce.t.", 16) == 0)
+	       && startswith (name, ".gnu.linkonce.t."))
  	{
  	  size_t len;
  	  char *rname;
@@ -817,7 +817,7 @@  default_function_rodata_section (tree decl, bool relocatable)
  	}
        /* For .text.foo we want to use .rodata.foo.  */
        else if (flag_function_sections && flag_data_sections
-	       && strncmp (name, ".text.", 6) == 0)
+	       && startswith (name, ".text."))
  	{
  	  size_t len = strlen (name) + 1;
  	  char *rname = (char *) alloca (len + strlen (sname) - 5);
@@ -2485,7 +2485,7 @@  incorporeal_function_p (tree decl)
        name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
        /* Atomic or sync builtins which have survived this far will be
  	 resolved externally and therefore are not incorporeal.  */
-      if (strncmp (name, "__builtin_", 10) == 0)
+      if (startswith (name, "__builtin_"))
  	return true;
      }
    return false;
@@ -6713,22 +6713,22 @@  default_section_type_flags (tree decl, const char *name, int reloc)
      flags |= SECTION_TLS | SECTION_WRITE;
  
    if (strcmp (name, ".bss") == 0
-      || strncmp (name, ".bss.", 5) == 0
-      || strncmp (name, ".gnu.linkonce.b.", 16) == 0
+      || startswith (name, ".bss.")
+      || startswith (name, ".gnu.linkonce.b.")
        || strcmp (name, ".persistent.bss") == 0
        || strcmp (name, ".sbss") == 0
-      || strncmp (name, ".sbss.", 6) == 0
-      || strncmp (name, ".gnu.linkonce.sb.", 17) == 0)
+      || startswith (name, ".sbss.")
+      || startswith (name, ".gnu.linkonce.sb."))
      flags |= SECTION_BSS;
  
    if (strcmp (name, ".tdata") == 0
-      || strncmp (name, ".tdata.", 7) == 0
-      || strncmp (name, ".gnu.linkonce.td.", 17) == 0)
+      || startswith (name, ".tdata.")
+      || startswith (name, ".gnu.linkonce.td."))
      flags |= SECTION_TLS;
  
    if (strcmp (name, ".tbss") == 0
-      || strncmp (name, ".tbss.", 6) == 0
-      || strncmp (name, ".gnu.linkonce.tb.", 17) == 0)
+      || startswith (name, ".tbss.")
+      || startswith (name, ".gnu.linkonce.tb."))
      flags |= SECTION_TLS | SECTION_BSS;
  
    if (strcmp (name, ".noinit") == 0)