Add CodeViewRecordBuildId

Until now we've been stuffing ELF debug symbol link information into a
CodeViewPDB70. This has reached the limits of its usefulness. We now add
a CodeViewRecord that can contain a proper ELF build ID.

Change-Id: Ice52cb2a958a1b9031943f280d9054da02d2f17d
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/1574107
Commit-Queue: Casey Dahlin <sadmac@google.com>
Reviewed-by: Mark Mentovai <mark@chromium.org>
diff --git a/minidump/minidump_module_writer.cc b/minidump/minidump_module_writer.cc
index 305d37c..6e53352 100644
--- a/minidump/minidump_module_writer.cc
+++ b/minidump/minidump_module_writer.cc
@@ -31,8 +31,7 @@
 
 namespace crashpad {
 
-MinidumpModuleCodeViewRecordWriter::~MinidumpModuleCodeViewRecordWriter() {
-}
+MinidumpModuleCodeViewRecordWriter::~MinidumpModuleCodeViewRecordWriter() {}
 
 namespace internal {
 
@@ -45,8 +44,7 @@
 
 template <typename CodeViewRecordType>
 MinidumpModuleCodeViewRecordPDBLinkWriter<
-    CodeViewRecordType>::~MinidumpModuleCodeViewRecordPDBLinkWriter() {
-}
+    CodeViewRecordType>::~MinidumpModuleCodeViewRecordPDBLinkWriter() {}
 
 template <typename CodeViewRecordType>
 size_t
@@ -82,8 +80,7 @@
     CodeViewRecordPDB20>;
 
 MinidumpModuleCodeViewRecordPDB20Writer::
-    ~MinidumpModuleCodeViewRecordPDB20Writer() {
-}
+    ~MinidumpModuleCodeViewRecordPDB20Writer() {}
 
 void MinidumpModuleCodeViewRecordPDB20Writer::SetTimestampAndAge(
     time_t timestamp,
@@ -100,8 +97,7 @@
     CodeViewRecordPDB70>;
 
 MinidumpModuleCodeViewRecordPDB70Writer::
-    ~MinidumpModuleCodeViewRecordPDB70Writer() {
-}
+    ~MinidumpModuleCodeViewRecordPDB70Writer() {}
 
 void MinidumpModuleCodeViewRecordPDB70Writer::InitializeFromSnapshot(
     const ModuleSnapshot* module_snapshot) {
@@ -115,15 +111,52 @@
   SetUUIDAndAge(uuid, age);
 }
 
+MinidumpModuleCodeViewRecordBuildIDWriter::
+    MinidumpModuleCodeViewRecordBuildIDWriter()
+    : MinidumpModuleCodeViewRecordWriter(), build_id_() {}
+
+MinidumpModuleCodeViewRecordBuildIDWriter::
+    ~MinidumpModuleCodeViewRecordBuildIDWriter() {}
+
+size_t MinidumpModuleCodeViewRecordBuildIDWriter::SizeOfObject() {
+  DCHECK_GE(state(), kStateFrozen);
+  return offsetof(CodeViewRecordBuildID, build_id) + build_id_.size();
+}
+
+void MinidumpModuleCodeViewRecordBuildIDWriter::SetBuildID(
+    const std::vector<uint8_t>& build_id) {
+  DCHECK_EQ(state(), kStateMutable);
+  build_id_ = build_id;
+}
+
+bool MinidumpModuleCodeViewRecordBuildIDWriter::WriteObject(
+    FileWriterInterface* file_writer) {
+  DCHECK_EQ(state(), kStateWritable);
+
+  CodeViewRecordBuildID cv;
+  cv.signature = CodeViewRecordBuildID::kSignature;
+
+  WritableIoVec iov;
+  iov.iov_base = &cv;
+  iov.iov_len = offsetof(CodeViewRecordBuildID, build_id);
+  std::vector<WritableIoVec> iovecs(1, iov);
+
+  if (!build_id_.empty()) {
+    iov.iov_base = build_id_.data();
+    iov.iov_len = build_id_.size();
+    iovecs.push_back(iov);
+  }
+
+  return file_writer->WriteIoVec(&iovecs);
+}
+
 MinidumpModuleMiscDebugRecordWriter::MinidumpModuleMiscDebugRecordWriter()
     : internal::MinidumpWritable(),
       image_debug_misc_(),
       data_(),
-      data_utf16_() {
-}
+      data_utf16_() {}
 
-MinidumpModuleMiscDebugRecordWriter::~MinidumpModuleMiscDebugRecordWriter() {
-}
+MinidumpModuleMiscDebugRecordWriter::~MinidumpModuleMiscDebugRecordWriter() {}
 
 void MinidumpModuleMiscDebugRecordWriter::SetData(const std::string& data,
                                                   bool utf16) {
@@ -203,8 +236,7 @@
   module_.VersionInfo.dwStrucVersion = VS_FFI_STRUCVERSION;
 }
 
-MinidumpModuleWriter::~MinidumpModuleWriter() {
-}
+MinidumpModuleWriter::~MinidumpModuleWriter() {}
 
 void MinidumpModuleWriter::InitializeFromSnapshot(
     const ModuleSnapshot* module_snapshot) {
@@ -242,9 +274,21 @@
   }
   SetFileTypeAndSubtype(file_type, VFT2_UNKNOWN);
 
-  auto codeview_record =
-      std::make_unique<MinidumpModuleCodeViewRecordPDB70Writer>();
-  codeview_record->InitializeFromSnapshot(module_snapshot);
+  auto build_id = module_snapshot->BuildID();
+
+  std::unique_ptr<MinidumpModuleCodeViewRecordWriter> codeview_record;
+  if (!build_id.empty()) {
+    auto cv_record_build_id =
+        std::make_unique<MinidumpModuleCodeViewRecordBuildIDWriter>();
+    cv_record_build_id->SetBuildID(build_id);
+    codeview_record = std::move(cv_record_build_id);
+  } else {
+    auto cv_record_pdb70 =
+        std::make_unique<MinidumpModuleCodeViewRecordPDB70Writer>();
+    cv_record_pdb70->InitializeFromSnapshot(module_snapshot);
+    codeview_record = std::move(cv_record_pdb70);
+  }
+
   SetCodeViewRecord(std::move(codeview_record));
 }
 
@@ -372,11 +416,9 @@
 }
 
 MinidumpModuleListWriter::MinidumpModuleListWriter()
-    : MinidumpStreamWriter(), modules_(), module_list_base_() {
-}
+    : MinidumpStreamWriter(), modules_(), module_list_base_() {}
 
-MinidumpModuleListWriter::~MinidumpModuleListWriter() {
-}
+MinidumpModuleListWriter::~MinidumpModuleListWriter() {}
 
 void MinidumpModuleListWriter::InitializeFromSnapshot(
     const std::vector<const ModuleSnapshot*>& module_snapshots) {
diff --git a/minidump/minidump_module_writer.h b/minidump/minidump_module_writer.h
index 555c411..b328bf0 100644
--- a/minidump/minidump_module_writer.h
+++ b/minidump/minidump_module_writer.h
@@ -88,7 +88,8 @@
 
 //! \brief The writer for a CodeViewRecordPDB20 object in a minidump file.
 //!
-//! Most users will want MinidumpModuleCodeViewRecordPDB70Writer instead.
+//! Most users will want MinidumpModuleCodeViewRecordPDB70Writer or
+//! MinidumpModuleCodeViewRecordBuildIDWriter instead.
 class MinidumpModuleCodeViewRecordPDB20Writer final
     : public internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
           CodeViewRecordPDB20> {
@@ -136,6 +137,26 @@
   DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordPDB70Writer);
 };
 
+//! \brief The writer for a CodeViewRecordBuildID object in a minidump file.
+class MinidumpModuleCodeViewRecordBuildIDWriter final
+    : public MinidumpModuleCodeViewRecordWriter {
+ public:
+  MinidumpModuleCodeViewRecordBuildIDWriter();
+  ~MinidumpModuleCodeViewRecordBuildIDWriter() override;
+
+  //! \brief Sets the build ID used for symbol lookup.
+  void SetBuildID(const std::vector<uint8_t>& build_id);
+
+ private:
+  // MinidumpWritable:
+  size_t SizeOfObject() override;
+  bool WriteObject(FileWriterInterface* file_writer) override;
+
+  std::vector<uint8_t> build_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordBuildIDWriter);
+};
+
 //! \brief The writer for an IMAGE_DEBUG_MISC object in a minidump file.
 //!
 //! Most users will want MinidumpModuleCodeViewRecordPDB70Writer instead.
