Fixes for GCC 10 compatibility

- pw_kvs: Remove use of std::enable_if that wasn't working property.
- pw_result, pw_rpc: Disable spurious GCC 10 warnings.
- pw_string: Size report code that is never executed was using the same
  pointer for snprintf input and output, which is not legal. This caused
  -Wrestrict warnings. Update the size report code to avoid this.
- Add the -pthread flag in GCC host builds to support building
  pw_thread.
- pw_tokenizer: For host builds, insert the tokenizer sections after
  .debug_info instead of .strtab, since inserting after .strtab does not
  work for GCC.

Change-Id: Ib141a102ed50eb639c8d0bcb236572b3dc142ec1
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/40620
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
Reviewed-by: Anthony DiGirolamo <tonymd@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
diff --git a/pw_kvs/public/pw_kvs/checksum.h b/pw_kvs/public/pw_kvs/checksum.h
index 6a42e00..7624e39 100644
--- a/pw_kvs/public/pw_kvs/checksum.h
+++ b/pw_kvs/public/pw_kvs/checksum.h
@@ -95,7 +95,7 @@
  protected:
   constexpr AlignedChecksum(std::span<const std::byte> state)
       : ChecksumAlgorithm(state),
-        output_(this),
+        output_(*this),
         writer_(kAlignmentBytes, output_) {}
 
   ~AlignedChecksum() = default;
@@ -112,10 +112,20 @@
 
   virtual void FinalizeAligned() = 0;
 
-  OutputToMethod<void (AlignedChecksum<kAlignmentBytes, kBufferSize>::*)(
-                     std::span<const std::byte>),
-                 &AlignedChecksum::UpdateAligned>
-      output_;
+  class CallUpdateAligned final : public Output {
+   public:
+    constexpr CallUpdateAligned(AlignedChecksum& object) : object_(object) {}
+
+   private:
+    StatusWithSize DoWrite(std::span<const std::byte> data) override {
+      object_.UpdateAligned(data);
+      return StatusWithSize(data.size());
+    }
+
+    AlignedChecksum& object_;
+  };
+
+  CallUpdateAligned output_;
   AlignedWriterBuffer<kBufferSize> writer_;
 };
 
diff --git a/pw_kvs/public/pw_kvs/io.h b/pw_kvs/public/pw_kvs/io.h
index 193427c..58fe24d 100644
--- a/pw_kvs/public/pw_kvs/io.h
+++ b/pw_kvs/public/pw_kvs/io.h
@@ -71,43 +71,6 @@
   virtual StatusWithSize DoRead(std::span<std::byte> data) = 0;
 };
 
