linux_core_dumper: support NT_SIGINFO for reading crashing address

The current core dumper only parses NT_PRSTATUS notes.  With signal
details, this note only includes three fields: signo, code, and errno.
We set exception_code to signo and exception_flag to code.  The errno
value isn't set by the kernel, so there's no need to save it.

However, we never fill in exception_address which means all converted
crashes look like they happen at address 0.  This implies a NULL jump
which is usually not the case, so it's just confusing.  The prstatus
structure doesn't offer anything directly that tracks this.

Starting with linux-3.7, the kernel writes out the full siginfo
structure in the NT_SIGINFO note.  So lets support that to pull out
si_addr which, for a bunch of common signals, is the value we want in
exception_address.

The size of the siginfo_t structure should be locked to 128 bytes at
build time for all architectures, so this should hopefully be stable.

Bug: google-breakpad:790
Change-Id: I458bad4787b1a8b73fad8fe068e9f23bec957599
Reviewed-on: https://chromium-review.googlesource.com/c/1497661
Reviewed-by: Mark Mentovai <mark@chromium.org>
diff --git a/src/client/linux/minidump_writer/linux_core_dumper.cc b/src/client/linux/minidump_writer/linux_core_dumper.cc
index 9bf2d2e..9a2f4d6 100644
--- a/src/client/linux/minidump_writer/linux_core_dumper.cc
+++ b/src/client/linux/minidump_writer/linux_core_dumper.cc
@@ -165,6 +165,7 @@
     //   -------------------------------------------------------------------
     //   1st thread       CORE          NT_PRSTATUS
     //   process-wide     CORE          NT_PRPSINFO
+    //   process-wide     CORE          NT_SIGINFO
     //   process-wide     CORE          NT_AUXV
     //   1st thread       CORE          NT_FPREGSET
     //   1st thread       LINUX         NT_PRXFPREG
@@ -219,6 +220,28 @@
         thread_infos_.push_back(info);
         break;
       }
+      case NT_SIGINFO: {
+        if (description.length() != sizeof(siginfo_t)) {
+          fprintf(stderr, "Found NT_SIGINFO descriptor of unexpected size\n");
+          return false;
+        }
+
+        const siginfo_t* info =
+            reinterpret_cast<const siginfo_t*>(description.data());
+
+        // Set crash_address when si_addr is valid for the signal.
+        switch (info->si_signo) {
+          case MD_EXCEPTION_CODE_LIN_SIGBUS:
+          case MD_EXCEPTION_CODE_LIN_SIGFPE:
+          case MD_EXCEPTION_CODE_LIN_SIGILL:
+          case MD_EXCEPTION_CODE_LIN_SIGSEGV:
+          case MD_EXCEPTION_CODE_LIN_SIGSYS:
+          case MD_EXCEPTION_CODE_LIN_SIGTRAP:
+            crash_address_ = reinterpret_cast<uintptr_t>(info->si_addr);
+            break;
+        }
+        break;
+      }
 #if defined(__i386) || defined(__x86_64)
       case NT_FPREGSET: {
         if (thread_infos_.empty())