diff --git a/minidump/minidump_module_writer_test.cc b/minidump/minidump_module_writer_test.cc
index 8b5fc06..71db3ac 100644
--- a/minidump/minidump_module_writer_test.cc
+++ b/minidump/minidump_module_writer_test.cc
@@ -265,6 +265,61 @@
                                                          expected_debug_utf16));
 }
 
+// ExpectModuleWithBuildIDCv() is like ExpectModule( but expects the module to
+// have a BuildID CodeView Record.
+void ExpectModuleWithBuildIDCv(const MINIDUMP_MODULE* expected,
+                               const MINIDUMP_MODULE* observed,
+                               const std::string& file_contents,
+                               const std::string& expected_module_name,
+                               const std::vector<uint8_t>& expected_build_id) {
+  EXPECT_EQ(observed->BaseOfImage, expected->BaseOfImage);
+  EXPECT_EQ(observed->SizeOfImage, expected->SizeOfImage);
+  EXPECT_EQ(observed->CheckSum, expected->CheckSum);
+  EXPECT_EQ(observed->TimeDateStamp, expected->TimeDateStamp);
+  EXPECT_EQ(observed->VersionInfo.dwSignature,
+            implicit_cast<uint32_t>(VS_FFI_SIGNATURE));
+  EXPECT_EQ(observed->VersionInfo.dwStrucVersion,
+            implicit_cast<uint32_t>(VS_FFI_STRUCVERSION));
+  EXPECT_EQ(observed->VersionInfo.dwFileVersionMS,
+            expected->VersionInfo.dwFileVersionMS);
+  EXPECT_EQ(observed->VersionInfo.dwFileVersionLS,
+            expected->VersionInfo.dwFileVersionLS);
+  EXPECT_EQ(observed->VersionInfo.dwProductVersionMS,
+            expected->VersionInfo.dwProductVersionMS);
+  EXPECT_EQ(observed->VersionInfo.dwProductVersionLS,
+            expected->VersionInfo.dwProductVersionLS);
+  EXPECT_EQ(observed->VersionInfo.dwFileFlagsMask,
+            expected->VersionInfo.dwFileFlagsMask);
+  EXPECT_EQ(observed->VersionInfo.dwFileFlags,
+            expected->VersionInfo.dwFileFlags);
+  EXPECT_EQ(observed->VersionInfo.dwFileOS, expected->VersionInfo.dwFileOS);
+  EXPECT_EQ(observed->VersionInfo.dwFileType, expected->VersionInfo.dwFileType);
+  EXPECT_EQ(observed->VersionInfo.dwFileSubtype,
+            expected->VersionInfo.dwFileSubtype);
+  EXPECT_EQ(observed->VersionInfo.dwFileDateMS,
+            expected->VersionInfo.dwFileDateMS);
+  EXPECT_EQ(observed->VersionInfo.dwFileDateLS,
+            expected->VersionInfo.dwFileDateLS);
+  EXPECT_EQ(observed->Reserved0, 0u);
+  EXPECT_EQ(observed->Reserved1, 0u);
+
+  EXPECT_NE(observed->ModuleNameRva, 0u);
+  base::string16 observed_module_name_utf16 =
+      MinidumpStringAtRVAAsString(file_contents, observed->ModuleNameRva);
+  base::string16 expected_module_name_utf16 =
+      base::UTF8ToUTF16(expected_module_name);
+  EXPECT_EQ(observed_module_name_utf16, expected_module_name_utf16);
+
+  const CodeViewRecordBuildID* codeview_build_id_record =
+      MinidumpWritableAtLocationDescriptor<CodeViewRecordBuildID>(
+          file_contents, observed->CvRecord);
+  ASSERT_TRUE(codeview_build_id_record);
+  EXPECT_EQ(memcmp(expected_build_id.data(),
+                   &codeview_build_id_record->build_id,
+                   expected_build_id.size()),
+            0);
+}
+
 TEST(MinidumpModuleWriter, EmptyModule) {
   MinidumpFileWriter minidump_file_writer;
   auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();
@@ -325,9 +380,22 @@
   constexpr uint32_t kFileType = VFT_DRV;
   constexpr uint32_t kFileSubtype = VFT2_DRV_KEYBOARD;
   static constexpr char kPDBName[] = "statical.pdb";
-  static constexpr uint8_t kPDBUUIDBytes[16] =
-      {0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10,
-       0x08, 0x19, 0x2a, 0x3b, 0x4c, 0x5d, 0x6e, 0x7f};
+  static constexpr uint8_t kPDBUUIDBytes[16] = {0xfe,
+                                                0xdc,
+                                                0xba,
+                                                0x98,
+                                                0x76,
+                                                0x54,
+                                                0x32,
+                                                0x10,
+                                                0x08,
+                                                0x19,
+                                                0x2a,
+                                                0x3b,
+                                                0x4c,
+                                                0x5d,
+                                                0x6e,
+                                                0x7f};
   UUID pdb_uuid;
   pdb_uuid.InitializeFromBytes(kPDBUUIDBytes);
   constexpr uint32_t kPDBAge = 1;
@@ -471,6 +539,50 @@
                                        kDebugUTF16));
 }
 