-// Output adapter that calls a method on a class with a std::span of bytes. If
-// the method returns void instead of the expected Status, Write always returns
-// OkStatus().
-template <typename T, T kMethod>
-class OutputToMethod final : public Output {
-  using Class = typename internal::FunctionTraits<decltype(kMethod)>::Class;
-
- public:
-  constexpr OutputToMethod(Class* object) : object_(*object) {}
-
- private:
-  using Return = typename internal::FunctionTraits<decltype(kMethod)>::Return;
-  template <T kMethodImpl = kMethod>
-  typename std::enable_if<std::is_void<typename internal::FunctionTraits<
-                              decltype(kMethodImpl)>::Return>::value,
-                          StatusWithSize>::type
-  DoWriteImpl(std::span<const std::byte> data) {
-    (object_.*kMethod)(data);
-    return StatusWithSize(data.size());
-  }
-
-  template <T kMethodImpl = kMethod>
-  typename std::enable_if<!std::is_void<typename internal::FunctionTraits<
-                              decltype(kMethodImpl)>::Return>::value,
-                          StatusWithSize>::type
-  DoWriteImpl(std::span<const std::byte> data) {
-    return (object_.*kMethod)(data);
-  }
-
-  StatusWithSize DoWrite(std::span<const std::byte> data) override {
-    return DoWriteImpl(data);
-  }
-
- private:
-  Class& object_;
-};
-
 // Output adapter that calls a free function.
 class OutputToFunction final : public Output {
  public:
diff --git a/pw_result/public/pw_result/result.h b/pw_result/public/pw_result/result.h
index a55c2b4..ba78e8a 100644
--- a/pw_result/public/pw_result/result.h
+++ b/pw_result/public/pw_result/result.h
@@ -68,7 +68,11 @@
   template <typename U>
   constexpr T value_or(U&& default_value) const& {
     if (ok()) {
+      PW_MODIFY_DIAGNOSTICS_PUSH();
+      // GCC 10 emits -Wmaybe-uninitialized warnings about value_.
+      PW_MODIFY_DIAGNOSTIC_GCC(ignored, "-Wmaybe-uninitialized");
       return value_;
+      PW_MODIFY_DIAGNOSTICS_POP();
     }
     return std::forward<U>(default_value);
   }
diff --git a/pw_rpc/public/pw_rpc/service.h b/pw_rpc/public/pw_rpc/service.h
index 3884d02..4e1a1a0 100644
--- a/pw_rpc/public/pw_rpc/service.h
+++ b/pw_rpc/public/pw_rpc/service.h
@@ -18,6 +18,7 @@
 #include <span>
 
 #include "pw_containers/intrusive_list.h"
+#include "pw_preprocessor/compiler.h"
 #include "pw_rpc/internal/method.h"
 #include "pw_rpc/internal/method_union.h"
 
@@ -40,7 +41,11 @@
         methods_(methods.data()),
         method_size_(sizeof(T)),
         method_count_(static_cast<uint16_t>(kMethodCount)) {
+    PW_MODIFY_DIAGNOSTICS_PUSH();
+    // GCC 10 emits spurious -Wtype-limits warnings for the static_assert.
+    PW_MODIFY_DIAGNOSTIC_GCC(ignored, "-Wtype-limits");
     static_assert(kMethodCount <= std::numeric_limits<uint16_t>::max());
+    PW_MODIFY_DIAGNOSTICS_POP();
   }
 
   // For use by tests with only one method.
diff --git a/pw_string/size_report/format_many_without_error_handling.cc b/pw_string/size_report/format_many_without_error_handling.cc
index 357fc2a..7a5ab2d 100644
--- a/pw_string/size_report/format_many_without_error_handling.cc
+++ b/pw_string/size_report/format_many_without_error_handling.cc
@@ -38,18 +38,22 @@
 
 namespace pw::string {
 
-char* volatile get_buffer;
+char buffer_1[128];
+char buffer_2[128];
+
+char* volatile get_buffer_1 = buffer_1;
+char* volatile get_buffer_2 = buffer_2;
 volatile unsigned get_size;
 
 void OutputStringsToBuffer() {
 #if USE_FORMAT
-  auto buffer = std::span(get_buffer, get_size);
+  auto buffer = std::span(get_buffer_1, get_size);
 #else
-  char* buffer = get_buffer;
+  char* buffer = get_buffer_1;
   unsigned buffer_size = get_size;
 #endif  // USE_FORMAT
 
-  const char* string = get_buffer;
+  const char* string = get_buffer_2;
   unsigned value = get_size;
 
   FORMAT_CASE("The quick brown");
diff --git a/pw_string/size_report/format_multiple.cc b/pw_string/size_report/format_multiple.cc
index b27df5a..7f516cb 100644
--- a/pw_string/size_report/format_multiple.cc
+++ b/pw_string/size_report/format_multiple.cc
@@ -82,12 +82,16 @@
 
 namespace pw::string {
 
-char* volatile get_buffer;
+char buffer_1[128];
+char buffer_2[128];
+
+char* volatile get_buffer_1 = buffer_1;
+char* volatile get_buffer_2 = buffer_2;
 volatile unsigned get_size;
 
 unsigned OutputStringsToBuffer() {
-  char* buffer = get_buffer;
-  const char* string = get_buffer;
+  char* buffer = get_buffer_1;
+  const char* string = get_buffer_2;
 
   unsigned buffer_size = get_size;
   unsigned string_size = 0;
diff --git a/pw_string/size_report/format_single.cc b/pw_string/size_report/format_single.cc
index 94afe76..6afee11 100644
--- a/pw_string/size_report/format_single.cc
+++ b/pw_string/size_report/format_single.cc
@@ -31,18 +31,22 @@
 
 namespace pw::string {
 
-char* volatile get_buffer;
+char buffer_1[128];
+char buffer_2[128];
+
+char* volatile get_buffer_1 = buffer_1;
+char* volatile get_buffer_2 = buffer_2;
 volatile unsigned get_size;
 
 unsigned OutputStringsToBuffer() {
-  char* buffer = get_buffer;
+  char* buffer = get_buffer_1;
   unsigned buffer_size = get_size;
 
 #if USE_FORMAT
   // The code for using pw::string::Format is much simpler and safer.
   return Format(std::span(buffer, buffer_size),
                 "hello %s %d",
-                get_buffer,
+                get_buffer_2,
                 get_size)
       .size();
 #else  // std::snprintf
@@ -51,7 +55,7 @@
   }
 
   int result =
-      std::snprintf(buffer, buffer_size, "hello %s %d", get_buffer, get_size);
+      std::snprintf(buffer, buffer_size, "hello %s %d", get_buffer_2, get_size);
   if (result < 0) {
     buffer[0] = '\0';
     return 0;
diff --git a/pw_tokenizer/add_tokenizer_sections_to_default_script.ld b/pw_tokenizer/add_tokenizer_sections_to_default_script.ld
index 41cc0c1..56890b0 100644
--- a/pw_tokenizer/add_tokenizer_sections_to_default_script.ld
+++ b/pw_tokenizer/add_tokenizer_sections_to_default_script.ld
@@ -20,6 +20,6 @@
  * The INSERT directive instructs the linker to append the directives in this
  * script to the default linker script, rather than replace the default with
  * this script. It doesn't matter where the tokenizer sections are inserted, so
- * insert them after the standard .strtab section.
+ * insert them after the .debug_info section, which both clang and GCC use.
  */
-INSERT AFTER .strtab
+INSERT AFTER .debug_info
diff --git a/pw_toolchain/host_gcc/BUILD.gn b/pw_toolchain/host_gcc/BUILD.gn
index e0cdbf1..e073821 100644
--- a/pw_toolchain/host_gcc/BUILD.gn
+++ b/pw_toolchain/host_gcc/BUILD.gn
@@ -40,3 +40,9 @@
     cflags = [ "-D__USE_MINGW_ANSI_STDIO=1" ]
   }
 }
+
+# GCC needs the -pthread option to support multithreading. This must be
+# specified to build e.g. pw_thread_stl.
+config("threading_support") {
+  ldflags = [ "-pthread" ]
+}
diff --git a/targets/host/target_toolchains.gni b/targets/host/target_toolchains.gni
index 9a7a733..07b769a 100644
--- a/targets/host/target_toolchains.gni
+++ b/targets/host/target_toolchains.gni
@@ -112,7 +112,10 @@
   "$dir_pw_build:extra_strict_warnings",
   "$dir_pw_build:clang_thread_safety_warnings",
 ]
-_gcc_default_configs = [ "$dir_pw_build:extra_strict_warnings" ]
+_gcc_default_configs = [
+  "$dir_pw_build:extra_strict_warnings",
+  "$dir_pw_toolchain/host_gcc:threading_support",
+]
 
 pw_target_toolchain_host = {
   _excluded_members = [