bug 603592 - make a file_id host binary that can spit out a Breakpad file id. r=mwu a=blocking-fennec
authorTed Mielczarek <ted.mielczarek@gmail.com>
Thu, 14 Oct 2010 20:38:19 -0400
changeset 56282 f2253edf5fc4879285a092f94f7b1a70a5c92b2f
parent 56281 d54d195f468fa0a16bbfc34948b9ad848ac69a15
child 56283 b799dd554e1259cb519ce2ee7048bf5e23587a01
push id16462
push userblassey@mozilla.com
push dateThu, 21 Oct 2010 00:44:42 +0000
treeherdermozilla-central@8fc42e3e185f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmwu, blocking-fennec
bugs603592
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 603592 - make a file_id host binary that can spit out a Breakpad file id. r=mwu a=blocking-fennec
toolkit/crashreporter/Makefile.in
toolkit/crashreporter/fileid/Makefile.in
toolkit/crashreporter/fileid/file_id.cc
toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc
toolkit/crashreporter/google-breakpad/src/common/linux/file_id_unittest.cc
--- a/toolkit/crashreporter/Makefile.in
+++ b/toolkit/crashreporter/Makefile.in
@@ -93,16 +93,20 @@ DIRS += \
   google-breakpad/src/common \
   google-breakpad/src/common/solaris \
   google-breakpad/src/client \
   google-breakpad/src/client/solaris/handler \
   google-breakpad/src/tools/solaris/dump_syms \
   $(NULL)
 endif
 