+TEST(MinidumpModuleWriter, OneModule_CodeViewBuildID) {
+  // MinidumpModuleWriter.OneModule tested with a BuildID CodeView
+  MinidumpFileWriter minidump_file_writer;
+  auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();
+
+  static constexpr char kModuleName[] = "dinosaur";
+  static constexpr char kBuildID[] =
+      "averylonghashcodeormaybeitsjustrandomnumbershardtosay";
+
+  std::vector<uint8_t> build_id_data(kBuildID, kBuildID + 53);
+
+  auto module_writer = std::make_unique<MinidumpModuleWriter>();
+  module_writer->SetName(kModuleName);
+
+  auto codeview_build_id_writer =
+      std::make_unique<MinidumpModuleCodeViewRecordBuildIDWriter>();
+  codeview_build_id_writer->SetBuildID(build_id_data);
+  module_writer->SetCodeViewRecord(std::move(codeview_build_id_writer));
+
+  module_list_writer->AddModule(std::move(module_writer));
+  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));
+
+  StringFile string_file;
+  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+  ASSERT_GT(string_file.string().size(),
+            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
+                sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));
+
+  const MINIDUMP_MODULE_LIST* module_list = nullptr;
+  ASSERT_NO_FATAL_FAILURE(
+      GetModuleListStream(string_file.string(), &module_list));
+
+  EXPECT_EQ(module_list->NumberOfModules, 1u);
+
+  MINIDUMP_MODULE expected = {};
+
+  ASSERT_NO_FATAL_FAILURE(ExpectModuleWithBuildIDCv(&expected,
+                                                    &module_list->Modules[0],
+                                                    string_file.string(),
+                                                    kModuleName,
+                                                    build_id_data));
+}
+
 TEST(MinidumpModuleWriter, ThreeModules) {
   // As good exercise, this test uses three modules, one with a PDB 7.0 link as
   // its CodeView record, one with no CodeView record, and one with a PDB 2.0
@@ -482,9 +594,22 @@
   constexpr uint64_t kModuleBase0 = 0x100101000;
   constexpr uint32_t kModuleSize0 = 0xf000;
   static constexpr char kPDBName0[] = "main";
-  static constexpr uint8_t kPDBUUIDBytes0[16] =
-      {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11,
-       0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99};
+  static constexpr uint8_t kPDBUUIDBytes0[16] = {0xaa,
+                                                 0xbb,
+                                                 0xcc,
+                                                 0xdd,
+                                                 0xee,
+                                                 0xff,
+                                                 0x00,
+                                                 0x11,
+                                                 0x22,
+                                                 0x33,
+                                                 0x44,
+                                                 0x55,
+                                                 0x66,
+                                                 0x77,
+                                                 0x88,
+                                                 0x99};
   UUID pdb_uuid_0;
   pdb_uuid_0.InitializeFromBytes(kPDBUUIDBytes0);
   constexpr uint32_t kPDBAge0 = 0;
@@ -666,9 +791,22 @@
   expect_modules[0].VersionInfo.dwFileType = VFT_APP;
   module_paths[0] = "/usr/bin/true";
   module_pdbs[0] = "true";
-  static constexpr uint8_t kUUIDBytes0[16] =
-      {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
-       0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
+  static constexpr uint8_t kUUIDBytes0[16] = {0x00,
+                                              0x11,
+                                              0x22,
+                                              0x33,
+                                              0x44,
+                                              0x55,
+                                              0x66,
+                                              0x77,
+                                              0x88,
+                                              0x99,
+                                              0xaa,
+                                              0xbb,
+                                              0xcc,
+                                              0xdd,
+                                              0xee,
+                                              0xff};
   uuids[0].InitializeFromBytes(kUUIDBytes0);
   ages[0] = 10;
 
@@ -682,9 +820,22 @@
   expect_modules[1].VersionInfo.dwFileType = VFT_DLL;
   module_paths[1] = "/usr/lib/libSystem.B.dylib";
   module_pdbs[1] = "libSystem.B.dylib.pdb";
-  static constexpr uint8_t kUUIDBytes1[16] =
-      {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
+  static constexpr uint8_t kUUIDBytes1[16] = {0x00,
+                                              0x01,
+                                              0x02,
+                                              0x03,
+                                              0x04,
+                                              0x05,
+                                              0x06,
+                                              0x07,
+                                              0x08,
+                                              0x09,
+                                              0x0a,
+                                              0x0b,
+                                              0x0c,
+                                              0x0d,
+                                              0x0e,
+                                              0x0f};
   uuids[1].InitializeFromBytes(kUUIDBytes1);
   ages[1] = 20;
 
@@ -698,9 +849,22 @@
   expect_modules[2].VersionInfo.dwFileType = VFT_UNKNOWN;
   module_paths[2] = "/usr/lib/dyld";
   module_pdbs[2] = "/usr/lib/dyld.pdb";
-  static constexpr uint8_t kUUIDBytes2[16] =
-      {0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
-       0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0};
+  static constexpr uint8_t kUUIDBytes2[16] = {0xff,
+                                              0xfe,
+                                              0xfd,
+                                              0xfc,
+                                              0xfb,
+                                              0xfa,
+                                              0xf9,
+                                              0xf8,
+                                              0xf7,
+                                              0xf6,
+                                              0xf5,
+                                              0xf4,
+                                              0xf3,
+                                              0xf2,
+                                              0xf1,
+                                              0xf0};
   uuids[2].InitializeFromBytes(kUUIDBytes2);
   ages[2] = 30;
 
diff --git a/minidump/test/minidump_writable_test_util.cc b/minidump/test/minidump_writable_test_util.cc
index 1a832bf..db07b36 100644
--- a/minidump/test/minidump_writable_test_util.cc
+++ b/minidump/test/minidump_writable_test_util.cc
@@ -210,17 +210,13 @@
 struct MinidumpModuleCrashpadInfoListTraits {
   using ListType = MinidumpModuleCrashpadInfoList;
   enum : size_t { kElementSize = sizeof(MinidumpModuleCrashpadInfoLink) };
-  static size_t ElementCount(const ListType* list) {
-    return list->count;
-  }
+  static size_t ElementCount(const ListType* list) { return list->count; }
 };
 
 struct MinidumpSimpleStringDictionaryListTraits {
   using ListType = MinidumpSimpleStringDictionary;
   enum : size_t { kElementSize = sizeof(MinidumpSimpleStringDictionaryEntry) };
-  static size_t ElementCount(const ListType* list) {
-    return list->count;
-  }
+  static size_t ElementCount(const ListType* list) { return list->count; }
 };
 
 struct MinidumpAnnotationListObjectsTraits {
@@ -253,17 +249,19 @@
 }  // namespace
 
 template <>
-const MINIDUMP_MEMORY_LIST* MinidumpWritableAtLocationDescriptor<
-    MINIDUMP_MEMORY_LIST>(const std::string& file_contents,
-                          const MINIDUMP_LOCATION_DESCRIPTOR& location) {
+const MINIDUMP_MEMORY_LIST*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(
+    const std::string& file_contents,
+    const MINIDUMP_LOCATION_DESCRIPTOR& location) {
   return MinidumpListAtLocationDescriptor<MinidumpMemoryListTraits>(
       file_contents, location);
 }
 
 template <>
-const MINIDUMP_MODULE_LIST* MinidumpWritableAtLocationDescriptor<
-    MINIDUMP_MODULE_LIST>(const std::string& file_contents,
-                          const MINIDUMP_LOCATION_DESCRIPTOR& location) {
+const MINIDUMP_MODULE_LIST*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(
+    const std::string& file_contents,
+    const MINIDUMP_LOCATION_DESCRIPTOR& location) {
   return MinidumpListAtLocationDescriptor<MinidumpModuleListTraits>(
       file_contents, location);
 }
@@ -278,25 +276,28 @@
 }
 
 template <>
-const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor<
-    MINIDUMP_THREAD_LIST>(const std::string& file_contents,
-                          const MINIDUMP_LOCATION_DESCRIPTOR& location) {
+const MINIDUMP_THREAD_LIST*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(
+    const std::string& file_contents,
+    const MINIDUMP_LOCATION_DESCRIPTOR& location) {
   return MinidumpListAtLocationDescriptor<MinidumpThreadListTraits>(
       file_contents, location);
 }
 
 template <>
-const MINIDUMP_HANDLE_DATA_STREAM* MinidumpWritableAtLocationDescriptor<
-    MINIDUMP_HANDLE_DATA_STREAM>(const std::string& file_contents,
-                                 const MINIDUMP_LOCATION_DESCRIPTOR& location) {
+const MINIDUMP_HANDLE_DATA_STREAM*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_HANDLE_DATA_STREAM>(
+    const std::string& file_contents,
+    const MINIDUMP_LOCATION_DESCRIPTOR& location) {
   return MinidumpListAtLocationDescriptor<MinidumpHandleDataStreamTraits>(
       file_contents, location);
 }
 
 template <>
-const MINIDUMP_MEMORY_INFO_LIST* MinidumpWritableAtLocationDescriptor<
-    MINIDUMP_MEMORY_INFO_LIST>(const std::string& file_contents,
-                               const MINIDUMP_LOCATION_DESCRIPTOR& location) {
+const MINIDUMP_MEMORY_INFO_LIST*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_INFO_LIST>(
+    const std::string& file_contents,
+    const MINIDUMP_LOCATION_DESCRIPTOR& location) {
   return MinidumpListAtLocationDescriptor<MinidumpMemoryInfoListTraits>(
       file_contents, location);
 }
@@ -357,28 +358,51 @@
 }  // namespace
 
 template <>
-const CodeViewRecordPDB20* MinidumpWritableAtLocationDescriptor<
-    CodeViewRecordPDB20>(const std::string& file_contents,
-                         const MINIDUMP_LOCATION_DESCRIPTOR& location) {
+const CodeViewRecordPDB20*
+MinidumpWritableAtLocationDescriptor<CodeViewRecordPDB20>(
+    const std::string& file_contents,
+    const MINIDUMP_LOCATION_DESCRIPTOR& location) {
   return MinidumpCVPDBAtLocationDescriptor<CodeViewRecordPDB20>(file_contents,
                                                                 location);
 }
 
 template <>
-const CodeViewRecordPDB70* MinidumpWritableAtLocationDescriptor<
-    CodeViewRecordPDB70>(const std::string& file_contents,
-                         const MINIDUMP_LOCATION_DESCRIPTOR& location) {
+const CodeViewRecordPDB70*
+MinidumpWritableAtLocationDescriptor<CodeViewRecordPDB70>(
+    const std::string& file_contents,
+    const MINIDUMP_LOCATION_DESCRIPTOR& location) {
   return MinidumpCVPDBAtLocationDescriptor<CodeViewRecordPDB70>(file_contents,
                                                                 location);
 }
 
-TestUInt32MinidumpWritable::TestUInt32MinidumpWritable(uint32_t value)
-    : MinidumpWritable(),
-      value_(value) {
+template <>
+const CodeViewRecordBuildID*
+MinidumpWritableAtLocationDescriptor<CodeViewRecordBuildID>(
+    const std::string& file_contents,
+    const MINIDUMP_LOCATION_DESCRIPTOR& location) {
+  const CodeViewRecordBuildID* cv =
+      reinterpret_cast<const CodeViewRecordBuildID*>(
+          MinidumpWritableAtLocationDescriptorInternal(
+              file_contents,
+              location,
+              offsetof(CodeViewRecordBuildID, build_id),
+              true));
+
+  if (!cv) {
+    return nullptr;
+  }
+
+  if (cv->signature != CodeViewRecordBuildID::kSignature) {
+    return nullptr;
+  }
+
+  return cv;
 }
 
-TestUInt32MinidumpWritable::~TestUInt32MinidumpWritable() {
-}
+TestUInt32MinidumpWritable::TestUInt32MinidumpWritable(uint32_t value)
+    : MinidumpWritable(), value_(value) {}
+
+TestUInt32MinidumpWritable::~TestUInt32MinidumpWritable() {}
 
 size_t TestUInt32MinidumpWritable::SizeOfObject() {
   return sizeof(value_);
diff --git a/minidump/test/minidump_writable_test_util.h b/minidump/test/minidump_writable_test_util.h
index 5b176d2..6c706fb 100644
--- a/minidump/test/minidump_writable_test_util.h
+++ b/minidump/test/minidump_writable_test_util.h
@@ -104,6 +104,7 @@
 MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_STRING);
 MINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordPDB20);
 MINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordPDB70);
+MINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordBuildID);
 MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpUTF8String);
 
 // minidump_file_writer_test accesses its variable-sized test streams via a
@@ -179,14 +180,16 @@
     const MINIDUMP_LOCATION_DESCRIPTOR& location);
 
 template <>
-const MINIDUMP_MEMORY_LIST* MinidumpWritableAtLocationDescriptor<
-    MINIDUMP_MEMORY_LIST>(const std::string& file_contents,
-                          const MINIDUMP_LOCATION_DESCRIPTOR& location);
+const MINIDUMP_MEMORY_LIST*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(
+    const std::string& file_contents,
+    const MINIDUMP_LOCATION_DESCRIPTOR& location);
 
 template <>
-const MINIDUMP_MODULE_LIST* MinidumpWritableAtLocationDescriptor<
-    MINIDUMP_MODULE_LIST>(const std::string& file_contents,
-                          const MINIDUMP_LOCATION_DESCRIPTOR& location);
+const MINIDUMP_MODULE_LIST*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(
+    const std::string& file_contents,
+    const MINIDUMP_LOCATION_DESCRIPTOR& location);
 
 template <>
 const MINIDUMP_UNLOADED_MODULE_LIST*
@@ -195,29 +198,40 @@
     const MINIDUMP_LOCATION_DESCRIPTOR& location);
 
 template <>
-const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor<
-    MINIDUMP_THREAD_LIST>(const std::string& file_contents,
-                          const MINIDUMP_LOCATION_DESCRIPTOR& location);
+const MINIDUMP_THREAD_LIST*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(
+    const std::string& file_contents,
+    const MINIDUMP_LOCATION_DESCRIPTOR& location);
 
 template <>
-const MINIDUMP_HANDLE_DATA_STREAM* MinidumpWritableAtLocationDescriptor<
-    MINIDUMP_HANDLE_DATA_STREAM>(const std::string& file_contents,
-                                 const MINIDUMP_LOCATION_DESCRIPTOR& location);
+const MINIDUMP_HANDLE_DATA_STREAM*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_HANDLE_DATA_STREAM>(
+    const std::string& file_contents,
+    const MINIDUMP_LOCATION_DESCRIPTOR& location);
 
 template <>
-const MINIDUMP_MEMORY_INFO_LIST* MinidumpWritableAtLocationDescriptor<
-    MINIDUMP_MEMORY_INFO_LIST>(const std::string& file_contents,
-                               const MINIDUMP_LOCATION_DESCRIPTOR& location);
+const MINIDUMP_MEMORY_INFO_LIST*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_INFO_LIST>(
+    const std::string& file_contents,
+    const MINIDUMP_LOCATION_DESCRIPTOR& location);
 
 template <>
-const CodeViewRecordPDB20* MinidumpWritableAtLocationDescriptor<
-    CodeViewRecordPDB20>(const std::string& file_contents,
-                         const MINIDUMP_LOCATION_DESCRIPTOR& location);
+const CodeViewRecordPDB20*
+MinidumpWritableAtLocationDescriptor<CodeViewRecordPDB20>(
+    const std::string& file_contents,
+    const MINIDUMP_LOCATION_DESCRIPTOR& location);
 
 template <>
-const CodeViewRecordPDB70* MinidumpWritableAtLocationDescriptor<
-    CodeViewRecordPDB70>(const std::string& file_contents,
-                         const MINIDUMP_LOCATION_DESCRIPTOR& location);
+const CodeViewRecordPDB70*
+MinidumpWritableAtLocationDescriptor<CodeViewRecordPDB70>(
+    const std::string& file_contents,
+    const MINIDUMP_LOCATION_DESCRIPTOR& location);
+
+template <>
+const CodeViewRecordBuildID*
+MinidumpWritableAtLocationDescriptor<CodeViewRecordBuildID>(
+    const std::string& file_contents,
+    const MINIDUMP_LOCATION_DESCRIPTOR& location);
 
 template <>
 const MinidumpModuleCrashpadInfoList*
diff --git a/snapshot/elf/module_snapshot_elf.cc b/snapshot/elf/module_snapshot_elf.cc
index 39385a6..cfe07dd 100644
--- a/snapshot/elf/module_snapshot_elf.cc
+++ b/snapshot/elf/module_snapshot_elf.cc
@@ -150,13 +150,10 @@
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
   *age = 0;
 
-  std::unique_ptr<ElfImageReader::NoteReader> notes =
-      elf_reader_->NotesWithNameAndType(ELF_NOTE_GNU, NT_GNU_BUILD_ID, 64);
-  std::string desc;
-  VMAddress desc_addr;
-  notes->NextNote(nullptr, nullptr, &desc, &desc_addr);
-  desc.insert(desc.end(), 16 - std::min(desc.size(), size_t{16}), '\0');
-  uuid->InitializeFromBytes(reinterpret_cast<const uint8_t*>(&desc[0]));
+  auto build_id = BuildID();
+  build_id.insert(
+      build_id.end(), 16 - std::min(build_id.size(), size_t{16}), '\0');
+  uuid->InitializeFromBytes(build_id.data());
 
   // TODO(scottmg): https://crashpad.chromium.org/bug/229. These are
   // endian-swapped to match FileID::ConvertIdentifierToUUIDString() in
@@ -171,6 +168,21 @@
   return base::FilePath(Name()).BaseName().value();
 }
 
+std::vector<uint8_t> ModuleSnapshotElf::BuildID() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+  std::unique_ptr<ElfImageReader::NoteReader> notes =
+      elf_reader_->NotesWithNameAndType(ELF_NOTE_GNU, NT_GNU_BUILD_ID, 64);
+  std::string desc;
+  VMAddress desc_addr;
+  notes->NextNote(nullptr, nullptr, &desc, &desc_addr);
+
+  std::vector<uint8_t> build_id;
+  build_id.reserve(desc.size());
+  std::copy(desc.begin(), desc.end(), std::back_inserter(build_id));
+  return build_id;
+}
+
 std::vector<std::string> ModuleSnapshotElf::AnnotationsVector() const {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
   return std::vector<std::string>();
diff --git a/snapshot/elf/module_snapshot_elf.h b/snapshot/elf/module_snapshot_elf.h
index 15c3f3c..be27242 100644
--- a/snapshot/elf/module_snapshot_elf.h
+++ b/snapshot/elf/module_snapshot_elf.h
@@ -77,6 +77,7 @@
   ModuleType GetModuleType() const override;
   void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;
   std::string DebugFileName() const override;
+  std::vector<uint8_t> BuildID() const override;
   std::vector<std::string> AnnotationsVector() const override;
   std::map<std::string, std::string> AnnotationsSimpleMap() const override;
   std::vector<AnnotationSnapshot> AnnotationObjects() const override;
diff --git a/snapshot/mac/module_snapshot_mac.cc b/snapshot/mac/module_snapshot_mac.cc
index 4160897..947744d 100644
--- a/snapshot/mac/module_snapshot_mac.cc
+++ b/snapshot/mac/module_snapshot_mac.cc
@@ -14,8 +14,8 @@
 
 #include "snapshot/mac/module_snapshot_mac.h"
 
-#include <mach/mach.h>
 #include <mach-o/loader.h>
+#include <mach/mach.h>
 
 #include "base/files/file_path.h"
 #include "base/strings/stringprintf.h"
@@ -34,11 +34,9 @@
       timestamp_(0),
       mach_o_image_reader_(nullptr),
       process_reader_(nullptr),
-      initialized_() {
-}
+      initialized_() {}
 
-ModuleSnapshotMac::~ModuleSnapshotMac() {
-}
+ModuleSnapshotMac::~ModuleSnapshotMac() {}
 
 bool ModuleSnapshotMac::Initialize(
     ProcessReaderMac* process_reader,
@@ -170,6 +168,11 @@
   return base::FilePath(Name()).BaseName().value();
 }
 
+std::vector<uint8_t> ModuleSnapshotMac::BuildID() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return std::vector<uint8_t>();
+}
+
 std::vector<std::string> ModuleSnapshotMac::AnnotationsVector() const {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
   MachOImageAnnotationsReader annotations_reader(
diff --git a/snapshot/mac/module_snapshot_mac.h b/snapshot/mac/module_snapshot_mac.h
index fe2d40a..d78c326 100644
--- a/snapshot/mac/module_snapshot_mac.h
+++ b/snapshot/mac/module_snapshot_mac.h
@@ -77,6 +77,7 @@
   ModuleType GetModuleType() const override;
   void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;
   std::string DebugFileName() const override;
+  std::vector<uint8_t> BuildID() const override;
   std::vector<std::string> AnnotationsVector() const override;
   std::map<std::string, std::string> AnnotationsSimpleMap() const override;
   std::vector<AnnotationSnapshot> AnnotationObjects() const override;
diff --git a/snapshot/minidump/module_snapshot_minidump.cc b/snapshot/minidump/module_snapshot_minidump.cc
index b3e33d0..a316e83 100644
--- a/snapshot/minidump/module_snapshot_minidump.cc
+++ b/snapshot/minidump/module_snapshot_minidump.cc
@@ -14,13 +14,15 @@
 
 #include "snapshot/minidump/module_snapshot_minidump.h"
 
+#include <stddef.h>
 #include <string.h>
 
+#include "base/logging.h"
 #include "minidump/minidump_extensions.h"
 #include "snapshot/minidump/minidump_annotation_reader.h"
-#include "snapshot/minidump/minidump_string_reader.h"
 #include "snapshot/minidump/minidump_simple_string_dictionary_reader.h"
 #include "snapshot/minidump/minidump_string_list_reader.h"
+#include "snapshot/minidump/minidump_string_reader.h"
 #include "util/misc/pdb_structures.h"
 
 namespace crashpad {
@@ -33,13 +35,13 @@
       annotations_simple_map_(),
       annotation_objects_(),
       uuid_(),
+      build_id_(),
       name_(),
+      debug_file_name_(),
       age_(0),
-      initialized_() {
-}
+      initialized_() {}
 
-ModuleSnapshotMinidump::~ModuleSnapshotMinidump() {
-}
+ModuleSnapshotMinidump::~ModuleSnapshotMinidump() {}
 
 bool ModuleSnapshotMinidump::Initialize(
     FileReaderInterface* file_reader,
@@ -63,33 +65,68 @@
 
   ReadMinidumpUTF16String(file_reader, minidump_module_.ModuleNameRva, &name_);
 
-  if (minidump_module_.CvRecord.Rva != 0) {
-    CodeViewRecordPDB70 cv;
-
-    if (!file_reader->SeekSet(minidump_module_.CvRecord.Rva)) {
-      return false;
-    }
-
-    if (!file_reader->ReadExactly(&cv, sizeof(cv))) {
-      return false;
-    }
-
-    if (cv.signature == 'SDSR') {
-      age_ = cv.age;
-      uuid_ = cv.uuid;
-    } else if (cv.signature != '01BN') {
-      LOG(ERROR) << "Bad CodeView signature in module";
-      return false;
-    } else {
-      LOG(ERROR) << "NB10 not supported";
-      return false;
-    }
+  if (minidump_module_.CvRecord.Rva != 0 &&
+      !InitializeModuleCodeView(file_reader)) {
+    return false;
   }
 
   INITIALIZATION_STATE_SET_VALID(initialized_);
   return true;
 }
 
+bool ModuleSnapshotMinidump::InitializeModuleCodeView(
+    FileReaderInterface* file_reader) {
+  uint32_t signature;
+
+  DCHECK_NE(minidump_module_.CvRecord.Rva, 0u);
+
+  if (minidump_module_.CvRecord.DataSize < sizeof(signature)) {
+    LOG(ERROR) << "CodeView record in module too small to contain signature";
+    return false;
+  }
+
+  if (!file_reader->SeekSet(minidump_module_.CvRecord.Rva)) {
+    return false;
+  }
+
+  std::vector<uint8_t> cv_record;
+  cv_record.resize(minidump_module_.CvRecord.DataSize);
+
+  if (!file_reader->ReadExactly(cv_record.data(), cv_record.size())) {
+    return false;
+  }
+
+  signature = *reinterpret_cast<uint32_t*>(cv_record.data());
+
+  if (signature == CodeViewRecordPDB70::kSignature) {
+    if (cv_record.size() < offsetof(CodeViewRecordPDB70, pdb_name)) {
+      LOG(ERROR) << "CodeView record in module marked as PDB70 but too small";
+      return false;
+    }
+
+    auto cv_record_pdb70 =
+        reinterpret_cast<CodeViewRecordPDB70*>(cv_record.data());
+
+    age_ = cv_record_pdb70->age;
+    uuid_ = cv_record_pdb70->uuid;
+
+    std::copy(cv_record.begin() + offsetof(CodeViewRecordPDB70, pdb_name),
+              cv_record.end(),
+              std::back_inserter(debug_file_name_));
+    return true;
+  }
+
+  if (signature == CodeViewRecordBuildID::kSignature) {
+    std::copy(cv_record.begin() + offsetof(CodeViewRecordBuildID, build_id),
+              cv_record.end(),
+              std::back_inserter(build_id_));
+    return true;
+  }
+
+  LOG(ERROR) << "Bad CodeView signature in module";
+  return false;
+}
+
 std::string ModuleSnapshotMinidump::Name() const {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
   return name_;
@@ -138,10 +175,10 @@
 ModuleSnapshot::ModuleType ModuleSnapshotMinidump::GetModuleType() const {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
   switch (minidump_module_.VersionInfo.dwFileType) {
-  case VFT_APP:
-    return kModuleTypeExecutable;
-  case VFT_DLL:
-    return kModuleTypeSharedLibrary;
+    case VFT_APP:
+      return kModuleTypeExecutable;
+    case VFT_DLL:
+      return kModuleTypeSharedLibrary;
   }
   return kModuleTypeUnknown;
 }
@@ -155,8 +192,12 @@
 
 std::string ModuleSnapshotMinidump::DebugFileName() const {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
-  NOTREACHED();  // https://crashpad.chromium.org/bug/10
-  return std::string();
+  return debug_file_name_;
+}
+
+std::vector<uint8_t> ModuleSnapshotMinidump::BuildID() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return build_id_;
 }
 
 std::vector<std::string> ModuleSnapshotMinidump::AnnotationsVector() const {
@@ -201,7 +242,7 @@
 
   MinidumpModuleCrashpadInfo minidump_module_crashpad_info;
   if (minidump_module_crashpad_info_location->DataSize <
-          sizeof(minidump_module_crashpad_info)) {
+      sizeof(minidump_module_crashpad_info)) {
     LOG(ERROR) << "minidump_module_crashpad_info size mismatch";
     return false;
   }
@@ -216,7 +257,7 @@
   }
 
   if (minidump_module_crashpad_info.version !=
-          MinidumpModuleCrashpadInfo::kVersion) {
+      MinidumpModuleCrashpadInfo::kVersion) {
     LOG(ERROR) << "minidump_module_crashpad_info version mismatch";
     return false;
   }
diff --git a/snapshot/minidump/module_snapshot_minidump.h b/snapshot/minidump/module_snapshot_minidump.h
index e6e50c5..023d0d5 100644
--- a/snapshot/minidump/module_snapshot_minidump.h
+++ b/snapshot/minidump/module_snapshot_minidump.h
@@ -75,6 +75,7 @@
   ModuleType GetModuleType() const override;
   void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;
   std::string DebugFileName() const override;
+  std::vector<uint8_t> BuildID() const override;
   std::vector<std::string> AnnotationsVector() const override;
   std::map<std::string, std::string> AnnotationsSimpleMap() const override;
   std::vector<AnnotationSnapshot> AnnotationObjects() const override;
@@ -88,12 +89,18 @@
                                     const MINIDUMP_LOCATION_DESCRIPTOR*
                                         minidump_module_crashpad_info_location);
 
+  // Initializes data from the CodeView record, which usually points toward
+  // debug symbols.
+  bool InitializeModuleCodeView(FileReaderInterface* file_reader);
+
   MINIDUMP_MODULE minidump_module_;
   std::vector<std::string> annotations_vector_;
   std::map<std::string, std::string> annotations_simple_map_;
   std::vector<AnnotationSnapshot> annotation_objects_;
   UUID uuid_;
+  std::vector<uint8_t> build_id_;
   std::string name_;
+  std::string debug_file_name_;
   uint32_t age_;
   InitializationStateDcheck initialized_;
 
diff --git a/snapshot/minidump/process_snapshot_minidump_test.cc b/snapshot/minidump/process_snapshot_minidump_test.cc
index a03edfb..7d5d16d 100644
--- a/snapshot/minidump/process_snapshot_minidump_test.cc
+++ b/snapshot/minidump/process_snapshot_minidump_test.cc
@@ -307,10 +307,10 @@
   constexpr uint32_t minidump_module_count = 4;
   RVA name_rvas[minidump_module_count];
   std::string names[minidump_module_count] = {
-    "libtacotruck",
-    "libevidencebased",
-    "libgeorgism",
-    "librealistutopia",
+      "libtacotruck",
+      "libevidencebased",
+      "libgeorgism",
+      "librealistutopia",
   };
 
   minidump_module.BaseOfImage = 0xbadf00d;
@@ -331,16 +331,28 @@
     EXPECT_TRUE(string_file.Write(&name16[0], size));
   }
 
-  CodeViewRecordPDB70 cv;
-  cv.signature = CodeViewRecordPDB70::kSignature;
-  cv.age = 7;
-  cv.uuid.InitializeFromString("00112233-4455-6677-8899-aabbccddeeff");
-  cv.pdb_name[0] = '\0';
+  CodeViewRecordPDB70 pdb70_cv;
+  pdb70_cv.signature = CodeViewRecordPDB70::kSignature;
+  pdb70_cv.age = 7;
+  pdb70_cv.uuid.InitializeFromString("00112233-4455-6677-8899-aabbccddeeff");
+  pdb70_cv.pdb_name[0] = '\0';
 
-  minidump_module.CvRecord.Rva = static_cast<RVA>(string_file.SeekGet());
-  minidump_module.CvRecord.DataSize = sizeof(cv);
+  auto pdb70_loc = static_cast<RVA>(string_file.SeekGet());
+  auto pdb70_size = sizeof(pdb70_cv);
 
-  EXPECT_TRUE(string_file.Write(&cv, sizeof(cv)));
+  EXPECT_TRUE(string_file.Write(&pdb70_cv, sizeof(pdb70_cv)));
+
+  CodeViewRecordBuildID build_id_cv;
+  build_id_cv.signature = CodeViewRecordBuildID::kSignature;
+
+  auto build_id_cv_loc = static_cast<RVA>(string_file.SeekGet());
+
+  EXPECT_TRUE(string_file.Write(&build_id_cv,
+                                offsetof(CodeViewRecordBuildID, build_id)));
+  EXPECT_TRUE(string_file.Write("atestbuildidbecausewhynot", 25));
+
+  auto build_id_cv_size =
+      static_cast<size_t>(string_file.SeekGet() - build_id_cv_loc);
 
   MINIDUMP_DIRECTORY minidump_module_list_directory = {};
   minidump_module_list_directory.StreamType = kMinidumpStreamTypeModuleList;
@@ -355,6 +367,15 @@
   for (uint32_t minidump_module_index = 0;
        minidump_module_index < minidump_module_count;
        ++minidump_module_index) {
+    if (minidump_module_index % 2) {
+      minidump_module.CvRecord.Rva = pdb70_loc;
+      minidump_module.CvRecord.DataSize = static_cast<uint32_t>(pdb70_size);
+    } else {
+      minidump_module.CvRecord.Rva = build_id_cv_loc;
+      minidump_module.CvRecord.DataSize =
+          static_cast<uint32_t>(build_id_cv_size);
+    }
+
     minidump_module.ModuleNameRva = name_rvas[minidump_module_index];
     EXPECT_TRUE(string_file.Write(&minidump_module, sizeof(minidump_module)));
     minidump_module.TimeDateStamp++;
@@ -479,12 +500,19 @@
     EXPECT_EQ(modules[i]->GetModuleType(),
               ModuleSnapshot::kModuleTypeExecutable);
 
-    uint32_t age;
-    UUID uuid;
-    modules[i]->UUIDAndAge(&uuid, &age);
+    if (i % 2) {
+      uint32_t age;
+      UUID uuid;
+      modules[i]->UUIDAndAge(&uuid, &age);
 
-    EXPECT_EQ(uuid.ToString(), "00112233-4455-6677-8899-aabbccddeeff");
-    EXPECT_EQ(age, 7U);
+      EXPECT_EQ(uuid.ToString(), "00112233-4455-6677-8899-aabbccddeeff");
+      EXPECT_EQ(age, 7U);
+    } else {
+      auto build_id = modules[i]->BuildID();
+      std::string build_id_text(build_id.data(),
+                                build_id.data() + build_id.size());
+      EXPECT_EQ(build_id_text, "atestbuildidbecausewhynot");
+    }
   }
 
   auto annotations_simple_map = modules[0]->AnnotationsSimpleMap();
@@ -626,8 +654,8 @@
   minidump_system_info_directory.Location.Rva =
       static_cast<RVA>(string_file.SeekGet());
 
-  ASSERT_TRUE(string_file.Write(&minidump_system_info,
-                                sizeof(minidump_system_info)));
+  ASSERT_TRUE(
+      string_file.Write(&minidump_system_info, sizeof(minidump_system_info)));
 
   header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
   ASSERT_TRUE(string_file.Write(&minidump_system_info_directory,
@@ -680,8 +708,8 @@
   minidump_system_info_directory.Location.Rva =
       static_cast<RVA>(string_file.SeekGet());
 
-  ASSERT_TRUE(string_file.Write(&minidump_system_info,
-                                sizeof(minidump_system_info)));
+  ASSERT_TRUE(
+      string_file.Write(&minidump_system_info, sizeof(minidump_system_info)));
 
   MINIDUMP_THREAD minidump_thread = {};
   uint32_t minidump_thread_count = 1;
@@ -801,8 +829,8 @@
   minidump_system_info_directory.Location.Rva =
       static_cast<RVA>(string_file.SeekGet());
 
-  ASSERT_TRUE(string_file.Write(&minidump_system_info,
-                                sizeof(minidump_system_info)));
+  ASSERT_TRUE(
+      string_file.Write(&minidump_system_info, sizeof(minidump_system_info)));
 
   MINIDUMP_THREAD minidump_thread = {};
   uint32_t minidump_thread_count = 1;
@@ -1015,7 +1043,7 @@
 
   MINIDUMP_DIRECTORY minidump_memory_info_list_directory = {};
   minidump_memory_info_list_directory.StreamType =
-    kMinidumpStreamTypeMemoryInfoList;
+      kMinidumpStreamTypeMemoryInfoList;
   minidump_memory_info_list_directory.Location.DataSize =
       sizeof(minidump_memory_info_list) +
       minidump_memory_info_count * sizeof(MINIDUMP_MEMORY_INFO);
@@ -1043,12 +1071,16 @@
   EXPECT_TRUE(process_snapshot.Initialize(&string_file));
 
   std::vector<const MemoryMapRegionSnapshot*> map =
-    process_snapshot.MemoryMap();
+      process_snapshot.MemoryMap();
   ASSERT_EQ(map.size(), minidump_memory_info_count);
-  EXPECT_EQ(memcmp(&map[0]->AsMinidumpMemoryInfo(), &minidump_memory_info_1,
-                   sizeof(minidump_memory_info_1)), 0);
-  EXPECT_EQ(memcmp(&map[1]->AsMinidumpMemoryInfo(), &minidump_memory_info_2,
-                   sizeof(minidump_memory_info_2)), 0);
+  EXPECT_EQ(memcmp(&map[0]->AsMinidumpMemoryInfo(),
+                   &minidump_memory_info_1,
+                   sizeof(minidump_memory_info_1)),
+            0);
+  EXPECT_EQ(memcmp(&map[1]->AsMinidumpMemoryInfo(),
+                   &minidump_memory_info_2,
+                   sizeof(minidump_memory_info_2)),
+            0);
 }
 
 TEST(ProcessSnapshotMinidump, Stacks) {
@@ -1063,9 +1095,21 @@
   minidump_thread.ThreadId = 42;
   minidump_thread.Stack.StartOfMemoryRange = 0xbeefd00d;
 
-  std::vector<uint8_t> minidump_stack = {
-    '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
-  };
+  std::vector<uint8_t> minidump_stack = {'1',
+                                         '2',
+                                         '3',
+                                         '4',
+                                         '5',
+                                         '6',
+                                         '7',
+                                         '8',
+                                         '9',
+                                         'a',
+                                         'b',
+                                         'c',
+                                         'd',
+                                         'e',
+                                         'f'};
 
   minidump_thread.Stack.Memory.DataSize =
       base::checked_cast<uint32_t>(minidump_stack.size());
diff --git a/snapshot/module_snapshot.h b/snapshot/module_snapshot.h
index eea7466..f7eb83c 100644
--- a/snapshot/module_snapshot.h
+++ b/snapshot/module_snapshot.h
@@ -146,6 +146,7 @@
   //! Windows with incremental linking. On other platforms \a age will always be
   //! `0`.
   //!
+  //! \sa BuildID()
   //! \sa DebugFileName()
   virtual void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const = 0;
 
@@ -159,6 +160,20 @@
   //! \sa UUIDAndAge()
   virtual std::string DebugFileName() const = 0;
 
+  //! \brief Returns the module’s build ID.
+  //!
+  //! On ELF platforms, the build ID is a variable-length byte stream that
+  //! identifies a library uniquely, and is usually used to look up its debug
+  //! symbols when stored separately. This will return an empty vector if it is
+  //! unsupported.
+  //!
+  //! BuildID() and UUIDAndAge() are never available in the same place. When
+  //! UUIDAndAge() is unavailable, it will be filled out with the contents of
+  //! BuildID() (either 0-padded or truncated) and age will be zero.
+  //!
+  //! \sa UUIDAndAge()
+  virtual std::vector<uint8_t> BuildID() const = 0;
+
   //! \brief Returns string annotations recorded in the module.
   //!
   //! This method retrieves annotations recorded in a module. These annotations
diff --git a/snapshot/sanitized/module_snapshot_sanitized.cc b/snapshot/sanitized/module_snapshot_sanitized.cc
index 94cb3d0..32b1357 100644
--- a/snapshot/sanitized/module_snapshot_sanitized.cc
+++ b/snapshot/sanitized/module_snapshot_sanitized.cc
@@ -81,6 +81,10 @@
   return snapshot_->DebugFileName();
 }
 
+std::vector<uint8_t> ModuleSnapshotSanitized::BuildID() const {
+  return snapshot_->BuildID();
+}
+
 std::vector<std::string> ModuleSnapshotSanitized::AnnotationsVector() const {
   // TODO(jperaza): If/when AnnotationsVector() begins to be used, determine
   // whether and how the content should be sanitized.
diff --git a/snapshot/sanitized/module_snapshot_sanitized.h b/snapshot/sanitized/module_snapshot_sanitized.h
index 4f375ce..bbe1f5f 100644
--- a/snapshot/sanitized/module_snapshot_sanitized.h
+++ b/snapshot/sanitized/module_snapshot_sanitized.h
@@ -56,6 +56,7 @@
   ModuleType GetModuleType() const override;
   void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;
   std::string DebugFileName() const override;
+  std::vector<uint8_t> BuildID() const override;
   std::vector<std::string> AnnotationsVector() const override;
   std::map<std::string, std::string> AnnotationsSimpleMap() const override;
   std::vector<AnnotationSnapshot> AnnotationObjects() const override;
diff --git a/snapshot/test/test_module_snapshot.cc b/snapshot/test/test_module_snapshot.cc
index 9141eda..ad0c2de 100644
--- a/snapshot/test/test_module_snapshot.cc
+++ b/snapshot/test/test_module_snapshot.cc
@@ -30,11 +30,9 @@
       debug_file_name_(),
       annotations_vector_(),
       annotations_simple_map_(),
-      extra_memory_ranges_() {
-}
+      extra_memory_ranges_() {}
 
-TestModuleSnapshot::~TestModuleSnapshot() {
-}
+TestModuleSnapshot::~TestModuleSnapshot() {}
 
 std::string TestModuleSnapshot::Name() const {
   return name_;
@@ -85,6 +83,10 @@
   return debug_file_name_;
 }
 
+std::vector<uint8_t> TestModuleSnapshot::BuildID() const {
+  return build_id_;
+}
+
 std::vector<std::string> TestModuleSnapshot::AnnotationsVector() const {
   return annotations_vector_;
 }
diff --git a/snapshot/test/test_module_snapshot.h b/snapshot/test/test_module_snapshot.h
index d1262fa..fb84aaf 100644
--- a/snapshot/test/test_module_snapshot.h
+++ b/snapshot/test/test_module_snapshot.h
@@ -64,6 +64,9 @@
     uuid_ = uuid;
     age_ = age;
   }
+  void SetBuildID(const std::vector<uint8_t>& build_id) {
+    build_id_ = build_id;
+  }
   void SetDebugFileName(const std::string& debug_file_name) {
     debug_file_name_ = debug_file_name;
   }
@@ -101,6 +104,7 @@
   ModuleType GetModuleType() const override;
   void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;
   std::string DebugFileName() const override;
+  std::vector<uint8_t> BuildID() const override;
   std::vector<std::string> AnnotationsVector() const override;
   std::map<std::string, std::string> AnnotationsSimpleMap() const override;
   std::vector<AnnotationSnapshot> AnnotationObjects() const override;
@@ -117,6 +121,7 @@
   ModuleType module_type_;
   uint32_t age_;
   crashpad::UUID uuid_;
+  std::vector<uint8_t> build_id_;
   std::string debug_file_name_;
   std::vector<std::string> annotations_vector_;
   std::map<std::string, std::string> annotations_simple_map_;
diff --git a/snapshot/win/module_snapshot_win.cc b/snapshot/win/module_snapshot_win.cc
index d9d74c1..49d178a 100644
--- a/snapshot/win/module_snapshot_win.cc
+++ b/snapshot/win/module_snapshot_win.cc
@@ -44,8 +44,7 @@
       age_(0),
       initialized_() {}
 
-ModuleSnapshotWin::~ModuleSnapshotWin() {
-}
+ModuleSnapshotWin::~ModuleSnapshotWin() {}
 
 bool ModuleSnapshotWin::Initialize(
     ProcessReaderWin* process_reader,
@@ -195,6 +194,11 @@
   return pdb_name_;
 }
 
+std::vector<uint8_t> ModuleSnapshotWin::BuildID() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return std::vector<uint8_t>();
+}
+
 std::vector<std::string> ModuleSnapshotWin::AnnotationsVector() const {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
   // These correspond to system-logged things on Mac. We don't currently track
diff --git a/snapshot/win/module_snapshot_win.h b/snapshot/win/module_snapshot_win.h
index f18a880..b32216c 100644
--- a/snapshot/win/module_snapshot_win.h
+++ b/snapshot/win/module_snapshot_win.h
@@ -84,6 +84,7 @@
   ModuleType GetModuleType() const override;
   void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;
   std::string DebugFileName() const override;
+  std::vector<uint8_t> BuildID() const override;
   std::vector<std::string> AnnotationsVector() const override;
   std::map<std::string, std::string> AnnotationsSimpleMap() const override;
   std::vector<AnnotationSnapshot> AnnotationObjects() const override;
diff --git a/util/misc/pdb_structures.cc b/util/misc/pdb_structures.cc
index c62f11c..e904dc6 100644
--- a/util/misc/pdb_structures.cc
+++ b/util/misc/pdb_structures.cc
@@ -18,5 +18,6 @@
 
 const uint32_t CodeViewRecordPDB20::kSignature;
 const uint32_t CodeViewRecordPDB70::kSignature;
+const uint32_t CodeViewRecordBuildID::kSignature;
 
 }  // namespace crashpad
diff --git a/util/misc/pdb_structures.h b/util/misc/pdb_structures.h
index d0cc9a3..834cfdc 100644
--- a/util/misc/pdb_structures.h
+++ b/util/misc/pdb_structures.h
@@ -90,12 +90,7 @@
   // UUID has a constructor, which makes it non-POD, which makes this structure
   // non-POD. In order for the default constructor to zero-initialize other
   // members, an explicit constructor must be provided.
-  CodeViewRecordPDB70()
-      : signature(),
-        uuid(),
-        age(),
-        pdb_name() {
-  }
+  CodeViewRecordPDB70() : signature(), uuid(), age(), pdb_name() {}
 
   //! \brief The magic number identifying this structure version, stored in
   //!     #signature.
@@ -127,6 +122,27 @@
   uint8_t pdb_name[1];
 };
 
+//! \brief A CodeView record containing an ELF build-id.
+//!
+//! This identifier comes from the ELF section `NT_GNU_BUILD_ID`.
+struct CodeViewRecordBuildID {
+  //! \brief The magic number identifying this structure version, stored in
+  //!     #signature.
+  //!
+  //! In a hex dump, this will appear as “LEpB” when produced by a little-endian
+  //! machine.
+  static const uint32_t kSignature = 'BpEL';
+
+  //! \brief The magic number identifying this structure version, the value of
+  //!     #kSignature.
+  uint32_t signature;
+
+  //! \brief The build ID for this object.
+  //!
+  //! This usually comes from `NT_GNU_BUILD_ID` on ELF objects.
+  uint8_t build_id[1];
+};
+
 }  // namespace crashpad
 
 #endif  // CRASHPAD_UTIL_MISC_PDB_STRUCTURES_H_