+ifeq ($(OS_TARGET),Android)
+DIRS += fileid
+endif
+
 DIRS += client
 
 LOCAL_INCLUDES = -I$(srcdir)/google-breakpad/src
 DEFINES += -DUNICODE -D_UNICODE
 
 EXPORTS = \
 	nsExceptionHandler.h \
 	$(NULL)
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/fileid/Makefile.in
@@ -0,0 +1,68 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla Breakpad integration
+#
+# The Initial Developer of the Original Code is
+# Ted Mielczarek <ted.mielczarek@gmail.com>
+# Portions created by the Initial Developer are Copyright (C) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+HOST_PROGRAM = file_id
+
+LOCAL_INCLUDES 	= \
+  -I$(srcdir)/../google-breakpad/src \
+  -I$(srcdir)/../google-breakpad/src/common/linux \
+  $(NULL)
+
+HOST_CPPSRCS = \
+  file_id.cc \
+  $(NULL)
+
+HOST_LIBS += \
+  $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/linux/$(LIB_PREFIX)host_breakpad_linux_common_s.$(LIB_SUFFIX) \
+  $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/$(LIB_PREFIX)host_breakpad_common_s.$(LIB_SUFFIX) \
+  $(NULL)
+
+# force C++ linking
+CPP_PROG_LINK = 1
+FORCE_USE_PIC = 1
+
+#XXX: bug 554854 causes us to be unable to run binaries on the build slaves
+# due to them having an older libstdc++
+HOST_LDFLAGS += -static
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/fileid/file_id.cc
@@ -0,0 +1,82 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Breakpad integration
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Ted Mielczarek <ted.mielczarek@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <string>
+
+#include <stdio.h>
+
+#include "common/linux/file_id.h"
+
+//TODO: move this somewhere common, this is copied from dump_symbols.cc
+// Format the Elf file identifier in IDENTIFIER as a UUID with the
+// dashes removed.
+std::string FormatIdentifier(unsigned char identifier[16]) {
+  char identifier_str[40];
+  google_breakpad::FileID::ConvertIdentifierToString(
+      identifier,
+      identifier_str,
+      sizeof(identifier_str));
+  std::string id_no_dash;
+  for (int i = 0; identifier_str[i] != '\0'; ++i)
+    if (identifier_str[i] != '-')
+      id_no_dash += identifier_str[i];
+  // Add an extra "0" by the end.  PDB files on Windows have an 'age'
+  // number appended to the end of the file identifier; this isn't
+  // really used or necessary on other platforms, but let's preserve
+  // the pattern.
+  id_no_dash += '0';
+  return id_no_dash;
+}
+
+
+int main(int argc, char** argv)
+{
+  if (argc != 2) {
+    fprintf(stderr, "usage: file_id <elf file>\n");
+    return 1;
+  }
+
+  unsigned char identifier[google_breakpad::kMDGUIDSize];
+  google_breakpad::FileID file_id(argv[1]);
+  if (!file_id.ElfFileIdentifier(identifier)) {
+    fprintf(stderr, "%s: unable to generate file identifier\n",
+            argv[1]);
+    return 1;
+  }
+  printf("%s\n", FormatIdentifier(identifier).c_str());
+  return 0;
+}
--- a/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc
@@ -55,62 +55,93 @@
 #include "common/linux/linux_syscall_support.h"
 
 namespace google_breakpad {
 
 FileID::FileID(const char* path) {
   strncpy(path_, path, sizeof(path_));
 }
 
-// These two functions are also used inside the crashed process, so be safe
+struct ElfClass32 {
+  typedef Elf32_Ehdr Ehdr;
+  typedef Elf32_Shdr Shdr;
+  static const int kClass = ELFCLASS32;
+};
+
+struct ElfClass64 {
+  typedef Elf64_Ehdr Ehdr;
+  typedef Elf64_Shdr Shdr;
+  static const int kClass = ELFCLASS64;
+};
+
+// These three functions are also used inside the crashed process, so be safe
 // and use the syscall/libc wrappers instead of direct syscalls or libc.
-  static bool FindElfTextSection(const void *elf_mapped_base,
-                                 const void **text_start,
-                                 int *text_size) {
-  assert(elf_mapped_base);
+template<typename ElfClass>
+static void FindElfClassTextSection(const char *elf_base,
+                                    const void **text_start,
+                                    int *text_size) {
+  typedef typename ElfClass::Ehdr Ehdr;
+  typedef typename ElfClass::Shdr Shdr;
+
+  assert(elf_base);
   assert(text_start);
   assert(text_size);
 
-  const char* elf_base =
-    static_cast<const char*>(elf_mapped_base);
-  const ElfW(Ehdr)* elf_header =
-    reinterpret_cast<const ElfW(Ehdr)*>(elf_base);
-  if (my_strncmp(elf_base, ELFMAG, SELFMAG) != 0)
-    return false;
-#if __ELF_NATIVE_CLASS == 32 || ELFSIZE == 32
-#define ELFCLASS ELFCLASS32
-#else
-#define ELFCLASS ELFCLASS64
-#endif
-  //TODO: support dumping 32-bit binaries from a 64-bit dump_syms?
-  if (elf_header->e_ident[EI_CLASS] != ELFCLASS)
-    return false;
-  *text_start = NULL;
-  *text_size = 0;
-  const ElfW(Shdr)* sections =
-    reinterpret_cast<const ElfW(Shdr)*>(elf_base + elf_header->e_shoff);
+  assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0);
+
   const char* text_section_name = ".text";
   int name_len = my_strlen(text_section_name);
-  const ElfW(Shdr)* string_section = sections + elf_header->e_shstrndx;
-  const ElfW(Shdr)* text_section = NULL;
+
+  const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
+  assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
+
+  const Shdr* sections =
+      reinterpret_cast<const Shdr*>(elf_base + elf_header->e_shoff);
+  const Shdr* string_section = sections + elf_header->e_shstrndx;
+
+  const Shdr* text_section = NULL;
   for (int i = 0; i < elf_header->e_shnum; ++i) {
     if (sections[i].sh_type == SHT_PROGBITS) {
       const char* section_name = (char*)(elf_base +
                                          string_section->sh_offset +
                                          sections[i].sh_name);
       if (!my_strncmp(section_name, text_section_name, name_len)) {
         text_section = &sections[i];
         break;
       }
     }
   }
   if (text_section != NULL && text_section->sh_size > 0) {
     *text_start = elf_base + text_section->sh_offset;
     *text_size = text_section->sh_size;
   }
+}
+
+static bool FindElfTextSection(const void *elf_mapped_base,
+                               const void **text_start,
+                               int *text_size) {
+  assert(elf_mapped_base);
+  assert(text_start);
+  assert(text_size);
+
+  const char* elf_base =
+    static_cast<const char*>(elf_mapped_base);
+  const ElfW(Ehdr)* elf_header =
+    reinterpret_cast<const ElfW(Ehdr)*>(elf_base);
+  if (my_strncmp(elf_base, ELFMAG, SELFMAG) != 0)
+    return false;
+
+  if (elf_header->e_ident[EI_CLASS] == ELFCLASS32) {
+    FindElfClassTextSection<ElfClass32>(elf_base, text_start, text_size);
+  } else if (elf_header->e_ident[EI_CLASS] == ELFCLASS64) {
+    FindElfClassTextSection<ElfClass64>(elf_base, text_start, text_size);
+  } else {
+    return false;
+  }
+
   return true;
 }
 
 // static
 bool FileID::ElfFileIdentifierFromMappedFile(void* base,
                                              uint8_t identifier[kMDGUIDSize])
 {
   const void* text_section = NULL;
--- a/toolkit/crashreporter/google-breakpad/src/common/linux/file_id_unittest.cc
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id_unittest.cc
@@ -1,9 +1,9 @@
-// Copyright (c) 2009, Google Inc.
+// Copyright (c) 2010, Google Inc.
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
 // met:
 //
 //     * Redistributions of source code must retain the above copyright
 // notice, this list of conditions and the following disclaimer.
@@ -24,24 +24,24 @@
 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // Unit tests for FileID
 
+#include <elf.h>
 #include <stdlib.h>
 
 #include "common/linux/file_id.h"
 #include "breakpad_googletest_includes.h"
 
 using namespace google_breakpad;
 
-
 namespace {
 typedef testing::Test FileIDTest;
 }
 
 TEST(FileIDTest, FileIDStrip) {
   // Calculate the File ID of our binary using
   // FileID::ElfFileIdentifier, then make a copy of our binary,
   // strip it, and ensure that we still get the same result.
@@ -69,8 +69,75 @@ TEST(FileIDTest, FileIDStrip) {
   char identifier_string2[37];
   FileID::ConvertIdentifierToString(identifier1, identifier_string1,
                                     37);
   FileID::ConvertIdentifierToString(identifier2, identifier_string2,
                                     37);
   EXPECT_STREQ(identifier_string1, identifier_string2);
   unlink(templ);
 }
+
+struct ElfClass32 {
+  typedef Elf32_Ehdr Ehdr;
+  typedef Elf32_Shdr Shdr;
+  static const int kClass = ELFCLASS32;
+};
+
+struct ElfClass64 {
+  typedef Elf64_Ehdr Ehdr;
+  typedef Elf64_Shdr Shdr;
+  static const int kClass = ELFCLASS64;
+};
+
+template<typename ElfClass>
+struct ElfishElf {
+  static const size_t kTextSectionSize = 128;
+  typedef typename ElfClass::Ehdr Ehdr;
+  typedef typename ElfClass::Shdr Shdr;
+
+  Ehdr elf_header;
+  Shdr text_header;
+  Shdr string_header;
+  char text_section[kTextSectionSize];
+  char string_section[8];
+
+  static void Populate(ElfishElf* elf) {
+    memset(elf, 0, sizeof(ElfishElf));
+    memcpy(elf, ELFMAG, SELFMAG);
+    elf->elf_header.e_ident[EI_CLASS] = ElfClass::kClass;
+    elf->elf_header.e_shoff = offsetof(ElfishElf, text_header);
+    elf->elf_header.e_shnum = 2;
+    elf->elf_header.e_shstrndx = 1;
+    elf->text_header.sh_name = 0;
+    elf->text_header.sh_type = SHT_PROGBITS;
+    elf->text_header.sh_offset = offsetof(ElfishElf, text_section);
+    elf->text_header.sh_size = kTextSectionSize;
+    for (size_t i = 0; i < kTextSectionSize; ++i) {
+      elf->text_section[i] = i * 3;
+    }
+    elf->string_header.sh_offset = offsetof(ElfishElf, string_section);
+    strcpy(elf->string_section, ".text");
+  }
+};
+
+TEST(FileIDTest, ElfClass) {
+  uint8_t identifier[sizeof(MDGUID)];
+  const char expected_identifier_string[] =
+      "80808080-8080-0000-0000-008080808080";
+  char identifier_string[sizeof(expected_identifier_string)];
+
+  ElfishElf<ElfClass32> elf32;
+  ElfishElf<ElfClass32>::Populate(&elf32);
+  EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(&elf32, identifier));
+  FileID::ConvertIdentifierToString(identifier, identifier_string,
+                                    sizeof(identifier_string));
+  EXPECT_STREQ(expected_identifier_string, identifier_string);
+
+  memset(identifier, 0, sizeof(identifier));
+  memset(identifier_string, 0, sizeof(identifier_string));
+
+  ElfishElf<ElfClass64> elf64;
+  ElfishElf<ElfClass64>::Populate(&elf64);
+  EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(&elf64, identifier));
+  FileID::ConvertIdentifierToString(identifier, identifier_string,
+                                    sizeof(identifier_string));
+  EXPECT_STREQ(expected_identifier_string, identifier_string);
+}