diff options
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/common')
217 files changed, 58933 insertions, 0 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/common/android/include/asm-mips/README.md b/toolkit/crashreporter/google-breakpad/src/common/android/include/asm-mips/README.md new file mode 100644 index 0000000000..b56ee60f13 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/android/include/asm-mips/README.md @@ -0,0 +1,9 @@ +# asm-mips + +The files in this directory are almost direct copies from Android NDK r12, with +the exception of changing the include guards to Breakpad ones. They are copied +from the MIPS asm/ directory, but are meant to be used as replacements for both +asm/ and machine/ includes since the files in each are largely duplicates. + +Some MIPS asm/ and all machine/ headers were removed in the move to unified NDK +headers, so Breakpad fails to compile on newer NDK versions without these files.
\ No newline at end of file diff --git a/toolkit/crashreporter/google-breakpad/src/common/android/include/asm-mips/asm.h b/toolkit/crashreporter/google-breakpad/src/common/android/include/asm-mips/asm.h new file mode 100644 index 0000000000..8f086e7566 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/android/include/asm-mips/asm.h @@ -0,0 +1,270 @@ +#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_ASM_H +#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_ASM_H + +#if defined(__has_include_next) && __has_include_next(<asm/asm.h>) +#include_next <asm/asm.h> +#else + +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + *** To edit the content of this header, modify the corresponding + *** source file (e.g. under external/kernel-headers/original/) then + *** run bionic/libc/kernel/tools/update_all.py + *** + *** Any manual change here will be lost the next time this script will + *** be run. You've been warned! + *** + **************************************************************************** + ****************************************************************************/ + +#include <asm/sgidefs.h> +#ifndef CAT +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#ifdef __STDC__ +#define __CAT(str1, str2) str1##str2 +#else +#define __CAT(str1, str2) str1 str2 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#endif +#define CAT(str1, str2) __CAT(str1, str2) +#endif +#ifdef __PIC__ +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define CPRESTORE(register) .cprestore register +#define CPADD(register) .cpadd register +#define CPLOAD(register) .cpload register +#else +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define CPRESTORE(register) +#define CPADD(register) +#define CPLOAD(register) +#endif +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define LEAF(symbol) .globl symbol; .align 2; .type symbol, @function; .ent symbol, 0; symbol: .frame sp, 0, ra +#define NESTED(symbol, framesize, rpc) .globl symbol; .align 2; .type symbol, @function; .ent symbol, 0; symbol: .frame sp, framesize, rpc +#define END(function) .end function; .size function, .-function +#define EXPORT(symbol) .globl symbol; symbol: +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define FEXPORT(symbol) .globl symbol; .type symbol, @function; symbol: +#define ABS(symbol,value) .globl symbol; symbol = value +#define PANIC(msg) .set push; .set reorder; PTR_LA a0, 8f; jal panic; 9: b 9b; .set pop; TEXT(msg) +#define PRINT(string) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define TEXT(msg) .pushsection .data; 8: .asciiz msg; .popsection; +#define TTABLE(string) .pushsection .text; .word 1f; .popsection .pushsection .data; 1: .asciiz string; .popsection +#define PREF(hint, addr) +#define PREFX(hint, addr) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#if _MIPS_ISA == _MIPS_ISA_MIPS1 +#define MOVN(rd, rs, rt) .set push; .set reorder; beqz rt, 9f; move rd, rs; .set pop; 9: +#define MOVZ(rd, rs, rt) .set push; .set reorder; bnez rt, 9f; move rd, rs; .set pop; 9: +#endif +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#if _MIPS_ISA == _MIPS_ISA_MIPS2 || _MIPS_ISA == _MIPS_ISA_MIPS3 +#define MOVN(rd, rs, rt) .set push; .set noreorder; bnezl rt, 9f; move rd, rs; .set pop; 9: +#define MOVZ(rd, rs, rt) .set push; .set noreorder; beqzl rt, 9f; move rd, rs; .set pop; 9: +#endif +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#if _MIPS_ISA == _MIPS_ISA_MIPS4 || _MIPS_ISA == _MIPS_ISA_MIPS5 || _MIPS_ISA == _MIPS_ISA_MIPS32 || _MIPS_ISA == _MIPS_ISA_MIPS64 +#define MOVN(rd, rs, rt) movn rd, rs, rt +#define MOVZ(rd, rs, rt) movz rd, rs, rt +#endif +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#if _MIPS_SIM == _MIPS_SIM_ABI32 +#define ALSZ 7 +#define ALMASK ~7 +#endif +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#if _MIPS_SIM == _MIPS_SIM_NABI32 || _MIPS_SIM == _MIPS_SIM_ABI64 +#define ALSZ 15 +#define ALMASK ~15 +#endif +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#ifdef __mips64 +#define SZREG 8 +#else +#define SZREG 4 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#endif +#if _MIPS_SIM == _MIPS_SIM_ABI32 +#define REG_S sw +#define REG_L lw +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define REG_SUBU subu +#define REG_ADDU addu +#endif +#if _MIPS_SIM == _MIPS_SIM_NABI32 || _MIPS_SIM == _MIPS_SIM_ABI64 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define REG_S sd +#define REG_L ld +#define REG_SUBU dsubu +#define REG_ADDU daddu +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#endif +#if _MIPS_SZINT == 32 +#define INT_ADD add +#define INT_ADDU addu +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define INT_ADDI addi +#define INT_ADDIU addiu +#define INT_SUB sub +#define INT_SUBU subu +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define INT_L lw +#define INT_S sw +#define INT_SLL sll +#define INT_SLLV sllv +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define INT_SRL srl +#define INT_SRLV srlv +#define INT_SRA sra +#define INT_SRAV srav +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#endif +#if _MIPS_SZINT == 64 +#define INT_ADD dadd +#define INT_ADDU daddu +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define INT_ADDI daddi +#define INT_ADDIU daddiu +#define INT_SUB dsub +#define INT_SUBU dsubu +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define INT_L ld +#define INT_S sd +#define INT_SLL dsll +#define INT_SLLV dsllv +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define INT_SRL dsrl +#define INT_SRLV dsrlv +#define INT_SRA dsra +#define INT_SRAV dsrav +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#endif +#if _MIPS_SZLONG == 32 +#define LONG_ADD add +#define LONG_ADDU addu +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define LONG_ADDI addi +#define LONG_ADDIU addiu +#define LONG_SUB sub +#define LONG_SUBU subu +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define LONG_L lw +#define LONG_S sw +#define LONG_SLL sll +#define LONG_SLLV sllv +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define LONG_SRL srl +#define LONG_SRLV srlv +#define LONG_SRA sra +#define LONG_SRAV srav +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define LONG .word +#define LONGSIZE 4 +#define LONGMASK 3 +#define LONGLOG 2 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#endif +#if _MIPS_SZLONG == 64 +#define LONG_ADD dadd +#define LONG_ADDU daddu +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define LONG_ADDI daddi +#define LONG_ADDIU daddiu +#define LONG_SUB dsub +#define LONG_SUBU dsubu +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define LONG_L ld +#define LONG_S sd +#define LONG_SLL dsll +#define LONG_SLLV dsllv +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define LONG_SRL dsrl +#define LONG_SRLV dsrlv +#define LONG_SRA dsra +#define LONG_SRAV dsrav +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define LONG .dword +#define LONGSIZE 8 +#define LONGMASK 7 +#define LONGLOG 3 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#endif +#if _MIPS_SZPTR == 32 +#define PTR_ADD add +#define PTR_ADDU addu +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define PTR_ADDI addi +#define PTR_ADDIU addiu +#define PTR_SUB sub +#define PTR_SUBU subu +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define PTR_L lw +#define PTR_S sw +#define PTR_LA la +#define PTR_LI li +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define PTR_SLL sll +#define PTR_SLLV sllv +#define PTR_SRL srl +#define PTR_SRLV srlv +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define PTR_SRA sra +#define PTR_SRAV srav +#define PTR_SCALESHIFT 2 +#define PTR .word +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define PTRSIZE 4 +#define PTRLOG 2 +#endif +#if _MIPS_SZPTR == 64 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define PTR_ADD dadd +#define PTR_ADDU daddu +#define PTR_ADDI daddi +#define PTR_ADDIU daddiu +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define PTR_SUB dsub +#define PTR_SUBU dsubu +#define PTR_L ld +#define PTR_S sd +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define PTR_LA dla +#define PTR_LI dli +#define PTR_SLL dsll +#define PTR_SLLV dsllv +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define PTR_SRL dsrl +#define PTR_SRLV dsrlv +#define PTR_SRA dsra +#define PTR_SRAV dsrav +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define PTR_SCALESHIFT 3 +#define PTR .dword +#define PTRSIZE 8 +#define PTRLOG 3 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#endif +#if _MIPS_SIM == _MIPS_SIM_ABI32 +#define MFC0 mfc0 +#define MTC0 mtc0 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#endif +#if _MIPS_SIM == _MIPS_SIM_NABI32 || _MIPS_SIM == _MIPS_SIM_ABI64 +#define MFC0 dmfc0 +#define MTC0 dmtc0 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#endif +#define SSNOP sll zero, zero, 1 +#define R10KCBARRIER(addr) +#endif // defined(__has_include_next) && __has_include_next(<asm/asm.h>) +#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_ASM_H +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ diff --git a/toolkit/crashreporter/google-breakpad/src/common/android/include/asm-mips/fpregdef.h b/toolkit/crashreporter/google-breakpad/src/common/android/include/asm-mips/fpregdef.h new file mode 100644 index 0000000000..a6eedc0e98 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/android/include/asm-mips/fpregdef.h @@ -0,0 +1,117 @@ +#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_FPREGDEF_H +#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_FPREGDEF_H + +#if defined(__has_include_next) && __has_include_next(<asm/fpregdef.h>) +#include_next <asm/fpregdef.h> +#else + +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + *** To edit the content of this header, modify the corresponding + *** source file (e.g. under external/kernel-headers/original/) then + *** run bionic/libc/kernel/tools/update_all.py + *** + *** Any manual change here will be lost the next time this script will + *** be run. You've been warned! + *** + **************************************************************************** + ****************************************************************************/ + +#include <asm/sgidefs.h> +#if _MIPS_SIM == _MIPS_SIM_ABI32 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define fv0 $f0 +#define fv0f $f1 +#define fv1 $f2 +#define fv1f $f3 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define fa0 $f12 +#define fa0f $f13 +#define fa1 $f14 +#define fa1f $f15 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define ft0 $f4 +#define ft0f $f5 +#define ft1 $f6 +#define ft1f $f7 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define ft2 $f8 +#define ft2f $f9 +#define ft3 $f10 +#define ft3f $f11 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define ft4 $f16 +#define ft4f $f17 +#define ft5 $f18 +#define ft5f $f19 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define fs0 $f20 +#define fs0f $f21 +#define fs1 $f22 +#define fs1f $f23 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define fs2 $f24 +#define fs2f $f25 +#define fs3 $f26 +#define fs3f $f27 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define fs4 $f28 +#define fs4f $f29 +#define fs5 $f30 +#define fs5f $f31 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define fcr31 $31 +#endif +#if _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32 +#define fv0 $f0 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define fv1 $f2 +#define fa0 $f12 +#define fa1 $f13 +#define fa2 $f14 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define fa3 $f15 +#define fa4 $f16 +#define fa5 $f17 +#define fa6 $f18 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define fa7 $f19 +#define ft0 $f4 +#define ft1 $f5 +#define ft2 $f6 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define ft3 $f7 +#define ft4 $f8 +#define ft5 $f9 +#define ft6 $f10 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define ft7 $f11 +#define ft8 $f20 +#define ft9 $f21 +#define ft10 $f22 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define ft11 $f23 +#define ft12 $f1 +#define ft13 $f3 +#define fs0 $f24 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define fs1 $f25 +#define fs2 $f26 +#define fs3 $f27 +#define fs4 $f28 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define fs5 $f29 +#define fs6 $f30 +#define fs7 $f31 +#define fcr31 $31 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#endif +#endif // defined(__has_include_next) && __has_include_next(<asm/fpregdef.h>) +#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_FPREGDEF_H diff --git a/toolkit/crashreporter/google-breakpad/src/common/android/include/asm-mips/regdef.h b/toolkit/crashreporter/google-breakpad/src/common/android/include/asm-mips/regdef.h new file mode 100644 index 0000000000..a7fd769059 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/android/include/asm-mips/regdef.h @@ -0,0 +1,125 @@ +#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_REGDEF_H +#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_REGDEF_H + +#if defined(__has_include_next) && __has_include_next(<asm/regdef.h>) +#include_next <asm/regdef.h> +#else + +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + *** To edit the content of this header, modify the corresponding + *** source file (e.g. under external/kernel-headers/original/) then + *** run bionic/libc/kernel/tools/update_all.py + *** + *** Any manual change here will be lost the next time this script will + *** be run. You've been warned! + *** + **************************************************************************** + ****************************************************************************/ + +#include <asm/sgidefs.h> +#if _MIPS_SIM == _MIPS_SIM_ABI32 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define zero $0 +#define AT $1 +#define v0 $2 +#define v1 $3 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define a0 $4 +#define a1 $5 +#define a2 $6 +#define a3 $7 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define t0 $8 +#define t1 $9 +#define t2 $10 +#define t3 $11 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define t4 $12 +#define t5 $13 +#define t6 $14 +#define t7 $15 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define s0 $16 +#define s1 $17 +#define s2 $18 +#define s3 $19 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define s4 $20 +#define s5 $21 +#define s6 $22 +#define s7 $23 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define t8 $24 +#define t9 $25 +#define jp $25 +#define k0 $26 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define k1 $27 +#define gp $28 +#define sp $29 +#define fp $30 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define s8 $30 +#define ra $31 +#endif +#if _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define zero $0 +#define AT $at +#define v0 $2 +#define v1 $3 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define a0 $4 +#define a1 $5 +#define a2 $6 +#define a3 $7 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define a4 $8 +#define ta0 $8 +#define a5 $9 +#define ta1 $9 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define a6 $10 +#define ta2 $10 +#define a7 $11 +#define ta3 $11 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define t0 $12 +#define t1 $13 +#define t2 $14 +#define t3 $15 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define s0 $16 +#define s1 $17 +#define s2 $18 +#define s3 $19 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define s4 $20 +#define s5 $21 +#define s6 $22 +#define s7 $23 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define t8 $24 +#define t9 $25 +#define jp $25 +#define k0 $26 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define k1 $27 +#define gp $28 +#define sp $29 +#define fp $30 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define s8 $30 +#define ra $31 +#endif +#endif // defined(__has_include_next) && __has_include_next(<asm/regdef.h>) +#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_REGDEF_H +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ diff --git a/toolkit/crashreporter/google-breakpad/src/common/android/include/elf.h b/toolkit/crashreporter/google-breakpad/src/common/android/include/elf.h new file mode 100644 index 0000000000..e6f0c672f9 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/android/include/elf.h @@ -0,0 +1,157 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ELF_H +#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ELF_H + +#include <stdint.h> +#include <libgen.h> + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// The Android <elf.h> provides BSD-based definitions for the ElfXX_Nhdr +// types +// always source-compatible with the GLibc/kernel ones. To overcome this +// issue without modifying a lot of code in Breakpad, use an ugly macro +// renaming trick with #include_next + +// Avoid conflict with BSD-based definition of ElfXX_Nhdr. +// Unfortunately, their field member names do not use a 'n_' prefix. +#define Elf32_Nhdr __bsd_Elf32_Nhdr +#define Elf64_Nhdr __bsd_Elf64_Nhdr + +// In case they are defined by the NDK version +#define Elf32_auxv_t __bionic_Elf32_auxv_t +#define Elf64_auxv_t __bionic_Elf64_auxv_t + +#define Elf32_Dyn __bionic_Elf32_Dyn +#define Elf64_Dyn __bionic_Elf64_Dyn + +#include_next <elf.h> + +#undef Elf32_Nhdr +#undef Elf64_Nhdr + +typedef struct { + Elf32_Word n_namesz; + Elf32_Word n_descsz; + Elf32_Word n_type; +} Elf32_Nhdr; + +typedef struct { + Elf64_Word n_namesz; + Elf64_Word n_descsz; + Elf64_Word n_type; +} Elf64_Nhdr; + +#undef Elf32_auxv_t +#undef Elf64_auxv_t + +typedef struct { + uint32_t a_type; + union { + uint32_t a_val; + } a_un; +} Elf32_auxv_t; + +typedef struct { + uint64_t a_type; + union { + uint64_t a_val; + } a_un; +} Elf64_auxv_t; + +#undef Elf32_Dyn +#undef Elf64_Dyn + +typedef struct { + Elf32_Sword d_tag; + union { + Elf32_Word d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; + +typedef struct { + Elf64_Sxword d_tag; + union { + Elf64_Xword d_val; + Elf64_Addr d_ptr; + } d_un; +} Elf64_Dyn; + + +// The Android headers don't always define this constant. +#ifndef EM_X86_64 +#define EM_X86_64 62 +#endif + +#ifndef EM_PPC64 +#define EM_PPC64 21 +#endif + +#ifndef EM_S390 +#define EM_S390 22 +#endif + +#if !defined(AT_SYSINFO_EHDR) +#define AT_SYSINFO_EHDR 33 +#endif + +#if !defined(NT_PRSTATUS) +#define NT_PRSTATUS 1 +#endif + +#if !defined(NT_PRPSINFO) +#define NT_PRPSINFO 3 +#endif + +#if !defined(NT_AUXV) +#define NT_AUXV 6 +#endif + +#if !defined(NT_PRXFPREG) +#define NT_PRXFPREG 0x46e62b7f +#endif + +#if !defined(NT_FPREGSET) +#define NT_FPREGSET 2 +#endif + +#if !defined(SHT_MIPS_DWARF) +#define SHT_MIPS_DWARF 0x7000001e +#endif + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ELF_H diff --git a/toolkit/crashreporter/google-breakpad/src/common/android/include/link.h b/toolkit/crashreporter/google-breakpad/src/common/android/include/link.h new file mode 100644 index 0000000000..4324629df7 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/android/include/link.h @@ -0,0 +1,77 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef GOOGLE_BREAKPAD_ANDROID_INCLUDE_LINK_H +#define GOOGLE_BREAKPAD_ANDROID_INCLUDE_LINK_H + +/* Android doesn't provide all the data-structures required in its <link.h>. + Provide custom version here. */ +#include_next <link.h> + +#include <android/api-level.h> + +// TODO(rmcilroy): Remove this file once the NDK API level is updated to at +// least 21 for all architectures. https://crbug.com/358831 + +// These structures are only present in traditional headers at API level 21 and +// above. Unified headers define these structures regardless of the chosen API +// level. __ANDROID_API_N__ is a proxy for determining whether unified headers +// are in use. It’s only defined by unified headers. +#if __ANDROID_API__ < 21 && !defined(__ANDROID_API_N__) + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +struct r_debug { + int r_version; + struct link_map* r_map; + ElfW(Addr) r_brk; + enum { + RT_CONSISTENT, + RT_ADD, + RT_DELETE } r_state; + ElfW(Addr) r_ldbase; +}; + +struct link_map { + ElfW(Addr) l_addr; + char* l_name; + ElfW(Dyn)* l_ld; + struct link_map* l_next; + struct link_map* l_prev; +}; + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // __ANDROID_API__ < 21 && !defined(__ANDROID_API_N__) + +#endif /* GOOGLE_BREAKPAD_ANDROID_INCLUDE_LINK_H */ diff --git a/toolkit/crashreporter/google-breakpad/src/common/android/include/stab.h b/toolkit/crashreporter/google-breakpad/src/common/android/include/stab.h new file mode 100644 index 0000000000..cd92902151 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/android/include/stab.h @@ -0,0 +1,100 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_STAB_H +#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_STAB_H + +#include <sys/cdefs.h> + +#ifdef __BIONIC_HAVE_STAB_H +#include <stab.h> +#else + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#define _STAB_CODE_LIST \ + _STAB_CODE_DEF(UNDF,0x00) \ + _STAB_CODE_DEF(GSYM,0x20) \ + _STAB_CODE_DEF(FNAME,0x22) \ + _STAB_CODE_DEF(FUN,0x24) \ + _STAB_CODE_DEF(STSYM,0x26) \ + _STAB_CODE_DEF(LCSYM,0x28) \ + _STAB_CODE_DEF(MAIN,0x2a) \ + _STAB_CODE_DEF(PC,0x30) \ + _STAB_CODE_DEF(NSYMS,0x32) \ + _STAB_CODE_DEF(NOMAP,0x34) \ + _STAB_CODE_DEF(OBJ,0x38) \ + _STAB_CODE_DEF(OPT,0x3c) \ + _STAB_CODE_DEF(RSYM,0x40) \ + _STAB_CODE_DEF(M2C,0x42) \ + _STAB_CODE_DEF(SLINE,0x44) \ + _STAB_CODE_DEF(DSLINE,0x46) \ + _STAB_CODE_DEF(BSLINE,0x48) \ + _STAB_CODE_DEF(BROWS,0x48) \ + _STAB_CODE_DEF(DEFD,0x4a) \ + _STAB_CODE_DEF(EHDECL,0x50) \ + _STAB_CODE_DEF(MOD2,0x50) \ + _STAB_CODE_DEF(CATCH,0x54) \ + _STAB_CODE_DEF(SSYM,0x60) \ + _STAB_CODE_DEF(SO,0x64) \ + _STAB_CODE_DEF(LSYM,0x80) \ + _STAB_CODE_DEF(BINCL,0x82) \ + _STAB_CODE_DEF(SOL,0x84) \ + _STAB_CODE_DEF(PSYM,0xa0) \ + _STAB_CODE_DEF(EINCL,0xa2) \ + _STAB_CODE_DEF(ENTRY,0xa4) \ + _STAB_CODE_DEF(LBRAC,0xc0) \ + _STAB_CODE_DEF(EXCL,0xc2) \ + _STAB_CODE_DEF(SCOPE,0xc4) \ + _STAB_CODE_DEF(RBRAC,0xe0) \ + _STAB_CODE_DEF(BCOMM,0xe2) \ + _STAB_CODE_DEF(ECOMM,0xe4) \ + _STAB_CODE_DEF(ECOML,0xe8) \ + _STAB_CODE_DEF(NBTEXT,0xf0) \ + _STAB_CODE_DEF(NBDATA,0xf2) \ + _STAB_CODE_DEF(NBBSS,0xf4) \ + _STAB_CODE_DEF(NBSTS,0xf6) \ + _STAB_CODE_DEF(NBLCS,0xf8) \ + _STAB_CODE_DEF(LENG,0xfe) + +enum __stab_debug_code { +#define _STAB_CODE_DEF(x,y) N_##x = y, +_STAB_CODE_LIST +#undef _STAB_CODE_DEF +}; + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // __BIONIC_HAVE_STAB_H + +#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_STAB_H diff --git a/toolkit/crashreporter/google-breakpad/src/common/android/include/sys/procfs.h b/toolkit/crashreporter/google-breakpad/src/common/android/include/sys/procfs.h new file mode 100644 index 0000000000..185124364e --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/android/include/sys/procfs.h @@ -0,0 +1,124 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_SYS_PROCFS_H +#define GOOGLE_BREAKPAD_COMMON_ANDROID_SYS_PROCFS_H + +#ifdef __BIONIC_HAVE_SYS_PROCFS_H + +#include_next <sys/procfs.h> + +#else + +#include <asm/ptrace.h> +#include <sys/cdefs.h> +#if defined (__mips__) +#include <sys/types.h> +#endif +#include <sys/user.h> +#include <unistd.h> + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#if defined(__x86_64__) || defined(__aarch64__) +typedef unsigned long long elf_greg_t; +#else +typedef unsigned long elf_greg_t; +#endif + +#ifdef __arm__ +#define ELF_NGREG (sizeof(struct user_regs) / sizeof(elf_greg_t)) +#elif defined(__aarch64__) +#define ELF_NGREG (sizeof(struct user_pt_regs) / sizeof(elf_greg_t)) +#elif defined(__mips__) +#define ELF_NGREG 45 +#else +#define ELF_NGREG (sizeof(struct user_regs_struct) / sizeof(elf_greg_t)) +#endif + +typedef elf_greg_t elf_gregset_t[ELF_NGREG]; + +struct elf_siginfo { + int si_signo; + int si_code; + int si_errno; +}; + +struct elf_prstatus { + struct elf_siginfo pr_info; + short pr_cursig; + unsigned long pr_sigpend; + unsigned long pr_sighold; + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pd_sid; + struct timeval pr_utime; + struct timeval pr_stime; + struct timeval pr_cutime; + struct timeval pr_cstime; + elf_gregset_t pr_reg; + int pr_fpvalid; +}; + +#define ELF_PRARGSZ 80 + +struct elf_prpsinfo { + char pr_state; + char pr_sname; + char pr_zomb; + char pr_nice; + unsigned long pr_flags; +#ifdef __x86_64__ + unsigned int pr_uid; + unsigned int pr_gid; +#elif defined(__mips__) + __kernel_uid_t pr_uid; + __kernel_gid_t pr_gid; +#else + unsigned short pr_uid; + unsigned short pr_gid; +#endif + int pr_pid; + int pr_ppid; + int pr_pgrp; + int pr_sid; + char pr_fname[16]; + char pr_psargs[ELF_PRARGSZ]; +}; + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // __BIONIC_HAVE_SYS_PROCFS_H + +#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_SYS_PROCFS_H diff --git a/toolkit/crashreporter/google-breakpad/src/common/android/include/sys/user.h b/toolkit/crashreporter/google-breakpad/src/common/android/include/sys/user.h new file mode 100644 index 0000000000..9c27ef0225 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/android/include/sys/user.h @@ -0,0 +1,69 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_USER_H +#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_USER_H + +// The purpose of this file is to glue the mismatching headers (Android NDK vs +// glibc) and therefore avoid doing otherwise awkward #ifdefs in the code. +// The following quirks are currently handled by this file: +// - i386: Use the Android NDK but alias user_fxsr_struct > user_fpxregs_struct. + +// TODO(primiano): remove these changes after Chromium has stably rolled to +// an NDK with the appropriate fixes. https://crbug.com/358831 + +// With traditional headers, <sys/user.h> forgot to do this. Unified headers get +// it right. +#include <sys/types.h> + +#include_next <sys/user.h> + +#include <android/api-level.h> + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#if defined(__i386__) +#if __ANDROID_API__ < 21 && !defined(__ANDROID_API_N__) + +// user_fpxregs_struct was called user_fxsr_struct in traditional headers before +// API level 21. Unified headers call it user_fpxregs_struct regardless of the +// chosen API level. __ANDROID_API_N__ is a proxy for determining whether +// unified headers are in use. It’s only defined by unified headers. +typedef struct user_fxsr_struct user_fpxregs_struct; + +#endif // __ANDROID_API__ < 21 && !defined(__ANDROID_API_N__) +#endif // defined(__i386__) + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_USER_H diff --git a/toolkit/crashreporter/google-breakpad/src/common/android/testing/include/wchar.h b/toolkit/crashreporter/google-breakpad/src/common/android/testing/include/wchar.h new file mode 100644 index 0000000000..85373fd2a0 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/android/testing/include/wchar.h @@ -0,0 +1,76 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Android doesn't provide wcscasecmp(), so provide an alternative here. +// +// Note that this header is not needed when Breakpad is compiled against +// a recent version of Googletest. It shall be considered for removal once +// src/testing/ is updated to an appropriate revision in the future. + +#ifndef GOOGLEBREAKPAD_COMMON_ANDROID_INCLUDE_WCHAR_H +#define GOOGLEBREAKPAD_COMMON_ANDROID_INCLUDE_WCHAR_H + +#include_next <wchar.h> + +#if !defined(__aarch64__) && !defined(__x86_64__) && \ + !(defined(__mips__) && _MIPS_SIM == _ABI64) + +// This needs to be in an extern "C" namespace, or Googletest will not +// compile against it. +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +static wchar_t inline wcstolower(wchar_t ch) { + if (ch >= L'a' && ch <= L'A') + ch -= L'a' - L'A'; + return ch; +} + +static int inline wcscasecmp(const wchar_t* s1, const wchar_t* s2) { + for (;;) { + wchar_t c1 = wcstolower(*s1); + wchar_t c2 = wcstolower(*s2); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + if (c1 == L'0') + return 0; + s1++; + s2++; + } +} + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus +#endif + +#endif // GOOGLEBREAKPAD_COMMON_ANDROID_INCLUDE_WCHAR_H diff --git a/toolkit/crashreporter/google-breakpad/src/common/android/testing/mkdtemp.h b/toolkit/crashreporter/google-breakpad/src/common/android/testing/mkdtemp.h new file mode 100644 index 0000000000..b86e2cd783 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/android/testing/mkdtemp.h @@ -0,0 +1,110 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// mkdtemp() wasn't declared in <stdlib.h> until NDK r9b due to a simple +// packaging bug (the function has always been implemented in all versions +// of the C library). This header is provided to build Breakpad with earlier +// NDK revisions (e.g. the one used by Chromium). It may be removed in the +// future once all major projects upgrade to use a more recent NDK. +// +// The reason this is inlined here is to avoid linking a new object file +// into each unit test program (i.e. keep build files simple). + +#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_MKDTEMP_H +#define GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_MKDTEMP_H + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> + +// Using a macro renaming trick here is necessary when building against +// NDK r9b. Otherwise the compiler will complain that calls to mkdtemp() +// are ambiguous. +#define mkdtemp breakpad_mkdtemp + +namespace { + +char* breakpad_mkdtemp(char* path) { + if (path == NULL) { + errno = EINVAL; + return NULL; + } + + // 'path' must be terminated with six 'X' + const char kSuffix[] = "XXXXXX"; + const size_t kSuffixLen = strlen(kSuffix); + char* path_end = path + strlen(path); + + if (static_cast<size_t>(path_end - path) < kSuffixLen || + memcmp(path_end - kSuffixLen, kSuffix, kSuffixLen) != 0) { + errno = EINVAL; + return NULL; + } + + // If 'path' contains a directory separator, check that it exists to + // avoid looping later. + char* sep = strrchr(path, '/'); + if (sep != NULL) { + struct stat st; + int ret; + *sep = '\0'; // temporarily zero-terminate the dirname. + ret = stat(path, &st); + *sep = '/'; // restore full path. + if (ret < 0) + return NULL; + if (!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + return NULL; + } + } + + // Loop. On each iteration, replace the XXXXXX suffix with a random + // number. + int tries; + for (tries = 128; tries > 0; tries--) { + int random = rand() % 1000000; + + snprintf(path_end - kSuffixLen, kSuffixLen + 1, "%0d", random); + if (mkdir(path, 0700) == 0) + return path; // Success + + if (errno != EEXIST) + return NULL; + } + + assert(errno == EEXIST); + return NULL; +} + +} // namespace + +#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_MKDTEMP_H diff --git a/toolkit/crashreporter/google-breakpad/src/common/android/testing/pthread_fixes.h b/toolkit/crashreporter/google-breakpad/src/common/android/testing/pthread_fixes.h new file mode 100644 index 0000000000..20c1208480 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/android/testing/pthread_fixes.h @@ -0,0 +1,94 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// This contains Pthread-related functions not provided by the Android NDK +// but required by the Breakpad unit test. The functions are inlined here +// in a C++ anonymous namespace in order to keep the build files simples. + +#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_PTHREAD_FIXES_H +#define GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_PTHREAD_FIXES_H + +#include <pthread.h> + +namespace { + +// Android doesn't provide pthread_barrier_t for now. +#ifndef PTHREAD_BARRIER_SERIAL_THREAD + +// Anything except 0 will do here. +#define PTHREAD_BARRIER_SERIAL_THREAD 0x12345 + +typedef struct { + pthread_mutex_t mutex; + pthread_cond_t cond; + unsigned count; +} pthread_barrier_t; + +int pthread_barrier_init(pthread_barrier_t* barrier, + const void* /* barrier_attr */, + unsigned count) { + barrier->count = count; + pthread_mutex_init(&barrier->mutex, NULL); + pthread_cond_init(&barrier->cond, NULL); + return 0; +} + +int pthread_barrier_wait(pthread_barrier_t* barrier) { + // Lock the mutex + pthread_mutex_lock(&barrier->mutex); + // Decrement the count. If this is the first thread to reach 0, wake up + // waiters, unlock the mutex, then return PTHREAD_BARRIER_SERIAL_THREAD. + if (--barrier->count == 0) { + // First thread to reach the barrier + pthread_cond_broadcast(&barrier->cond); + pthread_mutex_unlock(&barrier->mutex); + return PTHREAD_BARRIER_SERIAL_THREAD; + } + // Otherwise, wait for other threads until the count reaches 0, then + // return 0 to indicate this is not the first thread. + do { + pthread_cond_wait(&barrier->cond, &barrier->mutex); + } while (barrier->count > 0); + + pthread_mutex_unlock(&barrier->mutex); + return 0; +} + +int pthread_barrier_destroy(pthread_barrier_t *barrier) { + barrier->count = 0; + pthread_cond_destroy(&barrier->cond); + pthread_mutex_destroy(&barrier->mutex); + return 0; +} + +#endif // defined(PTHREAD_BARRIER_SERIAL_THREAD) + +} // namespace + +#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_PTHREAD_FIXES_H diff --git a/toolkit/crashreporter/google-breakpad/src/common/arm_ex_reader.cc b/toolkit/crashreporter/google-breakpad/src/common/arm_ex_reader.cc new file mode 100644 index 0000000000..2d1ed983dc --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/arm_ex_reader.cc @@ -0,0 +1,487 @@ + +/* libunwind - a platform-independent unwind library + Copyright 2011 Linaro Limited + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + + +// Derived from libunwind, with extensive modifications. + + +#include "common/arm_ex_reader.h" + +#include <assert.h> +#include <stdio.h> + +// This file, in conjunction with arm_ex_to_module.cc, translates +// EXIDX unwind information into the same format that Breakpad uses +// for CFI information. Hence Breakpad's CFI unwinding abilities +// also become usable for EXIDX. +// +// See: "Exception Handling ABI for the ARM Architecture", ARM IHI 0038A +// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf + +// EXIDX data is presented in two parts: +// +// * an index table. This contains two words per routine, +// the first of which identifies the routine, and the second +// of which is a reference to the unwind bytecode. If the +// bytecode is very compact -- 3 bytes or less -- it can be +// stored directly in the second word. +// +// * an area containing the unwind bytecodes. + +// General flow is: ExceptionTableInfo::Start iterates over all +// of the index table entries (pairs). For each entry, it: +// +// * calls ExceptionTableInfo::ExtabEntryExtract to copy the bytecode +// out into an intermediate buffer. + +// * uses ExceptionTableInfo::ExtabEntryDecode to parse the intermediate +// buffer. Each bytecode instruction is bundled into a +// arm_ex_to_module::extab_data structure, and handed to .. +// +// * .. ARMExToModule::ImproveStackFrame, which in turn hands it to +// ARMExToModule::TranslateCmd, and that generates the pseudo-CFI +// records that Breakpad stores. + +#define ARM_EXIDX_CANT_UNWIND 0x00000001 +#define ARM_EXIDX_COMPACT 0x80000000 +#define ARM_EXTBL_OP_FINISH 0xb0 +#define ARM_EXIDX_TABLE_LIMIT (255*4) + +namespace arm_ex_reader { + +using arm_ex_to_module::ARM_EXIDX_CMD_FINISH; +using arm_ex_to_module::ARM_EXIDX_CMD_SUB_FROM_VSP; +using arm_ex_to_module::ARM_EXIDX_CMD_ADD_TO_VSP; +using arm_ex_to_module::ARM_EXIDX_CMD_REG_POP; +using arm_ex_to_module::ARM_EXIDX_CMD_REG_TO_SP; +using arm_ex_to_module::ARM_EXIDX_CMD_VFP_POP; +using arm_ex_to_module::ARM_EXIDX_CMD_WREG_POP; +using arm_ex_to_module::ARM_EXIDX_CMD_WCGR_POP; +using arm_ex_to_module::ARM_EXIDX_CMD_RESERVED; +using arm_ex_to_module::ARM_EXIDX_CMD_REFUSED; +using arm_ex_to_module::exidx_entry; +using arm_ex_to_module::ARM_EXIDX_VFP_SHIFT_16; +using arm_ex_to_module::ARM_EXIDX_VFP_FSTMD; +using google_breakpad::MemoryRange; + + +static void* Prel31ToAddr(const void* addr) { + uint32_t offset32 = *reinterpret_cast<const uint32_t*>(addr); + // sign extend offset32[30:0] to 64 bits -- copy bit 30 to positions + // 63:31 inclusive. + uint64_t offset64 = offset32; + if (offset64 & (1ULL << 30)) + offset64 |= 0xFFFFFFFF80000000ULL; + else + offset64 &= 0x000000007FFFFFFFULL; + return ((char*)addr) + (uintptr_t)offset64; +} + + +// Extract unwind bytecode for the function denoted by |entry| into |buf|, +// and return the number of bytes of |buf| written, along with a code +// indicating the outcome. + +ExceptionTableInfo::ExExtractResult ExceptionTableInfo::ExtabEntryExtract( + const struct exidx_entry* entry, + uint8_t* buf, size_t buf_size, + size_t* buf_used) { + MemoryRange mr_out(buf, buf_size); + + *buf_used = 0; + +# define PUT_BUF_U8(_byte) \ + do { if (!mr_out.Covers(*buf_used, 1)) return ExOutBufOverflow; \ + buf[(*buf_used)++] = (_byte); } while (0) + +# define GET_EX_U32(_lval, _addr, _sec_mr) \ + do { if (!(_sec_mr).Covers(reinterpret_cast<const uint8_t*>(_addr) \ + - (_sec_mr).data(), 4)) \ + return ExInBufOverflow; \ + (_lval) = *(reinterpret_cast<const uint32_t*>(_addr)); } while (0) + +# define GET_EXIDX_U32(_lval, _addr) \ + GET_EX_U32(_lval, _addr, mr_exidx_) +# define GET_EXTAB_U32(_lval, _addr) \ + GET_EX_U32(_lval, _addr, mr_extab_) + + uint32_t data; + GET_EXIDX_U32(data, &entry->data); + + // A function can be marked CANT_UNWIND if (eg) it is known to be + // at the bottom of the stack. + if (data == ARM_EXIDX_CANT_UNWIND) + return ExCantUnwind; + + uint32_t pers; // personality number + uint32_t extra; // number of extra data words required + uint32_t extra_allowed; // number of extra data words allowed + uint32_t* extbl_data; // the handler entry, if not inlined + + if (data & ARM_EXIDX_COMPACT) { + // The handler table entry has been inlined into the index table entry. + // In this case it can only be an ARM-defined compact model, since + // bit 31 is 1. Only personalities 0, 1 and 2 are defined for the + // ARM compact model, but 1 and 2 are "Long format" and may require + // extra data words. Hence the allowable personalities here are: + // personality 0, in which case 'extra' has no meaning + // personality 1, with zero extra words + // personality 2, with zero extra words + extbl_data = NULL; + pers = (data >> 24) & 0x0F; + extra = (data >> 16) & 0xFF; + extra_allowed = 0; + } + else { + // The index table entry is a pointer to the handler entry. Note + // that Prel31ToAddr will read the given address, but we already + // range-checked above. + extbl_data = reinterpret_cast<uint32_t*>(Prel31ToAddr(&entry->data)); + GET_EXTAB_U32(data, extbl_data); + if (!(data & ARM_EXIDX_COMPACT)) { + // This denotes a "generic model" handler. That will involve + // executing arbitary machine code, which is something we + // can't represent here; hence reject it. + return ExCantRepresent; + } + // So we have a compact model representation. Again, 3 possible + // personalities, but this time up to 255 allowable extra words. + pers = (data >> 24) & 0x0F; + extra = (data >> 16) & 0xFF; + extra_allowed = 255; + extbl_data++; + } + + // Now look at the the handler table entry. The first word is + // |data| and subsequent words start at |*extbl_data|. The number + // of extra words to use is |extra|, provided that the personality + // allows extra words. Even if it does, none may be available -- + // extra_allowed is the maximum number of extra words allowed. */ + if (pers == 0) { + // "Su16" in the documentation -- 3 unwinding insn bytes + // |extra| has no meaning here; instead that byte is an unwind-info byte + PUT_BUF_U8(data >> 16); + PUT_BUF_U8(data >> 8); + PUT_BUF_U8(data); + } + else if ((pers == 1 || pers == 2) && extra <= extra_allowed) { + // "Lu16" or "Lu32" respectively -- 2 unwinding insn bytes, + // and up to 255 extra words. + PUT_BUF_U8(data >> 8); + PUT_BUF_U8(data); + for (uint32_t j = 0; j < extra; j++) { + GET_EXTAB_U32(data, extbl_data); + extbl_data++; + PUT_BUF_U8(data >> 24); + PUT_BUF_U8(data >> 16); + PUT_BUF_U8(data >> 8); + PUT_BUF_U8(data >> 0); + } + } + else { + // The entry is invalid. + return ExInvalid; + } + + // Make sure the entry is terminated with "FINISH" + if (*buf_used > 0 && buf[(*buf_used) - 1] != ARM_EXTBL_OP_FINISH) + PUT_BUF_U8(ARM_EXTBL_OP_FINISH); + + return ExSuccess; + +# undef GET_EXTAB_U32 +# undef GET_EXIDX_U32 +# undef GET_U32 +# undef PUT_BUF_U8 +} + + +// Take the unwind information extracted by ExtabEntryExtract +// and parse it into frame-unwind instructions. These are as +// specified in "Table 4, ARM-defined frame-unwinding instructions" +// in the specification document detailed in comments at the top +// of this file. +// +// This reads from |buf[0, +data_size)|. It checks for overruns of +// the input buffer and returns a negative value if that happens, or +// for any other failure cases. It returns zero in case of success. +int ExceptionTableInfo::ExtabEntryDecode(const uint8_t* buf, size_t buf_size) { + if (buf == NULL || buf_size == 0) + return -1; + + MemoryRange mr_in(buf, buf_size); + const uint8_t* buf_initially = buf; + +# define GET_BUF_U8(_lval) \ + do { if (!mr_in.Covers(buf - buf_initially, 1)) return -1; \ + (_lval) = *(buf++); } while (0) + + const uint8_t* end = buf + buf_size; + + while (buf < end) { + struct arm_ex_to_module::extab_data edata; + memset(&edata, 0, sizeof(edata)); + + uint8_t op; + GET_BUF_U8(op); + if ((op & 0xc0) == 0x00) { + // vsp = vsp + (xxxxxx << 2) + 4 + edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP; + edata.data = (((int)op & 0x3f) << 2) + 4; + } else if ((op & 0xc0) == 0x40) { + // vsp = vsp - (xxxxxx << 2) - 4 + edata.cmd = ARM_EXIDX_CMD_SUB_FROM_VSP; + edata.data = (((int)op & 0x3f) << 2) + 4; + } else if ((op & 0xf0) == 0x80) { + uint8_t op2; + GET_BUF_U8(op2); + if (op == 0x80 && op2 == 0x00) { + // Refuse to unwind + edata.cmd = ARM_EXIDX_CMD_REFUSED; + } else { + // Pop up to 12 integer registers under masks {r15-r12},{r11-r4} + edata.cmd = ARM_EXIDX_CMD_REG_POP; + edata.data = ((op & 0xf) << 8) | op2; + edata.data = edata.data << 4; + } + } else if ((op & 0xf0) == 0x90) { + if (op == 0x9d || op == 0x9f) { + // 9d: Reserved as prefix for ARM register to register moves + // 9f: Reserved as perfix for Intel Wireless MMX reg to reg moves + edata.cmd = ARM_EXIDX_CMD_RESERVED; + } else { + // Set vsp = r[nnnn] + edata.cmd = ARM_EXIDX_CMD_REG_TO_SP; + edata.data = op & 0x0f; + } + } else if ((op & 0xf0) == 0xa0) { + // Pop r4 to r[4+nnn], or + // Pop r4 to r[4+nnn] and r14 or + unsigned end = (op & 0x07); + edata.data = (1 << (end + 1)) - 1; + edata.data = edata.data << 4; + if (op & 0x08) edata.data |= 1 << 14; + edata.cmd = ARM_EXIDX_CMD_REG_POP; + } else if (op == ARM_EXTBL_OP_FINISH) { + // Finish + edata.cmd = ARM_EXIDX_CMD_FINISH; + buf = end; + } else if (op == 0xb1) { + uint8_t op2; + GET_BUF_U8(op2); + if (op2 == 0 || (op2 & 0xf0)) { + // Spare + edata.cmd = ARM_EXIDX_CMD_RESERVED; + } else { + // Pop integer registers under mask {r3,r2,r1,r0} + edata.cmd = ARM_EXIDX_CMD_REG_POP; + edata.data = op2 & 0x0f; + } + } else if (op == 0xb2) { + // vsp = vsp + 0x204 + (uleb128 << 2) + uint64_t offset = 0; + uint8_t byte, shift = 0; + do { + GET_BUF_U8(byte); + offset |= (byte & 0x7f) << shift; + shift += 7; + } while ((byte & 0x80) && buf < end); + edata.data = offset * 4 + 0x204; + edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP; + } else if (op == 0xb3 || op == 0xc8 || op == 0xc9) { + // b3: Pop VFP regs D[ssss] to D[ssss+cccc], FSTMFDX-ishly + // c8: Pop VFP regs D[16+ssss] to D[16+ssss+cccc], FSTMFDD-ishly + // c9: Pop VFP regs D[ssss] to D[ssss+cccc], FSTMFDD-ishly + edata.cmd = ARM_EXIDX_CMD_VFP_POP; + GET_BUF_U8(edata.data); + if (op == 0xc8) edata.data |= ARM_EXIDX_VFP_SHIFT_16; + if (op != 0xb3) edata.data |= ARM_EXIDX_VFP_FSTMD; + } else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0) { + // b8: Pop VFP regs D[8] to D[8+nnn], FSTMFDX-ishly + // d0: Pop VFP regs D[8] to D[8+nnn], FSTMFDD-ishly + edata.cmd = ARM_EXIDX_CMD_VFP_POP; + edata.data = 0x80 | (op & 0x07); + if ((op & 0xf8) == 0xd0) edata.data |= ARM_EXIDX_VFP_FSTMD; + } else if (op >= 0xc0 && op <= 0xc5) { + // Intel Wireless MMX pop wR[10]-wr[10+nnn], nnn != 6,7 + edata.cmd = ARM_EXIDX_CMD_WREG_POP; + edata.data = 0xa0 | (op & 0x07); + } else if (op == 0xc6) { + // Intel Wireless MMX pop wR[ssss] to wR[ssss+cccc] + edata.cmd = ARM_EXIDX_CMD_WREG_POP; + GET_BUF_U8(edata.data); + } else if (op == 0xc7) { + uint8_t op2; + GET_BUF_U8(op2); + if (op2 == 0 || (op2 & 0xf0)) { + // Spare + edata.cmd = ARM_EXIDX_CMD_RESERVED; + } else { + // Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0} + edata.cmd = ARM_EXIDX_CMD_WCGR_POP; + edata.data = op2 & 0x0f; + } + } else { + // Spare + edata.cmd = ARM_EXIDX_CMD_RESERVED; + } + + int ret = handler_->ImproveStackFrame(&edata); + if (ret < 0) + return ret; + } + return 0; + +# undef GET_BUF_U8 +} + +void ExceptionTableInfo::Start() { + const struct exidx_entry* start + = reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data()); + const struct exidx_entry* end + = reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data() + + mr_exidx_.length()); + + // Iterate over each of the EXIDX entries (pairs of 32-bit words). + // These occupy the entire .exidx section. + for (const struct exidx_entry* entry = start; entry < end; ++entry) { + // Figure out the code address range that this table entry is + // associated with. + uint32_t addr = (reinterpret_cast<char*>(Prel31ToAddr(&entry->addr)) + - mapping_addr_ + loading_addr_) & 0x7fffffff; + uint32_t next_addr; + if (entry < end - 1) { + next_addr = (reinterpret_cast<char*>(Prel31ToAddr(&((entry + 1)->addr))) + - mapping_addr_ + loading_addr_) & 0x7fffffff; + } else { + // This is the last EXIDX entry in the sequence, so we don't + // have an address for the start of the next function, to limit + // this one. Instead use the address of the last byte of the + // text section associated with this .exidx section, that we + // have been given. So as to avoid junking up the CFI unwind + // tables with absurdly large address ranges in the case where + // text_last_svma_ is wrong, only use the value if it is nonzero + // and within one page of |addr|. Otherwise assume a length of 1. + // + // In some cases, gcc has been observed to finish the exidx + // section with an entry of length 1 marked CANT_UNWIND, + // presumably exactly for the purpose of giving a definite + // length for the last real entry, without having to look at + // text segment boundaries. + bool plausible = false; + next_addr = addr + 1; + if (text_last_svma_ != 0) { + uint32_t maybe_next_addr = text_last_svma_ + 1; + if (maybe_next_addr > addr && maybe_next_addr - addr <= 4096) { + next_addr = maybe_next_addr; + plausible = true; + } + } + if (!plausible) { + fprintf(stderr, "ExceptionTableInfo: implausible EXIDX last entry size " + "%d, using 1 instead.", (int32_t)(text_last_svma_ - addr)); + } + } + + // Extract the unwind info into |buf|. This might fail for + // various reasons. It involves reading both the .exidx and + // .extab sections. All accesses to those sections are + // bounds-checked. + uint8_t buf[ARM_EXIDX_TABLE_LIMIT]; + size_t buf_used = 0; + ExExtractResult res = ExtabEntryExtract(entry, buf, sizeof(buf), &buf_used); + if (res != ExSuccess) { + // Couldn't extract the unwind info, for some reason. Move on. + switch (res) { + case ExInBufOverflow: + fprintf(stderr, "ExtabEntryExtract: .exidx/.extab section overrun"); + break; + case ExOutBufOverflow: + fprintf(stderr, "ExtabEntryExtract: bytecode buffer overflow"); + break; + case ExCantUnwind: + fprintf(stderr, "ExtabEntryExtract: function is marked CANT_UNWIND"); + break; + case ExCantRepresent: + fprintf(stderr, "ExtabEntryExtract: bytecode can't be represented"); + break; + case ExInvalid: + fprintf(stderr, "ExtabEntryExtract: index table entry is invalid"); + break; + default: + fprintf(stderr, "ExtabEntryExtract: unknown error: %d", (int)res); + break; + } + continue; + } + + // Finally, work through the unwind instructions in |buf| and + // create CFI entries that Breakpad can use. This can also fail. + // First, add a new stack frame entry, into which ExtabEntryDecode + // will write the CFI entries. + if (!handler_->HasStackFrame(addr, next_addr - addr)) { + handler_->AddStackFrame(addr, next_addr - addr); + int ret = ExtabEntryDecode(buf, buf_used); + if (ret < 0) { + handler_->DeleteStackFrame(); + fprintf(stderr, "ExtabEntryDecode: failed with error code: %d", ret); + continue; + } + handler_->SubmitStackFrame(); + } + + } /* iterating over .exidx */ +} + +} // namespace arm_ex_reader diff --git a/toolkit/crashreporter/google-breakpad/src/common/arm_ex_reader.h b/toolkit/crashreporter/google-breakpad/src/common/arm_ex_reader.h new file mode 100644 index 0000000000..9b54e8a0be --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/arm_ex_reader.h @@ -0,0 +1,114 @@ +/* libunwind - a platform-independent unwind library + Copyright 2011 Linaro Limited + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + + +// Derived from libunwind, with extensive modifications. + +#ifndef COMMON_ARM_EX_READER_H__ +#define COMMON_ARM_EX_READER_H__ + +#include "common/arm_ex_to_module.h" +#include "common/memory_range.h" + +namespace arm_ex_reader { + +// This class is a reader for ARM unwind information +// from .ARM.exidx and .ARM.extab sections. +class ExceptionTableInfo { + public: + ExceptionTableInfo(const char* exidx, size_t exidx_size, + const char* extab, size_t extab_size, + uint32_t text_last_svma, + arm_ex_to_module::ARMExToModule* handler, + const char* mapping_addr, + uint32_t loading_addr) + : mr_exidx_(google_breakpad::MemoryRange(exidx, exidx_size)), + mr_extab_(google_breakpad::MemoryRange(extab, extab_size)), + text_last_svma_(text_last_svma), + handler_(handler), mapping_addr_(mapping_addr), + loading_addr_(loading_addr) { } + + ~ExceptionTableInfo() { } + + // Parses the entries in .ARM.exidx and possibly + // in .ARM.extab tables, reports what we find to + // arm_ex_to_module::ARMExToModule. + void Start(); + + private: + google_breakpad::MemoryRange mr_exidx_; + google_breakpad::MemoryRange mr_extab_; + uint32_t text_last_svma_; + arm_ex_to_module::ARMExToModule* handler_; + const char* mapping_addr_; + uint32_t loading_addr_; + + enum ExExtractResult { + ExSuccess, // success + ExInBufOverflow, // out-of-range while reading .exidx + ExOutBufOverflow, // output buffer is too small + ExCantUnwind, // this function is marked CANT_UNWIND + ExCantRepresent, // entry valid, but we can't represent it + ExInvalid // entry is invalid + }; + ExExtractResult + ExtabEntryExtract(const struct arm_ex_to_module::exidx_entry* entry, + uint8_t* buf, size_t buf_size, + size_t* buf_used); + + int ExtabEntryDecode(const uint8_t* buf, size_t buf_size); +}; + +} // namespace arm_ex_reader + +#endif // COMMON_ARM_EX_READER_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/arm_ex_to_module.cc b/toolkit/crashreporter/google-breakpad/src/common/arm_ex_to_module.cc new file mode 100644 index 0000000000..c326744f64 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/arm_ex_to_module.cc @@ -0,0 +1,209 @@ + +/* libunwind - a platform-independent unwind library + Copyright 2011 Linaro Limited + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + + +// Derived from libunwind, with extensive modifications. + +#include "common/arm_ex_to_module.h" + +#include <stdio.h> +#include <assert.h> + +// For big-picture comments on how the EXIDX reader works, +// see arm_ex_reader.cc. + +#define ARM_EXBUF_START(x) (((x) >> 4) & 0x0f) +#define ARM_EXBUF_COUNT(x) ((x) & 0x0f) +#define ARM_EXBUF_END(x) (ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x)) + +using google_breakpad::Module; + +namespace arm_ex_to_module { + +static const char* const regnames[] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "fps", "cpsr" +}; + +// Translate command from extab_data to command for Module. +int ARMExToModule::TranslateCmd(const struct extab_data* edata, + Module::StackFrameEntry* entry, string& vsp) { + int ret = 0; + switch (edata->cmd) { + case ARM_EXIDX_CMD_FINISH: + /* Copy LR to PC if there isn't currently a rule for PC in force. */ + if (entry->initial_rules.find("pc") + == entry->initial_rules.end()) { + if (entry->initial_rules.find("lr") + == entry->initial_rules.end()) { + entry->initial_rules["pc"] = "lr"; + } else { + entry->initial_rules["pc"] = entry->initial_rules["lr"]; + } + } + break; + case ARM_EXIDX_CMD_SUB_FROM_VSP: + { + char c[16]; + sprintf(c, " %d -", edata->data); + vsp += c; + } + break; + case ARM_EXIDX_CMD_ADD_TO_VSP: + { + char c[16]; + sprintf(c, " %d +", edata->data); + vsp += c; + } + break; + case ARM_EXIDX_CMD_REG_POP: + for (unsigned int i = 0; i < 16; i++) { + if (edata->data & (1 << i)) { + entry->initial_rules[regnames[i]] + = vsp + " ^"; + vsp += " 4 +"; + } + } + /* Set cfa in case the SP got popped. */ + if (edata->data & (1 << 13)) { + vsp = entry->initial_rules["sp"]; + } + break; + case ARM_EXIDX_CMD_REG_TO_SP: { + assert (edata->data < 16); + const char* const regname = regnames[edata->data]; + if (entry->initial_rules.find(regname) == entry->initial_rules.end()) { + entry->initial_rules["sp"] = regname; + } else { + entry->initial_rules["sp"] = entry->initial_rules[regname]; + } + vsp = entry->initial_rules["sp"]; + break; + } + case ARM_EXIDX_CMD_VFP_POP: + /* Don't recover VFP registers, but be sure to adjust the stack + pointer. */ + for (unsigned int i = ARM_EXBUF_START(edata->data); + i <= ARM_EXBUF_END(edata->data); i++) { + vsp += " 8 +"; + } + if (!(edata->data & ARM_EXIDX_VFP_FSTMD)) { + vsp += " 4 +"; + } + break; + case ARM_EXIDX_CMD_WREG_POP: + for (unsigned int i = ARM_EXBUF_START(edata->data); + i <= ARM_EXBUF_END(edata->data); i++) { + vsp += " 8 +"; + } + break; + case ARM_EXIDX_CMD_WCGR_POP: + // Pop wCGR registers under mask {wCGR3,2,1,0}, hence "i < 4" + for (unsigned int i = 0; i < 4; i++) { + if (edata->data & (1 << i)) { + vsp += " 4 +"; + } + } + break; + case ARM_EXIDX_CMD_REFUSED: + case ARM_EXIDX_CMD_RESERVED: + ret = -1; + break; + } + return ret; +} + +bool ARMExToModule::HasStackFrame(uintptr_t addr, size_t size) { + // Invariant: the range [addr,covered) is covered by existing stack + // frame entries. + uintptr_t covered = addr; + while (covered < addr + size) { + const Module::StackFrameEntry *old_entry = + module_->FindStackFrameEntryByAddress(covered); + if (!old_entry) { + return false; + } + covered = old_entry->address + old_entry->size; + } + return true; +} + +void ARMExToModule::AddStackFrame(uintptr_t addr, size_t size) { + stack_frame_entry_ = new Module::StackFrameEntry; + stack_frame_entry_->address = addr; + stack_frame_entry_->size = size; + stack_frame_entry_->initial_rules[".cfa"] = "sp"; + vsp_ = "sp"; +} + +int ARMExToModule::ImproveStackFrame(const struct extab_data* edata) { + return TranslateCmd(edata, stack_frame_entry_, vsp_) ; +} + +void ARMExToModule::DeleteStackFrame() { + delete stack_frame_entry_; +} + +void ARMExToModule::SubmitStackFrame() { + // return address always winds up in pc + stack_frame_entry_->initial_rules[".ra"] + = stack_frame_entry_->initial_rules["pc"]; + // the final value of vsp is the new value of sp + stack_frame_entry_->initial_rules["sp"] = vsp_; + module_->AddStackFrameEntry(stack_frame_entry_); +} + +} // namespace arm_ex_to_module diff --git a/toolkit/crashreporter/google-breakpad/src/common/arm_ex_to_module.h b/toolkit/crashreporter/google-breakpad/src/common/arm_ex_to_module.h new file mode 100644 index 0000000000..f413a16a93 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/arm_ex_to_module.h @@ -0,0 +1,119 @@ +/* libunwind - a platform-independent unwind library + Copyright 2011 Linaro Limited + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + + +// Derived from libunwind, with extensive modifications. + +#ifndef COMMON_ARM_EX_TO_MODULE__ +#define COMMON_ARM_EX_TO_MODULE__ + +#include "common/module.h" + +#include <string.h> + +namespace arm_ex_to_module { + +using google_breakpad::Module; + +typedef enum extab_cmd { + ARM_EXIDX_CMD_FINISH, + ARM_EXIDX_CMD_SUB_FROM_VSP, + ARM_EXIDX_CMD_ADD_TO_VSP, + ARM_EXIDX_CMD_REG_POP, + ARM_EXIDX_CMD_REG_TO_SP, + ARM_EXIDX_CMD_VFP_POP, + ARM_EXIDX_CMD_WREG_POP, + ARM_EXIDX_CMD_WCGR_POP, + ARM_EXIDX_CMD_RESERVED, + ARM_EXIDX_CMD_REFUSED, +} extab_cmd_t; + +struct exidx_entry { + uint32_t addr; + uint32_t data; +}; + +struct extab_data { + extab_cmd_t cmd; + uint32_t data; +}; + +enum extab_cmd_flags { + ARM_EXIDX_VFP_SHIFT_16 = 1 << 16, + ARM_EXIDX_VFP_FSTMD = 1 << 17, // distinguishes FSTMxxD from FSTMxxX +}; + +// Receives information from arm_ex_reader::ExceptionTableInfo +// and adds it to the Module object +class ARMExToModule { + public: + ARMExToModule(Module* module) + : module_(module) { } + ~ARMExToModule() { } + bool HasStackFrame(uintptr_t addr, size_t size); + void AddStackFrame(uintptr_t addr, size_t size); + int ImproveStackFrame(const struct extab_data* edata); + void DeleteStackFrame(); + void SubmitStackFrame(); + private: + Module* module_; + Module::StackFrameEntry* stack_frame_entry_; + string vsp_; + int TranslateCmd(const struct extab_data* edata, + Module::StackFrameEntry* entry, + string& vsp); +}; + +} // namespace arm_ex_to_module + +#endif // COMMON_ARM_EX_TO_MODULE__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/basictypes.h b/toolkit/crashreporter/google-breakpad/src/common/basictypes.h new file mode 100644 index 0000000000..9426c1f6c2 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/basictypes.h @@ -0,0 +1,58 @@ +// Copyright (c) 2011 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef COMMON_BASICTYPES_H_ +#define COMMON_BASICTYPES_H_ + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#ifndef DISALLOW_COPY_AND_ASSIGN +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif // DISALLOW_COPY_AND_ASSIGN + +namespace google_breakpad { + +// Used to explicitly mark the return value of a function as unused. If you are +// really sure you don't want to do anything with the return value of a function +// that has been marked with __attribute__((warn_unused_result)), wrap it with +// this. Example: +// +// scoped_ptr<MyType> my_var = ...; +// if (TakeOwnership(my_var.get()) == SUCCESS) +// ignore_result(my_var.release()); +// +template<typename T> +inline void ignore_result(const T&) { +} + +} // namespace google_breakpad + +#endif // COMMON_BASICTYPES_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/byte_cursor.h b/toolkit/crashreporter/google-breakpad/src/common/byte_cursor.h new file mode 100644 index 0000000000..accd54e0a4 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/byte_cursor.h @@ -0,0 +1,265 @@ +// -*- mode: c++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// byte_cursor.h: Classes for parsing values from a buffer of bytes. +// The ByteCursor class provides a convenient interface for reading +// fixed-size integers of arbitrary endianness, being thorough about +// checking for buffer overruns. + +#ifndef COMMON_BYTE_CURSOR_H_ +#define COMMON_BYTE_CURSOR_H_ + +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <string> + +#include "common/using_std_string.h" + +namespace google_breakpad { + +// A buffer holding a series of bytes. +struct ByteBuffer { + ByteBuffer() : start(0), end(0) { } + ByteBuffer(const uint8_t *set_start, size_t set_size) + : start(set_start), end(set_start + set_size) { } + ~ByteBuffer() { }; + + // Equality operators. Useful in unit tests, and when we're using + // ByteBuffers to refer to regions of a larger buffer. + bool operator==(const ByteBuffer &that) const { + return start == that.start && end == that.end; + } + bool operator!=(const ByteBuffer &that) const { + return start != that.start || end != that.end; + } + + // Not C++ style guide compliant, but this definitely belongs here. + size_t Size() const { + assert(start <= end); + return end - start; + } + + const uint8_t *start, *end; +}; + +// A cursor pointing into a ByteBuffer that can parse numbers of various +// widths and representations, strings, and data blocks, advancing through +// the buffer as it goes. All ByteCursor operations check that accesses +// haven't gone beyond the end of the enclosing ByteBuffer. +class ByteCursor { + public: + // Create a cursor reading bytes from the start of BUFFER. By default, the + // cursor reads multi-byte values in little-endian form. + ByteCursor(const ByteBuffer *buffer, bool big_endian = false) + : buffer_(buffer), here_(buffer->start), + big_endian_(big_endian), complete_(true) { } + + // Accessor and setter for this cursor's endianness flag. + bool big_endian() const { return big_endian_; } + void set_big_endian(bool big_endian) { big_endian_ = big_endian; } + + // Accessor and setter for this cursor's current position. The setter + // returns a reference to this cursor. + const uint8_t *here() const { return here_; } + ByteCursor &set_here(const uint8_t *here) { + assert(buffer_->start <= here && here <= buffer_->end); + here_ = here; + return *this; + } + + // Return the number of bytes available to read at the cursor. + size_t Available() const { return size_t(buffer_->end - here_); } + + // Return true if this cursor is at the end of its buffer. + bool AtEnd() const { return Available() == 0; } + + // When used as a boolean value this cursor converts to true if all + // prior reads have been completed, or false if we ran off the end + // of the buffer. + operator bool() const { return complete_; } + + // Read a SIZE-byte integer at this cursor, signed if IS_SIGNED is true, + // unsigned otherwise, using the cursor's established endianness, and set + // *RESULT to the number. If we read off the end of our buffer, clear + // this cursor's complete_ flag, and store a dummy value in *RESULT. + // Return a reference to this cursor. + template<typename T> + ByteCursor &Read(size_t size, bool is_signed, T *result) { + if (CheckAvailable(size)) { + T v = 0; + if (big_endian_) { + for (size_t i = 0; i < size; i++) + v = (v << 8) + here_[i]; + } else { + // This loop condition looks weird, but size_t is unsigned, so + // decrementing i after it is zero yields the largest size_t value. + for (size_t i = size - 1; i < size; i--) + v = (v << 8) + here_[i]; + } + if (is_signed && size < sizeof(T)) { + size_t sign_bit = (T)1 << (size * 8 - 1); + v = (v ^ sign_bit) - sign_bit; + } + here_ += size; + *result = v; + } else { + *result = (T) 0xdeadbeef; + } + return *this; + } + + // Read an integer, using the cursor's established endianness and + // *RESULT's size and signedness, and set *RESULT to the number. If we + // read off the end of our buffer, clear this cursor's complete_ flag. + // Return a reference to this cursor. + template<typename T> + ByteCursor &operator>>(T &result) { + bool T_is_signed = (T)-1 < 0; + return Read(sizeof(T), T_is_signed, &result); + } + + // Copy the SIZE bytes at the cursor to BUFFER, and advance this + // cursor to the end of them. If we read off the end of our buffer, + // clear this cursor's complete_ flag, and set *POINTER to NULL. + // Return a reference to this cursor. + ByteCursor &Read(uint8_t *buffer, size_t size) { + if (CheckAvailable(size)) { + memcpy(buffer, here_, size); + here_ += size; + } + return *this; + } + + // Set STR to a copy of the '\0'-terminated string at the cursor. If the + // byte buffer does not contain a terminating zero, clear this cursor's + // complete_ flag, and set STR to the empty string. Return a reference to + // this cursor. + ByteCursor &CString(string *str) { + const uint8_t *end + = static_cast<const uint8_t *>(memchr(here_, '\0', Available())); + if (end) { + str->assign(reinterpret_cast<const char *>(here_), end - here_); + here_ = end + 1; + } else { + str->clear(); + here_ = buffer_->end; + complete_ = false; + } + return *this; + } + + // Like CString(STR), but extract the string from a fixed-width buffer + // LIMIT bytes long, which may or may not contain a terminating '\0' + // byte. Specifically: + // + // - If there are not LIMIT bytes available at the cursor, clear the + // cursor's complete_ flag and set STR to the empty string. + // + // - Otherwise, if the LIMIT bytes at the cursor contain any '\0' + // characters, set *STR to a copy of the bytes before the first '\0', + // and advance the cursor by LIMIT bytes. + // + // - Otherwise, set *STR to a copy of those LIMIT bytes, and advance the + // cursor by LIMIT bytes. + ByteCursor &CString(string *str, size_t limit) { + if (CheckAvailable(limit)) { + const uint8_t *end + = static_cast<const uint8_t *>(memchr(here_, '\0', limit)); + if (end) + str->assign(reinterpret_cast<const char *>(here_), end - here_); + else + str->assign(reinterpret_cast<const char *>(here_), limit); + here_ += limit; + } else { + str->clear(); + } + return *this; + } + + // Set *POINTER to point to the SIZE bytes at the cursor, and advance + // this cursor to the end of them. If SIZE is omitted, don't move the + // cursor. If we read off the end of our buffer, clear this cursor's + // complete_ flag, and set *POINTER to NULL. Return a reference to this + // cursor. + ByteCursor &PointTo(const uint8_t **pointer, size_t size = 0) { + if (CheckAvailable(size)) { + *pointer = here_; + here_ += size; + } else { + *pointer = NULL; + } + return *this; + } + + // Skip SIZE bytes at the cursor. If doing so would advance us off + // the end of our buffer, clear this cursor's complete_ flag, and + // set *POINTER to NULL. Return a reference to this cursor. + ByteCursor &Skip(size_t size) { + if (CheckAvailable(size)) + here_ += size; + return *this; + } + + private: + // If there are at least SIZE bytes available to read from the buffer, + // return true. Otherwise, set here_ to the end of the buffer, set + // complete_ to false, and return false. + bool CheckAvailable(size_t size) { + if (Available() >= size) { + return true; + } else { + here_ = buffer_->end; + complete_ = false; + return false; + } + } + + // The buffer we're reading bytes from. + const ByteBuffer *buffer_; + + // The next byte within buffer_ that we'll read. + const uint8_t *here_; + + // True if we should read numbers in big-endian form; false if we + // should read in little-endian form. + bool big_endian_; + + // True if we've been able to read all we've been asked to. + bool complete_; +}; + +} // namespace google_breakpad + +#endif // COMMON_BYTE_CURSOR_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/byte_cursor_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/byte_cursor_unittest.cc new file mode 100644 index 0000000000..06bfd89d73 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/byte_cursor_unittest.cc @@ -0,0 +1,776 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// byte_cursor_unittest.cc: Unit tests for google_breakpad::ByteBuffer +// and google_breakpad::ByteCursor. + +#include <string> + +#include <string.h> + +#include "breakpad_googletest_includes.h" +#include "common/byte_cursor.h" +#include "common/using_std_string.h" + +using google_breakpad::ByteBuffer; +using google_breakpad::ByteCursor; + +TEST(Buffer, SizeOfNothing) { + uint8_t data[1]; + ByteBuffer buffer(data, 0); + EXPECT_EQ(0U, buffer.Size()); +} + +TEST(Buffer, SizeOfSomething) { + uint8_t data[10]; + ByteBuffer buffer(data, sizeof(data)); + EXPECT_EQ(10U, buffer.Size()); +} + +TEST(Extent, AvailableEmpty) { + uint8_t data[1]; + ByteBuffer buffer(data, 0); + ByteCursor cursor(&buffer); + EXPECT_EQ(0U, cursor.Available()); +} + +TEST(Extent, AtEndEmpty) { + uint8_t data[1]; + ByteBuffer buffer(data, 0); + ByteCursor cursor(&buffer); + EXPECT_TRUE(cursor.AtEnd()); +} + +TEST(Extent, AsBoolEmpty) { + uint8_t data[1]; + ByteBuffer buffer(data, 0); + ByteCursor cursor(&buffer); + EXPECT_TRUE(cursor); +} + +TEST(Extent, AvailableSome) { + uint8_t data[10]; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + EXPECT_EQ(10U, cursor.Available()); +} + +TEST(Extent, AtEndSome) { + uint8_t data[10]; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + EXPECT_FALSE(cursor.AtEnd()); + EXPECT_TRUE(cursor.Skip(sizeof(data)).AtEnd()); +} + +TEST(Extent, AsBoolSome) { + uint8_t data[10]; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + EXPECT_TRUE(cursor); + EXPECT_TRUE(cursor.Skip(sizeof(data))); + EXPECT_FALSE(cursor.Skip(1)); +} + +TEST(Extent, Cursor) { + uint8_t data[] = { 0xf7, + 0x9f, 0xbe, + 0x67, 0xfb, 0xd3, 0x58, + 0x6f, 0x36, 0xde, 0xd1, + 0x2a, 0x2a, 0x2a }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + + uint8_t a; + uint16_t b; + uint32_t c; + uint32_t d; + uint8_t stars[3]; + + EXPECT_EQ(data + 0U, cursor.here()); + + EXPECT_TRUE(cursor >> a); + EXPECT_EQ(data + 1U, cursor.here()); + + EXPECT_TRUE(cursor >> b); + EXPECT_EQ(data + 3U, cursor.here()); + + EXPECT_TRUE(cursor >> c); + EXPECT_EQ(data + 7U, cursor.here()); + + EXPECT_TRUE(cursor.Skip(4)); + EXPECT_EQ(data + 11U, cursor.here()); + + EXPECT_TRUE(cursor.Read(stars, 3)); + EXPECT_EQ(data + 14U, cursor.here()); + + EXPECT_FALSE(cursor >> d); + EXPECT_EQ(data + 14U, cursor.here()); +} + +TEST(Extent, SetOffset) { + uint8_t data[] = { 0x5c, 0x79, 0x8c, 0xd5 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + + uint8_t a, b, c, d, e; + EXPECT_TRUE(cursor >> a); + EXPECT_EQ(0x5cU, a); + EXPECT_EQ(data + 1U, cursor.here()); + EXPECT_TRUE(((cursor >> b).set_here(data + 3) >> c).set_here(data + 1) + >> d >> e); + EXPECT_EQ(0x79U, b); + EXPECT_EQ(0xd5U, c); + EXPECT_EQ(0x79U, d); + EXPECT_EQ(0x8cU, e); + EXPECT_EQ(data + 3U, cursor.here()); +} + +TEST(BigEndian, Signed1) { + uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + cursor.set_big_endian(true); + int a, b, c, d, e; + ASSERT_TRUE(cursor + .Read(1, true, &a) + .Read(1, true, &b) + .Read(1, true, &c) + .Read(1, true, &d)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7f, b); + EXPECT_EQ(-0x80, c); + EXPECT_EQ(-1, d); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(1, true, &e)); +} + +TEST(BigEndian, Signed2) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x80, 0x7f, 0xff, + 0x80, 0x00, 0x80, 0x80, 0xff, 0xff, + 0x39, 0xf1, 0x8a, 0xbc, 0x5a, 0xec }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer, true); + int a, b, c, d, e, f, g, h, i, j; + ASSERT_TRUE(cursor + .Read(2, true, &a) + .Read(2, true, &b) + .Read(2, true, &c) + .Read(2, true, &d) + .Read(2, true, &e) + .Read(2, true, &f) + .Read(2, true, &g) + .Read(2, true, &h) + .Read(2, true, &i)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x80, b); + EXPECT_EQ(0x7fff, c); + EXPECT_EQ(-0x8000, d); + EXPECT_EQ(-0x7f80, e); + EXPECT_EQ(-1, f); + EXPECT_EQ(0x39f1, g); + EXPECT_EQ(-0x7544, h); + EXPECT_EQ(0x5aec, i); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(2, true, &j)); +} + +TEST(BigEndian, Signed4) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, + 0x80, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, + 0xb6, 0xb1, 0xff, 0xef, + 0x19, 0x6a, 0xca, 0x46 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + cursor.set_big_endian(true); + int64_t a, b, c, d, e, f, g; + ASSERT_TRUE(cursor + .Read(4, true, &a) + .Read(4, true, &b) + .Read(4, true, &c) + .Read(4, true, &d) + .Read(4, true, &e) + .Read(4, true, &f)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7fffffff, b); + EXPECT_EQ(-0x80000000LL, c); + EXPECT_EQ(-1, d); + EXPECT_EQ((int32_t) 0xb6b1ffef, e); + EXPECT_EQ(0x196aca46, f); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(4, true, &g)); +} + +TEST(BigEndian, Signed8) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x93, 0x20, 0xd5, 0xe9, 0xd2, 0xd5, 0x87, 0x9c, + 0x4e, 0x42, 0x49, 0xd2, 0x7f, 0x84, 0x14, 0xa4 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer, true); + int64_t a, b, c, d, e, f, g; + ASSERT_TRUE(cursor + .Read(8, true, &a) + .Read(8, true, &b) + .Read(8, true, &c) + .Read(8, true, &d) + .Read(8, true, &e) + .Read(8, true, &f)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7fffffffffffffffLL, b); + EXPECT_EQ(-0x7fffffffffffffffLL - 1, c); + EXPECT_EQ(-1, d); + EXPECT_EQ((int64_t) 0x9320d5e9d2d5879cULL, e); + EXPECT_EQ(0x4e4249d27f8414a4LL, f); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(8, true, &g)); +} + +TEST(BigEndian, Unsigned1) { + uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + cursor.set_big_endian(true); + int32_t a, b, c, d, e; + ASSERT_TRUE(cursor + .Read(1, false, &a) + .Read(1, false, &b) + .Read(1, false, &c) + .Read(1, false, &d)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7f, b); + EXPECT_EQ(0x80, c); + EXPECT_EQ(0xff, d); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(1, false, &e)); +} + +TEST(BigEndian, Unsigned2) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x80, 0x7f, 0xff, + 0x80, 0x00, 0x80, 0x80, 0xff, 0xff, + 0x39, 0xf1, 0x8a, 0xbc, 0x5a, 0xec }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer, true); + int64_t a, b, c, d, e, f, g, h, i, j; + ASSERT_TRUE(cursor + .Read(2, false, &a) + .Read(2, false, &b) + .Read(2, false, &c) + .Read(2, false, &d) + .Read(2, false, &e) + .Read(2, false, &f) + .Read(2, false, &g) + .Read(2, false, &h) + .Read(2, false, &i)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x80, b); + EXPECT_EQ(0x7fff, c); + EXPECT_EQ(0x8000, d); + EXPECT_EQ(0x8080, e); + EXPECT_EQ(0xffff, f); + EXPECT_EQ(0x39f1, g); + EXPECT_EQ(0x8abc, h); + EXPECT_EQ(0x5aec, i); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(2, false, &j)); +} + +TEST(BigEndian, Unsigned4) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, + 0x80, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, + 0xb6, 0xb1, 0xff, 0xef, + 0x19, 0x6a, 0xca, 0x46 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + cursor.set_big_endian(true); + int64_t a, b, c, d, e, f, g; + ASSERT_TRUE(cursor + .Read(4, false, &a) + .Read(4, false, &b) + .Read(4, false, &c) + .Read(4, false, &d) + .Read(4, false, &e) + .Read(4, false, &f)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7fffffff, b); + EXPECT_EQ(0x80000000, c); + EXPECT_EQ(0xffffffff, d); + EXPECT_EQ(0xb6b1ffef, e); + EXPECT_EQ(0x196aca46, f); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(4, false, &g)); +} + +TEST(BigEndian, Unsigned8) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x93, 0x20, 0xd5, 0xe9, 0xd2, 0xd5, 0x87, 0x9c, + 0x4e, 0x42, 0x49, 0xd2, 0x7f, 0x84, 0x14, 0xa4 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer, true); + uint64_t a, b, c, d, e, f, g; + ASSERT_TRUE(cursor + .Read(8, false, &a) + .Read(8, false, &b) + .Read(8, false, &c) + .Read(8, false, &d) + .Read(8, false, &e) + .Read(8, false, &f)); + EXPECT_EQ(0U, a); + EXPECT_EQ(0x7fffffffffffffffULL, b); + EXPECT_EQ(0x8000000000000000ULL, c); + EXPECT_EQ(0xffffffffffffffffULL, d); + EXPECT_EQ(0x9320d5e9d2d5879cULL, e); + EXPECT_EQ(0x4e4249d27f8414a4ULL, f); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(8, false, &g)); +} + +TEST(LittleEndian, Signed1) { + uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + int32_t a, b, c, d, e; + ASSERT_TRUE(cursor + .Read(1, true, &a) + .Read(1, true, &b) + .Read(1, true, &c) + .Read(1, true, &d)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7f, b); + EXPECT_EQ(-0x80, c); + EXPECT_EQ(-1, d); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(1, true, &e)); +} + +TEST(LittleEndian, Signed2) { + uint8_t data[] = { 0x00, 0x00, 0x80, 0x00, 0xff, 0x7f, + 0x00, 0x80, 0x80, 0x80, 0xff, 0xff, + 0xf1, 0x39, 0xbc, 0x8a, 0xec, 0x5a }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer, false); + int32_t a, b, c, d, e, f, g, h, i, j; + ASSERT_TRUE(cursor + .Read(2, true, &a) + .Read(2, true, &b) + .Read(2, true, &c) + .Read(2, true, &d) + .Read(2, true, &e) + .Read(2, true, &f) + .Read(2, true, &g) + .Read(2, true, &h) + .Read(2, true, &i)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x80, b); + EXPECT_EQ(0x7fff, c); + EXPECT_EQ(-0x8000, d); + EXPECT_EQ(-0x7f80, e); + EXPECT_EQ(-1, f); + EXPECT_EQ(0x39f1, g); + EXPECT_EQ(-0x7544, h); + EXPECT_EQ(0x5aec, i); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(2, true, &j)); +} + +TEST(LittleEndian, Signed4) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x7f, + 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xff, 0xb1, 0xb6, + 0x46, 0xca, 0x6a, 0x19 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + int64_t a, b, c, d, e, f, g; + ASSERT_TRUE(cursor + .Read(4, true, &a) + .Read(4, true, &b) + .Read(4, true, &c) + .Read(4, true, &d) + .Read(4, true, &e) + .Read(4, true, &f)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7fffffff, b); + EXPECT_EQ(-0x80000000LL, c); + EXPECT_EQ(-1, d); + EXPECT_EQ((int32_t) 0xb6b1ffef, e); + EXPECT_EQ(0x196aca46, f); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(4, true, &g)); +} + +TEST(LittleEndian, Signed8) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x9c, 0x87, 0xd5, 0xd2, 0xe9, 0xd5, 0x20, 0x93, + 0xa4, 0x14, 0x84, 0x7f, 0xd2, 0x49, 0x42, 0x4e }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer, false); + int64_t a, b, c, d, e, f, g; + ASSERT_TRUE(cursor + .Read(8, true, &a) + .Read(8, true, &b) + .Read(8, true, &c) + .Read(8, true, &d) + .Read(8, true, &e) + .Read(8, true, &f)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7fffffffffffffffLL, b); + EXPECT_EQ(-0x7fffffffffffffffLL - 1, c); + EXPECT_EQ(-1, d); + EXPECT_EQ((int64_t) 0x9320d5e9d2d5879cULL, e); + EXPECT_EQ(0x4e4249d27f8414a4LL, f); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(8, true, &g)); +} + +TEST(LittleEndian, Unsigned1) { + uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + int32_t a, b, c, d, e; + ASSERT_TRUE(cursor + .Read(1, false, &a) + .Read(1, false, &b) + .Read(1, false, &c) + .Read(1, false, &d)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7f, b); + EXPECT_EQ(0x80, c); + EXPECT_EQ(0xff, d); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(1, false, &e)); +} + +TEST(LittleEndian, Unsigned2) { + uint8_t data[] = { 0x00, 0x00, 0x80, 0x00, 0xff, 0x7f, + 0x00, 0x80, 0x80, 0x80, 0xff, 0xff, + 0xf1, 0x39, 0xbc, 0x8a, 0xec, 0x5a }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + int32_t a, b, c, d, e, f, g, h, i, j; + ASSERT_TRUE(cursor + .Read(2, false, &a) + .Read(2, false, &b) + .Read(2, false, &c) + .Read(2, false, &d) + .Read(2, false, &e) + .Read(2, false, &f) + .Read(2, false, &g) + .Read(2, false, &h) + .Read(2, false, &i)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x80, b); + EXPECT_EQ(0x7fff, c); + EXPECT_EQ(0x8000, d); + EXPECT_EQ(0x8080, e); + EXPECT_EQ(0xffff, f); + EXPECT_EQ(0x39f1, g); + EXPECT_EQ(0x8abc, h); + EXPECT_EQ(0x5aec, i); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(2, false, &j)); +} + +TEST(LittleEndian, Unsigned4) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x7f, + 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xff, 0xb1, 0xb6, + 0x46, 0xca, 0x6a, 0x19 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + int64_t a, b, c, d, e, f, g; + ASSERT_TRUE(cursor + .Read(4, false, &a) + .Read(4, false, &b) + .Read(4, false, &c) + .Read(4, false, &d) + .Read(4, false, &e) + .Read(4, false, &f)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7fffffff, b); + EXPECT_EQ(0x80000000, c); + EXPECT_EQ(0xffffffff, d); + EXPECT_EQ(0xb6b1ffef, e); + EXPECT_EQ(0x196aca46, f); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(4, false, &g)); +} + +TEST(LittleEndian, Unsigned8) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x9c, 0x87, 0xd5, 0xd2, 0xe9, 0xd5, 0x20, 0x93, + 0xa4, 0x14, 0x84, 0x7f, 0xd2, 0x49, 0x42, 0x4e }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + uint64_t a, b, c, d, e, f, g; + ASSERT_TRUE(cursor + .Read(8, false, &a) + .Read(8, false, &b) + .Read(8, false, &c) + .Read(8, false, &d) + .Read(8, false, &e) + .Read(8, false, &f)); + EXPECT_EQ(0U, a); + EXPECT_EQ(0x7fffffffffffffffULL, b); + EXPECT_EQ(0x8000000000000000ULL, c); + EXPECT_EQ(0xffffffffffffffffULL, d); + EXPECT_EQ(0x9320d5e9d2d5879cULL, e); + EXPECT_EQ(0x4e4249d27f8414a4ULL, f); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(8, false, &g)); +} + +TEST(Extractor, Signed1) { + uint8_t data[] = { 0xfd }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + int8_t a; + EXPECT_TRUE(cursor >> a); + EXPECT_EQ(-3, a); + EXPECT_FALSE(cursor >> a); +} + +TEST(Extractor, Signed2) { + uint8_t data[] = { 0x13, 0xcd }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + int16_t a; + EXPECT_TRUE(cursor >> a); + EXPECT_EQ(-13037, a); + EXPECT_FALSE(cursor >> a); +} + +TEST(Extractor, Signed4) { + uint8_t data[] = { 0xd2, 0xe4, 0x53, 0xe9 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + int32_t a; + // For some reason, G++ 4.4.1 complains: + // warning: array subscript is above array bounds + // in ByteCursor::Read(size_t, bool, T *) as it inlines this call, but + // I'm not able to see how such a reference would occur. + EXPECT_TRUE(cursor >> a); + EXPECT_EQ(-380377902, a); + EXPECT_FALSE(cursor >> a); +} + +TEST(Extractor, Unsigned1) { + uint8_t data[] = { 0xfd }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + uint8_t a; + EXPECT_TRUE(cursor >> a); + EXPECT_EQ(0xfd, a); + EXPECT_FALSE(cursor >> a); +} + +TEST(Extractor, Unsigned2) { + uint8_t data[] = { 0x13, 0xcd }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + uint16_t a; + EXPECT_TRUE(cursor >> a); + EXPECT_EQ(0xcd13, a); + EXPECT_FALSE(cursor >> a); +} + +TEST(Extractor, Unsigned4) { + uint8_t data[] = { 0xd2, 0xe4, 0x53, 0xe9 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + uint32_t a; + // For some reason, G++ 4.4.1 complains: + // warning: array subscript is above array bounds + // in ByteCursor::Read(size_t, bool, T *) as it inlines this call, but + // I'm not able to see how such a reference would occur. + EXPECT_TRUE(cursor >> a); + EXPECT_EQ(0xe953e4d2, a); + EXPECT_FALSE(cursor >> a); + EXPECT_FALSE(cursor >> a); +} + +TEST(Extractor, Mixed) { + uint8_t data[] = { 0x42, + 0x25, 0x0b, + 0x3d, 0x25, 0xed, 0x2a, + 0xec, 0x16, 0x9e, 0x14, 0x61, 0x5b, 0x2c, 0xcf, + 0xd8, + 0x22, 0xa5, + 0x3a, 0x02, 0x6a, 0xd7, + 0x93, 0x2a, 0x2d, 0x8d, 0xb4, 0x95, 0xe0, 0xc6 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + cursor.set_big_endian(true); + + uint8_t a; + uint16_t b; + uint32_t c; + uint64_t d; + int8_t e; + int16_t f; + int32_t g; + int64_t h; + int z; + EXPECT_FALSE(cursor.AtEnd()); + EXPECT_TRUE(cursor >> a >> b >> c >> d >> e >> f >> g >> h); + EXPECT_EQ(0x42U, a); + EXPECT_EQ(0x250bU, b); + EXPECT_EQ(0x3d25ed2aU, c); + EXPECT_EQ(0xec169e14615b2ccfULL, d); + EXPECT_EQ(-40, e); + EXPECT_EQ(0x22a5, f); + EXPECT_EQ(0x3a026ad7, g); + EXPECT_EQ(-7842405714468937530LL, h); + + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor >> z); +} + +TEST(Strings, Zero) { + uint8_t data[] = { 0xa6 }; + ByteBuffer buffer(data, 0); + ByteCursor cursor(&buffer); + + uint8_t received[1]; + received[0] = 0xc2; + EXPECT_TRUE(cursor.Read(received, 0)); + EXPECT_EQ(0xc2U, received[0]); +} + +TEST(Strings, Some) { + uint8_t data[] = { 0x5d, 0x31, 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xbb }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + + uint8_t received[7] = { 0xa7, 0xf7, 0x43, 0x0c, 0x27, 0xea, 0xed }; + EXPECT_TRUE(cursor.Skip(2).Read(received, 5)); + uint8_t expected[7] = { 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xea, 0xed }; + EXPECT_TRUE(memcmp(received, expected, 7) == 0); +} + +TEST(Strings, TooMuch) { + uint8_t data[] = { 0x5d, 0x31, 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xbb }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + + uint8_t received1[3]; + uint8_t received2[3]; + uint8_t received3[3]; + EXPECT_FALSE(cursor + .Read(received1, 3) + .Read(received2, 3) + .Read(received3, 3)); + uint8_t expected1[3] = { 0x5d, 0x31, 0x09 }; + uint8_t expected2[3] = { 0xa6, 0x2e, 0x2c }; + + EXPECT_TRUE(memcmp(received1, expected1, 3) == 0); + EXPECT_TRUE(memcmp(received2, expected2, 3) == 0); +} + +TEST(Strings, PointTo) { + uint8_t data[] = { 0x83, 0x80, 0xb4, 0x38, 0x00, 0x2c, 0x0a, 0x27 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + + const uint8_t *received1; + const uint8_t *received2; + const uint8_t *received3; + const uint8_t *received4; + EXPECT_FALSE(cursor + .PointTo(&received1, 3) + .PointTo(&received2, 3) + .PointTo(&received3) + .PointTo(&received4, 3)); + EXPECT_EQ(data + 0, received1); + EXPECT_EQ(data + 3, received2); + EXPECT_EQ(data + 6, received3); + EXPECT_EQ(NULL, received4); +} + +TEST(Strings, CString) { + uint8_t data[] = "abc\0\0foo"; + ByteBuffer buffer(data, sizeof(data) - 1); // don't include terminating '\0' + ByteCursor cursor(&buffer); + + string a, b, c; + EXPECT_TRUE(cursor.CString(&a).CString(&b)); + EXPECT_EQ("abc", a); + EXPECT_EQ("", b); + EXPECT_FALSE(cursor.CString(&c)); + EXPECT_EQ("", c); + EXPECT_TRUE(cursor.AtEnd()); +} + +TEST(Strings, CStringLimit) { + uint8_t data[] = "abcdef\0\0foobar"; + ByteBuffer buffer(data, sizeof(data) - 1); // don't include terminating '\0' + ByteCursor cursor(&buffer); + + string a, b, c, d, e; + + EXPECT_TRUE(cursor.CString(&a, 3)); + EXPECT_EQ("abc", a); + + EXPECT_TRUE(cursor.CString(&b, 0)); + EXPECT_EQ("", b); + + EXPECT_TRUE(cursor.CString(&c, 6)); + EXPECT_EQ("def", c); + + EXPECT_TRUE(cursor.CString(&d, 4)); + EXPECT_EQ("ooba", d); + + EXPECT_FALSE(cursor.CString(&e, 4)); + EXPECT_EQ("", e); + + EXPECT_TRUE(cursor.AtEnd()); +} + +// uint8_t data[] = { 0xa6, 0x54, 0xdf, 0x67, 0x51, 0x43, 0xac, 0xf1 }; +// ByteBuffer buffer(data, sizeof(data)); diff --git a/toolkit/crashreporter/google-breakpad/src/common/common.gyp b/toolkit/crashreporter/google-breakpad/src/common/common.gyp new file mode 100644 index 0000000000..c1dbb0febf --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/common.gyp @@ -0,0 +1,260 @@ +# Copyright 2014 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. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# 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. + +{ + 'target_defaults': { + 'target_conditions': [ + ['OS=="mac"', { + 'defines': ['HAVE_MACH_O_NLIST_H'], + }], + ['OS=="linux"', { + # Assume glibc. + 'defines': ['HAVE_A_OUT_H', 'HAVE_GETCONTEXT'], + 'sources!': [ + 'linux/breakpad_getcontext.S', + 'linux/breakpad_getcontext.h', + 'linux/breakpad_getcontext_unittest.cc', + ], + }], + ['OS!="android"', {'sources/': [['exclude', '(^|/)android/']]}], + ['OS!="linux"', {'sources/': [['exclude', '(^|/)linux/']]}], + ['OS!="mac"', {'sources/': [['exclude', '(^|/)mac/']]}], + ['OS!="solaris"', {'sources/': [['exclude', '(^|/)solaris/']]}], + ['OS!="win"', {'sources/': [['exclude', '(^|/)windows/']]}], + ], + }, + 'targets': [ + { + 'target_name': 'common', + 'type': 'static_library', + 'sources': [ + 'android/include/elf.h', + 'android/include/link.h', + 'android/include/stab.h', + 'android/include/sys/procfs.h', + 'android/include/sys/user.h', + 'android/testing/include/wchar.h', + 'android/testing/mkdtemp.h', + 'android/testing/pthread_fixes.h', + 'android/ucontext_constants.h', + 'basictypes.h', + 'byte_cursor.h', + 'convert_UTF.cc', + 'convert_UTF.h', + 'dwarf/bytereader-inl.h', + 'dwarf/bytereader.cc', + 'dwarf/bytereader.h', + 'dwarf/cfi_assembler.cc', + 'dwarf/cfi_assembler.h', + 'dwarf/dwarf2diehandler.cc', + 'dwarf/dwarf2diehandler.h', + 'dwarf/dwarf2enums.h', + 'dwarf/dwarf2reader.cc', + 'dwarf/dwarf2reader.h', + 'dwarf/dwarf2reader_test_common.h', + 'dwarf/elf_reader.cc', + 'dwarf/elf_reader.h', + 'dwarf/functioninfo.cc', + 'dwarf/functioninfo.h', + 'dwarf/line_state_machine.h', + 'dwarf/types.h', + 'dwarf_cfi_to_module.cc', + 'dwarf_cfi_to_module.h', + 'dwarf_cu_to_module.cc', + 'dwarf_cu_to_module.h', + 'dwarf_line_to_module.cc', + 'dwarf_line_to_module.h', + 'language.cc', + 'language.h', + 'linux/breakpad_getcontext.S', + 'linux/breakpad_getcontext.h', + 'linux/crc32.cc', + 'linux/crc32.h', + 'linux/dump_symbols.cc', + 'linux/dump_symbols.h', + 'linux/eintr_wrapper.h', + 'linux/elf_core_dump.cc', + 'linux/elf_core_dump.h', + 'linux/elf_gnu_compat.h', + 'linux/elf_symbols_to_module.cc', + 'linux/elf_symbols_to_module.h', + 'linux/elfutils-inl.h', + 'linux/elfutils.cc', + 'linux/elfutils.h', + 'linux/file_id.cc', + 'linux/file_id.h', + 'linux/google_crashdump_uploader.cc', + 'linux/google_crashdump_uploader.h', + 'linux/guid_creator.cc', + 'linux/guid_creator.h', + 'linux/http_upload.cc', + 'linux/http_upload.h', + 'linux/ignore_ret.h', + 'linux/libcurl_wrapper.cc', + 'linux/libcurl_wrapper.h', + 'linux/linux_libc_support.cc', + 'linux/linux_libc_support.h', + 'linux/memory_mapped_file.cc', + 'linux/memory_mapped_file.h', + 'linux/safe_readlink.cc', + 'linux/safe_readlink.h', + 'linux/symbol_collector_client.cc', + 'linux/symbol_collector_client.h', + 'linux/synth_elf.cc', + 'linux/synth_elf.h', + 'long_string_dictionary.cc', + 'long_string_dictionary.h', + 'mac/arch_utilities.cc', + 'mac/arch_utilities.h', + 'mac/bootstrap_compat.cc', + 'mac/bootstrap_compat.h', + 'mac/byteswap.h', + 'mac/dump_syms.h', + 'mac/dump_syms.cc', + 'mac/file_id.cc', + 'mac/file_id.h', + 'mac/GTMDefines.h', + 'mac/GTMLogger.h', + 'mac/GTMLogger.m', + 'mac/HTTPMultipartUpload.h', + 'mac/HTTPMultipartUpload.m', + 'mac/MachIPC.h', + 'mac/MachIPC.mm', + 'mac/macho_id.cc', + 'mac/macho_id.h', + 'mac/macho_reader.cc', + 'mac/macho_reader.h', + 'mac/macho_utilities.cc', + 'mac/macho_utilities.h', + 'mac/macho_walker.cc', + 'mac/macho_walker.h', + 'mac/scoped_task_suspend-inl.h', + 'mac/string_utilities.cc', + 'mac/string_utilities.h', + 'mac/super_fat_arch.h', + 'md5.cc', + 'md5.h', + 'memory_allocator.h', + 'memory_range.h', + 'module.cc', + 'module.h', + 'scoped_ptr.h', + 'simple_string_dictionary.cc', + 'simple_string_dictionary.h', + 'solaris/dump_symbols.cc', + 'solaris/dump_symbols.h', + 'solaris/file_id.cc', + 'solaris/file_id.h', + 'solaris/guid_creator.cc', + 'solaris/guid_creator.h', + 'solaris/message_output.h', + 'stabs_reader.cc', + 'stabs_reader.h', + 'stabs_to_module.cc', + 'stabs_to_module.h', + 'string_conversion.cc', + 'string_conversion.h', + 'symbol_data.h', + 'test_assembler.cc', + 'test_assembler.h', + 'unordered.h', + 'using_std_string.h', + 'windows/common_windows.gyp', + 'windows/dia_util.cc', + 'windows/dia_util.h', + 'windows/guid_string.cc', + 'windows/guid_string.h', + 'windows/http_upload.cc', + 'windows/http_upload.h', + 'windows/omap.cc', + 'windows/omap.h', + 'windows/omap_internal.h', + 'windows/pdb_source_line_writer.cc', + 'windows/pdb_source_line_writer.h', + 'windows/string_utils-inl.h', + 'windows/string_utils.cc', + ], + 'include_dirs': [ + '..', + ], + }, + { + 'target_name': 'common_unittests', + 'type': 'executable', + 'sources': [ + 'byte_cursor_unittest.cc', + 'dwarf/bytereader_unittest.cc', + 'dwarf/dwarf2diehandler_unittest.cc', + 'dwarf/dwarf2reader_cfi_unittest.cc', + 'dwarf/dwarf2reader_die_unittest.cc', + 'dwarf_cfi_to_module_unittest.cc', + 'dwarf_cu_to_module_unittest.cc', + 'dwarf_line_to_module_unittest.cc', + 'linux/breakpad_getcontext_unittest.cc', + 'linux/dump_symbols_unittest.cc', + 'linux/elf_core_dump_unittest.cc', + 'linux/elf_symbols_to_module_unittest.cc', + 'linux/file_id_unittest.cc', + 'linux/google_crashdump_uploader_test.cc', + 'linux/linux_libc_support_unittest.cc', + 'linux/memory_mapped_file_unittest.cc', + 'linux/safe_readlink_unittest.cc', + 'linux/synth_elf_unittest.cc', + 'linux/tests/auto_testfile.h', + 'linux/tests/crash_generator.cc', + 'linux/tests/crash_generator.h', + 'long_string_dictionary_unittest.cc', + 'mac/macho_reader_unittest.cc', + 'memory_allocator_unittest.cc', + 'memory_range_unittest.cc', + 'module_unittest.cc', + 'simple_string_dictionary_unittest.cc', + 'stabs_reader_unittest.cc', + 'stabs_to_module_unittest.cc', + 'string_conversion_unittest.cc', + 'test_assembler_unittest.cc', + 'tests/auto_tempdir.h', + 'tests/file_utils.cc', + 'tests/file_utils.h', + 'windows/omap_unittest.cc', + ], + 'include_dirs': [ + '..', + ], + 'dependencies': [ + 'common', + '../build/testing.gyp:gmock_main', + '../build/testing.gyp:gmock', + '../build/testing.gyp:gtest', + ], + 'libraries': [ + '-ldl', + ], + }, + ], +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/convert_UTF.cc b/toolkit/crashreporter/google-breakpad/src/common/convert_UTF.cc new file mode 100644 index 0000000000..4a5df1eb27 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/convert_UTF.cc @@ -0,0 +1,591 @@ +/* + * Copyright © 1991-2015 Unicode, Inc. All rights reserved. + * Distributed under the Terms of Use in + * http://www.unicode.org/copyright.html. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of the Unicode data files and any associated documentation + * (the "Data Files") or Unicode software and any associated documentation + * (the "Software") to deal in the Data Files or Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, and/or sell copies of + * the Data Files or Software, and to permit persons to whom the Data Files + * or Software are furnished to do so, provided that + * (a) this copyright and permission notice appear with all copies + * of the Data Files or Software, + * (b) this copyright and permission notice appear in associated + * documentation, and + * (c) there is clear notice in each modified Data File or in the Software + * as well as in the documentation associated with the Data File(s) or + * Software that the data or software has been modified. + * + * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF + * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS + * NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL + * DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THE DATA FILES OR SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, + * use or other dealings in these Data Files or Software without prior + * written authorization of the copyright holder. + */ + +/* --------------------------------------------------------------------- + +Conversions between UTF32, UTF-16, and UTF-8. Source code file. +Author: Mark E. Davis, 1994. +Rev History: Rick McGowan, fixes & updates May 2001. +Sept 2001: fixed const & error conditions per +mods suggested by S. Parent & A. Lillich. +June 2002: Tim Dodd added detection and handling of incomplete +source sequences, enhanced error detection, added casts +to eliminate compiler warnings. +July 2003: slight mods to back out aggressive FFFE detection. +Jan 2004: updated switches in from-UTF8 conversions. +Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. + +See the header file "ConvertUTF.h" for complete documentation. + +------------------------------------------------------------------------ */ + + +#include "convert_UTF.h" +#ifdef CVTUTF_DEBUG +#include <stdio.h> +#endif + +#include "common/macros.h" + +namespace google_breakpad { + +namespace { + +const int halfShift = 10; /* used for shifting by 10 bits */ + +const UTF32 halfBase = 0x0010000UL; +const UTF32 halfMask = 0x3FFUL; + +} // namespace + +#define UNI_SUR_HIGH_START (UTF32)0xD800 +#define UNI_SUR_HIGH_END (UTF32)0xDBFF +#define UNI_SUR_LOW_START (UTF32)0xDC00 +#define UNI_SUR_LOW_END (UTF32)0xDFFF + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF32toUTF16 (const UTF32** sourceStart, const UTF32* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF32* source = *sourceStart; + UTF16* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + if (target >= targetEnd) { + result = targetExhausted; break; + } + ch = *source++; + if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = (UTF16)ch; /* normal case */ + } + } else if (ch > UNI_MAX_LEGAL_UTF32) { + if (flags == strictConversion) { + result = sourceIllegal; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if (target + 1 >= targetEnd) { + --source; /* Back up source pointer! */ + result = targetExhausted; break; + } + ch -= halfBase; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); + } + } +*sourceStart = source; +*targetStart = target; +return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF16toUTF32 (const UTF16** sourceStart, const UTF16* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF16* source = *sourceStart; + UTF32* target = *targetStart; + UTF32 ch, ch2; + while (source < sourceEnd) { + const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ + ch = *source++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) { + ch2 = *source; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + + (ch2 - UNI_SUR_LOW_START) + halfBase; + ++source; + } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } else if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + if (target >= targetEnd) { + source = oldSource; /* Back up source pointer! */ + result = targetExhausted; break; + } + *target++ = ch; + } + *sourceStart = source; + *targetStart = target; +#ifdef CVTUTF_DEBUG + if (result == sourceIllegal) { + fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2); + fflush(stderr); + } +#endif + return result; +} + +/* --------------------------------------------------------------------- */ + +namespace { + +/* + * Index into the table below with the first byte of a UTF-8 sequence to + * get the number of trailing bytes that are supposed to follow it. + * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is + * left as-is for anyone who may want to do such conversion, which was + * allowed in earlier algorithms. + */ +const char trailingBytesForUTF8[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 +}; + +/* + * Magic values subtracted from a buffer value during UTF8 conversion. + * This table contains as many values as there might be trailing bytes + * in a UTF-8 sequence. + */ +const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; + +/* + * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed + * into the first byte, depending on how many bytes follow. There are + * as many entries in this table as there are UTF-8 sequence types. + * (I.e., one byte sequence, two byte... etc.). Remember that sequencs + * for *legal* UTF-8 will be 4 or fewer bytes total. + */ +const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +/* --------------------------------------------------------------------- */ + +/* The interface converts a whole buffer to avoid function-call overhead. +* Constants have been gathered. Loops & conditionals have been removed as +* much as possible for efficiency, in favor of drop-through switches. +* (See "Note A" at the bottom of the file for equivalent code.) +* If your compiler supports it, the "isLegalUTF8" call can be turned +* into an inline function. +*/ + +} // namespace + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF16toUTF8 (const UTF16** sourceStart, const UTF16* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF16* source = *sourceStart; + UTF8* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + unsigned short bytesToWrite = 0; + const UTF32 byteMask = 0xBF; + const UTF32 byteMark = 0x80; + const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ + ch = *source++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) { + UTF32 ch2 = *source; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + + (ch2 - UNI_SUR_LOW_START) + halfBase; + ++source; + } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } else if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + /* Figure out how many bytes the result will require */ + if (ch < (UTF32)0x80) { bytesToWrite = 1; + } else if (ch < (UTF32)0x800) { bytesToWrite = 2; + } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; + } else if (ch < (UTF32)0x110000) { bytesToWrite = 4; + } else { bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + } + + target += bytesToWrite; + if (target > targetEnd) { + source = oldSource; /* Back up source pointer! */ + target -= bytesToWrite; result = targetExhausted; break; + } + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + BP_FALLTHROUGH; + case 3: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + BP_FALLTHROUGH; + case 2: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + BP_FALLTHROUGH; + case 1: + *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } +*sourceStart = source; +*targetStart = target; +return result; +} + +/* --------------------------------------------------------------------- */ + +namespace { + +/* + * Utility routine to tell whether a sequence of bytes is legal UTF-8. + * This must be called with the length pre-determined by the first byte. + * If not calling this from ConvertUTF8to*, then the length can be set by: + * length = trailingBytesForUTF8[*source]+1; + * and the sequence is illegal right away if there aren't that many bytes + * available. + * If presented with a length > 4, this returns false. The Unicode + * definition of UTF-8 goes up to 4-byte sequences. + */ +Boolean isLegalUTF8(const UTF8 *source, int length) { + UTF8 a; + const UTF8 *srcptr = source+length; + switch (length) { + default: return false; + /* Everything else falls through when "true"... */ + case 4: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + BP_FALLTHROUGH; + case 3: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + BP_FALLTHROUGH; + case 2: + if ((a = (*--srcptr)) > 0xBF) return false; + + switch (*source) { + /* no fall-through in this inner switch */ + case 0xE0: if (a < 0xA0) return false; break; + case 0xED: if (a > 0x9F) return false; break; + case 0xF0: if (a < 0x90) return false; break; + case 0xF4: if (a > 0x8F) return false; break; + default: if (a < 0x80) return false; + } + BP_FALLTHROUGH; + case 1: if (*source >= 0x80 && *source < 0xC2) return false; + } + if (*source > 0xF4) return false; + return true; +} + +} // namespace + +/* --------------------------------------------------------------------- */ + +/* + * Exported function to return whether a UTF-8 sequence is legal or not. + * This is not used here; it's just exported. + */ +Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) { + int length = trailingBytesForUTF8[*source]+1; + if (source+length > sourceEnd) { + return false; + } + return isLegalUTF8(source, length); +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF8toUTF16 (const UTF8** sourceStart, const UTF8* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF8* source = *sourceStart; + UTF16* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (source + extraBytesToRead >= sourceEnd) { + result = sourceExhausted; break; + } + /* Do this check whether lenient or strict */ + if (! isLegalUTF8(source, extraBytesToRead+1)) { + result = sourceIllegal; + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + /* remember, illegal UTF-8 */ + case 5: ch += *source++; ch <<= 6; BP_FALLTHROUGH; + /* remember, illegal UTF-8 */ + case 4: ch += *source++; ch <<= 6; BP_FALLTHROUGH; + case 3: ch += *source++; ch <<= 6; BP_FALLTHROUGH; + case 2: ch += *source++; ch <<= 6; BP_FALLTHROUGH; + case 1: ch += *source++; ch <<= 6; BP_FALLTHROUGH; + case 0: ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (target >= targetEnd) { + source -= (extraBytesToRead+1); /* Back up source pointer! */ + result = targetExhausted; break; + } + if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + source -= (extraBytesToRead+1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = (UTF16)ch; /* normal case */ + } + } else if (ch > UNI_MAX_UTF16) { + if (flags == strictConversion) { + result = sourceIllegal; + source -= (extraBytesToRead+1); /* return to the start */ + break; /* Bail out; shouldn't continue */ + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if (target + 1 >= targetEnd) { + source -= (extraBytesToRead+1); /* Back up source pointer! */ + result = targetExhausted; break; + } + ch -= halfBase; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); + } + } +*sourceStart = source; +*targetStart = target; +return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF32toUTF8 (const UTF32** sourceStart, const UTF32* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF32* source = *sourceStart; + UTF8* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + unsigned short bytesToWrite = 0; + const UTF32 byteMask = 0xBF; + const UTF32 byteMark = 0x80; + ch = *source++; + if (flags == strictConversion ) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + /* + * Figure out how many bytes the result will require. Turn any + * illegally large UTF32 things (> Plane 17) into replacement chars. + */ + if (ch < (UTF32)0x80) { bytesToWrite = 1; + } else if (ch < (UTF32)0x800) { bytesToWrite = 2; + } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; + } else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4; + } else { bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + result = sourceIllegal; + } + + target += bytesToWrite; + if (target > targetEnd) { + --source; /* Back up source pointer! */ + target -= bytesToWrite; result = targetExhausted; break; + } + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + BP_FALLTHROUGH; + case 3: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + BP_FALLTHROUGH; + case 2: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + BP_FALLTHROUGH; + case 1: + *--target = (UTF8) (ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } +*sourceStart = source; +*targetStart = target; +return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF8toUTF32 (const UTF8** sourceStart, const UTF8* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF8* source = *sourceStart; + UTF32* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (source + extraBytesToRead >= sourceEnd) { + result = sourceExhausted; break; + } + /* Do this check whether lenient or strict */ + if (! isLegalUTF8(source, extraBytesToRead+1)) { + result = sourceIllegal; + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 5: ch += *source++; ch <<= 6; BP_FALLTHROUGH; + case 4: ch += *source++; ch <<= 6; BP_FALLTHROUGH; + case 3: ch += *source++; ch <<= 6; BP_FALLTHROUGH; + case 2: ch += *source++; ch <<= 6; BP_FALLTHROUGH; + case 1: ch += *source++; ch <<= 6; BP_FALLTHROUGH; + case 0: ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (target >= targetEnd) { + source -= (extraBytesToRead+1); /* Back up the source pointer! */ + result = targetExhausted; break; + } + if (ch <= UNI_MAX_LEGAL_UTF32) { + /* + * UTF-16 surrogate values are illegal in UTF-32, and anything + * over Plane 17 (> 0x10FFFF) is illegal. + */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + source -= (extraBytesToRead+1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = ch; + } + } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */ + result = sourceIllegal; + *target++ = UNI_REPLACEMENT_CHAR; + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- + +Note A. +The fall-through switches in UTF-8 reading code save a +temp variable, some decrements & conditionals. The switches +are equivalent to the following loop: +{ + int tmpBytesToRead = extraBytesToRead+1; + do { + ch += *source++; + --tmpBytesToRead; + if (tmpBytesToRead) ch <<= 6; + } while (tmpBytesToRead > 0); +} +In UTF-8 writing code, the switches on "bytesToWrite" are +similarly unrolled loops. + +--------------------------------------------------------------------- */ + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/convert_UTF.h b/toolkit/crashreporter/google-breakpad/src/common/convert_UTF.h new file mode 100644 index 0000000000..2f69495d24 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/convert_UTF.h @@ -0,0 +1,159 @@ +/* + * Copyright © 1991-2015 Unicode, Inc. All rights reserved. + * Distributed under the Terms of Use in + * http://www.unicode.org/copyright.html. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of the Unicode data files and any associated documentation + * (the "Data Files") or Unicode software and any associated documentation + * (the "Software") to deal in the Data Files or Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, and/or sell copies of + * the Data Files or Software, and to permit persons to whom the Data Files + * or Software are furnished to do so, provided that + * (a) this copyright and permission notice appear with all copies + * of the Data Files or Software, + * (b) this copyright and permission notice appear in associated + * documentation, and + * (c) there is clear notice in each modified Data File or in the Software + * as well as in the documentation associated with the Data File(s) or + * Software that the data or software has been modified. + * + * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF + * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS + * NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL + * DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THE DATA FILES OR SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, + * use or other dealings in these Data Files or Software without prior + * written authorization of the copyright holder. + */ + +#ifndef COMMON_CONVERT_UTF_H_ +#define COMMON_CONVERT_UTF_H_ + +/* --------------------------------------------------------------------- + +Conversions between UTF32, UTF-16, and UTF-8. Header file. + +Several funtions are included here, forming a complete set of +conversions between the three formats. UTF-7 is not included +here, but is handled in a separate source file. + +Each of these routines takes pointers to input buffers and output +buffers. The input buffers are const. + +Each routine converts the text between *sourceStart and sourceEnd, +putting the result into the buffer between *targetStart and +targetEnd. Note: the end pointers are *after* the last item: e.g. +*(sourceEnd - 1) is the last item. + +The return result indicates whether the conversion was successful, +and if not, whether the problem was in the source or target buffers. +(Only the first encountered problem is indicated.) + +After the conversion, *sourceStart and *targetStart are both +updated to point to the end of last text successfully converted in +the respective buffers. + +Input parameters: +sourceStart - pointer to a pointer to the source buffer. +The contents of this are modified on return so that +it points at the next thing to be converted. +targetStart - similarly, pointer to pointer to the target buffer. +sourceEnd, targetEnd - respectively pointers to the ends of the +two buffers, for overflow checking only. + +These conversion functions take a ConversionFlags argument. When this +flag is set to strict, both irregular sequences and isolated surrogates +will cause an error. When the flag is set to lenient, both irregular +sequences and isolated surrogates are converted. + +Whether the flag is strict or lenient, all illegal sequences will cause +an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>, +or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code +must check for illegal sequences. + +When the flag is set to lenient, characters over 0x10FFFF are converted +to the replacement character; otherwise (when the flag is set to strict) +they constitute an error. + +Output parameters: +The value "sourceIllegal" is returned from some routines if the input +sequence is malformed. When "sourceIllegal" is returned, the source +value will point to the illegal value that caused the problem. E.g., +in UTF-8 when a sequence is malformed, it points to the start of the +malformed sequence. + +Author: Mark E. Davis, 1994. +Rev History: Rick McGowan, fixes & updates May 2001. +Fixes & updates, Sept 2001. + +------------------------------------------------------------------------ */ + +/* --------------------------------------------------------------------- +The following 4 definitions are compiler-specific. +The C standard does not guarantee that wchar_t has at least +16 bits, so wchar_t is no less portable than unsigned short! +All should be unsigned values to avoid sign extension during +bit mask & shift operations. +------------------------------------------------------------------------ */ + +namespace google_breakpad { + +typedef unsigned long UTF32; /* at least 32 bits */ +typedef unsigned short UTF16; /* at least 16 bits */ +typedef unsigned char UTF8; /* typically 8 bits */ +typedef unsigned char Boolean; /* 0 or 1 */ + +/* Some fundamental constants */ +#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD +#define UNI_MAX_BMP (UTF32)0x0000FFFF +#define UNI_MAX_UTF16 (UTF32)0x0010FFFF +#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF +#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF + +typedef enum { + conversionOK, /* conversion successful */ + sourceExhausted, /* partial character in source, but hit end */ + targetExhausted, /* insuff. room in target for conversion */ + sourceIllegal /* source sequence is illegal/malformed */ +} ConversionResult; + +typedef enum { + strictConversion = 0, + lenientConversion +} ConversionFlags; + +ConversionResult ConvertUTF8toUTF16 (const UTF8** sourceStart, const UTF8* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF16toUTF8 (const UTF16** sourceStart, const UTF16* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF8toUTF32 (const UTF8** sourceStart, const UTF8* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF32toUTF8 (const UTF32** sourceStart, const UTF32* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF16toUTF32 (const UTF16** sourceStart, const UTF16* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF32toUTF16 (const UTF32** sourceStart, const UTF32* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); + +Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); + +} // namespace google_breakpad + +/* --------------------------------------------------------------------- */ + +#endif // COMMON_CONVERT_UTF_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader-inl.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader-inl.h new file mode 100644 index 0000000000..42c92f943f --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader-inl.h @@ -0,0 +1,170 @@ +// Copyright 2006 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef UTIL_DEBUGINFO_BYTEREADER_INL_H__ +#define UTIL_DEBUGINFO_BYTEREADER_INL_H__ + +#include "common/dwarf/bytereader.h" + +#include <assert.h> +#include <stdint.h> + +namespace dwarf2reader { + +inline uint8 ByteReader::ReadOneByte(const uint8_t *buffer) const { + return buffer[0]; +} + +inline uint16 ByteReader::ReadTwoBytes(const uint8_t *buffer) const { + const uint16 buffer0 = buffer[0]; + const uint16 buffer1 = buffer[1]; + if (endian_ == ENDIANNESS_LITTLE) { + return buffer0 | buffer1 << 8; + } else { + return buffer1 | buffer0 << 8; + } +} + +inline uint64 ByteReader::ReadFourBytes(const uint8_t *buffer) const { + const uint32 buffer0 = buffer[0]; + const uint32 buffer1 = buffer[1]; + const uint32 buffer2 = buffer[2]; + const uint32 buffer3 = buffer[3]; + if (endian_ == ENDIANNESS_LITTLE) { + return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24; + } else { + return buffer3 | buffer2 << 8 | buffer1 << 16 | buffer0 << 24; + } +} + +inline uint64 ByteReader::ReadEightBytes(const uint8_t *buffer) const { + const uint64 buffer0 = buffer[0]; + const uint64 buffer1 = buffer[1]; + const uint64 buffer2 = buffer[2]; + const uint64 buffer3 = buffer[3]; + const uint64 buffer4 = buffer[4]; + const uint64 buffer5 = buffer[5]; + const uint64 buffer6 = buffer[6]; + const uint64 buffer7 = buffer[7]; + if (endian_ == ENDIANNESS_LITTLE) { + return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24 | + buffer4 << 32 | buffer5 << 40 | buffer6 << 48 | buffer7 << 56; + } else { + return buffer7 | buffer6 << 8 | buffer5 << 16 | buffer4 << 24 | + buffer3 << 32 | buffer2 << 40 | buffer1 << 48 | buffer0 << 56; + } +} + +// Read an unsigned LEB128 number. Each byte contains 7 bits of +// information, plus one bit saying whether the number continues or +// not. + +inline uint64 ByteReader::ReadUnsignedLEB128(const uint8_t *buffer, + size_t* len) const { + uint64 result = 0; + size_t num_read = 0; + unsigned int shift = 0; + uint8_t byte; + + do { + byte = *buffer++; + num_read++; + + result |= (static_cast<uint64>(byte & 0x7f)) << shift; + + shift += 7; + + } while (byte & 0x80); + + *len = num_read; + + return result; +} + +// Read a signed LEB128 number. These are like regular LEB128 +// numbers, except the last byte may have a sign bit set. + +inline int64 ByteReader::ReadSignedLEB128(const uint8_t *buffer, + size_t* len) const { + int64 result = 0; + unsigned int shift = 0; + size_t num_read = 0; + uint8_t byte; + + do { + byte = *buffer++; + num_read++; + result |= (static_cast<uint64>(byte & 0x7f) << shift); + shift += 7; + } while (byte & 0x80); + + if ((shift < 8 * sizeof (result)) && (byte & 0x40)) + result |= -((static_cast<int64>(1)) << shift); + *len = num_read; + return result; +} + +inline uint64 ByteReader::ReadOffset(const uint8_t *buffer) const { + assert(this->offset_reader_); + return (this->*offset_reader_)(buffer); +} + +inline uint64 ByteReader::ReadAddress(const uint8_t *buffer) const { + assert(this->address_reader_); + return (this->*address_reader_)(buffer); +} + +inline void ByteReader::SetCFIDataBase(uint64 section_base, + const uint8_t *buffer_base) { + section_base_ = section_base; + buffer_base_ = buffer_base; + have_section_base_ = true; +} + +inline void ByteReader::SetTextBase(uint64 text_base) { + text_base_ = text_base; + have_text_base_ = true; +} + +inline void ByteReader::SetDataBase(uint64 data_base) { + data_base_ = data_base; + have_data_base_ = true; +} + +inline void ByteReader::SetFunctionBase(uint64 function_base) { + function_base_ = function_base; + have_function_base_ = true; +} + +inline void ByteReader::ClearFunctionBase() { + have_function_base_ = false; +} + +} // namespace dwarf2reader + +#endif // UTIL_DEBUGINFO_BYTEREADER_INL_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader.cc new file mode 100644 index 0000000000..14b43adb8f --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader.cc @@ -0,0 +1,250 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> + +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/bytereader.h" + +namespace dwarf2reader { + +ByteReader::ByteReader(enum Endianness endian) + :offset_reader_(NULL), address_reader_(NULL), endian_(endian), + address_size_(0), offset_size_(0), + have_section_base_(), have_text_base_(), have_data_base_(), + have_function_base_() { } + +ByteReader::~ByteReader() { } + +void ByteReader::SetOffsetSize(uint8 size) { + offset_size_ = size; + assert(size == 4 || size == 8); + if (size == 4) { + this->offset_reader_ = &ByteReader::ReadFourBytes; + } else { + this->offset_reader_ = &ByteReader::ReadEightBytes; + } +} + +void ByteReader::SetAddressSize(uint8 size) { + address_size_ = size; + assert(size == 4 || size == 8); + if (size == 4) { + this->address_reader_ = &ByteReader::ReadFourBytes; + } else { + this->address_reader_ = &ByteReader::ReadEightBytes; + } +} + +uint64 ByteReader::ReadInitialLength(const uint8_t *start, size_t* len) { + const uint64 initial_length = ReadFourBytes(start); + start += 4; + + // In DWARF2/3, if the initial length is all 1 bits, then the offset + // size is 8 and we need to read the next 8 bytes for the real length. + if (initial_length == 0xffffffff) { + SetOffsetSize(8); + *len = 12; + return ReadOffset(start); + } else { + SetOffsetSize(4); + *len = 4; + } + return initial_length; +} + +bool ByteReader::ValidEncoding(DwarfPointerEncoding encoding) const { + if (encoding == DW_EH_PE_omit) return true; + if (encoding == DW_EH_PE_aligned) return true; + if ((encoding & 0x7) > DW_EH_PE_udata8) + return false; + if ((encoding & 0x70) > DW_EH_PE_funcrel) + return false; + return true; +} + +bool ByteReader::UsableEncoding(DwarfPointerEncoding encoding) const { + switch (encoding & 0x70) { + case DW_EH_PE_absptr: return true; + case DW_EH_PE_pcrel: return have_section_base_; + case DW_EH_PE_textrel: return have_text_base_; + case DW_EH_PE_datarel: return have_data_base_; + case DW_EH_PE_funcrel: return have_function_base_; + default: return false; + } +} + +uint64 ByteReader::ReadEncodedPointer(const uint8_t *buffer, + DwarfPointerEncoding encoding, + size_t *len) const { + // UsableEncoding doesn't approve of DW_EH_PE_omit, so we shouldn't + // see it here. + assert(encoding != DW_EH_PE_omit); + + // The Linux Standards Base 4.0 does not make this clear, but the + // GNU tools (gcc/unwind-pe.h; readelf/dwarf.c; gdb/dwarf2-frame.c) + // agree that aligned pointers are always absolute, machine-sized, + // machine-signed pointers. + if (encoding == DW_EH_PE_aligned) { + assert(have_section_base_); + + // We don't need to align BUFFER in *our* address space. Rather, we + // need to find the next position in our buffer that would be aligned + // when the .eh_frame section the buffer contains is loaded into the + // program's memory. So align assuming that buffer_base_ gets loaded at + // address section_base_, where section_base_ itself may or may not be + // aligned. + + // First, find the offset to START from the closest prior aligned + // address. + uint64 skew = section_base_ & (AddressSize() - 1); + // Now find the offset from that aligned address to buffer. + uint64 offset = skew + (buffer - buffer_base_); + // Round up to the next boundary. + uint64 aligned = (offset + AddressSize() - 1) & -AddressSize(); + // Convert back to a pointer. + const uint8_t *aligned_buffer = buffer_base_ + (aligned - skew); + // Finally, store the length and actually fetch the pointer. + *len = aligned_buffer - buffer + AddressSize(); + return ReadAddress(aligned_buffer); + } + + // Extract the value first, ignoring whether it's a pointer or an + // offset relative to some base. + uint64 offset; + switch (encoding & 0x0f) { + case DW_EH_PE_absptr: + // DW_EH_PE_absptr is weird, as it is used as a meaningful value for + // both the high and low nybble of encoding bytes. When it appears in + // the high nybble, it means that the pointer is absolute, not an + // offset from some base address. When it appears in the low nybble, + // as here, it means that the pointer is stored as a normal + // machine-sized and machine-signed address. A low nybble of + // DW_EH_PE_absptr does not imply that the pointer is absolute; it is + // correct for us to treat the value as an offset from a base address + // if the upper nybble is not DW_EH_PE_absptr. + offset = ReadAddress(buffer); + *len = AddressSize(); + break; + + case DW_EH_PE_uleb128: + offset = ReadUnsignedLEB128(buffer, len); + break; + + case DW_EH_PE_udata2: + offset = ReadTwoBytes(buffer); + *len = 2; + break; + + case DW_EH_PE_udata4: + offset = ReadFourBytes(buffer); + *len = 4; + break; + + case DW_EH_PE_udata8: + offset = ReadEightBytes(buffer); + *len = 8; + break; + + case DW_EH_PE_sleb128: + offset = ReadSignedLEB128(buffer, len); + break; + + case DW_EH_PE_sdata2: + offset = ReadTwoBytes(buffer); + // Sign-extend from 16 bits. + offset = (offset ^ 0x8000) - 0x8000; + *len = 2; + break; + + case DW_EH_PE_sdata4: + offset = ReadFourBytes(buffer); + // Sign-extend from 32 bits. + offset = (offset ^ 0x80000000ULL) - 0x80000000ULL; + *len = 4; + break; + + case DW_EH_PE_sdata8: + // No need to sign-extend; this is the full width of our type. + offset = ReadEightBytes(buffer); + *len = 8; + break; + + default: + abort(); + } + + // Find the appropriate base address. + uint64 base; + switch (encoding & 0x70) { + case DW_EH_PE_absptr: + base = 0; + break; + + case DW_EH_PE_pcrel: + assert(have_section_base_); + base = section_base_ + (buffer - buffer_base_); + break; + + case DW_EH_PE_textrel: + assert(have_text_base_); + base = text_base_; + break; + + case DW_EH_PE_datarel: + assert(have_data_base_); + base = data_base_; + break; + + case DW_EH_PE_funcrel: + assert(have_function_base_); + base = function_base_; + break; + + default: + abort(); + } + + uint64 pointer = base + offset; + + // Remove inappropriate upper bits. + if (AddressSize() == 4) + pointer = pointer & 0xffffffff; + else + assert(AddressSize() == sizeof(uint64)); + + return pointer; +} + +Endianness ByteReader::GetEndianness() const { + return endian_; +} + +} // namespace dwarf2reader diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader.h new file mode 100644 index 0000000000..59d4303480 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader.h @@ -0,0 +1,315 @@ +// -*- mode: C++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef COMMON_DWARF_BYTEREADER_H__ +#define COMMON_DWARF_BYTEREADER_H__ + +#include <stdint.h> + +#include <string> + +#include "common/dwarf/types.h" +#include "common/dwarf/dwarf2enums.h" + +namespace dwarf2reader { + +// We can't use the obvious name of LITTLE_ENDIAN and BIG_ENDIAN +// because it conflicts with a macro +enum Endianness { + ENDIANNESS_BIG, + ENDIANNESS_LITTLE +}; + +// A ByteReader knows how to read single- and multi-byte values of +// various endiannesses, sizes, and encodings, as used in DWARF +// debugging information and Linux C++ exception handling data. +class ByteReader { + public: + // Construct a ByteReader capable of reading one-, two-, four-, and + // eight-byte values according to ENDIANNESS, absolute machine-sized + // addresses, DWARF-style "initial length" values, signed and + // unsigned LEB128 numbers, and Linux C++ exception handling data's + // encoded pointers. + explicit ByteReader(enum Endianness endianness); + virtual ~ByteReader(); + + // Read a single byte from BUFFER and return it as an unsigned 8 bit + // number. + uint8 ReadOneByte(const uint8_t *buffer) const; + + // Read two bytes from BUFFER and return them as an unsigned 16 bit + // number, using this ByteReader's endianness. + uint16 ReadTwoBytes(const uint8_t *buffer) const; + + // Read four bytes from BUFFER and return them as an unsigned 32 bit + // number, using this ByteReader's endianness. This function returns + // a uint64 so that it is compatible with ReadAddress and + // ReadOffset. The number it returns will never be outside the range + // of an unsigned 32 bit integer. + uint64 ReadFourBytes(const uint8_t *buffer) const; + + // Read eight bytes from BUFFER and return them as an unsigned 64 + // bit number, using this ByteReader's endianness. + uint64 ReadEightBytes(const uint8_t *buffer) const; + + // Read an unsigned LEB128 (Little Endian Base 128) number from + // BUFFER and return it as an unsigned 64 bit integer. Set LEN to + // the number of bytes read. + // + // The unsigned LEB128 representation of an integer N is a variable + // number of bytes: + // + // - If N is between 0 and 0x7f, then its unsigned LEB128 + // representation is a single byte whose value is N. + // + // - Otherwise, its unsigned LEB128 representation is (N & 0x7f) | + // 0x80, followed by the unsigned LEB128 representation of N / + // 128, rounded towards negative infinity. + // + // In other words, we break VALUE into groups of seven bits, put + // them in little-endian order, and then write them as eight-bit + // bytes with the high bit on all but the last. + uint64 ReadUnsignedLEB128(const uint8_t *buffer, size_t *len) const; + + // Read a signed LEB128 number from BUFFER and return it as an + // signed 64 bit integer. Set LEN to the number of bytes read. + // + // The signed LEB128 representation of an integer N is a variable + // number of bytes: + // + // - If N is between -0x40 and 0x3f, then its signed LEB128 + // representation is a single byte whose value is N in two's + // complement. + // + // - Otherwise, its signed LEB128 representation is (N & 0x7f) | + // 0x80, followed by the signed LEB128 representation of N / 128, + // rounded towards negative infinity. + // + // In other words, we break VALUE into groups of seven bits, put + // them in little-endian order, and then write them as eight-bit + // bytes with the high bit on all but the last. + int64 ReadSignedLEB128(const uint8_t *buffer, size_t *len) const; + + // Indicate that addresses on this architecture are SIZE bytes long. SIZE + // must be either 4 or 8. (DWARF allows addresses to be any number of + // bytes in length from 1 to 255, but we only support 32- and 64-bit + // addresses at the moment.) You must call this before using the + // ReadAddress member function. + // + // For data in a .debug_info section, or something that .debug_info + // refers to like line number or macro data, the compilation unit + // header's address_size field indicates the address size to use. Call + // frame information doesn't indicate its address size (a shortcoming of + // the spec); you must supply the appropriate size based on the + // architecture of the target machine. + void SetAddressSize(uint8 size); + + // Return the current address size, in bytes. This is either 4, + // indicating 32-bit addresses, or 8, indicating 64-bit addresses. + uint8 AddressSize() const { return address_size_; } + + // Read an address from BUFFER and return it as an unsigned 64 bit + // integer, respecting this ByteReader's endianness and address size. You + // must call SetAddressSize before calling this function. + uint64 ReadAddress(const uint8_t *buffer) const; + + // DWARF actually defines two slightly different formats: 32-bit DWARF + // and 64-bit DWARF. This is *not* related to the size of registers or + // addresses on the target machine; it refers only to the size of section + // offsets and data lengths appearing in the DWARF data. One only needs + // 64-bit DWARF when the debugging data itself is larger than 4GiB. + // 32-bit DWARF can handle x86_64 or PPC64 code just fine, unless the + // debugging data itself is very large. + // + // DWARF information identifies itself as 32-bit or 64-bit DWARF: each + // compilation unit and call frame information entry begins with an + // "initial length" field, which, in addition to giving the length of the + // data, also indicates the size of section offsets and lengths appearing + // in that data. The ReadInitialLength member function, below, reads an + // initial length and sets the ByteReader's offset size as a side effect. + // Thus, in the normal process of reading DWARF data, the appropriate + // offset size is set automatically. So, you should only need to call + // SetOffsetSize if you are using the same ByteReader to jump from the + // midst of one block of DWARF data into another. + + // Read a DWARF "initial length" field from START, and return it as + // an unsigned 64 bit integer, respecting this ByteReader's + // endianness. Set *LEN to the length of the initial length in + // bytes, either four or twelve. As a side effect, set this + // ByteReader's offset size to either 4 (if we see a 32-bit DWARF + // initial length) or 8 (if we see a 64-bit DWARF initial length). + // + // A DWARF initial length is either: + // + // - a byte count stored as an unsigned 32-bit value less than + // 0xffffff00, indicating that the data whose length is being + // measured uses the 32-bit DWARF format, or + // + // - The 32-bit value 0xffffffff, followed by a 64-bit byte count, + // indicating that the data whose length is being measured uses + // the 64-bit DWARF format. + uint64 ReadInitialLength(const uint8_t *start, size_t *len); + + // Read an offset from BUFFER and return it as an unsigned 64 bit + // integer, respecting the ByteReader's endianness. In 32-bit DWARF, the + // offset is 4 bytes long; in 64-bit DWARF, the offset is eight bytes + // long. You must call ReadInitialLength or SetOffsetSize before calling + // this function; see the comments above for details. + uint64 ReadOffset(const uint8_t *buffer) const; + + // Return the current offset size, in bytes. + // A return value of 4 indicates that we are reading 32-bit DWARF. + // A return value of 8 indicates that we are reading 64-bit DWARF. + uint8 OffsetSize() const { return offset_size_; } + + // Indicate that section offsets and lengths are SIZE bytes long. SIZE + // must be either 4 (meaning 32-bit DWARF) or 8 (meaning 64-bit DWARF). + // Usually, you should not call this function yourself; instead, let a + // call to ReadInitialLength establish the data's offset size + // automatically. + void SetOffsetSize(uint8 size); + + // The Linux C++ ABI uses a variant of DWARF call frame information + // for exception handling. This data is included in the program's + // address space as the ".eh_frame" section, and intepreted at + // runtime to walk the stack, find exception handlers, and run + // cleanup code. The format is mostly the same as DWARF CFI, with + // some adjustments made to provide the additional + // exception-handling data, and to make the data easier to work with + // in memory --- for example, to allow it to be placed in read-only + // memory even when describing position-independent code. + // + // In particular, exception handling data can select a number of + // different encodings for pointers that appear in the data, as + // described by the DwarfPointerEncoding enum. There are actually + // four axes(!) to the encoding: + // + // - The pointer size: pointers can be 2, 4, or 8 bytes long, or use + // the DWARF LEB128 encoding. + // + // - The pointer's signedness: pointers can be signed or unsigned. + // + // - The pointer's base address: the data stored in the exception + // handling data can be the actual address (that is, an absolute + // pointer), or relative to one of a number of different base + // addreses --- including that of the encoded pointer itself, for + // a form of "pc-relative" addressing. + // + // - The pointer may be indirect: it may be the address where the + // true pointer is stored. (This is used to refer to things via + // global offset table entries, program linkage table entries, or + // other tricks used in position-independent code.) + // + // There are also two options that fall outside that matrix + // altogether: the pointer may be omitted, or it may have padding to + // align it on an appropriate address boundary. (That last option + // may seem like it should be just another axis, but it is not.) + + // Indicate that the exception handling data is loaded starting at + // SECTION_BASE, and that the start of its buffer in our own memory + // is BUFFER_BASE. This allows us to find the address that a given + // byte in our buffer would have when loaded into the program the + // data describes. We need this to resolve DW_EH_PE_pcrel pointers. + void SetCFIDataBase(uint64 section_base, const uint8_t *buffer_base); + + // Indicate that the base address of the program's ".text" section + // is TEXT_BASE. We need this to resolve DW_EH_PE_textrel pointers. + void SetTextBase(uint64 text_base); + + // Indicate that the base address for DW_EH_PE_datarel pointers is + // DATA_BASE. The proper value depends on the ABI; it is usually the + // address of the global offset table, held in a designated register in + // position-independent code. You will need to look at the startup code + // for the target system to be sure. I tried; my eyes bled. + void SetDataBase(uint64 data_base); + + // Indicate that the base address for the FDE we are processing is + // FUNCTION_BASE. This is the start address of DW_EH_PE_funcrel + // pointers. (This encoding does not seem to be used by the GNU + // toolchain.) + void SetFunctionBase(uint64 function_base); + + // Indicate that we are no longer processing any FDE, so any use of + // a DW_EH_PE_funcrel encoding is an error. + void ClearFunctionBase(); + + // Return true if ENCODING is a valid pointer encoding. + bool ValidEncoding(DwarfPointerEncoding encoding) const; + + // Return true if we have all the information we need to read a + // pointer that uses ENCODING. This checks that the appropriate + // SetFooBase function for ENCODING has been called. + bool UsableEncoding(DwarfPointerEncoding encoding) const; + + // Read an encoded pointer from BUFFER using ENCODING; return the + // absolute address it represents, and set *LEN to the pointer's + // length in bytes, including any padding for aligned pointers. + // + // This function calls 'abort' if ENCODING is invalid or refers to a + // base address this reader hasn't been given, so you should check + // with ValidEncoding and UsableEncoding first if you would rather + // die in a more helpful way. + uint64 ReadEncodedPointer(const uint8_t *buffer, + DwarfPointerEncoding encoding, + size_t *len) const; + + Endianness GetEndianness() const; + private: + + // Function pointer type for our address and offset readers. + typedef uint64 (ByteReader::*AddressReader)(const uint8_t *) const; + + // Read an offset from BUFFER and return it as an unsigned 64 bit + // integer. DWARF2/3 define offsets as either 4 or 8 bytes, + // generally depending on the amount of DWARF2/3 info present. + // This function pointer gets set by SetOffsetSize. + AddressReader offset_reader_; + + // Read an address from BUFFER and return it as an unsigned 64 bit + // integer. DWARF2/3 allow addresses to be any size from 0-255 + // bytes currently. Internally we support 4 and 8 byte addresses, + // and will CHECK on anything else. + // This function pointer gets set by SetAddressSize. + AddressReader address_reader_; + + Endianness endian_; + uint8 address_size_; + uint8 offset_size_; + + // Base addresses for Linux C++ exception handling data's encoded pointers. + bool have_section_base_, have_text_base_, have_data_base_; + bool have_function_base_; + uint64 section_base_, text_base_, data_base_, function_base_; + const uint8_t *buffer_base_; +}; + +} // namespace dwarf2reader + +#endif // COMMON_DWARF_BYTEREADER_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader_unittest.cc new file mode 100644 index 0000000000..e66062d1fe --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader_unittest.cc @@ -0,0 +1,707 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// bytereader_unittest.cc: Unit tests for dwarf2reader::ByteReader + +#include <stdint.h> + +#include <string> + +#include "breakpad_googletest_includes.h" +#include "common/dwarf/bytereader.h" +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/cfi_assembler.h" +#include "common/using_std_string.h" + +using dwarf2reader::ByteReader; +using dwarf2reader::DwarfPointerEncoding; +using dwarf2reader::ENDIANNESS_BIG; +using dwarf2reader::ENDIANNESS_LITTLE; +using google_breakpad::CFISection; +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; +using google_breakpad::test_assembler::Section; +using testing::Test; + +struct ReaderFixture { + string contents; + size_t pointer_size; +}; + +class Reader: public ReaderFixture, public Test { }; +class ReaderDeathTest: public ReaderFixture, public Test { }; + +TEST_F(Reader, SimpleConstructor) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + CFISection section(kBigEndian, 4); + section + .D8(0xc0) + .D16(0xcf0d) + .D32(0x96fdd219) + .D64(0xbbf55fef0825f117ULL) + .ULEB128(0xa0927048ba8121afULL) + .LEB128(-0x4f337badf4483f83LL) + .D32(0xfec319c9); + ASSERT_TRUE(section.GetContents(&contents)); + const uint8_t *data = reinterpret_cast<const uint8_t *>(contents.data()); + EXPECT_EQ(0xc0U, reader.ReadOneByte(data)); + EXPECT_EQ(0xcf0dU, reader.ReadTwoBytes(data + 1)); + EXPECT_EQ(0x96fdd219U, reader.ReadFourBytes(data + 3)); + EXPECT_EQ(0xbbf55fef0825f117ULL, reader.ReadEightBytes(data + 7)); + size_t leb128_size; + EXPECT_EQ(0xa0927048ba8121afULL, + reader.ReadUnsignedLEB128(data + 15, &leb128_size)); + EXPECT_EQ(10U, leb128_size); + EXPECT_EQ(-0x4f337badf4483f83LL, + reader.ReadSignedLEB128(data + 25, &leb128_size)); + EXPECT_EQ(10U, leb128_size); + EXPECT_EQ(0xfec319c9, reader.ReadAddress(data + 35)); +} + +TEST_F(Reader, ValidEncodings) { + ByteReader reader(ENDIANNESS_LITTLE); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_omit))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_aligned))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_funcrel))); + + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x05))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x07))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0d))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0f))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x51))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x60))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x70))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xf0))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xd0))); +} + +TEST_F(ReaderDeathTest, DW_EH_PE_omit) { + static const uint8_t data[] = { 42 }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + EXPECT_DEATH(reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_omit, + &pointer_size), + "encoding != DW_EH_PE_omit"); +} + +TEST_F(Reader, DW_EH_PE_absptr4) { + static const uint8_t data[] = { 0x27, 0x57, 0xea, 0x40 }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(4); + EXPECT_EQ(0x40ea5727U, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_absptr, + &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_absptr8) { + static const uint8_t data[] = { + 0x60, 0x27, 0x57, 0xea, 0x40, 0xc2, 0x98, 0x05, 0x01, 0x50 + }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(8); + EXPECT_EQ(0x010598c240ea5727ULL, + reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_absptr, + &pointer_size)); + EXPECT_EQ(8U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_uleb128) { + static const uint8_t data[] = { 0x81, 0x84, 0x4c }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(4); + EXPECT_EQ(0x130201U, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_uleb128, + &pointer_size)); + EXPECT_EQ(3U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_udata2) { + static const uint8_t data[] = { 0xf4, 0x8d }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + EXPECT_EQ(0xf48dU, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_udata2, + &pointer_size)); + EXPECT_EQ(2U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_udata4) { + static const uint8_t data[] = { 0xb2, 0x68, 0xa5, 0x62, 0x8f, 0x8b }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(8); + EXPECT_EQ(0xa5628f8b, + reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_udata4, + &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_udata8Addr8) { + static const uint8_t data[] = { + 0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe + }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(8); + EXPECT_EQ(0x8fed199f69047304ULL, + reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8, + &pointer_size)); + EXPECT_EQ(8U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_udata8Addr4) { + static const uint8_t data[] = { + 0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe + }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(4); + EXPECT_EQ(0x69047304ULL, + reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8, + &pointer_size)); + EXPECT_EQ(8U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_sleb128) { + static const uint8_t data[] = { 0x42, 0xff, 0xfb, 0x73 }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + EXPECT_EQ(-0x030201U & 0xffffffff, + reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sleb128, + &pointer_size)); + EXPECT_EQ(3U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_sdata2) { + static const uint8_t data[] = { 0xb9, 0xbf }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(8); + EXPECT_EQ(0xffffffffffffbfb9ULL, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_sdata2, + &pointer_size)); + EXPECT_EQ(2U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_sdata4) { + static const uint8_t data[] = { 0xa0, 0xca, 0xf2, 0xb8, 0xc2, 0xad }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(8); + EXPECT_EQ(0xffffffffadc2b8f2ULL, + reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_sdata4, + &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_sdata8) { + static const uint8_t data[] = { + 0xf6, 0x66, 0x57, 0x79, 0xe0, 0x0c, 0x9b, 0x26, 0x87 + }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(8); + EXPECT_EQ(0x87269b0ce0795766ULL, + reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sdata8, + &pointer_size)); + EXPECT_EQ(8U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_pcrel) { + static const uint8_t data[] = { + 0x4a, 0x8b, 0x1b, 0x14, 0xc8, 0xc4, 0x02, 0xce + }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + DwarfPointerEncoding encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_pcrel + | dwarf2reader::DW_EH_PE_absptr); + reader.SetCFIDataBase(0x89951377, data); + EXPECT_EQ(0x89951377 + 3 + 0x14c8c402, + reader.ReadEncodedPointer(data + 3, encoding, &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_textrel) { + static const uint8_t data[] = { + 0xd9, 0x0d, 0x05, 0x17, 0xc9, 0x7a, 0x42, 0x1e + }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(4); + reader.SetTextBase(0xb91beaf0); + DwarfPointerEncoding encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel + | dwarf2reader::DW_EH_PE_sdata2); + EXPECT_EQ((0xb91beaf0 + 0xffffc917) & 0xffffffff, + reader.ReadEncodedPointer(data + 3, encoding, &pointer_size)); + EXPECT_EQ(2U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_datarel) { + static const uint8_t data[] = { + 0x16, 0xf2, 0xbb, 0x82, 0x68, 0xa7, 0xbc, 0x39 + }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(8); + reader.SetDataBase(0xbef308bd25ce74f0ULL); + DwarfPointerEncoding encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_datarel + | dwarf2reader::DW_EH_PE_sleb128); + EXPECT_EQ(0xbef308bd25ce74f0ULL + 0xfffffffffffa013bULL, + reader.ReadEncodedPointer(data + 2, encoding, &pointer_size)); + EXPECT_EQ(3U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_funcrel) { + static const uint8_t data[] = { + 0x84, 0xf8, 0x14, 0x01, 0x61, 0xd1, 0x48, 0xc9 + }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + reader.SetFunctionBase(0x823c3520); + DwarfPointerEncoding encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_funcrel + | dwarf2reader::DW_EH_PE_udata2); + EXPECT_EQ(0x823c3520 + 0xd148, + reader.ReadEncodedPointer(data + 5, encoding, &pointer_size)); + EXPECT_EQ(2U, pointer_size); +} + +TEST(UsableBase, CFI) { + static const uint8_t data[] = { 0x42 }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetCFIDataBase(0xb31cbd20, data); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +TEST(UsableBase, Text) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetTextBase(0xa899ccb9); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +TEST(UsableBase, Data) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetDataBase(0xf7b10bcd); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +TEST(UsableBase, Function) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetFunctionBase(0xc2c0ed81); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +TEST(UsableBase, ClearFunction) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetFunctionBase(0xc2c0ed81); + reader.ClearFunctionBase(); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +struct AlignedFixture { + AlignedFixture() : reader(ENDIANNESS_BIG) { reader.SetAddressSize(4); } + static const uint8_t data[10]; + ByteReader reader; + size_t pointer_size; +}; + +const uint8_t AlignedFixture::data[10] = { + 0xfe, 0x6e, 0x93, 0xd8, 0x34, 0xd5, 0x1c, 0xd3, 0xac, 0x2b +}; + +class Aligned: public AlignedFixture, public Test { }; + +TEST_F(Aligned, DW_EH_PE_aligned0) { + reader.SetCFIDataBase(0xb440305c, data); + EXPECT_EQ(0xfe6e93d8U, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned1) { + reader.SetCFIDataBase(0xb440305d, data); + EXPECT_EQ(0xd834d51cU, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(7U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned2) { + reader.SetCFIDataBase(0xb440305e, data); + EXPECT_EQ(0x93d834d5U, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(6U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned3) { + reader.SetCFIDataBase(0xb440305f, data); + EXPECT_EQ(0x6e93d834U, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(5U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned11) { + reader.SetCFIDataBase(0xb4403061, data); + EXPECT_EQ(0xd834d51cU, + reader.ReadEncodedPointer(data + 1, + dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(6U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned30) { + reader.SetCFIDataBase(0xb4403063, data); + EXPECT_EQ(0x6e93d834U, + reader.ReadEncodedPointer(data + 1, + dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned23) { + reader.SetCFIDataBase(0xb4403062, data); + EXPECT_EQ(0x1cd3ac2bU, + reader.ReadEncodedPointer(data + 3, + dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(7U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned03) { + reader.SetCFIDataBase(0xb4403064, data); + EXPECT_EQ(0x34d51cd3U, + reader.ReadEncodedPointer(data + 3, + dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(5U, pointer_size); +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/cfi_assembler.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf/cfi_assembler.cc new file mode 100644 index 0000000000..2dc2208577 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/cfi_assembler.cc @@ -0,0 +1,204 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// cfi_assembler.cc: Implementation of google_breakpad::CFISection class. +// See cfi_assembler.h for details. + +#include "common/dwarf/cfi_assembler.h" + +#include <assert.h> +#include <stdlib.h> + +namespace google_breakpad { + +using dwarf2reader::DwarfPointerEncoding; + +CFISection &CFISection::CIEHeader(uint64_t code_alignment_factor, + int data_alignment_factor, + unsigned return_address_register, + uint8_t version, + const string &augmentation, + bool dwarf64, + uint8_t address_size, + uint8_t segment_size) { + assert(!entry_length_); + entry_length_ = new PendingLength(); + in_fde_ = false; + + if (dwarf64) { + D32(kDwarf64InitialLengthMarker); + D64(entry_length_->length); + entry_length_->start = Here(); + D64(eh_frame_ ? kEHFrame64CIEIdentifier : kDwarf64CIEIdentifier); + } else { + D32(entry_length_->length); + entry_length_->start = Here(); + D32(eh_frame_ ? kEHFrame32CIEIdentifier : kDwarf32CIEIdentifier); + } + D8(version); + AppendCString(augmentation); + if (version >= 4) { + D8(address_size); + D8(segment_size); + } + ULEB128(code_alignment_factor); + LEB128(data_alignment_factor); + if (version == 1) + D8(return_address_register); + else + ULEB128(return_address_register); + return *this; +} + +CFISection &CFISection::FDEHeader(Label cie_pointer, + uint64_t initial_location, + uint64_t address_range, + bool dwarf64) { + assert(!entry_length_); + entry_length_ = new PendingLength(); + in_fde_ = true; + fde_start_address_ = initial_location; + + if (dwarf64) { + D32(0xffffffff); + D64(entry_length_->length); + entry_length_->start = Here(); + if (eh_frame_) + D64(Here() - cie_pointer); + else + D64(cie_pointer); + } else { + D32(entry_length_->length); + entry_length_->start = Here(); + if (eh_frame_) + D32(Here() - cie_pointer); + else + D32(cie_pointer); + } + EncodedPointer(initial_location); + // The FDE length in an .eh_frame section uses the same encoding as the + // initial location, but ignores the base address (selected by the upper + // nybble of the encoding), as it's a length, not an address that can be + // made relative. + EncodedPointer(address_range, + DwarfPointerEncoding(pointer_encoding_ & 0x0f)); + return *this; +} + +CFISection &CFISection::FinishEntry() { + assert(entry_length_); + Align(address_size_, dwarf2reader::DW_CFA_nop); + entry_length_->length = Here() - entry_length_->start; + delete entry_length_; + entry_length_ = NULL; + in_fde_ = false; + return *this; +} + +CFISection &CFISection::EncodedPointer(uint64_t address, + DwarfPointerEncoding encoding, + const EncodedPointerBases &bases) { + // Omitted data is extremely easy to emit. + if (encoding == dwarf2reader::DW_EH_PE_omit) + return *this; + + // If (encoding & dwarf2reader::DW_EH_PE_indirect) != 0, then we assume + // that ADDRESS is the address at which the pointer is stored --- in + // other words, that bit has no effect on how we write the pointer. + encoding = DwarfPointerEncoding(encoding & ~dwarf2reader::DW_EH_PE_indirect); + + // Find the base address to which this pointer is relative. The upper + // nybble of the encoding specifies this. + uint64_t base; + switch (encoding & 0xf0) { + case dwarf2reader::DW_EH_PE_absptr: base = 0; break; + case dwarf2reader::DW_EH_PE_pcrel: base = bases.cfi + Size(); break; + case dwarf2reader::DW_EH_PE_textrel: base = bases.text; break; + case dwarf2reader::DW_EH_PE_datarel: base = bases.data; break; + case dwarf2reader::DW_EH_PE_funcrel: base = fde_start_address_; break; + case dwarf2reader::DW_EH_PE_aligned: base = 0; break; + default: abort(); + }; + + // Make ADDRESS relative. Yes, this is appropriate even for "absptr" + // values; see gcc/unwind-pe.h. + address -= base; + + // Align the pointer, if required. + if ((encoding & 0xf0) == dwarf2reader::DW_EH_PE_aligned) + Align(AddressSize()); + + // Append ADDRESS to this section in the appropriate form. For the + // fixed-width forms, we don't need to differentiate between signed and + // unsigned encodings, because ADDRESS has already been extended to 64 + // bits before it was passed to us. + switch (encoding & 0x0f) { + case dwarf2reader::DW_EH_PE_absptr: + Address(address); + break; + + case dwarf2reader::DW_EH_PE_uleb128: + ULEB128(address); + break; + + case dwarf2reader::DW_EH_PE_sleb128: + LEB128(address); + break; + + case dwarf2reader::DW_EH_PE_udata2: + case dwarf2reader::DW_EH_PE_sdata2: + D16(address); + break; + + case dwarf2reader::DW_EH_PE_udata4: + case dwarf2reader::DW_EH_PE_sdata4: + D32(address); + break; + + case dwarf2reader::DW_EH_PE_udata8: + case dwarf2reader::DW_EH_PE_sdata8: + D64(address); + break; + + default: + abort(); + } + + return *this; +}; + +const uint32_t CFISection::kDwarf64InitialLengthMarker; +const uint32_t CFISection::kDwarf32CIEIdentifier; +const uint64_t CFISection::kDwarf64CIEIdentifier; +const uint32_t CFISection::kEHFrame32CIEIdentifier; +const uint64_t CFISection::kEHFrame64CIEIdentifier; + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/cfi_assembler.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf/cfi_assembler.h new file mode 100644 index 0000000000..bd7354d127 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/cfi_assembler.h @@ -0,0 +1,271 @@ +// -*- mode: C++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// cfi_assembler.h: Define CFISection, a class for creating properly +// (and improperly) formatted DWARF CFI data for unit tests. + +#ifndef PROCESSOR_CFI_ASSEMBLER_H_ +#define PROCESSOR_CFI_ASSEMBLER_H_ + +#include <string> + +#include "common/dwarf/dwarf2enums.h" +#include "common/test_assembler.h" +#include "common/using_std_string.h" +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +using dwarf2reader::DwarfPointerEncoding; +using google_breakpad::test_assembler::Endianness; +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::Section; + +class CFISection: public Section { + public: + + // CFI augmentation strings beginning with 'z', defined by the + // Linux/IA-64 C++ ABI, can specify interesting encodings for + // addresses appearing in FDE headers and call frame instructions (and + // for additional fields whose presence the augmentation string + // specifies). In particular, pointers can be specified to be relative + // to various base address: the start of the .text section, the + // location holding the address itself, and so on. These allow the + // frame data to be position-independent even when they live in + // write-protected pages. These variants are specified at the + // following two URLs: + // + // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html + // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html + // + // CFISection leaves the production of well-formed 'z'-augmented CIEs and + // FDEs to the user, but does provide EncodedPointer, to emit + // properly-encoded addresses for a given pointer encoding. + // EncodedPointer uses an instance of this structure to find the base + // addresses it should use; you can establish a default for all encoded + // pointers appended to this section with SetEncodedPointerBases. + struct EncodedPointerBases { + EncodedPointerBases() : cfi(), text(), data() { } + + // The starting address of this CFI section in memory, for + // DW_EH_PE_pcrel. DW_EH_PE_pcrel pointers may only be used in data + // that has is loaded into the program's address space. + uint64_t cfi; + + // The starting address of this file's .text section, for DW_EH_PE_textrel. + uint64_t text; + + // The starting address of this file's .got or .eh_frame_hdr section, + // for DW_EH_PE_datarel. + uint64_t data; + }; + + // Create a CFISection whose endianness is ENDIANNESS, and where + // machine addresses are ADDRESS_SIZE bytes long. If EH_FRAME is + // true, use the .eh_frame format, as described by the Linux + // Standards Base Core Specification, instead of the DWARF CFI + // format. + CFISection(Endianness endianness, size_t address_size, + bool eh_frame = false) + : Section(endianness), address_size_(address_size), eh_frame_(eh_frame), + pointer_encoding_(dwarf2reader::DW_EH_PE_absptr), + encoded_pointer_bases_(), entry_length_(NULL), in_fde_(false) { + // The 'start', 'Here', and 'Mark' members of a CFISection all refer + // to section offsets. + start() = 0; + } + + // Return this CFISection's address size. + size_t AddressSize() const { return address_size_; } + + // Return true if this CFISection uses the .eh_frame format, or + // false if it contains ordinary DWARF CFI data. + bool ContainsEHFrame() const { return eh_frame_; } + + // Use ENCODING for pointers in calls to FDEHeader and EncodedPointer. + void SetPointerEncoding(DwarfPointerEncoding encoding) { + pointer_encoding_ = encoding; + } + + // Use the addresses in BASES as the base addresses for encoded + // pointers in subsequent calls to FDEHeader or EncodedPointer. + // This function makes a copy of BASES. + void SetEncodedPointerBases(const EncodedPointerBases &bases) { + encoded_pointer_bases_ = bases; + } + + // Append a Common Information Entry header to this section with the + // given values. If dwarf64 is true, use the 64-bit DWARF initial + // length format for the CIE's initial length. Return a reference to + // this section. You should call FinishEntry after writing the last + // instruction for the CIE. + // + // Before calling this function, you will typically want to use Mark + // or Here to make a label to pass to FDEHeader that refers to this + // CIE's position in the section. + CFISection &CIEHeader(uint64_t code_alignment_factor, + int data_alignment_factor, + unsigned return_address_register, + uint8_t version = 3, + const string &augmentation = "", + bool dwarf64 = false, + uint8_t address_size = 8, + uint8_t segment_size = 0); + + // Append a Frame Description Entry header to this section with the + // given values. If dwarf64 is true, use the 64-bit DWARF initial + // length format for the CIE's initial length. Return a reference to + // this section. You should call FinishEntry after writing the last + // instruction for the CIE. + // + // This function doesn't support entries that are longer than + // 0xffffff00 bytes. (The "initial length" is always a 32-bit + // value.) Nor does it support .debug_frame sections longer than + // 0xffffff00 bytes. + CFISection &FDEHeader(Label cie_pointer, + uint64_t initial_location, + uint64_t address_range, + bool dwarf64 = false); + + // Note the current position as the end of the last CIE or FDE we + // started, after padding with DW_CFA_nops for alignment. This + // defines the label representing the entry's length, cited in the + // entry's header. Return a reference to this section. + CFISection &FinishEntry(); + + // Append the contents of BLOCK as a DW_FORM_block value: an + // unsigned LEB128 length, followed by that many bytes of data. + CFISection &Block(const string &block) { + ULEB128(block.size()); + Append(block); + return *this; + } + + // Append ADDRESS to this section, in the appropriate size and + // endianness. Return a reference to this section. + CFISection &Address(uint64_t address) { + Section::Append(endianness(), address_size_, address); + return *this; + } + CFISection &Address(Label address) { + Section::Append(endianness(), address_size_, address); + return *this; + } + + // Append ADDRESS to this section, using ENCODING and BASES. ENCODING + // defaults to this section's default encoding, established by + // SetPointerEncoding. BASES defaults to this section's bases, set by + // SetEncodedPointerBases. If the DW_EH_PE_indirect bit is set in the + // encoding, assume that ADDRESS is where the true address is stored. + // Return a reference to this section. + // + // (C++ doesn't let me use default arguments here, because I want to + // refer to members of *this in the default argument expression.) + CFISection &EncodedPointer(uint64_t address) { + return EncodedPointer(address, pointer_encoding_, encoded_pointer_bases_); + } + CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding) { + return EncodedPointer(address, encoding, encoded_pointer_bases_); + } + CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding, + const EncodedPointerBases &bases); + + // Restate some member functions, to keep chaining working nicely. + CFISection &Mark(Label *label) { Section::Mark(label); return *this; } + CFISection &D8(uint8_t v) { Section::D8(v); return *this; } + CFISection &D16(uint16_t v) { Section::D16(v); return *this; } + CFISection &D16(Label v) { Section::D16(v); return *this; } + CFISection &D32(uint32_t v) { Section::D32(v); return *this; } + CFISection &D32(const Label &v) { Section::D32(v); return *this; } + CFISection &D64(uint64_t v) { Section::D64(v); return *this; } + CFISection &D64(const Label &v) { Section::D64(v); return *this; } + CFISection &LEB128(long long v) { Section::LEB128(v); return *this; } + CFISection &ULEB128(uint64_t v) { Section::ULEB128(v); return *this; } + + private: + // A length value that we've appended to the section, but is not yet + // known. LENGTH is the appended value; START is a label referring + // to the start of the data whose length was cited. + struct PendingLength { + Label length; + Label start; + }; + + // Constants used in CFI/.eh_frame data: + + // If the first four bytes of an "initial length" are this constant, then + // the data uses the 64-bit DWARF format, and the length itself is the + // subsequent eight bytes. + static const uint32_t kDwarf64InitialLengthMarker = 0xffffffffU; + + // The CIE identifier for 32- and 64-bit DWARF CFI and .eh_frame data. + static const uint32_t kDwarf32CIEIdentifier = ~(uint32_t)0; + static const uint64_t kDwarf64CIEIdentifier = ~(uint64_t)0; + static const uint32_t kEHFrame32CIEIdentifier = 0; + static const uint64_t kEHFrame64CIEIdentifier = 0; + + // The size of a machine address for the data in this section. + size_t address_size_; + + // If true, we are generating a Linux .eh_frame section, instead of + // a standard DWARF .debug_frame section. + bool eh_frame_; + + // The encoding to use for FDE pointers. + DwarfPointerEncoding pointer_encoding_; + + // The base addresses to use when emitting encoded pointers. + EncodedPointerBases encoded_pointer_bases_; + + // The length value for the current entry. + // + // Oddly, this must be dynamically allocated. Labels never get new + // values; they only acquire constraints on the value they already + // have, or assert if you assign them something incompatible. So + // each header needs truly fresh Label objects to cite in their + // headers and track their positions. The alternative is explicit + // destructor invocation and a placement new. Ick. + PendingLength *entry_length_; + + // True if we are currently emitting an FDE --- that is, we have + // called FDEHeader but have not yet called FinishEntry. + bool in_fde_; + + // If in_fde_ is true, this is its starting address. We use this for + // emitting DW_EH_PE_funcrel pointers. + uint64_t fde_start_address_; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_CFI_ASSEMBLER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.cc new file mode 100644 index 0000000000..94542b5ea9 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.cc @@ -0,0 +1,199 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// dwarf2diehandler.cc: Implement the dwarf2reader::DieDispatcher class. +// See dwarf2diehandler.h for details. + +#include <assert.h> +#include <stdint.h> + +#include <string> + +#include "common/dwarf/dwarf2diehandler.h" +#include "common/using_std_string.h" + +namespace dwarf2reader { + +DIEDispatcher::~DIEDispatcher() { + while (!die_handlers_.empty()) { + HandlerStack &entry = die_handlers_.top(); + if (entry.handler_ != root_handler_) + delete entry.handler_; + die_handlers_.pop(); + } +} + +bool DIEDispatcher::StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version) { + return root_handler_->StartCompilationUnit(offset, address_size, + offset_size, cu_length, + dwarf_version); +} + +bool DIEDispatcher::StartDIE(uint64 offset, enum DwarfTag tag) { + // The stack entry for the parent of this DIE, if there is one. + HandlerStack *parent = die_handlers_.empty() ? NULL : &die_handlers_.top(); + + // Does this call indicate that we're done receiving the parent's + // attributes' values? If so, call its EndAttributes member function. + if (parent && parent->handler_ && !parent->reported_attributes_end_) { + parent->reported_attributes_end_ = true; + if (!parent->handler_->EndAttributes()) { + // Finish off this handler now. and edit *PARENT to indicate that + // we don't want to visit any of the children. + parent->handler_->Finish(); + if (parent->handler_ != root_handler_) + delete parent->handler_; + parent->handler_ = NULL; + return false; + } + } + + // Find a handler for this DIE. + DIEHandler *handler; + if (parent) { + if (parent->handler_) + // Ask the parent to find a handler. + handler = parent->handler_->FindChildHandler(offset, tag); + else + // No parent handler means we're not interested in any of our + // children. + handler = NULL; + } else { + // This is the root DIE. For a non-root DIE, the parent's handler + // decides whether to visit it, but the root DIE has no parent + // handler, so we have a special method on the root DIE handler + // itself to decide. + if (root_handler_->StartRootDIE(offset, tag)) + handler = root_handler_; + else + handler = NULL; + } + + // Push a handler stack entry for this new handler. As an + // optimization, we don't push NULL-handler entries on top of other + // NULL-handler entries; we just let the oldest such entry stand for + // the whole subtree. + if (handler || !parent || parent->handler_) { + HandlerStack entry; + entry.offset_ = offset; + entry.handler_ = handler; + entry.reported_attributes_end_ = false; + die_handlers_.push(entry); + } + + return handler != NULL; +} + +void DIEDispatcher::EndDIE(uint64 offset) { + assert(!die_handlers_.empty()); + HandlerStack *entry = &die_handlers_.top(); + if (entry->handler_) { + // This entry had better be the handler for this DIE. + assert(entry->offset_ == offset); + // If a DIE has no children, this EndDIE call indicates that we're + // done receiving its attributes' values. + if (!entry->reported_attributes_end_) + entry->handler_->EndAttributes(); // Ignore return value: no children. + entry->handler_->Finish(); + if (entry->handler_ != root_handler_) + delete entry->handler_; + } else { + // If this DIE is within a tree we're ignoring, then don't pop the + // handler stack: that entry stands for the whole tree. + if (entry->offset_ != offset) + return; + } + die_handlers_.pop(); +} + +void DIEDispatcher::ProcessAttributeUnsigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeUnsigned(attr, form, data); +} + +void DIEDispatcher::ProcessAttributeSigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeSigned(attr, form, data); +} + +void DIEDispatcher::ProcessAttributeReference(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeReference(attr, form, data); +} + +void DIEDispatcher::ProcessAttributeBuffer(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const uint8_t *data, + uint64 len) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeBuffer(attr, form, data, len); +} + +void DIEDispatcher::ProcessAttributeString(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const string& data) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeString(attr, form, data); +} + +void DIEDispatcher::ProcessAttributeSignature(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 signature) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeSignature(attr, form, signature); +} + +} // namespace dwarf2reader diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.h new file mode 100644 index 0000000000..a1e589a863 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.h @@ -0,0 +1,365 @@ +// -*- mode: c++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// dwarf2reader::CompilationUnit is a simple and direct parser for +// DWARF data, but its handler interface is not convenient to use. In +// particular: +// +// - CompilationUnit calls Dwarf2Handler's member functions to report +// every attribute's value, regardless of what sort of DIE it is. +// As a result, the ProcessAttributeX functions end up looking like +// this: +// +// switch (parent_die_tag) { +// case DW_TAG_x: +// switch (attribute_name) { +// case DW_AT_y: +// handle attribute y of DIE type x +// ... +// } break; +// ... +// } +// +// In C++ it's much nicer to use virtual function dispatch to find +// the right code for a given case than to switch on the DIE tag +// like this. +// +// - Processing different kinds of DIEs requires different sets of +// data: lexical block DIEs have start and end addresses, but struct +// type DIEs don't. It would be nice to be able to have separate +// handler classes for separate kinds of DIEs, each with the members +// appropriate to its role, instead of having one handler class that +// needs to hold data for every DIE type. +// +// - There should be a separate instance of the appropriate handler +// class for each DIE, instead of a single object with tables +// tracking all the dies in the compilation unit. +// +// - It's not convenient to take some action after all a DIE's +// attributes have been seen, but before visiting any of its +// children. The only indication you have that a DIE's attribute +// list is complete is that you get either a StartDIE or an EndDIE +// call. +// +// - It's not convenient to make use of the tree structure of the +// DIEs. Skipping all the children of a given die requires +// maintaining state and returning false from StartDIE until we get +// an EndDIE call with the appropriate offset. +// +// This interface tries to take care of all that. (You're shocked, I'm sure.) +// +// Using the classes here, you provide an initial handler for the root +// DIE of the compilation unit. Each handler receives its DIE's +// attributes, and provides fresh handler objects for children of +// interest, if any. The three classes are: +// +// - DIEHandler: the base class for your DIE-type-specific handler +// classes. +// +// - RootDIEHandler: derived from DIEHandler, the base class for your +// root DIE handler class. +// +// - DIEDispatcher: derived from Dwarf2Handler, an instance of this +// invokes your DIE-type-specific handler objects. +// +// In detail: +// +// - Define handler classes specialized for the DIE types you're +// interested in. These handler classes must inherit from +// DIEHandler. Thus: +// +// class My_DW_TAG_X_Handler: public DIEHandler { ... }; +// class My_DW_TAG_Y_Handler: public DIEHandler { ... }; +// +// DIEHandler subclasses needn't correspond exactly to single DIE +// types, as shown here; the point is that you can have several +// different classes appropriate to different kinds of DIEs. +// +// - In particular, define a handler class for the compilation +// unit's root DIE, that inherits from RootDIEHandler: +// +// class My_DW_TAG_compile_unit_Handler: public RootDIEHandler { ... }; +// +// RootDIEHandler inherits from DIEHandler, adding a few additional +// member functions for examining the compilation unit as a whole, +// and other quirks of rootness. +// +// - Then, create a DIEDispatcher instance, passing it an instance of +// your root DIE handler class, and use that DIEDispatcher as the +// dwarf2reader::CompilationUnit's handler: +// +// My_DW_TAG_compile_unit_Handler root_die_handler(...); +// DIEDispatcher die_dispatcher(&root_die_handler); +// CompilationUnit reader(sections, offset, bytereader, &die_dispatcher); +// +// Here, 'die_dispatcher' acts as a shim between 'reader' and the +// various DIE-specific handlers you have defined. +// +// - When you call reader.Start(), die_dispatcher behaves as follows, +// starting with your root die handler and the compilation unit's +// root DIE: +// +// - It calls the handler's ProcessAttributeX member functions for +// each of the DIE's attributes. +// +// - It calls the handler's EndAttributes member function. This +// should return true if any of the DIE's children should be +// visited, in which case: +// +// - For each of the DIE's children, die_dispatcher calls the +// DIE's handler's FindChildHandler member function. If that +// returns a pointer to a DIEHandler instance, then +// die_dispatcher uses that handler to process the child, using +// this procedure recursively. Alternatively, if +// FindChildHandler returns NULL, die_dispatcher ignores that +// child and its descendants. +// +// - When die_dispatcher has finished processing all the DIE's +// children, it invokes the handler's Finish() member function, +// and destroys the handler. (As a special case, it doesn't +// destroy the root DIE handler.) +// +// This allows the code for handling a particular kind of DIE to be +// gathered together in a single class, makes it easy to skip all the +// children or individual children of a particular DIE, and provides +// appropriate parental context for each die. + +#ifndef COMMON_DWARF_DWARF2DIEHANDLER_H__ +#define COMMON_DWARF_DWARF2DIEHANDLER_H__ + +#include <stdint.h> + +#include <stack> +#include <string> + +#include "common/dwarf/types.h" +#include "common/dwarf/dwarf2enums.h" +#include "common/dwarf/dwarf2reader.h" +#include "common/using_std_string.h" + +namespace dwarf2reader { + +// A base class for handlers for specific DIE types. The series of +// calls made on a DIE handler is as follows: +// +// - for each attribute of the DIE: +// - ProcessAttributeX() +// - EndAttributes() +// - if that returned true, then for each child: +// - FindChildHandler() +// - if that returns a non-NULL pointer to a new handler: +// - recurse, with the new handler and the child die +// - Finish() +// - destruction +class DIEHandler { + public: + DIEHandler() { } + virtual ~DIEHandler() { } + + // When we visit a DIE, we first use these member functions to + // report the DIE's attributes and their values. These have the + // same restrictions as the corresponding member functions of + // dwarf2reader::Dwarf2Handler. + // + // Since DWARF does not specify in what order attributes must + // appear, avoid making decisions in these functions that would be + // affected by the presence of other attributes. The EndAttributes + // function is a more appropriate place for such work, as all the + // DIE's attributes have been seen at that point. + // + // The default definitions ignore the values they are passed. + virtual void ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { } + virtual void ProcessAttributeSigned(enum DwarfAttribute attr, + enum DwarfForm form, + int64 data) { } + virtual void ProcessAttributeReference(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { } + virtual void ProcessAttributeBuffer(enum DwarfAttribute attr, + enum DwarfForm form, + const uint8_t *data, + uint64 len) { } + virtual void ProcessAttributeString(enum DwarfAttribute attr, + enum DwarfForm form, + const string& data) { } + virtual void ProcessAttributeSignature(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 signture) { } + + // Once we have reported all the DIE's attributes' values, we call + // this member function. If it returns false, we skip all the DIE's + // children. If it returns true, we call FindChildHandler on each + // child. If that returns a handler object, we use that to visit + // the child; otherwise, we skip the child. + // + // This is a good place to make decisions that depend on more than + // one attribute. DWARF does not specify in what order attributes + // must appear, so only when the EndAttributes function is called + // does the handler have a complete picture of the DIE's attributes. + // + // The default definition elects to ignore the DIE's children. + // You'll need to override this if you override FindChildHandler, + // but at least the default behavior isn't to pass the children to + // FindChildHandler, which then ignores them all. + virtual bool EndAttributes() { return false; } + + // If EndAttributes returns true to indicate that some of the DIE's + // children might be of interest, then we apply this function to + // each of the DIE's children. If it returns a handler object, then + // we use that to visit the child DIE. If it returns NULL, we skip + // that child DIE (and all its descendants). + // + // OFFSET is the offset of the child; TAG indicates what kind of DIE + // it is. + // + // The default definition skips all children. + virtual DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag) { + return NULL; + } + + // When we are done processing a DIE, we call this member function. + // This happens after the EndAttributes call, all FindChildHandler + // calls (if any), and all operations on the children themselves (if + // any). We call Finish on every handler --- even if EndAttributes + // returns false. + virtual void Finish() { }; +}; + +// A subclass of DIEHandler, with additional kludges for handling the +// compilation unit's root die. +class RootDIEHandler: public DIEHandler { + public: + RootDIEHandler() { } + virtual ~RootDIEHandler() { } + + // We pass the values reported via Dwarf2Handler::StartCompilationUnit + // to this member function, and skip the entire compilation unit if it + // returns false. So the root DIE handler is actually also + // responsible for handling the compilation unit metadata. + // The default definition always visits the compilation unit. + virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version) { return true; } + + // For the root DIE handler only, we pass the offset, tag and + // attributes of the compilation unit's root DIE. This is the only + // way the root DIE handler can find the root DIE's tag. If this + // function returns true, we will visit the root DIE using the usual + // DIEHandler methods; otherwise, we skip the entire compilation + // unit. + // + // The default definition elects to visit the root DIE. + virtual bool StartRootDIE(uint64 offset, enum DwarfTag tag) { return true; } +}; + +class DIEDispatcher: public Dwarf2Handler { + public: + // Create a Dwarf2Handler which uses ROOT_HANDLER as the handler for + // the compilation unit's root die, as described for the DIEHandler + // class. + DIEDispatcher(RootDIEHandler *root_handler) : root_handler_(root_handler) { } + // Destroying a DIEDispatcher destroys all active handler objects + // except the root handler. + ~DIEDispatcher(); + bool StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version); + bool StartDIE(uint64 offset, enum DwarfTag tag); + void ProcessAttributeUnsigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + void ProcessAttributeSigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data); + void ProcessAttributeReference(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + void ProcessAttributeBuffer(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const uint8_t *data, + uint64 len); + void ProcessAttributeString(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const string &data); + void ProcessAttributeSignature(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 signature); + void EndDIE(uint64 offset); + + private: + + // The type of a handler stack entry. This includes some fields + // which don't really need to be on the stack --- they could just be + // single data members of DIEDispatcher --- but putting them here + // makes it easier to see that the code is correct. + struct HandlerStack { + // The offset of the DIE for this handler stack entry. + uint64 offset_; + + // The handler object interested in this DIE's attributes and + // children. If NULL, we're not interested in either. + DIEHandler *handler_; + + // Have we reported the end of this DIE's attributes to the handler? + bool reported_attributes_end_; + }; + + // Stack of DIE attribute handlers. At StartDIE(D), the top of the + // stack is the handler of D's parent, whom we may ask for a handler + // for D itself. At EndDIE(D), the top of the stack is D's handler. + // Special cases: + // + // - Before we've seen the compilation unit's root DIE, the stack is + // empty; we'll call root_handler_'s special member functions, and + // perhaps push root_handler_ on the stack to look at the root's + // immediate children. + // + // - When we decide to ignore a subtree, we only push an entry on + // the stack for the root of the tree being ignored, rather than + // pushing lots of stack entries with handler_ set to NULL. + std::stack<HandlerStack> die_handlers_; + + // The root handler. We don't push it on die_handlers_ until we + // actually get the StartDIE call for the root. + RootDIEHandler *root_handler_; +}; + +} // namespace dwarf2reader +#endif // COMMON_DWARF_DWARF2DIEHANDLER_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc new file mode 100644 index 0000000000..db70eb31b9 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc @@ -0,0 +1,527 @@ +// -*- mode: c++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher. + +#include <stdint.h> + +#include <string> +#include <utility> + +#include "breakpad_googletest_includes.h" + +#include "common/dwarf/dwarf2diehandler.h" +#include "common/using_std_string.h" + +using std::make_pair; + +using ::testing::_; +using ::testing::ContainerEq; +using ::testing::ElementsAreArray; +using ::testing::Eq; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::Sequence; +using ::testing::StrEq; + +using dwarf2reader::DIEDispatcher; +using dwarf2reader::DIEHandler; +using dwarf2reader::DwarfAttribute; +using dwarf2reader::DwarfForm; +using dwarf2reader::DwarfTag; +using dwarf2reader::RootDIEHandler; + +class MockDIEHandler: public DIEHandler { + public: + MOCK_METHOD3(ProcessAttributeUnsigned, + void(DwarfAttribute, DwarfForm, uint64)); + MOCK_METHOD3(ProcessAttributeSigned, + void(DwarfAttribute, DwarfForm, int64)); + MOCK_METHOD3(ProcessAttributeReference, + void(DwarfAttribute, DwarfForm, uint64)); + MOCK_METHOD4(ProcessAttributeBuffer, + void(DwarfAttribute, DwarfForm, const uint8_t *, uint64)); + MOCK_METHOD3(ProcessAttributeString, + void(DwarfAttribute, DwarfForm, const string &)); + MOCK_METHOD3(ProcessAttributeSignature, + void(DwarfAttribute, DwarfForm, uint64)); + MOCK_METHOD0(EndAttributes, bool()); + MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag)); + MOCK_METHOD0(Finish, void()); +}; + +class MockRootDIEHandler: public RootDIEHandler { + public: + MOCK_METHOD3(ProcessAttributeUnsigned, + void(DwarfAttribute, DwarfForm, uint64)); + MOCK_METHOD3(ProcessAttributeSigned, + void(DwarfAttribute, DwarfForm, int64)); + MOCK_METHOD3(ProcessAttributeReference, + void(DwarfAttribute, DwarfForm, uint64)); + MOCK_METHOD4(ProcessAttributeBuffer, + void(DwarfAttribute, DwarfForm, const uint8_t *, uint64)); + MOCK_METHOD3(ProcessAttributeString, + void(DwarfAttribute, DwarfForm, const string &)); + MOCK_METHOD3(ProcessAttributeSignature, + void(DwarfAttribute, DwarfForm, uint64)); + MOCK_METHOD0(EndAttributes, bool()); + MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag)); + MOCK_METHOD0(Finish, void()); + MOCK_METHOD5(StartCompilationUnit, bool(uint64, uint8, uint8, uint64, uint8)); + MOCK_METHOD2(StartRootDIE, bool(uint64, DwarfTag)); +}; + +// If the handler elects to skip the compilation unit, the dispatcher +// should tell the reader so. +TEST(Dwarf2DIEHandler, SkipCompilationUnit) { + Sequence s; + MockRootDIEHandler mock_root_handler; + DIEDispatcher die_dispatcher(&mock_root_handler); + + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0x8d42aed77cfccf3eLL, + 0x89, 0xdc, + 0x2ecb4dc778a80f21LL, + 0x66)) + .InSequence(s) + .WillOnce(Return(false)); + + EXPECT_FALSE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL, + 0x89, 0xdc, + 0x2ecb4dc778a80f21LL, + 0x66)); +} + +// If the handler elects to skip the root DIE, the dispatcher should +// tell the reader so. +TEST(Dwarf2DIEHandler, SkipRootDIE) { + Sequence s; + MockRootDIEHandler mock_root_handler; + DIEDispatcher die_dispatcher(&mock_root_handler); + + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0xde8994029fc8b999LL, 0xf4, 0x02, + 0xb00febffa76e2b2bLL, 0x5c)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(mock_root_handler, + StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6)) + .InSequence(s) + .WillOnce(Return(false)); + + EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0xde8994029fc8b999LL, + 0xf4, 0x02, + 0xb00febffa76e2b2bLL, 0x5c)); + EXPECT_FALSE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL, + (DwarfTag) 0xb4f98da6)); + die_dispatcher.EndDIE(0x7d08242b4b510cf2LL); +} + +// If the handler elects to skip the root DIE's children, the +// dispatcher should tell the reader so --- and avoid deleting the +// root handler. +TEST(Dwarf2DIEHandler, SkipRootDIEChildren) { + MockRootDIEHandler mock_root_handler; + DIEDispatcher die_dispatcher(&mock_root_handler); + + { + InSequence s; + + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0x15d6897480cc65a7LL, 0x26, 0xa0, + 0x09f8bf0767f91675LL, 0xdb)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_root_handler, + StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6)) + .WillOnce(Return(true)); + // Please don't tell me about my children. + EXPECT_CALL(mock_root_handler, EndAttributes()) + .WillOnce(Return(false)); + EXPECT_CALL(mock_root_handler, Finish()) + .WillOnce(Return()); + } + + EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x15d6897480cc65a7LL, + 0x26, 0xa0, + 0x09f8bf0767f91675LL, 0xdb)); + EXPECT_TRUE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL, + (DwarfTag) 0xb4f98da6)); + EXPECT_FALSE(die_dispatcher.StartDIE(0x435150ceedccda18LL, + (DwarfTag) 0xc3a17bba)); + die_dispatcher.EndDIE(0x435150ceedccda18LL); + die_dispatcher.EndDIE(0x7d08242b4b510cf2LL); +} + +// The dispatcher should pass attribute values through to the die +// handler accurately. +TEST(Dwarf2DIEHandler, PassAttributeValues) { + MockRootDIEHandler mock_root_handler; + DIEDispatcher die_dispatcher(&mock_root_handler); + + const uint8_t buffer[10] = { + 0x24, 0x24, 0x35, 0x9a, 0xca, 0xcf, 0xa8, 0x84, 0xa7, 0x18 + }; + string str = "\xc8\x26\x2e\x0d\xa4\x9c\x37\xd6\xfb\x1d"; + + // Set expectations. + { + InSequence s; + + // We'll like the compilation unit header. + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0x8d42aed77cfccf3eLL, 0x89, 0xdc, + 0x2ecb4dc778a80f21LL, 0x66)) + .WillOnce(Return(true)); + + // We'll like the root DIE. + EXPECT_CALL(mock_root_handler, + StartRootDIE(0xe2222da01e29f2a9LL, (DwarfTag) 0x9829445c)) + .WillOnce(Return(true)); + + // Expect some attribute values. + EXPECT_CALL(mock_root_handler, + ProcessAttributeUnsigned((DwarfAttribute) 0x1cc0bfed, + (DwarfForm) 0x424f1468, + 0xa592571997facda1ULL)) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, + ProcessAttributeSigned((DwarfAttribute) 0x43694dc9, + (DwarfForm) 0xf6f78901L, + 0x92602a4e3bf1f446LL)) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, + ProcessAttributeReference((DwarfAttribute) 0x4033e8cL, + (DwarfForm) 0xf66fbe0bL, + 0x50fddef44734fdecULL)) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, + ProcessAttributeBuffer((DwarfAttribute) 0x25d7e0af, + (DwarfForm) 0xe99a539a, + buffer, sizeof(buffer))) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, + ProcessAttributeString((DwarfAttribute) 0x310ed065, + (DwarfForm) 0x15762fec, + StrEq(str))) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, + ProcessAttributeSignature((DwarfAttribute) 0x58790d72, + (DwarfForm) 0x4159f138, + 0x94682463613e6a5fULL)) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, EndAttributes()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_root_handler, FindChildHandler(_, _)) + .Times(0); + EXPECT_CALL(mock_root_handler, Finish()) + .WillOnce(Return()); + } + + // Drive the dispatcher. + + // Report the CU header. + EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL, + 0x89, 0xdc, + 0x2ecb4dc778a80f21LL, + 0x66)); + // Report the root DIE. + EXPECT_TRUE(die_dispatcher.StartDIE(0xe2222da01e29f2a9LL, + (DwarfTag) 0x9829445c)); + + // Report some attribute values. + die_dispatcher.ProcessAttributeUnsigned(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x1cc0bfed, + (DwarfForm) 0x424f1468, + 0xa592571997facda1ULL); + die_dispatcher.ProcessAttributeSigned(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x43694dc9, + (DwarfForm) 0xf6f78901, + 0x92602a4e3bf1f446LL); + die_dispatcher.ProcessAttributeReference(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x4033e8c, + (DwarfForm) 0xf66fbe0b, + 0x50fddef44734fdecULL); + die_dispatcher.ProcessAttributeBuffer(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x25d7e0af, + (DwarfForm) 0xe99a539a, + buffer, sizeof(buffer)); + die_dispatcher.ProcessAttributeString(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x310ed065, + (DwarfForm) 0x15762fec, + str); + die_dispatcher.ProcessAttributeSignature(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x58790d72, + (DwarfForm) 0x4159f138, + 0x94682463613e6a5fULL); + + // Finish the root DIE (and thus the CU). + die_dispatcher.EndDIE(0xe2222da01e29f2a9LL); +} + +TEST(Dwarf2DIEHandler, FindAndSkipChildren) { + MockRootDIEHandler mock_root_handler; + MockDIEHandler *mock_child1_handler = new(MockDIEHandler); + MockDIEHandler *mock_child3_handler = new(MockDIEHandler); + DIEDispatcher die_dispatcher(&mock_root_handler); + + { + InSequence s; + + // We'll like the compilation unit header. + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21, + 0x47dd3c764275a216LL, 0xa5)) + .WillOnce(Return(true)); + + // Root DIE. + { + EXPECT_CALL(mock_root_handler, + StartRootDIE(0x15f0e06bdfe3c372LL, (DwarfTag) 0xf5d60c59)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_root_handler, + ProcessAttributeSigned((DwarfAttribute) 0xf779a642, + (DwarfForm) 0x2cb63027, + 0x18e744661769d08fLL)) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, EndAttributes()) + .WillOnce(Return(true)); + + // First child DIE. + EXPECT_CALL(mock_root_handler, + FindChildHandler(0x149f644f8116fe8cLL, + (DwarfTag) 0xac2cbd8c)) + .WillOnce(Return(mock_child1_handler)); + { + EXPECT_CALL(*mock_child1_handler, + ProcessAttributeSigned((DwarfAttribute) 0xa6fd6f65, + (DwarfForm) 0xe4f64c41, + 0x1b04e5444a55fe67LL)) + .WillOnce(Return()); + EXPECT_CALL(*mock_child1_handler, EndAttributes()) + .WillOnce(Return(false)); + // Skip first grandchild DIE and first great-grandchild DIE. + EXPECT_CALL(*mock_child1_handler, Finish()) + .WillOnce(Return()); + } + + // Second child DIE. Root handler will decline to return a handler + // for this child. + EXPECT_CALL(mock_root_handler, + FindChildHandler(0x97412be24875de9dLL, + (DwarfTag) 0x505a068b)) + .WillOnce(Return((DIEHandler *) NULL)); + + // Third child DIE. + EXPECT_CALL(mock_root_handler, + FindChildHandler(0x753c964c8ab538aeLL, + (DwarfTag) 0x8c22970e)) + .WillOnce(Return(mock_child3_handler)); + { + EXPECT_CALL(*mock_child3_handler, + ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb, + (DwarfForm) 0x610b7ae1, + 0x3ea5c609d7d7560fLL)) + .WillOnce(Return()); + EXPECT_CALL(*mock_child3_handler, EndAttributes()) + .WillOnce(Return(true)); + EXPECT_CALL(*mock_child3_handler, Finish()) + .WillOnce(Return()); + } + + EXPECT_CALL(mock_root_handler, Finish()) + .WillOnce(Return()); + } + } + + + // Drive the dispatcher. + + // Report the CU header. + EXPECT_TRUE(die_dispatcher + .StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21, + 0x47dd3c764275a216LL, 0xa5)); + // Report the root DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0x15f0e06bdfe3c372LL, + (DwarfTag) 0xf5d60c59)); + die_dispatcher.ProcessAttributeSigned(0x15f0e06bdfe3c372LL, + (DwarfAttribute) 0xf779a642, + (DwarfForm) 0x2cb63027, + 0x18e744661769d08fLL); + + // First child DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0x149f644f8116fe8cLL, + (DwarfTag) 0xac2cbd8c)); + die_dispatcher.ProcessAttributeSigned(0x149f644f8116fe8cLL, + (DwarfAttribute) 0xa6fd6f65, + (DwarfForm) 0xe4f64c41, + 0x1b04e5444a55fe67LL); + + // First grandchild DIE. Will be skipped. + { + EXPECT_FALSE(die_dispatcher.StartDIE(0xd68de1ee0bd29419LL, + (DwarfTag) 0x22f05a15)); + // First great-grandchild DIE. Will be skipped without being + // mentioned to any handler. + { + EXPECT_FALSE(die_dispatcher + .StartDIE(0xb3076285d25cac25LL, + (DwarfTag) 0xcff4061b)); + die_dispatcher.EndDIE(0xb3076285d25cac25LL); + } + die_dispatcher.EndDIE(0xd68de1ee0bd29419LL); + } + die_dispatcher.EndDIE(0x149f644f8116fe8cLL); + } + + // Second child DIE. Root handler will decline to find a handler for it. + { + EXPECT_FALSE(die_dispatcher.StartDIE(0x97412be24875de9dLL, + (DwarfTag) 0x505a068b)); + die_dispatcher.EndDIE(0x97412be24875de9dLL); + } + + // Third child DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0x753c964c8ab538aeLL, + (DwarfTag) 0x8c22970e)); + die_dispatcher.ProcessAttributeSigned(0x753c964c8ab538aeLL, + (DwarfAttribute) 0x4e2b7cfb, + (DwarfForm) 0x610b7ae1, + 0x3ea5c609d7d7560fLL); + die_dispatcher.EndDIE(0x753c964c8ab538aeLL); + } + + // Finish the root DIE (and thus the CU). + die_dispatcher.EndDIE(0x15f0e06bdfe3c372LL); + } +} + +// The DIEDispatcher destructor is supposed to delete all handlers on +// the stack, except for the root. +TEST(Dwarf2DIEHandler, FreeHandlersOnStack) { + MockRootDIEHandler mock_root_handler; + MockDIEHandler *mock_child_handler = new(MockDIEHandler); + MockDIEHandler *mock_grandchild_handler = new(MockDIEHandler); + + { + InSequence s; + + // We'll like the compilation unit header. + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89, + 0x76d392ff393ddda2LL, 0xbf)) + .WillOnce(Return(true)); + + // Root DIE. + { + EXPECT_CALL(mock_root_handler, + StartRootDIE(0xbf13b761691ddc91LL, (DwarfTag) 0x98980361)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_root_handler, EndAttributes()) + .WillOnce(Return(true)); + + // Child DIE. + EXPECT_CALL(mock_root_handler, + FindChildHandler(0x058f09240c5fc8c9LL, + (DwarfTag) 0x898bf0d0)) + .WillOnce(Return(mock_child_handler)); + { + EXPECT_CALL(*mock_child_handler, EndAttributes()) + .WillOnce(Return(true)); + + // Grandchild DIE. + EXPECT_CALL(*mock_child_handler, + FindChildHandler(0x32dc00c9945dc0c8LL, + (DwarfTag) 0x2802d007)) + .WillOnce(Return(mock_grandchild_handler)); + { + EXPECT_CALL(*mock_grandchild_handler, + ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb, + (DwarfForm) 0x610b7ae1, + 0x3ea5c609d7d7560fLL)) + .WillOnce(Return()); + + // At this point, we abandon the traversal, so none of the + // usual stuff should get called. + EXPECT_CALL(*mock_grandchild_handler, EndAttributes()) + .Times(0); + EXPECT_CALL(*mock_grandchild_handler, Finish()) + .Times(0); + } + + EXPECT_CALL(*mock_child_handler, Finish()) + .Times(0); + } + + EXPECT_CALL(mock_root_handler, Finish()) + .Times(0); + } + } + + // The dispatcher. + DIEDispatcher die_dispatcher(&mock_root_handler); + + // Report the CU header. + EXPECT_TRUE(die_dispatcher + .StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89, + 0x76d392ff393ddda2LL, 0xbf)); + // Report the root DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0xbf13b761691ddc91LL, + (DwarfTag) 0x98980361)); + + // Child DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0x058f09240c5fc8c9LL, + (DwarfTag) 0x898bf0d0)); + + // Grandchild DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0x32dc00c9945dc0c8LL, + (DwarfTag) 0x2802d007)); + die_dispatcher.ProcessAttributeSigned(0x32dc00c9945dc0c8LL, + (DwarfAttribute) 0x4e2b7cfb, + (DwarfForm) 0x610b7ae1, + 0x3ea5c609d7d7560fLL); + + // Stop the traversal abruptly, so that there will still be + // handlers on the stack when the dispatcher is destructed. + + // No EndDIE call... + } + // No EndDIE call... + } + // No EndDIE call... + } +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2enums.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2enums.h new file mode 100644 index 0000000000..4316a89ccd --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2enums.h @@ -0,0 +1,679 @@ +// -*- mode: c++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef COMMON_DWARF_DWARF2ENUMS_H__ +#define COMMON_DWARF_DWARF2ENUMS_H__ + +namespace dwarf2reader { + +// These enums do not follow the google3 style only because they are +// known universally (specs, other implementations) by the names in +// exactly this capitalization. +// Tag names and codes. +enum DwarfTag { + DW_TAG_padding = 0x00, + DW_TAG_array_type = 0x01, + DW_TAG_class_type = 0x02, + DW_TAG_entry_point = 0x03, + DW_TAG_enumeration_type = 0x04, + DW_TAG_formal_parameter = 0x05, + DW_TAG_imported_declaration = 0x08, + DW_TAG_label = 0x0a, + DW_TAG_lexical_block = 0x0b, + DW_TAG_member = 0x0d, + DW_TAG_pointer_type = 0x0f, + DW_TAG_reference_type = 0x10, + DW_TAG_compile_unit = 0x11, + DW_TAG_string_type = 0x12, + DW_TAG_structure_type = 0x13, + DW_TAG_subroutine_type = 0x15, + DW_TAG_typedef = 0x16, + DW_TAG_union_type = 0x17, + DW_TAG_unspecified_parameters = 0x18, + DW_TAG_variant = 0x19, + DW_TAG_common_block = 0x1a, + DW_TAG_common_inclusion = 0x1b, + DW_TAG_inheritance = 0x1c, + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_module = 0x1e, + DW_TAG_ptr_to_member_type = 0x1f, + DW_TAG_set_type = 0x20, + DW_TAG_subrange_type = 0x21, + DW_TAG_with_stmt = 0x22, + DW_TAG_access_declaration = 0x23, + DW_TAG_base_type = 0x24, + DW_TAG_catch_block = 0x25, + DW_TAG_const_type = 0x26, + DW_TAG_constant = 0x27, + DW_TAG_enumerator = 0x28, + DW_TAG_file_type = 0x29, + DW_TAG_friend = 0x2a, + DW_TAG_namelist = 0x2b, + DW_TAG_namelist_item = 0x2c, + DW_TAG_packed_type = 0x2d, + DW_TAG_subprogram = 0x2e, + DW_TAG_template_type_param = 0x2f, + DW_TAG_template_value_param = 0x30, + DW_TAG_thrown_type = 0x31, + DW_TAG_try_block = 0x32, + DW_TAG_variant_part = 0x33, + DW_TAG_variable = 0x34, + DW_TAG_volatile_type = 0x35, + // DWARF 3. + DW_TAG_dwarf_procedure = 0x36, + DW_TAG_restrict_type = 0x37, + DW_TAG_interface_type = 0x38, + DW_TAG_namespace = 0x39, + DW_TAG_imported_module = 0x3a, + DW_TAG_unspecified_type = 0x3b, + DW_TAG_partial_unit = 0x3c, + DW_TAG_imported_unit = 0x3d, + // SGI/MIPS Extensions. + DW_TAG_MIPS_loop = 0x4081, + // HP extensions. See: + // ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz + DW_TAG_HP_array_descriptor = 0x4090, + // GNU extensions. + DW_TAG_format_label = 0x4101, // For FORTRAN 77 and Fortran 90. + DW_TAG_function_template = 0x4102, // For C++. + DW_TAG_class_template = 0x4103, // For C++. + DW_TAG_GNU_BINCL = 0x4104, + DW_TAG_GNU_EINCL = 0x4105, + // Extensions for UPC. See: http://upc.gwu.edu/~upc. + DW_TAG_upc_shared_type = 0x8765, + DW_TAG_upc_strict_type = 0x8766, + DW_TAG_upc_relaxed_type = 0x8767, + // PGI (STMicroelectronics) extensions. No documentation available. + DW_TAG_PGI_kanji_type = 0xA000, + DW_TAG_PGI_interface_block = 0xA020 +}; + + +enum DwarfHasChild { + DW_children_no = 0, + DW_children_yes = 1 +}; + +// Form names and codes. +enum DwarfForm { + DW_FORM_addr = 0x01, + DW_FORM_block2 = 0x03, + DW_FORM_block4 = 0x04, + DW_FORM_data2 = 0x05, + DW_FORM_data4 = 0x06, + DW_FORM_data8 = 0x07, + DW_FORM_string = 0x08, + DW_FORM_block = 0x09, + DW_FORM_block1 = 0x0a, + DW_FORM_data1 = 0x0b, + DW_FORM_flag = 0x0c, + DW_FORM_sdata = 0x0d, + DW_FORM_strp = 0x0e, + DW_FORM_udata = 0x0f, + DW_FORM_ref_addr = 0x10, + DW_FORM_ref1 = 0x11, + DW_FORM_ref2 = 0x12, + DW_FORM_ref4 = 0x13, + DW_FORM_ref8 = 0x14, + DW_FORM_ref_udata = 0x15, + DW_FORM_indirect = 0x16, + + // Added in DWARF 4: + DW_FORM_sec_offset = 0x17, + DW_FORM_exprloc = 0x18, + DW_FORM_flag_present = 0x19, + DW_FORM_ref_sig8 = 0x20, + // Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission. + DW_FORM_GNU_addr_index = 0x1f01, + DW_FORM_GNU_str_index = 0x1f02 +}; + +// Attribute names and codes +enum DwarfAttribute { + DW_AT_sibling = 0x01, + DW_AT_location = 0x02, + DW_AT_name = 0x03, + DW_AT_ordering = 0x09, + DW_AT_subscr_data = 0x0a, + DW_AT_byte_size = 0x0b, + DW_AT_bit_offset = 0x0c, + DW_AT_bit_size = 0x0d, + DW_AT_element_list = 0x0f, + DW_AT_stmt_list = 0x10, + DW_AT_low_pc = 0x11, + DW_AT_high_pc = 0x12, + DW_AT_language = 0x13, + DW_AT_member = 0x14, + DW_AT_discr = 0x15, + DW_AT_discr_value = 0x16, + DW_AT_visibility = 0x17, + DW_AT_import = 0x18, + DW_AT_string_length = 0x19, + DW_AT_common_reference = 0x1a, + DW_AT_comp_dir = 0x1b, + DW_AT_const_value = 0x1c, + DW_AT_containing_type = 0x1d, + DW_AT_default_value = 0x1e, + DW_AT_inline = 0x20, + DW_AT_is_optional = 0x21, + DW_AT_lower_bound = 0x22, + DW_AT_producer = 0x25, + DW_AT_prototyped = 0x27, + DW_AT_return_addr = 0x2a, + DW_AT_start_scope = 0x2c, + DW_AT_stride_size = 0x2e, + DW_AT_upper_bound = 0x2f, + DW_AT_abstract_origin = 0x31, + DW_AT_accessibility = 0x32, + DW_AT_address_class = 0x33, + DW_AT_artificial = 0x34, + DW_AT_base_types = 0x35, + DW_AT_calling_convention = 0x36, + DW_AT_count = 0x37, + DW_AT_data_member_location = 0x38, + DW_AT_decl_column = 0x39, + DW_AT_decl_file = 0x3a, + DW_AT_decl_line = 0x3b, + DW_AT_declaration = 0x3c, + DW_AT_discr_list = 0x3d, + DW_AT_encoding = 0x3e, + DW_AT_external = 0x3f, + DW_AT_frame_base = 0x40, + DW_AT_friend = 0x41, + DW_AT_identifier_case = 0x42, + DW_AT_macro_info = 0x43, + DW_AT_namelist_items = 0x44, + DW_AT_priority = 0x45, + DW_AT_segment = 0x46, + DW_AT_specification = 0x47, + DW_AT_static_link = 0x48, + DW_AT_type = 0x49, + DW_AT_use_location = 0x4a, + DW_AT_variable_parameter = 0x4b, + DW_AT_virtuality = 0x4c, + DW_AT_vtable_elem_location = 0x4d, + // DWARF 3 values. + DW_AT_allocated = 0x4e, + DW_AT_associated = 0x4f, + DW_AT_data_location = 0x50, + DW_AT_stride = 0x51, + DW_AT_entry_pc = 0x52, + DW_AT_use_UTF8 = 0x53, + DW_AT_extension = 0x54, + DW_AT_ranges = 0x55, + DW_AT_trampoline = 0x56, + DW_AT_call_column = 0x57, + DW_AT_call_file = 0x58, + DW_AT_call_line = 0x59, + // DWARF 4 + DW_AT_linkage_name = 0x6e, + // SGI/MIPS extensions. + DW_AT_MIPS_fde = 0x2001, + DW_AT_MIPS_loop_begin = 0x2002, + DW_AT_MIPS_tail_loop_begin = 0x2003, + DW_AT_MIPS_epilog_begin = 0x2004, + DW_AT_MIPS_loop_unroll_factor = 0x2005, + DW_AT_MIPS_software_pipeline_depth = 0x2006, + DW_AT_MIPS_linkage_name = 0x2007, + DW_AT_MIPS_stride = 0x2008, + DW_AT_MIPS_abstract_name = 0x2009, + DW_AT_MIPS_clone_origin = 0x200a, + DW_AT_MIPS_has_inlines = 0x200b, + // HP extensions. + DW_AT_HP_block_index = 0x2000, + DW_AT_HP_unmodifiable = 0x2001, // Same as DW_AT_MIPS_fde. + DW_AT_HP_actuals_stmt_list = 0x2010, + DW_AT_HP_proc_per_section = 0x2011, + DW_AT_HP_raw_data_ptr = 0x2012, + DW_AT_HP_pass_by_reference = 0x2013, + DW_AT_HP_opt_level = 0x2014, + DW_AT_HP_prof_version_id = 0x2015, + DW_AT_HP_opt_flags = 0x2016, + DW_AT_HP_cold_region_low_pc = 0x2017, + DW_AT_HP_cold_region_high_pc = 0x2018, + DW_AT_HP_all_variables_modifiable = 0x2019, + DW_AT_HP_linkage_name = 0x201a, + DW_AT_HP_prof_flags = 0x201b, // In comp unit of procs_info for -g. + // GNU extensions. + DW_AT_sf_names = 0x2101, + DW_AT_src_info = 0x2102, + DW_AT_mac_info = 0x2103, + DW_AT_src_coords = 0x2104, + DW_AT_body_begin = 0x2105, + DW_AT_body_end = 0x2106, + DW_AT_GNU_vector = 0x2107, + // Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission. + DW_AT_GNU_dwo_name = 0x2130, + DW_AT_GNU_dwo_id = 0x2131, + DW_AT_GNU_ranges_base = 0x2132, + DW_AT_GNU_addr_base = 0x2133, + DW_AT_GNU_pubnames = 0x2134, + DW_AT_GNU_pubtypes = 0x2135, + // VMS extensions. + DW_AT_VMS_rtnbeg_pd_address = 0x2201, + // UPC extension. + DW_AT_upc_threads_scaled = 0x3210, + // PGI (STMicroelectronics) extensions. + DW_AT_PGI_lbase = 0x3a00, + DW_AT_PGI_soffset = 0x3a01, + DW_AT_PGI_lstride = 0x3a02 +}; + + +// Line number opcodes. +enum DwarfLineNumberOps { + DW_LNS_extended_op = 0, + DW_LNS_copy = 1, + DW_LNS_advance_pc = 2, + DW_LNS_advance_line = 3, + DW_LNS_set_file = 4, + DW_LNS_set_column = 5, + DW_LNS_negate_stmt = 6, + DW_LNS_set_basic_block = 7, + DW_LNS_const_add_pc = 8, + DW_LNS_fixed_advance_pc = 9, + // DWARF 3. + DW_LNS_set_prologue_end = 10, + DW_LNS_set_epilogue_begin = 11, + DW_LNS_set_isa = 12 +}; + +// Line number extended opcodes. +enum DwarfLineNumberExtendedOps { + DW_LNE_end_sequence = 1, + DW_LNE_set_address = 2, + DW_LNE_define_file = 3, + // HP extensions. + DW_LNE_HP_negate_is_UV_update = 0x11, + DW_LNE_HP_push_context = 0x12, + DW_LNE_HP_pop_context = 0x13, + DW_LNE_HP_set_file_line_column = 0x14, + DW_LNE_HP_set_routine_name = 0x15, + DW_LNE_HP_set_sequence = 0x16, + DW_LNE_HP_negate_post_semantics = 0x17, + DW_LNE_HP_negate_function_exit = 0x18, + DW_LNE_HP_negate_front_end_logical = 0x19, + DW_LNE_HP_define_proc = 0x20 +}; + +// Type encoding names and codes +enum DwarfEncoding { + DW_ATE_address =0x1, + DW_ATE_boolean =0x2, + DW_ATE_complex_float =0x3, + DW_ATE_float =0x4, + DW_ATE_signed =0x5, + DW_ATE_signed_char =0x6, + DW_ATE_unsigned =0x7, + DW_ATE_unsigned_char =0x8, + // DWARF3/DWARF3f + DW_ATE_imaginary_float =0x9, + DW_ATE_packed_decimal =0xa, + DW_ATE_numeric_string =0xb, + DW_ATE_edited =0xc, + DW_ATE_signed_fixed =0xd, + DW_ATE_unsigned_fixed =0xe, + DW_ATE_decimal_float =0xf, + DW_ATE_lo_user =0x80, + DW_ATE_hi_user =0xff +}; + +// Location virtual machine opcodes +enum DwarfOpcode { + DW_OP_addr =0x03, + DW_OP_deref =0x06, + DW_OP_const1u =0x08, + DW_OP_const1s =0x09, + DW_OP_const2u =0x0a, + DW_OP_const2s =0x0b, + DW_OP_const4u =0x0c, + DW_OP_const4s =0x0d, + DW_OP_const8u =0x0e, + DW_OP_const8s =0x0f, + DW_OP_constu =0x10, + DW_OP_consts =0x11, + DW_OP_dup =0x12, + DW_OP_drop =0x13, + DW_OP_over =0x14, + DW_OP_pick =0x15, + DW_OP_swap =0x16, + DW_OP_rot =0x17, + DW_OP_xderef =0x18, + DW_OP_abs =0x19, + DW_OP_and =0x1a, + DW_OP_div =0x1b, + DW_OP_minus =0x1c, + DW_OP_mod =0x1d, + DW_OP_mul =0x1e, + DW_OP_neg =0x1f, + DW_OP_not =0x20, + DW_OP_or =0x21, + DW_OP_plus =0x22, + DW_OP_plus_uconst =0x23, + DW_OP_shl =0x24, + DW_OP_shr =0x25, + DW_OP_shra =0x26, + DW_OP_xor =0x27, + DW_OP_bra =0x28, + DW_OP_eq =0x29, + DW_OP_ge =0x2a, + DW_OP_gt =0x2b, + DW_OP_le =0x2c, + DW_OP_lt =0x2d, + DW_OP_ne =0x2e, + DW_OP_skip =0x2f, + DW_OP_lit0 =0x30, + DW_OP_lit1 =0x31, + DW_OP_lit2 =0x32, + DW_OP_lit3 =0x33, + DW_OP_lit4 =0x34, + DW_OP_lit5 =0x35, + DW_OP_lit6 =0x36, + DW_OP_lit7 =0x37, + DW_OP_lit8 =0x38, + DW_OP_lit9 =0x39, + DW_OP_lit10 =0x3a, + DW_OP_lit11 =0x3b, + DW_OP_lit12 =0x3c, + DW_OP_lit13 =0x3d, + DW_OP_lit14 =0x3e, + DW_OP_lit15 =0x3f, + DW_OP_lit16 =0x40, + DW_OP_lit17 =0x41, + DW_OP_lit18 =0x42, + DW_OP_lit19 =0x43, + DW_OP_lit20 =0x44, + DW_OP_lit21 =0x45, + DW_OP_lit22 =0x46, + DW_OP_lit23 =0x47, + DW_OP_lit24 =0x48, + DW_OP_lit25 =0x49, + DW_OP_lit26 =0x4a, + DW_OP_lit27 =0x4b, + DW_OP_lit28 =0x4c, + DW_OP_lit29 =0x4d, + DW_OP_lit30 =0x4e, + DW_OP_lit31 =0x4f, + DW_OP_reg0 =0x50, + DW_OP_reg1 =0x51, + DW_OP_reg2 =0x52, + DW_OP_reg3 =0x53, + DW_OP_reg4 =0x54, + DW_OP_reg5 =0x55, + DW_OP_reg6 =0x56, + DW_OP_reg7 =0x57, + DW_OP_reg8 =0x58, + DW_OP_reg9 =0x59, + DW_OP_reg10 =0x5a, + DW_OP_reg11 =0x5b, + DW_OP_reg12 =0x5c, + DW_OP_reg13 =0x5d, + DW_OP_reg14 =0x5e, + DW_OP_reg15 =0x5f, + DW_OP_reg16 =0x60, + DW_OP_reg17 =0x61, + DW_OP_reg18 =0x62, + DW_OP_reg19 =0x63, + DW_OP_reg20 =0x64, + DW_OP_reg21 =0x65, + DW_OP_reg22 =0x66, + DW_OP_reg23 =0x67, + DW_OP_reg24 =0x68, + DW_OP_reg25 =0x69, + DW_OP_reg26 =0x6a, + DW_OP_reg27 =0x6b, + DW_OP_reg28 =0x6c, + DW_OP_reg29 =0x6d, + DW_OP_reg30 =0x6e, + DW_OP_reg31 =0x6f, + DW_OP_breg0 =0x70, + DW_OP_breg1 =0x71, + DW_OP_breg2 =0x72, + DW_OP_breg3 =0x73, + DW_OP_breg4 =0x74, + DW_OP_breg5 =0x75, + DW_OP_breg6 =0x76, + DW_OP_breg7 =0x77, + DW_OP_breg8 =0x78, + DW_OP_breg9 =0x79, + DW_OP_breg10 =0x7a, + DW_OP_breg11 =0x7b, + DW_OP_breg12 =0x7c, + DW_OP_breg13 =0x7d, + DW_OP_breg14 =0x7e, + DW_OP_breg15 =0x7f, + DW_OP_breg16 =0x80, + DW_OP_breg17 =0x81, + DW_OP_breg18 =0x82, + DW_OP_breg19 =0x83, + DW_OP_breg20 =0x84, + DW_OP_breg21 =0x85, + DW_OP_breg22 =0x86, + DW_OP_breg23 =0x87, + DW_OP_breg24 =0x88, + DW_OP_breg25 =0x89, + DW_OP_breg26 =0x8a, + DW_OP_breg27 =0x8b, + DW_OP_breg28 =0x8c, + DW_OP_breg29 =0x8d, + DW_OP_breg30 =0x8e, + DW_OP_breg31 =0x8f, + DW_OP_regX =0x90, + DW_OP_fbreg =0x91, + DW_OP_bregX =0x92, + DW_OP_piece =0x93, + DW_OP_deref_size =0x94, + DW_OP_xderef_size =0x95, + DW_OP_nop =0x96, + // DWARF3/DWARF3f + DW_OP_push_object_address =0x97, + DW_OP_call2 =0x98, + DW_OP_call4 =0x99, + DW_OP_call_ref =0x9a, + DW_OP_form_tls_address =0x9b, + DW_OP_call_frame_cfa =0x9c, + DW_OP_bit_piece =0x9d, + DW_OP_lo_user =0xe0, + DW_OP_hi_user =0xff, + // GNU extensions + DW_OP_GNU_push_tls_address =0xe0, + // Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission. + DW_OP_GNU_addr_index =0xfb, + DW_OP_GNU_const_index =0xfc +}; + +// Section identifiers for DWP files +enum DwarfSectionId { + DW_SECT_INFO = 1, + DW_SECT_TYPES = 2, + DW_SECT_ABBREV = 3, + DW_SECT_LINE = 4, + DW_SECT_LOC = 5, + DW_SECT_STR_OFFSETS = 6, + DW_SECT_MACINFO = 7, + DW_SECT_MACRO = 8 +}; + +// Source languages. These are values for DW_AT_language. +enum DwarfLanguage + { + DW_LANG_none =0x0000, + DW_LANG_C89 =0x0001, + DW_LANG_C =0x0002, + DW_LANG_Ada83 =0x0003, + DW_LANG_C_plus_plus =0x0004, + DW_LANG_Cobol74 =0x0005, + DW_LANG_Cobol85 =0x0006, + DW_LANG_Fortran77 =0x0007, + DW_LANG_Fortran90 =0x0008, + DW_LANG_Pascal83 =0x0009, + DW_LANG_Modula2 =0x000a, + DW_LANG_Java =0x000b, + DW_LANG_C99 =0x000c, + DW_LANG_Ada95 =0x000d, + DW_LANG_Fortran95 =0x000e, + DW_LANG_PLI =0x000f, + DW_LANG_ObjC =0x0010, + DW_LANG_ObjC_plus_plus =0x0011, + DW_LANG_UPC =0x0012, + DW_LANG_D =0x0013, + DW_LANG_Rust =0x001c, + DW_LANG_Swift =0x001e, + // Implementation-defined language code range. + DW_LANG_lo_user = 0x8000, + DW_LANG_hi_user = 0xffff, + + // Extensions. + + // MIPS assembly language. The GNU toolchain uses this for all + // assembly languages, since there's no generic DW_LANG_ value for that. + // See include/dwarf2.h in the binutils, gdb, or gcc source trees. + DW_LANG_Mips_Assembler =0x8001, + DW_LANG_Upc =0x8765 // Unified Parallel C + }; + +// Inline codes. These are values for DW_AT_inline. +enum DwarfInline { + DW_INL_not_inlined =0x0, + DW_INL_inlined =0x1, + DW_INL_declared_not_inlined =0x2, + DW_INL_declared_inlined =0x3 +}; + +// Call Frame Info instructions. +enum DwarfCFI + { + DW_CFA_advance_loc = 0x40, + DW_CFA_offset = 0x80, + DW_CFA_restore = 0xc0, + DW_CFA_nop = 0x00, + DW_CFA_set_loc = 0x01, + DW_CFA_advance_loc1 = 0x02, + DW_CFA_advance_loc2 = 0x03, + DW_CFA_advance_loc4 = 0x04, + DW_CFA_offset_extended = 0x05, + DW_CFA_restore_extended = 0x06, + DW_CFA_undefined = 0x07, + DW_CFA_same_value = 0x08, + DW_CFA_register = 0x09, + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, + DW_CFA_def_cfa_register = 0x0d, + DW_CFA_def_cfa_offset = 0x0e, + DW_CFA_def_cfa_expression = 0x0f, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + + // Opcodes in this range are reserved for user extensions. + DW_CFA_lo_user = 0x1c, + DW_CFA_hi_user = 0x3f, + + // SGI/MIPS specific. + DW_CFA_MIPS_advance_loc8 = 0x1d, + + // GNU extensions. + DW_CFA_GNU_window_save = 0x2d, + DW_CFA_GNU_args_size = 0x2e, + DW_CFA_GNU_negative_offset_extended = 0x2f + }; + +// Exception handling 'z' augmentation letters. +enum DwarfZAugmentationCodes { + // If the CFI augmentation string begins with 'z', then the CIE and FDE + // have an augmentation data area just before the instructions, whose + // contents are determined by the subsequent augmentation letters. + DW_Z_augmentation_start = 'z', + + // If this letter is present in a 'z' augmentation string, the CIE + // augmentation data includes a pointer encoding, and the FDE + // augmentation data includes a language-specific data area pointer, + // represented using that encoding. + DW_Z_has_LSDA = 'L', + + // If this letter is present in a 'z' augmentation string, the CIE + // augmentation data includes a pointer encoding, followed by a pointer + // to a personality routine, represented using that encoding. + DW_Z_has_personality_routine = 'P', + + // If this letter is present in a 'z' augmentation string, the CIE + // augmentation data includes a pointer encoding describing how the FDE's + // initial location, address range, and DW_CFA_set_loc operands are + // encoded. + DW_Z_has_FDE_address_encoding = 'R', + + // If this letter is present in a 'z' augmentation string, then code + // addresses covered by FDEs that cite this CIE are signal delivery + // trampolines. Return addresses of frames in trampolines should not be + // adjusted as described in section 6.4.4 of the DWARF 3 spec. + DW_Z_is_signal_trampoline = 'S' +}; + +// Exception handling frame description pointer formats, as described +// by the Linux Standard Base Core Specification 4.0, section 11.5, +// DWARF Extensions. +enum DwarfPointerEncoding + { + DW_EH_PE_absptr = 0x00, + DW_EH_PE_omit = 0xff, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + + // The GNU toolchain sources define this enum value as well, + // simply to help classify the lower nybble values into signed and + // unsigned groups. + DW_EH_PE_signed = 0x08, + + // This is not documented in LSB 4.0, but it is used in both the + // Linux and OS X toolchains. It can be added to any other + // encoding (except DW_EH_PE_aligned), and indicates that the + // encoded value represents the address at which the true address + // is stored, not the true address itself. + DW_EH_PE_indirect = 0x80 + }; + +} // namespace dwarf2reader +#endif // COMMON_DWARF_DWARF2ENUMS_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.cc new file mode 100644 index 0000000000..3e6a3e89f4 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.cc @@ -0,0 +1,2815 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// CFI reader author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// Implementation of dwarf2reader::LineInfo, dwarf2reader::CompilationUnit, +// and dwarf2reader::CallFrameInfo. See dwarf2reader.h for details. + +#include "common/dwarf/dwarf2reader.h" + +#include <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <map> +#include <memory> +#include <stack> +#include <string> +#include <utility> + +#include <sys/stat.h> + +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/bytereader.h" +#include "common/dwarf/line_state_machine.h" +#include "common/using_std_string.h" + +namespace dwarf2reader { + +CompilationUnit::CompilationUnit(const string& path, + const SectionMap& sections, uint64 offset, + ByteReader* reader, Dwarf2Handler* handler) + : path_(path), offset_from_section_start_(offset), reader_(reader), + sections_(sections), handler_(handler), abbrevs_(), + string_buffer_(NULL), string_buffer_length_(0), + str_offsets_buffer_(NULL), str_offsets_buffer_length_(0), + addr_buffer_(NULL), addr_buffer_length_(0), + is_split_dwarf_(false), dwo_id_(0), dwo_name_(), + skeleton_dwo_id_(0), ranges_base_(0), addr_base_(0), + have_checked_for_dwp_(false), dwp_path_(), + dwp_byte_reader_(), dwp_reader_() {} + +// Initialize a compilation unit from a .dwo or .dwp file. +// In this case, we need the .debug_addr section from the +// executable file that contains the corresponding skeleton +// compilation unit. We also inherit the Dwarf2Handler from +// the executable file, and call it as if we were still +// processing the original compilation unit. + +void CompilationUnit::SetSplitDwarf(const uint8_t* addr_buffer, + uint64 addr_buffer_length, + uint64 addr_base, + uint64 ranges_base, + uint64 dwo_id) { + is_split_dwarf_ = true; + addr_buffer_ = addr_buffer; + addr_buffer_length_ = addr_buffer_length; + addr_base_ = addr_base; + ranges_base_ = ranges_base; + skeleton_dwo_id_ = dwo_id; +} + +// Read a DWARF2/3 abbreviation section. +// Each abbrev consists of a abbreviation number, a tag, a byte +// specifying whether the tag has children, and a list of +// attribute/form pairs. +// The list of forms is terminated by a 0 for the attribute, and a +// zero for the form. The entire abbreviation section is terminated +// by a zero for the code. + +void CompilationUnit::ReadAbbrevs() { + if (abbrevs_) + return; + + // First get the debug_abbrev section. ".debug_abbrev" is the name + // recommended in the DWARF spec, and used on Linux; + // "__debug_abbrev" is the name used in Mac OS X Mach-O files. + SectionMap::const_iterator iter = sections_.find(".debug_abbrev"); + if (iter == sections_.end()) + iter = sections_.find("__debug_abbrev"); + assert(iter != sections_.end()); + + abbrevs_ = new std::vector<Abbrev>; + abbrevs_->resize(1); + + // The only way to check whether we are reading over the end of the + // buffer would be to first compute the size of the leb128 data by + // reading it, then go back and read it again. + const uint8_t *abbrev_start = iter->second.first + + header_.abbrev_offset; + const uint8_t *abbrevptr = abbrev_start; +#ifndef NDEBUG + const uint64 abbrev_length = iter->second.second - header_.abbrev_offset; +#endif + + while (1) { + CompilationUnit::Abbrev abbrev; + size_t len; + const uint64 number = reader_->ReadUnsignedLEB128(abbrevptr, &len); + + if (number == 0) + break; + abbrev.number = number; + abbrevptr += len; + + assert(abbrevptr < abbrev_start + abbrev_length); + const uint64 tag = reader_->ReadUnsignedLEB128(abbrevptr, &len); + abbrevptr += len; + abbrev.tag = static_cast<enum DwarfTag>(tag); + + assert(abbrevptr < abbrev_start + abbrev_length); + abbrev.has_children = reader_->ReadOneByte(abbrevptr); + abbrevptr += 1; + + assert(abbrevptr < abbrev_start + abbrev_length); + + while (1) { + const uint64 nametemp = reader_->ReadUnsignedLEB128(abbrevptr, &len); + abbrevptr += len; + + assert(abbrevptr < abbrev_start + abbrev_length); + const uint64 formtemp = reader_->ReadUnsignedLEB128(abbrevptr, &len); + abbrevptr += len; + if (nametemp == 0 && formtemp == 0) + break; + + const enum DwarfAttribute name = + static_cast<enum DwarfAttribute>(nametemp); + const enum DwarfForm form = static_cast<enum DwarfForm>(formtemp); + abbrev.attributes.push_back(std::make_pair(name, form)); + } + assert(abbrev.number == abbrevs_->size()); + abbrevs_->push_back(abbrev); + } +} + +// Skips a single DIE's attributes. +const uint8_t *CompilationUnit::SkipDIE(const uint8_t* start, + const Abbrev& abbrev) { + for (AttributeList::const_iterator i = abbrev.attributes.begin(); + i != abbrev.attributes.end(); + i++) { + start = SkipAttribute(start, i->second); + } + return start; +} + +// Skips a single attribute form's data. +const uint8_t *CompilationUnit::SkipAttribute(const uint8_t *start, + enum DwarfForm form) { + size_t len; + + switch (form) { + case DW_FORM_indirect: + form = static_cast<enum DwarfForm>(reader_->ReadUnsignedLEB128(start, + &len)); + start += len; + return SkipAttribute(start, form); + + case DW_FORM_flag_present: + return start; + case DW_FORM_data1: + case DW_FORM_flag: + case DW_FORM_ref1: + return start + 1; + case DW_FORM_ref2: + case DW_FORM_data2: + return start + 2; + case DW_FORM_ref4: + case DW_FORM_data4: + return start + 4; + case DW_FORM_ref8: + case DW_FORM_data8: + case DW_FORM_ref_sig8: + return start + 8; + case DW_FORM_string: + return start + strlen(reinterpret_cast<const char *>(start)) + 1; + case DW_FORM_udata: + case DW_FORM_ref_udata: + case DW_FORM_GNU_str_index: + case DW_FORM_GNU_addr_index: + reader_->ReadUnsignedLEB128(start, &len); + return start + len; + + case DW_FORM_sdata: + reader_->ReadSignedLEB128(start, &len); + return start + len; + case DW_FORM_addr: + return start + reader_->AddressSize(); + case DW_FORM_ref_addr: + // DWARF2 and 3/4 differ on whether ref_addr is address size or + // offset size. + assert(header_.version >= 2); + if (header_.version == 2) { + return start + reader_->AddressSize(); + } else if (header_.version >= 3) { + return start + reader_->OffsetSize(); + } + break; + + case DW_FORM_block1: + return start + 1 + reader_->ReadOneByte(start); + case DW_FORM_block2: + return start + 2 + reader_->ReadTwoBytes(start); + case DW_FORM_block4: + return start + 4 + reader_->ReadFourBytes(start); + case DW_FORM_block: + case DW_FORM_exprloc: { + uint64 size = reader_->ReadUnsignedLEB128(start, &len); + return start + size + len; + } + case DW_FORM_strp: + case DW_FORM_sec_offset: + return start + reader_->OffsetSize(); + } + fprintf(stderr,"Unhandled form type"); + return NULL; +} + +// Read a DWARF2/3 header. +// The header is variable length in DWARF3 (and DWARF2 as extended by +// most compilers), and consists of an length field, a version number, +// the offset in the .debug_abbrev section for our abbrevs, and an +// address size. +void CompilationUnit::ReadHeader() { + const uint8_t *headerptr = buffer_; + size_t initial_length_size; + + assert(headerptr + 4 < buffer_ + buffer_length_); + const uint64 initial_length + = reader_->ReadInitialLength(headerptr, &initial_length_size); + headerptr += initial_length_size; + header_.length = initial_length; + + assert(headerptr + 2 < buffer_ + buffer_length_); + header_.version = reader_->ReadTwoBytes(headerptr); + headerptr += 2; + + assert(headerptr + reader_->OffsetSize() < buffer_ + buffer_length_); + header_.abbrev_offset = reader_->ReadOffset(headerptr); + headerptr += reader_->OffsetSize(); + + // Compare against less than or equal because this may be the last + // section in the file. + assert(headerptr + 1 <= buffer_ + buffer_length_); + header_.address_size = reader_->ReadOneByte(headerptr); + reader_->SetAddressSize(header_.address_size); + headerptr += 1; + + after_header_ = headerptr; + + // This check ensures that we don't have to do checking during the + // reading of DIEs. header_.length does not include the size of the + // initial length. + assert(buffer_ + initial_length_size + header_.length <= + buffer_ + buffer_length_); +} + +uint64 CompilationUnit::Start() { + // First get the debug_info section. ".debug_info" is the name + // recommended in the DWARF spec, and used on Linux; "__debug_info" + // is the name used in Mac OS X Mach-O files. + SectionMap::const_iterator iter = sections_.find(".debug_info"); + if (iter == sections_.end()) + iter = sections_.find("__debug_info"); + assert(iter != sections_.end()); + + // Set up our buffer + buffer_ = iter->second.first + offset_from_section_start_; + buffer_length_ = iter->second.second - offset_from_section_start_; + + // Read the header + ReadHeader(); + + // Figure out the real length from the end of the initial length to + // the end of the compilation unit, since that is the value we + // return. + uint64 ourlength = header_.length; + if (reader_->OffsetSize() == 8) + ourlength += 12; + else + ourlength += 4; + + // See if the user wants this compilation unit, and if not, just return. + if (!handler_->StartCompilationUnit(offset_from_section_start_, + reader_->AddressSize(), + reader_->OffsetSize(), + header_.length, + header_.version)) + return ourlength; + + // Otherwise, continue by reading our abbreviation entries. + ReadAbbrevs(); + + // Set the string section if we have one. ".debug_str" is the name + // recommended in the DWARF spec, and used on Linux; "__debug_str" + // is the name used in Mac OS X Mach-O files. + iter = sections_.find(".debug_str"); + if (iter == sections_.end()) + iter = sections_.find("__debug_str"); + if (iter != sections_.end()) { + string_buffer_ = iter->second.first; + string_buffer_length_ = iter->second.second; + } + + // Set the string offsets section if we have one. + iter = sections_.find(".debug_str_offsets"); + if (iter != sections_.end()) { + str_offsets_buffer_ = iter->second.first; + str_offsets_buffer_length_ = iter->second.second; + } + + // Set the address section if we have one. + iter = sections_.find(".debug_addr"); + if (iter != sections_.end()) { + addr_buffer_ = iter->second.first; + addr_buffer_length_ = iter->second.second; + } + + // Now that we have our abbreviations, start processing DIE's. + ProcessDIEs(); + + // If this is a skeleton compilation unit generated with split DWARF, + // and the client needs the full debug info, we need to find the full + // compilation unit in a .dwo or .dwp file. + if (!is_split_dwarf_ + && dwo_name_ != NULL + && handler_->NeedSplitDebugInfo()) + ProcessSplitDwarf(); + + return ourlength; +} + +// If one really wanted, you could merge SkipAttribute and +// ProcessAttribute +// This is all boring data manipulation and calling of the handler. +const uint8_t *CompilationUnit::ProcessAttribute( + uint64 dieoffset, const uint8_t *start, enum DwarfAttribute attr, + enum DwarfForm form) { + size_t len; + + switch (form) { + // DW_FORM_indirect is never used because it is such a space + // waster. + case DW_FORM_indirect: + form = static_cast<enum DwarfForm>(reader_->ReadUnsignedLEB128(start, + &len)); + start += len; + return ProcessAttribute(dieoffset, start, attr, form); + + case DW_FORM_flag_present: + ProcessAttributeUnsigned(dieoffset, attr, form, 1); + return start; + case DW_FORM_data1: + case DW_FORM_flag: + ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadOneByte(start)); + return start + 1; + case DW_FORM_data2: + ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadTwoBytes(start)); + return start + 2; + case DW_FORM_data4: + ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadFourBytes(start)); + return start + 4; + case DW_FORM_data8: + ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadEightBytes(start)); + return start + 8; + case DW_FORM_string: { + const char *str = reinterpret_cast<const char *>(start); + ProcessAttributeString(dieoffset, attr, form, str); + return start + strlen(str) + 1; + } + case DW_FORM_udata: + ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadUnsignedLEB128(start, &len)); + return start + len; + + case DW_FORM_sdata: + ProcessAttributeSigned(dieoffset, attr, form, + reader_->ReadSignedLEB128(start, &len)); + return start + len; + case DW_FORM_addr: + ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadAddress(start)); + return start + reader_->AddressSize(); + case DW_FORM_sec_offset: + ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadOffset(start)); + return start + reader_->OffsetSize(); + + case DW_FORM_ref1: + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadOneByte(start) + + offset_from_section_start_); + return start + 1; + case DW_FORM_ref2: + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadTwoBytes(start) + + offset_from_section_start_); + return start + 2; + case DW_FORM_ref4: + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadFourBytes(start) + + offset_from_section_start_); + return start + 4; + case DW_FORM_ref8: + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadEightBytes(start) + + offset_from_section_start_); + return start + 8; + case DW_FORM_ref_udata: + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadUnsignedLEB128(start, + &len) + + offset_from_section_start_); + return start + len; + case DW_FORM_ref_addr: + // DWARF2 and 3/4 differ on whether ref_addr is address size or + // offset size. + assert(header_.version >= 2); + if (header_.version == 2) { + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadAddress(start)); + return start + reader_->AddressSize(); + } else if (header_.version >= 3) { + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadOffset(start)); + return start + reader_->OffsetSize(); + } + break; + case DW_FORM_ref_sig8: + handler_->ProcessAttributeSignature(dieoffset, attr, form, + reader_->ReadEightBytes(start)); + return start + 8; + + case DW_FORM_block1: { + uint64 datalen = reader_->ReadOneByte(start); + handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 1, + datalen); + return start + 1 + datalen; + } + case DW_FORM_block2: { + uint64 datalen = reader_->ReadTwoBytes(start); + handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 2, + datalen); + return start + 2 + datalen; + } + case DW_FORM_block4: { + uint64 datalen = reader_->ReadFourBytes(start); + handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 4, + datalen); + return start + 4 + datalen; + } + case DW_FORM_block: + case DW_FORM_exprloc: { + uint64 datalen = reader_->ReadUnsignedLEB128(start, &len); + handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + len, + datalen); + return start + datalen + len; + } + case DW_FORM_strp: { + assert(string_buffer_ != NULL); + + const uint64 offset = reader_->ReadOffset(start); + assert(string_buffer_ + offset < string_buffer_ + string_buffer_length_); + + const char *str = reinterpret_cast<const char *>(string_buffer_ + offset); + ProcessAttributeString(dieoffset, attr, form, str); + return start + reader_->OffsetSize(); + } + + case DW_FORM_GNU_str_index: { + uint64 str_index = reader_->ReadUnsignedLEB128(start, &len); + const uint8_t* offset_ptr = + str_offsets_buffer_ + str_index * reader_->OffsetSize(); + const uint64 offset = reader_->ReadOffset(offset_ptr); + if (offset >= string_buffer_length_) { + return NULL; + } + + const char* str = reinterpret_cast<const char *>(string_buffer_) + offset; + ProcessAttributeString(dieoffset, attr, form, str); + return start + len; + break; + } + case DW_FORM_GNU_addr_index: { + uint64 addr_index = reader_->ReadUnsignedLEB128(start, &len); + const uint8_t* addr_ptr = + addr_buffer_ + addr_base_ + addr_index * reader_->AddressSize(); + ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadAddress(addr_ptr)); + return start + len; + } + } + fprintf(stderr, "Unhandled form type\n"); + return NULL; +} + +const uint8_t *CompilationUnit::ProcessDIE(uint64 dieoffset, + const uint8_t *start, + const Abbrev& abbrev) { + for (AttributeList::const_iterator i = abbrev.attributes.begin(); + i != abbrev.attributes.end(); + i++) { + start = ProcessAttribute(dieoffset, start, i->first, i->second); + } + + // If this is a compilation unit in a split DWARF object, verify that + // the dwo_id matches. If it does not match, we will ignore this + // compilation unit. + if (abbrev.tag == DW_TAG_compile_unit + && is_split_dwarf_ + && dwo_id_ != skeleton_dwo_id_) { + return NULL; + } + + return start; +} + +void CompilationUnit::ProcessDIEs() { + const uint8_t *dieptr = after_header_; + size_t len; + + // lengthstart is the place the length field is based on. + // It is the point in the header after the initial length field + const uint8_t *lengthstart = buffer_; + + // In 64 bit dwarf, the initial length is 12 bytes, because of the + // 0xffffffff at the start. + if (reader_->OffsetSize() == 8) + lengthstart += 12; + else + lengthstart += 4; + + std::stack<uint64> die_stack; + + while (dieptr < (lengthstart + header_.length)) { + // We give the user the absolute offset from the beginning of + // debug_info, since they need it to deal with ref_addr forms. + uint64 absolute_offset = (dieptr - buffer_) + offset_from_section_start_; + + uint64 abbrev_num = reader_->ReadUnsignedLEB128(dieptr, &len); + + dieptr += len; + + // Abbrev == 0 represents the end of a list of children, or padding + // at the end of the compilation unit. + if (abbrev_num == 0) { + if (die_stack.size() == 0) + // If it is padding, then we are done with the compilation unit's DIEs. + return; + const uint64 offset = die_stack.top(); + die_stack.pop(); + handler_->EndDIE(offset); + continue; + } + + const Abbrev& abbrev = abbrevs_->at(static_cast<size_t>(abbrev_num)); + const enum DwarfTag tag = abbrev.tag; + if (!handler_->StartDIE(absolute_offset, tag)) { + dieptr = SkipDIE(dieptr, abbrev); + } else { + dieptr = ProcessDIE(absolute_offset, dieptr, abbrev); + } + + if (abbrev.has_children) { + die_stack.push(absolute_offset); + } else { + handler_->EndDIE(absolute_offset); + } + } +} + +// Check for a valid ELF file and return the Address size. +// Returns 0 if not a valid ELF file. +inline int GetElfWidth(const ElfReader& elf) { + if (elf.IsElf32File()) + return 4; + if (elf.IsElf64File()) + return 8; + return 0; +} + +void CompilationUnit::ProcessSplitDwarf() { + struct stat statbuf; + if (!have_checked_for_dwp_) { + // Look for a .dwp file in the same directory as the executable. + have_checked_for_dwp_ = true; + string dwp_suffix(".dwp"); + dwp_path_ = path_ + dwp_suffix; + if (stat(dwp_path_.c_str(), &statbuf) != 0) { + // Fall back to a split .debug file in the same directory. + string debug_suffix(".debug"); + dwp_path_ = path_; + size_t found = path_.rfind(debug_suffix); + if (found + debug_suffix.length() == path_.length()) + dwp_path_ = dwp_path_.replace(found, debug_suffix.length(), dwp_suffix); + } + if (stat(dwp_path_.c_str(), &statbuf) == 0) { + ElfReader* elf = new ElfReader(dwp_path_); + int width = GetElfWidth(*elf); + if (width != 0) { + dwp_byte_reader_.reset(new ByteReader(reader_->GetEndianness())); + dwp_byte_reader_->SetAddressSize(width); + dwp_reader_.reset(new DwpReader(*dwp_byte_reader_, elf)); + dwp_reader_->Initialize(); + } else { + delete elf; + } + } + } + bool found_in_dwp = false; + if (dwp_reader_) { + // If we have a .dwp file, read the debug sections for the requested CU. + SectionMap sections; + dwp_reader_->ReadDebugSectionsForCU(dwo_id_, §ions); + if (!sections.empty()) { + found_in_dwp = true; + CompilationUnit dwp_comp_unit(dwp_path_, sections, 0, + dwp_byte_reader_.get(), handler_); + dwp_comp_unit.SetSplitDwarf(addr_buffer_, addr_buffer_length_, addr_base_, + ranges_base_, dwo_id_); + dwp_comp_unit.Start(); + } + } + if (!found_in_dwp) { + // If no .dwp file, try to open the .dwo file. + if (stat(dwo_name_, &statbuf) == 0) { + ElfReader elf(dwo_name_); + int width = GetElfWidth(elf); + if (width != 0) { + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(width); + SectionMap sections; + ReadDebugSectionsFromDwo(&elf, §ions); + CompilationUnit dwo_comp_unit(dwo_name_, sections, 0, &reader, + handler_); + dwo_comp_unit.SetSplitDwarf(addr_buffer_, addr_buffer_length_, + addr_base_, ranges_base_, dwo_id_); + dwo_comp_unit.Start(); + } + } + } +} + +void CompilationUnit::ReadDebugSectionsFromDwo(ElfReader* elf_reader, + SectionMap* sections) { + static const char* const section_names[] = { + ".debug_abbrev", + ".debug_info", + ".debug_str_offsets", + ".debug_str" + }; + for (unsigned int i = 0u; + i < sizeof(section_names)/sizeof(*(section_names)); ++i) { + string base_name = section_names[i]; + string dwo_name = base_name + ".dwo"; + size_t section_size; + const char* section_data = elf_reader->GetSectionByName(dwo_name, + §ion_size); + if (section_data != NULL) + sections->insert(std::make_pair( + base_name, std::make_pair( + reinterpret_cast<const uint8_t *>(section_data), + section_size))); + } +} + +DwpReader::DwpReader(const ByteReader& byte_reader, ElfReader* elf_reader) + : elf_reader_(elf_reader), byte_reader_(byte_reader), + cu_index_(NULL), cu_index_size_(0), string_buffer_(NULL), + string_buffer_size_(0), version_(0), ncolumns_(0), nunits_(0), + nslots_(0), phash_(NULL), pindex_(NULL), shndx_pool_(NULL), + offset_table_(NULL), size_table_(NULL), abbrev_data_(NULL), + abbrev_size_(0), info_data_(NULL), info_size_(0), + str_offsets_data_(NULL), str_offsets_size_(0) {} + +DwpReader::~DwpReader() { + if (elf_reader_) delete elf_reader_; +} + +void DwpReader::Initialize() { + cu_index_ = elf_reader_->GetSectionByName(".debug_cu_index", + &cu_index_size_); + if (cu_index_ == NULL) { + return; + } + // The .debug_str.dwo section is shared by all CUs in the file. + string_buffer_ = elf_reader_->GetSectionByName(".debug_str.dwo", + &string_buffer_size_); + + version_ = byte_reader_.ReadFourBytes( + reinterpret_cast<const uint8_t *>(cu_index_)); + + if (version_ == 1) { + nslots_ = byte_reader_.ReadFourBytes( + reinterpret_cast<const uint8_t *>(cu_index_) + + 3 * sizeof(uint32)); + phash_ = cu_index_ + 4 * sizeof(uint32); + pindex_ = phash_ + nslots_ * sizeof(uint64); + shndx_pool_ = pindex_ + nslots_ * sizeof(uint32); + if (shndx_pool_ >= cu_index_ + cu_index_size_) { + version_ = 0; + } + } else if (version_ == 2) { + ncolumns_ = byte_reader_.ReadFourBytes( + reinterpret_cast<const uint8_t *>(cu_index_) + sizeof(uint32)); + nunits_ = byte_reader_.ReadFourBytes( + reinterpret_cast<const uint8_t *>(cu_index_) + 2 * sizeof(uint32)); + nslots_ = byte_reader_.ReadFourBytes( + reinterpret_cast<const uint8_t *>(cu_index_) + 3 * sizeof(uint32)); + phash_ = cu_index_ + 4 * sizeof(uint32); + pindex_ = phash_ + nslots_ * sizeof(uint64); + offset_table_ = pindex_ + nslots_ * sizeof(uint32); + size_table_ = offset_table_ + ncolumns_ * (nunits_ + 1) * sizeof(uint32); + abbrev_data_ = elf_reader_->GetSectionByName(".debug_abbrev.dwo", + &abbrev_size_); + info_data_ = elf_reader_->GetSectionByName(".debug_info.dwo", &info_size_); + str_offsets_data_ = elf_reader_->GetSectionByName(".debug_str_offsets.dwo", + &str_offsets_size_); + if (size_table_ >= cu_index_ + cu_index_size_) { + version_ = 0; + } + } +} + +void DwpReader::ReadDebugSectionsForCU(uint64 dwo_id, + SectionMap* sections) { + if (version_ == 1) { + int slot = LookupCU(dwo_id); + if (slot == -1) { + return; + } + + // The index table points to the section index pool, where we + // can read a list of section indexes for the debug sections + // for the CU whose dwo_id we are looking for. + int index = byte_reader_.ReadFourBytes( + reinterpret_cast<const uint8_t *>(pindex_) + + slot * sizeof(uint32)); + const char* shndx_list = shndx_pool_ + index * sizeof(uint32); + for (;;) { + if (shndx_list >= cu_index_ + cu_index_size_) { + version_ = 0; + return; + } + unsigned int shndx = byte_reader_.ReadFourBytes( + reinterpret_cast<const uint8_t *>(shndx_list)); + shndx_list += sizeof(uint32); + if (shndx == 0) + break; + const char* section_name = elf_reader_->GetSectionName(shndx); + size_t section_size; + const char* section_data; + // We're only interested in these four debug sections. + // The section names in the .dwo file end with ".dwo", but we + // add them to the sections table with their normal names. + if (!strncmp(section_name, ".debug_abbrev", strlen(".debug_abbrev"))) { + section_data = elf_reader_->GetSectionByIndex(shndx, §ion_size); + sections->insert(std::make_pair( + ".debug_abbrev", + std::make_pair(reinterpret_cast<const uint8_t *> (section_data), + section_size))); + } else if (!strncmp(section_name, ".debug_info", strlen(".debug_info"))) { + section_data = elf_reader_->GetSectionByIndex(shndx, §ion_size); + sections->insert(std::make_pair( + ".debug_info", + std::make_pair(reinterpret_cast<const uint8_t *> (section_data), + section_size))); + } else if (!strncmp(section_name, ".debug_str_offsets", + strlen(".debug_str_offsets"))) { + section_data = elf_reader_->GetSectionByIndex(shndx, §ion_size); + sections->insert(std::make_pair( + ".debug_str_offsets", + std::make_pair(reinterpret_cast<const uint8_t *> (section_data), + section_size))); + } + } + sections->insert(std::make_pair( + ".debug_str", + std::make_pair(reinterpret_cast<const uint8_t *> (string_buffer_), + string_buffer_size_))); + } else if (version_ == 2) { + uint32 index = LookupCUv2(dwo_id); + if (index == 0) { + return; + } + + // The index points to a row in each of the section offsets table + // and the section size table, where we can read the offsets and sizes + // of the contributions to each debug section from the CU whose dwo_id + // we are looking for. Row 0 of the section offsets table has the + // section ids for each column of the table. The size table begins + // with row 1. + const char* id_row = offset_table_; + const char* offset_row = offset_table_ + + index * ncolumns_ * sizeof(uint32); + const char* size_row = + size_table_ + (index - 1) * ncolumns_ * sizeof(uint32); + if (size_row + ncolumns_ * sizeof(uint32) > cu_index_ + cu_index_size_) { + version_ = 0; + return; + } + for (unsigned int col = 0u; col < ncolumns_; ++col) { + uint32 section_id = + byte_reader_.ReadFourBytes(reinterpret_cast<const uint8_t *>(id_row) + + col * sizeof(uint32)); + uint32 offset = byte_reader_.ReadFourBytes( + reinterpret_cast<const uint8_t *>(offset_row) + + col * sizeof(uint32)); + uint32 size = byte_reader_.ReadFourBytes( + reinterpret_cast<const uint8_t *>(size_row) + col * sizeof(uint32)); + if (section_id == DW_SECT_ABBREV) { + sections->insert(std::make_pair( + ".debug_abbrev", + std::make_pair(reinterpret_cast<const uint8_t *> (abbrev_data_) + + offset, size))); + } else if (section_id == DW_SECT_INFO) { + sections->insert(std::make_pair( + ".debug_info", + std::make_pair(reinterpret_cast<const uint8_t *> (info_data_) + + offset, size))); + } else if (section_id == DW_SECT_STR_OFFSETS) { + sections->insert(std::make_pair( + ".debug_str_offsets", + std::make_pair(reinterpret_cast<const uint8_t *> (str_offsets_data_) + + offset, size))); + } + } + sections->insert(std::make_pair( + ".debug_str", + std::make_pair(reinterpret_cast<const uint8_t *> (string_buffer_), + string_buffer_size_))); + } +} + +int DwpReader::LookupCU(uint64 dwo_id) { + uint32 slot = static_cast<uint32>(dwo_id) & (nslots_ - 1); + uint64 probe = byte_reader_.ReadEightBytes( + reinterpret_cast<const uint8_t *>(phash_) + slot * sizeof(uint64)); + if (probe != 0 && probe != dwo_id) { + uint32 secondary_hash = + (static_cast<uint32>(dwo_id >> 32) & (nslots_ - 1)) | 1; + do { + slot = (slot + secondary_hash) & (nslots_ - 1); + probe = byte_reader_.ReadEightBytes( + reinterpret_cast<const uint8_t *>(phash_) + slot * sizeof(uint64)); + } while (probe != 0 && probe != dwo_id); + } + if (probe == 0) + return -1; + return slot; +} + +uint32 DwpReader::LookupCUv2(uint64 dwo_id) { + uint32 slot = static_cast<uint32>(dwo_id) & (nslots_ - 1); + uint64 probe = byte_reader_.ReadEightBytes( + reinterpret_cast<const uint8_t *>(phash_) + slot * sizeof(uint64)); + uint32 index = byte_reader_.ReadFourBytes( + reinterpret_cast<const uint8_t *>(pindex_) + slot * sizeof(uint32)); + if (index != 0 && probe != dwo_id) { + uint32 secondary_hash = + (static_cast<uint32>(dwo_id >> 32) & (nslots_ - 1)) | 1; + do { + slot = (slot + secondary_hash) & (nslots_ - 1); + probe = byte_reader_.ReadEightBytes( + reinterpret_cast<const uint8_t *>(phash_) + slot * sizeof(uint64)); + index = byte_reader_.ReadFourBytes( + reinterpret_cast<const uint8_t *>(pindex_) + slot * sizeof(uint32)); + } while (index != 0 && probe != dwo_id); + } + return index; +} + +LineInfo::LineInfo(const uint8_t *buffer, uint64 buffer_length, + ByteReader* reader, LineInfoHandler* handler): + handler_(handler), reader_(reader), buffer_(buffer) { +#ifndef NDEBUG + buffer_length_ = buffer_length; +#endif + header_.std_opcode_lengths = NULL; +} + +uint64 LineInfo::Start() { + ReadHeader(); + ReadLines(); + return after_header_ - buffer_; +} + +// The header for a debug_line section is mildly complicated, because +// the line info is very tightly encoded. +void LineInfo::ReadHeader() { + const uint8_t *lineptr = buffer_; + size_t initial_length_size; + + const uint64 initial_length + = reader_->ReadInitialLength(lineptr, &initial_length_size); + + lineptr += initial_length_size; + header_.total_length = initial_length; + assert(buffer_ + initial_length_size + header_.total_length <= + buffer_ + buffer_length_); + + // Address size *must* be set by CU ahead of time. + assert(reader_->AddressSize() != 0); + + header_.version = reader_->ReadTwoBytes(lineptr); + lineptr += 2; + + header_.prologue_length = reader_->ReadOffset(lineptr); + lineptr += reader_->OffsetSize(); + + header_.min_insn_length = reader_->ReadOneByte(lineptr); + lineptr += 1; + + if (header_.version >= 4) { + __attribute__((unused)) uint8 max_ops_per_insn = + reader_->ReadOneByte(lineptr); + ++lineptr; + assert(max_ops_per_insn == 1); + } + + header_.default_is_stmt = reader_->ReadOneByte(lineptr); + lineptr += 1; + + header_.line_base = *reinterpret_cast<const int8*>(lineptr); + lineptr += 1; + + header_.line_range = reader_->ReadOneByte(lineptr); + lineptr += 1; + + header_.opcode_base = reader_->ReadOneByte(lineptr); + lineptr += 1; + + header_.std_opcode_lengths = new std::vector<unsigned char>; + header_.std_opcode_lengths->resize(header_.opcode_base + 1); + (*header_.std_opcode_lengths)[0] = 0; + for (int i = 1; i < header_.opcode_base; i++) { + (*header_.std_opcode_lengths)[i] = reader_->ReadOneByte(lineptr); + lineptr += 1; + } + + // It is legal for the directory entry table to be empty. + if (*lineptr) { + uint32 dirindex = 1; + while (*lineptr) { + const char *dirname = reinterpret_cast<const char *>(lineptr); + handler_->DefineDir(dirname, dirindex); + lineptr += strlen(dirname) + 1; + dirindex++; + } + } + lineptr++; + + // It is also legal for the file entry table to be empty. + if (*lineptr) { + uint32 fileindex = 1; + size_t len; + while (*lineptr) { + const char *filename = reinterpret_cast<const char *>(lineptr); + lineptr += strlen(filename) + 1; + + uint64 dirindex = reader_->ReadUnsignedLEB128(lineptr, &len); + lineptr += len; + + uint64 mod_time = reader_->ReadUnsignedLEB128(lineptr, &len); + lineptr += len; + + uint64 filelength = reader_->ReadUnsignedLEB128(lineptr, &len); + lineptr += len; + handler_->DefineFile(filename, fileindex, static_cast<uint32>(dirindex), + mod_time, filelength); + fileindex++; + } + } + lineptr++; + + after_header_ = lineptr; +} + +/* static */ +bool LineInfo::ProcessOneOpcode(ByteReader* reader, + LineInfoHandler* handler, + const struct LineInfoHeader &header, + const uint8_t *start, + struct LineStateMachine* lsm, + size_t* len, + uintptr pc, + bool *lsm_passes_pc) { + size_t oplen = 0; + size_t templen; + uint8 opcode = reader->ReadOneByte(start); + oplen++; + start++; + + // If the opcode is great than the opcode_base, it is a special + // opcode. Most line programs consist mainly of special opcodes. + if (opcode >= header.opcode_base) { + opcode -= header.opcode_base; + const int64 advance_address = (opcode / header.line_range) + * header.min_insn_length; + const int32 advance_line = (opcode % header.line_range) + + header.line_base; + + // Check if the lsm passes "pc". If so, mark it as passed. + if (lsm_passes_pc && + lsm->address <= pc && pc < lsm->address + advance_address) { + *lsm_passes_pc = true; + } + + lsm->address += advance_address; + lsm->line_num += advance_line; + lsm->basic_block = true; + *len = oplen; + return true; + } + + // Otherwise, we have the regular opcodes + switch (opcode) { + case DW_LNS_copy: { + lsm->basic_block = false; + *len = oplen; + return true; + } + + case DW_LNS_advance_pc: { + uint64 advance_address = reader->ReadUnsignedLEB128(start, &templen); + oplen += templen; + + // Check if the lsm passes "pc". If so, mark it as passed. + if (lsm_passes_pc && lsm->address <= pc && + pc < lsm->address + header.min_insn_length * advance_address) { + *lsm_passes_pc = true; + } + + lsm->address += header.min_insn_length * advance_address; + } + break; + case DW_LNS_advance_line: { + const int64 advance_line = reader->ReadSignedLEB128(start, &templen); + oplen += templen; + lsm->line_num += static_cast<int32>(advance_line); + + // With gcc 4.2.1, we can get the line_no here for the first time + // since DW_LNS_advance_line is called after DW_LNE_set_address is + // called. So we check if the lsm passes "pc" here, not in + // DW_LNE_set_address. + if (lsm_passes_pc && lsm->address == pc) { + *lsm_passes_pc = true; + } + } + break; + case DW_LNS_set_file: { + const uint64 fileno = reader->ReadUnsignedLEB128(start, &templen); + oplen += templen; + lsm->file_num = static_cast<uint32>(fileno); + } + break; + case DW_LNS_set_column: { + const uint64 colno = reader->ReadUnsignedLEB128(start, &templen); + oplen += templen; + lsm->column_num = static_cast<uint32>(colno); + } + break; + case DW_LNS_negate_stmt: { + lsm->is_stmt = !lsm->is_stmt; + } + break; + case DW_LNS_set_basic_block: { + lsm->basic_block = true; + } + break; + case DW_LNS_fixed_advance_pc: { + const uint16 advance_address = reader->ReadTwoBytes(start); + oplen += 2; + + // Check if the lsm passes "pc". If so, mark it as passed. + if (lsm_passes_pc && + lsm->address <= pc && pc < lsm->address + advance_address) { + *lsm_passes_pc = true; + } + + lsm->address += advance_address; + } + break; + case DW_LNS_const_add_pc: { + const int64 advance_address = header.min_insn_length + * ((255 - header.opcode_base) + / header.line_range); + + // Check if the lsm passes "pc". If so, mark it as passed. + if (lsm_passes_pc && + lsm->address <= pc && pc < lsm->address + advance_address) { + *lsm_passes_pc = true; + } + + lsm->address += advance_address; + } + break; + case DW_LNS_extended_op: { + const uint64 extended_op_len = reader->ReadUnsignedLEB128(start, + &templen); + start += templen; + oplen += templen + extended_op_len; + + const uint64 extended_op = reader->ReadOneByte(start); + start++; + + switch (extended_op) { + case DW_LNE_end_sequence: { + lsm->end_sequence = true; + *len = oplen; + return true; + } + break; + case DW_LNE_set_address: { + // With gcc 4.2.1, we cannot tell the line_no here since + // DW_LNE_set_address is called before DW_LNS_advance_line is + // called. So we do not check if the lsm passes "pc" here. See + // also the comment in DW_LNS_advance_line. + uint64 address = reader->ReadAddress(start); + lsm->address = address; + } + break; + case DW_LNE_define_file: { + const char *filename = reinterpret_cast<const char *>(start); + + templen = strlen(filename) + 1; + start += templen; + + uint64 dirindex = reader->ReadUnsignedLEB128(start, &templen); + oplen += templen; + + const uint64 mod_time = reader->ReadUnsignedLEB128(start, + &templen); + oplen += templen; + + const uint64 filelength = reader->ReadUnsignedLEB128(start, + &templen); + oplen += templen; + + if (handler) { + handler->DefineFile(filename, -1, static_cast<uint32>(dirindex), + mod_time, filelength); + } + } + break; + } + } + break; + + default: { + // Ignore unknown opcode silently + if (header.std_opcode_lengths) { + for (int i = 0; i < (*header.std_opcode_lengths)[opcode]; i++) { + reader->ReadUnsignedLEB128(start, &templen); + start += templen; + oplen += templen; + } + } + } + break; + } + *len = oplen; + return false; +} + +void LineInfo::ReadLines() { + struct LineStateMachine lsm; + + // lengthstart is the place the length field is based on. + // It is the point in the header after the initial length field + const uint8_t *lengthstart = buffer_; + + // In 64 bit dwarf, the initial length is 12 bytes, because of the + // 0xffffffff at the start. + if (reader_->OffsetSize() == 8) + lengthstart += 12; + else + lengthstart += 4; + + const uint8_t *lineptr = after_header_; + lsm.Reset(header_.default_is_stmt); + + // The LineInfoHandler interface expects each line's length along + // with its address, but DWARF only provides addresses (sans + // length), and an end-of-sequence address; one infers the length + // from the next address. So we report a line only when we get the + // next line's address, or the end-of-sequence address. + bool have_pending_line = false; + uint64 pending_address = 0; + uint32 pending_file_num = 0, pending_line_num = 0, pending_column_num = 0; + + while (lineptr < lengthstart + header_.total_length) { + size_t oplength; + bool add_row = ProcessOneOpcode(reader_, handler_, header_, + lineptr, &lsm, &oplength, (uintptr)-1, + NULL); + if (add_row) { + if (have_pending_line) + handler_->AddLine(pending_address, lsm.address - pending_address, + pending_file_num, pending_line_num, + pending_column_num); + if (lsm.end_sequence) { + lsm.Reset(header_.default_is_stmt); + have_pending_line = false; + } else { + pending_address = lsm.address; + pending_file_num = lsm.file_num; + pending_line_num = lsm.line_num; + pending_column_num = lsm.column_num; + have_pending_line = true; + } + } + lineptr += oplength; + } + + after_header_ = lengthstart + header_.total_length; +} + +RangeListReader::RangeListReader(const uint8_t *buffer, uint64 size, + ByteReader *reader, RangeListHandler *handler) + : buffer_(buffer), size_(size), reader_(reader), handler_(handler) { } + +bool RangeListReader::ReadRangeList(uint64 offset) { + const uint64 max_address = + (reader_->AddressSize() == 4) ? 0xffffffffUL + : 0xffffffffffffffffULL; + const uint64 entry_size = reader_->AddressSize() * 2; + bool list_end = false; + + do { + if (offset > size_ - entry_size) { + return false; // Invalid range detected + } + + uint64 start_address = reader_->ReadAddress(buffer_ + offset); + uint64 end_address = + reader_->ReadAddress(buffer_ + offset + reader_->AddressSize()); + + if (start_address == max_address) { // Base address selection + handler_->SetBaseAddress(end_address); + } else if (start_address == 0 && end_address == 0) { // End-of-list + handler_->Finish(); + list_end = true; + } else { // Add a range entry + handler_->AddRange(start_address, end_address); + } + + offset += entry_size; + } while (!list_end); + + return true; +} + +// A DWARF rule for recovering the address or value of a register, or +// computing the canonical frame address. There is one subclass of this for +// each '*Rule' member function in CallFrameInfo::Handler. +// +// It's annoying that we have to handle Rules using pointers (because +// the concrete instances can have an arbitrary size). They're small, +// so it would be much nicer if we could just handle them by value +// instead of fretting about ownership and destruction. +// +// It seems like all these could simply be instances of std::tr1::bind, +// except that we need instances to be EqualityComparable, too. +// +// This could logically be nested within State, but then the qualified names +// get horrendous. +class CallFrameInfo::Rule { + public: + virtual ~Rule() { } + + // Tell HANDLER that, at ADDRESS in the program, REG can be recovered using + // this rule. If REG is kCFARegister, then this rule describes how to compute + // the canonical frame address. Return what the HANDLER member function + // returned. + virtual bool Handle(Handler *handler, + uint64 address, int reg) const = 0; + + // Equality on rules. We use these to decide which rules we need + // to report after a DW_CFA_restore_state instruction. + virtual bool operator==(const Rule &rhs) const = 0; + + bool operator!=(const Rule &rhs) const { return ! (*this == rhs); } + + // Return a pointer to a copy of this rule. + virtual Rule *Copy() const = 0; + + // If this is a base+offset rule, change its base register to REG. + // Otherwise, do nothing. (Ugly, but required for DW_CFA_def_cfa_register.) + virtual void SetBaseRegister(unsigned reg) { } + + // If this is a base+offset rule, change its offset to OFFSET. Otherwise, + // do nothing. (Ugly, but required for DW_CFA_def_cfa_offset.) + virtual void SetOffset(long long offset) { } +}; + +// Rule: the value the register had in the caller cannot be recovered. +class CallFrameInfo::UndefinedRule: public CallFrameInfo::Rule { + public: + UndefinedRule() { } + ~UndefinedRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->UndefinedRule(address, reg); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is allowed by the Google C++ Style Guide, if the use has + // been carefully considered; cheap RTTI-like workarounds are forbidden. + const UndefinedRule *our_rhs = dynamic_cast<const UndefinedRule *>(&rhs); + return (our_rhs != NULL); + } + Rule *Copy() const { return new UndefinedRule(*this); } +}; + +// Rule: the register's value is the same as that it had in the caller. +class CallFrameInfo::SameValueRule: public CallFrameInfo::Rule { + public: + SameValueRule() { } + ~SameValueRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->SameValueRule(address, reg); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is allowed by the Google C++ Style Guide, if the use has + // been carefully considered; cheap RTTI-like workarounds are forbidden. + const SameValueRule *our_rhs = dynamic_cast<const SameValueRule *>(&rhs); + return (our_rhs != NULL); + } + Rule *Copy() const { return new SameValueRule(*this); } +}; + +// Rule: the register is saved at OFFSET from BASE_REGISTER. BASE_REGISTER +// may be CallFrameInfo::Handler::kCFARegister. +class CallFrameInfo::OffsetRule: public CallFrameInfo::Rule { + public: + OffsetRule(int base_register, long offset) + : base_register_(base_register), offset_(offset) { } + ~OffsetRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->OffsetRule(address, reg, base_register_, offset_); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is allowed by the Google C++ Style Guide, if the use has + // been carefully considered; cheap RTTI-like workarounds are forbidden. + const OffsetRule *our_rhs = dynamic_cast<const OffsetRule *>(&rhs); + return (our_rhs && + base_register_ == our_rhs->base_register_ && + offset_ == our_rhs->offset_); + } + Rule *Copy() const { return new OffsetRule(*this); } + // We don't actually need SetBaseRegister or SetOffset here, since they + // are only ever applied to CFA rules, for DW_CFA_def_cfa_offset, and it + // doesn't make sense to use OffsetRule for computing the CFA: it + // computes the address at which a register is saved, not a value. + private: + int base_register_; + long offset_; +}; + +// Rule: the value the register had in the caller is the value of +// BASE_REGISTER plus offset. BASE_REGISTER may be +// CallFrameInfo::Handler::kCFARegister. +class CallFrameInfo::ValOffsetRule: public CallFrameInfo::Rule { + public: + ValOffsetRule(int base_register, long offset) + : base_register_(base_register), offset_(offset) { } + ~ValOffsetRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->ValOffsetRule(address, reg, base_register_, offset_); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is allowed by the Google C++ Style Guide, if the use has + // been carefully considered; cheap RTTI-like workarounds are forbidden. + const ValOffsetRule *our_rhs = dynamic_cast<const ValOffsetRule *>(&rhs); + return (our_rhs && + base_register_ == our_rhs->base_register_ && + offset_ == our_rhs->offset_); + } + Rule *Copy() const { return new ValOffsetRule(*this); } + void SetBaseRegister(unsigned reg) { base_register_ = reg; } + void SetOffset(long long offset) { offset_ = offset; } + private: + int base_register_; + long offset_; +}; + +// Rule: the register has been saved in another register REGISTER_NUMBER_. +class CallFrameInfo::RegisterRule: public CallFrameInfo::Rule { + public: + explicit RegisterRule(int register_number) + : register_number_(register_number) { } + ~RegisterRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->RegisterRule(address, reg, register_number_); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is allowed by the Google C++ Style Guide, if the use has + // been carefully considered; cheap RTTI-like workarounds are forbidden. + const RegisterRule *our_rhs = dynamic_cast<const RegisterRule *>(&rhs); + return (our_rhs && register_number_ == our_rhs->register_number_); + } + Rule *Copy() const { return new RegisterRule(*this); } + private: + int register_number_; +}; + +// Rule: EXPRESSION evaluates to the address at which the register is saved. +class CallFrameInfo::ExpressionRule: public CallFrameInfo::Rule { + public: + explicit ExpressionRule(const string &expression) + : expression_(expression) { } + ~ExpressionRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->ExpressionRule(address, reg, expression_); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is allowed by the Google C++ Style Guide, if the use has + // been carefully considered; cheap RTTI-like workarounds are forbidden. + const ExpressionRule *our_rhs = dynamic_cast<const ExpressionRule *>(&rhs); + return (our_rhs && expression_ == our_rhs->expression_); + } + Rule *Copy() const { return new ExpressionRule(*this); } + private: + string expression_; +}; + +// Rule: EXPRESSION evaluates to the address at which the register is saved. +class CallFrameInfo::ValExpressionRule: public CallFrameInfo::Rule { + public: + explicit ValExpressionRule(const string &expression) + : expression_(expression) { } + ~ValExpressionRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->ValExpressionRule(address, reg, expression_); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is allowed by the Google C++ Style Guide, if the use has + // been carefully considered; cheap RTTI-like workarounds are forbidden. + const ValExpressionRule *our_rhs = + dynamic_cast<const ValExpressionRule *>(&rhs); + return (our_rhs && expression_ == our_rhs->expression_); + } + Rule *Copy() const { return new ValExpressionRule(*this); } + private: + string expression_; +}; + +// A map from register numbers to rules. +class CallFrameInfo::RuleMap { + public: + RuleMap() : cfa_rule_(NULL) { } + RuleMap(const RuleMap &rhs) : cfa_rule_(NULL) { *this = rhs; } + ~RuleMap() { Clear(); } + + RuleMap &operator=(const RuleMap &rhs); + + // Set the rule for computing the CFA to RULE. Take ownership of RULE. + void SetCFARule(Rule *rule) { delete cfa_rule_; cfa_rule_ = rule; } + + // Return the current CFA rule. Unlike RegisterRule, this RuleMap retains + // ownership of the rule. We use this for DW_CFA_def_cfa_offset and + // DW_CFA_def_cfa_register, and for detecting references to the CFA before + // a rule for it has been established. + Rule *CFARule() const { return cfa_rule_; } + + // Return the rule for REG, or NULL if there is none. The caller takes + // ownership of the result. + Rule *RegisterRule(int reg) const; + + // Set the rule for computing REG to RULE. Take ownership of RULE. + void SetRegisterRule(int reg, Rule *rule); + + // Make all the appropriate calls to HANDLER as if we were changing from + // this RuleMap to NEW_RULES at ADDRESS. We use this to implement + // DW_CFA_restore_state, where lots of rules can change simultaneously. + // Return true if all handlers returned true; otherwise, return false. + bool HandleTransitionTo(Handler *handler, uint64 address, + const RuleMap &new_rules) const; + + private: + // A map from register numbers to Rules. + typedef std::map<int, Rule *> RuleByNumber; + + // Remove all register rules and clear cfa_rule_. + void Clear(); + + // The rule for computing the canonical frame address. This RuleMap owns + // this rule. + Rule *cfa_rule_; + + // A map from register numbers to postfix expressions to recover + // their values. This RuleMap owns the Rules the map refers to. + RuleByNumber registers_; +}; + +CallFrameInfo::RuleMap &CallFrameInfo::RuleMap::operator=(const RuleMap &rhs) { + Clear(); + // Since each map owns the rules it refers to, assignment must copy them. + if (rhs.cfa_rule_) cfa_rule_ = rhs.cfa_rule_->Copy(); + for (RuleByNumber::const_iterator it = rhs.registers_.begin(); + it != rhs.registers_.end(); it++) + registers_[it->first] = it->second->Copy(); + return *this; +} + +CallFrameInfo::Rule *CallFrameInfo::RuleMap::RegisterRule(int reg) const { + assert(reg != Handler::kCFARegister); + RuleByNumber::const_iterator it = registers_.find(reg); + if (it != registers_.end()) + return it->second->Copy(); + else + return NULL; +} + +void CallFrameInfo::RuleMap::SetRegisterRule(int reg, Rule *rule) { + assert(reg != Handler::kCFARegister); + assert(rule); + Rule **slot = ®isters_[reg]; + delete *slot; + *slot = rule; +} + +bool CallFrameInfo::RuleMap::HandleTransitionTo( + Handler *handler, + uint64 address, + const RuleMap &new_rules) const { + // Transition from cfa_rule_ to new_rules.cfa_rule_. + if (cfa_rule_ && new_rules.cfa_rule_) { + if (*cfa_rule_ != *new_rules.cfa_rule_ && + !new_rules.cfa_rule_->Handle(handler, address, + Handler::kCFARegister)) + return false; + } else if (cfa_rule_) { + // this RuleMap has a CFA rule but new_rules doesn't. + // CallFrameInfo::Handler has no way to handle this --- and shouldn't; + // it's garbage input. The instruction interpreter should have + // detected this and warned, so take no action here. + } else if (new_rules.cfa_rule_) { + // This shouldn't be possible: NEW_RULES is some prior state, and + // there's no way to remove entries. + assert(0); + } else { + // Both CFA rules are empty. No action needed. + } + + // Traverse the two maps in order by register number, and report + // whatever differences we find. + RuleByNumber::const_iterator old_it = registers_.begin(); + RuleByNumber::const_iterator new_it = new_rules.registers_.begin(); + while (old_it != registers_.end() && new_it != new_rules.registers_.end()) { + if (old_it->first < new_it->first) { + // This RuleMap has an entry for old_it->first, but NEW_RULES + // doesn't. + // + // This isn't really the right thing to do, but since CFI generally + // only mentions callee-saves registers, and GCC's convention for + // callee-saves registers is that they are unchanged, it's a good + // approximation. + if (!handler->SameValueRule(address, old_it->first)) + return false; + old_it++; + } else if (old_it->first > new_it->first) { + // NEW_RULES has entry for new_it->first, but this RuleMap + // doesn't. This shouldn't be possible: NEW_RULES is some prior + // state, and there's no way to remove entries. + assert(0); + } else { + // Both maps have an entry for this register. Report the new + // rule if it is different. + if (*old_it->second != *new_it->second && + !new_it->second->Handle(handler, address, new_it->first)) + return false; + new_it++, old_it++; + } + } + // Finish off entries from this RuleMap with no counterparts in new_rules. + while (old_it != registers_.end()) { + if (!handler->SameValueRule(address, old_it->first)) + return false; + old_it++; + } + // Since we only make transitions from a rule set to some previously + // saved rule set, and we can only add rules to the map, NEW_RULES + // must have fewer rules than *this. + assert(new_it == new_rules.registers_.end()); + + return true; +} + +// Remove all register rules and clear cfa_rule_. +void CallFrameInfo::RuleMap::Clear() { + delete cfa_rule_; + cfa_rule_ = NULL; + for (RuleByNumber::iterator it = registers_.begin(); + it != registers_.end(); it++) + delete it->second; + registers_.clear(); +} + +// The state of the call frame information interpreter as it processes +// instructions from a CIE and FDE. +class CallFrameInfo::State { + public: + // Create a call frame information interpreter state with the given + // reporter, reader, handler, and initial call frame info address. + State(ByteReader *reader, Handler *handler, Reporter *reporter, + uint64 address) + : reader_(reader), handler_(handler), reporter_(reporter), + address_(address), entry_(NULL), cursor_(NULL) { } + + // Interpret instructions from CIE, save the resulting rule set for + // DW_CFA_restore instructions, and return true. On error, report + // the problem to reporter_ and return false. + bool InterpretCIE(const CIE &cie); + + // Interpret instructions from FDE, and return true. On error, + // report the problem to reporter_ and return false. + bool InterpretFDE(const FDE &fde); + + private: + // The operands of a CFI instruction, for ParseOperands. + struct Operands { + unsigned register_number; // A register number. + uint64 offset; // An offset or address. + long signed_offset; // A signed offset. + string expression; // A DWARF expression. + }; + + // Parse CFI instruction operands from STATE's instruction stream as + // described by FORMAT. On success, populate OPERANDS with the + // results, and return true. On failure, report the problem and + // return false. + // + // Each character of FORMAT should be one of the following: + // + // 'r' unsigned LEB128 register number (OPERANDS->register_number) + // 'o' unsigned LEB128 offset (OPERANDS->offset) + // 's' signed LEB128 offset (OPERANDS->signed_offset) + // 'a' machine-size address (OPERANDS->offset) + // (If the CIE has a 'z' augmentation string, 'a' uses the + // encoding specified by the 'R' argument.) + // '1' a one-byte offset (OPERANDS->offset) + // '2' a two-byte offset (OPERANDS->offset) + // '4' a four-byte offset (OPERANDS->offset) + // '8' an eight-byte offset (OPERANDS->offset) + // 'e' a DW_FORM_block holding a (OPERANDS->expression) + // DWARF expression + bool ParseOperands(const char *format, Operands *operands); + + // Interpret one CFI instruction from STATE's instruction stream, update + // STATE, report any rule changes to handler_, and return true. On + // failure, report the problem and return false. + bool DoInstruction(); + + // The following Do* member functions are subroutines of DoInstruction, + // factoring out the actual work of operations that have several + // different encodings. + + // Set the CFA rule to be the value of BASE_REGISTER plus OFFSET, and + // return true. On failure, report and return false. (Used for + // DW_CFA_def_cfa and DW_CFA_def_cfa_sf.) + bool DoDefCFA(unsigned base_register, long offset); + + // Change the offset of the CFA rule to OFFSET, and return true. On + // failure, report and return false. (Subroutine for + // DW_CFA_def_cfa_offset and DW_CFA_def_cfa_offset_sf.) + bool DoDefCFAOffset(long offset); + + // Specify that REG can be recovered using RULE, and return true. On + // failure, report and return false. + bool DoRule(unsigned reg, Rule *rule); + + // Specify that REG can be found at OFFSET from the CFA, and return true. + // On failure, report and return false. (Subroutine for DW_CFA_offset, + // DW_CFA_offset_extended, and DW_CFA_offset_extended_sf.) + bool DoOffset(unsigned reg, long offset); + + // Specify that the caller's value for REG is the CFA plus OFFSET, + // and return true. On failure, report and return false. (Subroutine + // for DW_CFA_val_offset and DW_CFA_val_offset_sf.) + bool DoValOffset(unsigned reg, long offset); + + // Restore REG to the rule established in the CIE, and return true. On + // failure, report and return false. (Subroutine for DW_CFA_restore and + // DW_CFA_restore_extended.) + bool DoRestore(unsigned reg); + + // Return the section offset of the instruction at cursor. For use + // in error messages. + uint64 CursorOffset() { return entry_->offset + (cursor_ - entry_->start); } + + // Report that entry_ is incomplete, and return false. For brevity. + bool ReportIncomplete() { + reporter_->Incomplete(entry_->offset, entry_->kind); + return false; + } + + // For reading multi-byte values with the appropriate endianness. + ByteReader *reader_; + + // The handler to which we should report the data we find. + Handler *handler_; + + // For reporting problems in the info we're parsing. + Reporter *reporter_; + + // The code address to which the next instruction in the stream applies. + uint64 address_; + + // The entry whose instructions we are currently processing. This is + // first a CIE, and then an FDE. + const Entry *entry_; + + // The next instruction to process. + const uint8_t *cursor_; + + // The current set of rules. + RuleMap rules_; + + // The set of rules established by the CIE, used by DW_CFA_restore + // and DW_CFA_restore_extended. We set this after interpreting the + // CIE's instructions. + RuleMap cie_rules_; + + // A stack of saved states, for DW_CFA_remember_state and + // DW_CFA_restore_state. + std::stack<RuleMap> saved_rules_; +}; + +bool CallFrameInfo::State::InterpretCIE(const CIE &cie) { + entry_ = &cie; + cursor_ = entry_->instructions; + while (cursor_ < entry_->end) + if (!DoInstruction()) + return false; + // Note the rules established by the CIE, for use by DW_CFA_restore + // and DW_CFA_restore_extended. + cie_rules_ = rules_; + return true; +} + +bool CallFrameInfo::State::InterpretFDE(const FDE &fde) { + entry_ = &fde; + cursor_ = entry_->instructions; + while (cursor_ < entry_->end) + if (!DoInstruction()) + return false; + return true; +} + +bool CallFrameInfo::State::ParseOperands(const char *format, + Operands *operands) { + size_t len; + const char *operand; + + for (operand = format; *operand; operand++) { + size_t bytes_left = entry_->end - cursor_; + switch (*operand) { + case 'r': + operands->register_number = reader_->ReadUnsignedLEB128(cursor_, &len); + if (len > bytes_left) return ReportIncomplete(); + cursor_ += len; + break; + + case 'o': + operands->offset = reader_->ReadUnsignedLEB128(cursor_, &len); + if (len > bytes_left) return ReportIncomplete(); + cursor_ += len; + break; + + case 's': + operands->signed_offset = reader_->ReadSignedLEB128(cursor_, &len); + if (len > bytes_left) return ReportIncomplete(); + cursor_ += len; + break; + + case 'a': + operands->offset = + reader_->ReadEncodedPointer(cursor_, entry_->cie->pointer_encoding, + &len); + if (len > bytes_left) return ReportIncomplete(); + cursor_ += len; + break; + + case '1': + if (1 > bytes_left) return ReportIncomplete(); + operands->offset = static_cast<unsigned char>(*cursor_++); + break; + + case '2': + if (2 > bytes_left) return ReportIncomplete(); + operands->offset = reader_->ReadTwoBytes(cursor_); + cursor_ += 2; + break; + + case '4': + if (4 > bytes_left) return ReportIncomplete(); + operands->offset = reader_->ReadFourBytes(cursor_); + cursor_ += 4; + break; + + case '8': + if (8 > bytes_left) return ReportIncomplete(); + operands->offset = reader_->ReadEightBytes(cursor_); + cursor_ += 8; + break; + + case 'e': { + size_t expression_length = reader_->ReadUnsignedLEB128(cursor_, &len); + if (len > bytes_left || expression_length > bytes_left - len) + return ReportIncomplete(); + cursor_ += len; + operands->expression = string(reinterpret_cast<const char *>(cursor_), + expression_length); + cursor_ += expression_length; + break; + } + + default: + assert(0); + } + } + + return true; +} + +bool CallFrameInfo::State::DoInstruction() { + CIE *cie = entry_->cie; + Operands ops; + + // Our entry's kind should have been set by now. + assert(entry_->kind != kUnknown); + + // We shouldn't have been invoked unless there were more + // instructions to parse. + assert(cursor_ < entry_->end); + + unsigned opcode = *cursor_++; + if ((opcode & 0xc0) != 0) { + switch (opcode & 0xc0) { + // Advance the address. + case DW_CFA_advance_loc: { + size_t code_offset = opcode & 0x3f; + address_ += code_offset * cie->code_alignment_factor; + break; + } + + // Find a register at an offset from the CFA. + case DW_CFA_offset: + if (!ParseOperands("o", &ops) || + !DoOffset(opcode & 0x3f, ops.offset * cie->data_alignment_factor)) + return false; + break; + + // Restore the rule established for a register by the CIE. + case DW_CFA_restore: + if (!DoRestore(opcode & 0x3f)) return false; + break; + + // The 'if' above should have excluded this possibility. + default: + assert(0); + } + + // Return here, so the big switch below won't be indented. + return true; + } + + switch (opcode) { + // Set the address. + case DW_CFA_set_loc: + if (!ParseOperands("a", &ops)) return false; + address_ = ops.offset; + break; + + // Advance the address. + case DW_CFA_advance_loc1: + if (!ParseOperands("1", &ops)) return false; + address_ += ops.offset * cie->code_alignment_factor; + break; + + // Advance the address. + case DW_CFA_advance_loc2: + if (!ParseOperands("2", &ops)) return false; + address_ += ops.offset * cie->code_alignment_factor; + break; + + // Advance the address. + case DW_CFA_advance_loc4: + if (!ParseOperands("4", &ops)) return false; + address_ += ops.offset * cie->code_alignment_factor; + break; + + // Advance the address. + case DW_CFA_MIPS_advance_loc8: + if (!ParseOperands("8", &ops)) return false; + address_ += ops.offset * cie->code_alignment_factor; + break; + + // Compute the CFA by adding an offset to a register. + case DW_CFA_def_cfa: + if (!ParseOperands("ro", &ops) || + !DoDefCFA(ops.register_number, ops.offset)) + return false; + break; + + // Compute the CFA by adding an offset to a register. + case DW_CFA_def_cfa_sf: + if (!ParseOperands("rs", &ops) || + !DoDefCFA(ops.register_number, + ops.signed_offset * cie->data_alignment_factor)) + return false; + break; + + // Change the base register used to compute the CFA. + case DW_CFA_def_cfa_register: { + if (!ParseOperands("r", &ops)) return false; + Rule *cfa_rule = rules_.CFARule(); + if (!cfa_rule) { + if (!DoDefCFA(ops.register_number, ops.offset)) { + reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); + return false; + } + } else { + cfa_rule->SetBaseRegister(ops.register_number); + if (!cfa_rule->Handle(handler_, address_, + Handler::kCFARegister)) + return false; + } + break; + } + + // Change the offset used to compute the CFA. + case DW_CFA_def_cfa_offset: + if (!ParseOperands("o", &ops) || + !DoDefCFAOffset(ops.offset)) + return false; + break; + + // Change the offset used to compute the CFA. + case DW_CFA_def_cfa_offset_sf: + if (!ParseOperands("s", &ops) || + !DoDefCFAOffset(ops.signed_offset * cie->data_alignment_factor)) + return false; + break; + + // Specify an expression whose value is the CFA. + case DW_CFA_def_cfa_expression: { + if (!ParseOperands("e", &ops)) + return false; + Rule *rule = new ValExpressionRule(ops.expression); + rules_.SetCFARule(rule); + if (!rule->Handle(handler_, address_, + Handler::kCFARegister)) + return false; + break; + } + + // The register's value cannot be recovered. + case DW_CFA_undefined: { + if (!ParseOperands("r", &ops) || + !DoRule(ops.register_number, new UndefinedRule())) + return false; + break; + } + + // The register's value is unchanged from its value in the caller. + case DW_CFA_same_value: { + if (!ParseOperands("r", &ops) || + !DoRule(ops.register_number, new SameValueRule())) + return false; + break; + } + + // Find a register at an offset from the CFA. + case DW_CFA_offset_extended: + if (!ParseOperands("ro", &ops) || + !DoOffset(ops.register_number, + ops.offset * cie->data_alignment_factor)) + return false; + break; + + // The register is saved at an offset from the CFA. + case DW_CFA_offset_extended_sf: + if (!ParseOperands("rs", &ops) || + !DoOffset(ops.register_number, + ops.signed_offset * cie->data_alignment_factor)) + return false; + break; + + // The register is saved at an offset from the CFA. + case DW_CFA_GNU_negative_offset_extended: + if (!ParseOperands("ro", &ops) || + !DoOffset(ops.register_number, + -ops.offset * cie->data_alignment_factor)) + return false; + break; + + // The register's value is the sum of the CFA plus an offset. + case DW_CFA_val_offset: + if (!ParseOperands("ro", &ops) || + !DoValOffset(ops.register_number, + ops.offset * cie->data_alignment_factor)) + return false; + break; + + // The register's value is the sum of the CFA plus an offset. + case DW_CFA_val_offset_sf: + if (!ParseOperands("rs", &ops) || + !DoValOffset(ops.register_number, + ops.signed_offset * cie->data_alignment_factor)) + return false; + break; + + // The register has been saved in another register. + case DW_CFA_register: { + if (!ParseOperands("ro", &ops) || + !DoRule(ops.register_number, new RegisterRule(ops.offset))) + return false; + break; + } + + // An expression yields the address at which the register is saved. + case DW_CFA_expression: { + if (!ParseOperands("re", &ops) || + !DoRule(ops.register_number, new ExpressionRule(ops.expression))) + return false; + break; + } + + // An expression yields the caller's value for the register. + case DW_CFA_val_expression: { + if (!ParseOperands("re", &ops) || + !DoRule(ops.register_number, new ValExpressionRule(ops.expression))) + return false; + break; + } + + // Restore the rule established for a register by the CIE. + case DW_CFA_restore_extended: + if (!ParseOperands("r", &ops) || + !DoRestore( ops.register_number)) + return false; + break; + + // Save the current set of rules on a stack. + case DW_CFA_remember_state: + saved_rules_.push(rules_); + break; + + // Pop the current set of rules off the stack. + case DW_CFA_restore_state: { + if (saved_rules_.empty()) { + reporter_->EmptyStateStack(entry_->offset, entry_->kind, + CursorOffset()); + return false; + } + const RuleMap &new_rules = saved_rules_.top(); + if (rules_.CFARule() && !new_rules.CFARule()) { + reporter_->ClearingCFARule(entry_->offset, entry_->kind, + CursorOffset()); + return false; + } + rules_.HandleTransitionTo(handler_, address_, new_rules); + rules_ = new_rules; + saved_rules_.pop(); + break; + } + + // No operation. (Padding instruction.) + case DW_CFA_nop: + break; + + // A SPARC register window save: Registers 8 through 15 (%o0-%o7) + // are saved in registers 24 through 31 (%i0-%i7), and registers + // 16 through 31 (%l0-%l7 and %i0-%i7) are saved at CFA offsets + // (0-15 * the register size). The register numbers must be + // hard-coded. A GNU extension, and not a pretty one. + case DW_CFA_GNU_window_save: { + // Save %o0-%o7 in %i0-%i7. + for (int i = 8; i < 16; i++) + if (!DoRule(i, new RegisterRule(i + 16))) + return false; + // Save %l0-%l7 and %i0-%i7 at the CFA. + for (int i = 16; i < 32; i++) + // Assume that the byte reader's address size is the same as + // the architecture's register size. !@#%*^ hilarious. + if (!DoRule(i, new OffsetRule(Handler::kCFARegister, + (i - 16) * reader_->AddressSize()))) + return false; + break; + } + + // I'm not sure what this is. GDB doesn't use it for unwinding. + case DW_CFA_GNU_args_size: + if (!ParseOperands("o", &ops)) return false; + break; + + // An opcode we don't recognize. + default: { + reporter_->BadInstruction(entry_->offset, entry_->kind, CursorOffset()); + return false; + } + } + + return true; +} + +bool CallFrameInfo::State::DoDefCFA(unsigned base_register, long offset) { + Rule *rule = new ValOffsetRule(base_register, offset); + rules_.SetCFARule(rule); + return rule->Handle(handler_, address_, + Handler::kCFARegister); +} + +bool CallFrameInfo::State::DoDefCFAOffset(long offset) { + Rule *cfa_rule = rules_.CFARule(); + if (!cfa_rule) { + reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); + return false; + } + cfa_rule->SetOffset(offset); + return cfa_rule->Handle(handler_, address_, + Handler::kCFARegister); +} + +bool CallFrameInfo::State::DoRule(unsigned reg, Rule *rule) { + rules_.SetRegisterRule(reg, rule); + return rule->Handle(handler_, address_, reg); +} + +bool CallFrameInfo::State::DoOffset(unsigned reg, long offset) { + if (!rules_.CFARule()) { + reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); + return false; + } + return DoRule(reg, + new OffsetRule(Handler::kCFARegister, offset)); +} + +bool CallFrameInfo::State::DoValOffset(unsigned reg, long offset) { + if (!rules_.CFARule()) { + reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); + return false; + } + return DoRule(reg, + new ValOffsetRule(Handler::kCFARegister, offset)); +} + +bool CallFrameInfo::State::DoRestore(unsigned reg) { + // DW_CFA_restore and DW_CFA_restore_extended don't make sense in a CIE. + if (entry_->kind == kCIE) { + reporter_->RestoreInCIE(entry_->offset, CursorOffset()); + return false; + } + Rule *rule = cie_rules_.RegisterRule(reg); + if (!rule) { + // This isn't really the right thing to do, but since CFI generally + // only mentions callee-saves registers, and GCC's convention for + // callee-saves registers is that they are unchanged, it's a good + // approximation. + rule = new SameValueRule(); + } + return DoRule(reg, rule); +} + +bool CallFrameInfo::ReadEntryPrologue(const uint8_t *cursor, Entry *entry) { + const uint8_t *buffer_end = buffer_ + buffer_length_; + + // Initialize enough of ENTRY for use in error reporting. + entry->offset = cursor - buffer_; + entry->start = cursor; + entry->kind = kUnknown; + entry->end = NULL; + + // Read the initial length. This sets reader_'s offset size. + size_t length_size; + uint64 length = reader_->ReadInitialLength(cursor, &length_size); + if (length_size > size_t(buffer_end - cursor)) + return ReportIncomplete(entry); + cursor += length_size; + + // In a .eh_frame section, a length of zero marks the end of the series + // of entries. + if (length == 0 && eh_frame_) { + entry->kind = kTerminator; + entry->end = cursor; + return true; + } + + // Validate the length. + if (length > size_t(buffer_end - cursor)) + return ReportIncomplete(entry); + + // The length is the number of bytes after the initial length field; + // we have that position handy at this point, so compute the end + // now. (If we're parsing 64-bit-offset DWARF on a 32-bit machine, + // and the length didn't fit in a size_t, we would have rejected it + // above.) + entry->end = cursor + length; + + // Parse the next field: either the offset of a CIE or a CIE id. + size_t offset_size = reader_->OffsetSize(); + if (offset_size > size_t(entry->end - cursor)) return ReportIncomplete(entry); + entry->id = reader_->ReadOffset(cursor); + + // Don't advance cursor past id field yet; in .eh_frame data we need + // the id's position to compute the section offset of an FDE's CIE. + + // Now we can decide what kind of entry this is. + if (eh_frame_) { + // In .eh_frame data, an ID of zero marks the entry as a CIE, and + // anything else is an offset from the id field of the FDE to the start + // of the CIE. + if (entry->id == 0) { + entry->kind = kCIE; + } else { + entry->kind = kFDE; + // Turn the offset from the id into an offset from the buffer's start. + entry->id = (cursor - buffer_) - entry->id; + } + } else { + // In DWARF CFI data, an ID of ~0 (of the appropriate width, given the + // offset size for the entry) marks the entry as a CIE, and anything + // else is the offset of the CIE from the beginning of the section. + if (offset_size == 4) + entry->kind = (entry->id == 0xffffffff) ? kCIE : kFDE; + else { + assert(offset_size == 8); + entry->kind = (entry->id == 0xffffffffffffffffULL) ? kCIE : kFDE; + } + } + + // Now advance cursor past the id. + cursor += offset_size; + + // The fields specific to this kind of entry start here. + entry->fields = cursor; + + entry->cie = NULL; + + return true; +} + +bool CallFrameInfo::ReadCIEFields(CIE *cie) { + const uint8_t *cursor = cie->fields; + size_t len; + + assert(cie->kind == kCIE); + + // Prepare for early exit. + cie->version = 0; + cie->augmentation.clear(); + cie->code_alignment_factor = 0; + cie->data_alignment_factor = 0; + cie->return_address_register = 0; + cie->has_z_augmentation = false; + cie->pointer_encoding = DW_EH_PE_absptr; + cie->instructions = 0; + + // Parse the version number. + if (cie->end - cursor < 1) + return ReportIncomplete(cie); + cie->version = reader_->ReadOneByte(cursor); + cursor++; + + // If we don't recognize the version, we can't parse any more fields of the + // CIE. For DWARF CFI, we handle versions 1 through 4 (there was never a + // version 2 of CFI data). For .eh_frame, we handle versions 1 and 4 as well; + // the difference between those versions seems to be the same as for + // .debug_frame. + if (cie->version < 1 || cie->version > 4) { + reporter_->UnrecognizedVersion(cie->offset, cie->version); + return false; + } + + const uint8_t *augmentation_start = cursor; + const uint8_t *augmentation_end = + reinterpret_cast<const uint8_t *>(memchr(augmentation_start, '\0', + cie->end - augmentation_start)); + if (! augmentation_end) return ReportIncomplete(cie); + cursor = augmentation_end; + cie->augmentation = string(reinterpret_cast<const char *>(augmentation_start), + cursor - augmentation_start); + // Skip the terminating '\0'. + cursor++; + + // Is this CFI augmented? + if (!cie->augmentation.empty()) { + // Is it an augmentation we recognize? + if (cie->augmentation[0] == DW_Z_augmentation_start) { + // Linux C++ ABI 'z' augmentation, used for exception handling data. + cie->has_z_augmentation = true; + } else { + // Not an augmentation we recognize. Augmentations can have arbitrary + // effects on the form of rest of the content, so we have to give up. + reporter_->UnrecognizedAugmentation(cie->offset, cie->augmentation); + return false; + } + } + + if (cie->version >= 4) { + cie->address_size = *cursor++; + if (cie->address_size != 8 && cie->address_size != 4) { + reporter_->UnexpectedAddressSize(cie->offset, cie->address_size); + return false; + } + + cie->segment_size = *cursor++; + if (cie->segment_size != 0) { + reporter_->UnexpectedSegmentSize(cie->offset, cie->segment_size); + return false; + } + } + + // Parse the code alignment factor. + cie->code_alignment_factor = reader_->ReadUnsignedLEB128(cursor, &len); + if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie); + cursor += len; + + // Parse the data alignment factor. + cie->data_alignment_factor = reader_->ReadSignedLEB128(cursor, &len); + if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie); + cursor += len; + + // Parse the return address register. This is a ubyte in version 1, and + // a ULEB128 in version 3. + if (cie->version == 1) { + if (cursor >= cie->end) return ReportIncomplete(cie); + cie->return_address_register = uint8(*cursor++); + } else { + cie->return_address_register = reader_->ReadUnsignedLEB128(cursor, &len); + if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie); + cursor += len; + } + + // If we have a 'z' augmentation string, find the augmentation data and + // use the augmentation string to parse it. + if (cie->has_z_augmentation) { + uint64_t data_size = reader_->ReadUnsignedLEB128(cursor, &len); + if (size_t(cie->end - cursor) < len + data_size) + return ReportIncomplete(cie); + cursor += len; + const uint8_t *data = cursor; + cursor += data_size; + const uint8_t *data_end = cursor; + + cie->has_z_lsda = false; + cie->has_z_personality = false; + cie->has_z_signal_frame = false; + + // Walk the augmentation string, and extract values from the + // augmentation data as the string directs. + for (size_t i = 1; i < cie->augmentation.size(); i++) { + switch (cie->augmentation[i]) { + case DW_Z_has_LSDA: + // The CIE's augmentation data holds the language-specific data + // area pointer's encoding, and the FDE's augmentation data holds + // the pointer itself. + cie->has_z_lsda = true; + // Fetch the LSDA encoding from the augmentation data. + if (data >= data_end) return ReportIncomplete(cie); + cie->lsda_encoding = DwarfPointerEncoding(*data++); + if (!reader_->ValidEncoding(cie->lsda_encoding)) { + reporter_->InvalidPointerEncoding(cie->offset, cie->lsda_encoding); + return false; + } + // Don't check if the encoding is usable here --- we haven't + // read the FDE's fields yet, so we're not prepared for + // DW_EH_PE_funcrel, although that's a fine encoding for the + // LSDA to use, since it appears in the FDE. + break; + + case DW_Z_has_personality_routine: + // The CIE's augmentation data holds the personality routine + // pointer's encoding, followed by the pointer itself. + cie->has_z_personality = true; + // Fetch the personality routine pointer's encoding from the + // augmentation data. + if (data >= data_end) return ReportIncomplete(cie); + cie->personality_encoding = DwarfPointerEncoding(*data++); + if (!reader_->ValidEncoding(cie->personality_encoding)) { + reporter_->InvalidPointerEncoding(cie->offset, + cie->personality_encoding); + return false; + } + if (!reader_->UsableEncoding(cie->personality_encoding)) { + reporter_->UnusablePointerEncoding(cie->offset, + cie->personality_encoding); + return false; + } + // Fetch the personality routine's pointer itself from the data. + cie->personality_address = + reader_->ReadEncodedPointer(data, cie->personality_encoding, + &len); + if (len > size_t(data_end - data)) + return ReportIncomplete(cie); + data += len; + break; + + case DW_Z_has_FDE_address_encoding: + // The CIE's augmentation data holds the pointer encoding to use + // for addresses in the FDE. + if (data >= data_end) return ReportIncomplete(cie); + cie->pointer_encoding = DwarfPointerEncoding(*data++); + if (!reader_->ValidEncoding(cie->pointer_encoding)) { + reporter_->InvalidPointerEncoding(cie->offset, + cie->pointer_encoding); + return false; + } + if (!reader_->UsableEncoding(cie->pointer_encoding)) { + reporter_->UnusablePointerEncoding(cie->offset, + cie->pointer_encoding); + return false; + } + break; + + case DW_Z_is_signal_trampoline: + // Frames using this CIE are signal delivery frames. + cie->has_z_signal_frame = true; + break; + + default: + // An augmentation we don't recognize. + reporter_->UnrecognizedAugmentation(cie->offset, cie->augmentation); + return false; + } + } + } + + // The CIE's instructions start here. + cie->instructions = cursor; + + return true; +} + +bool CallFrameInfo::ReadFDEFields(FDE *fde) { + const uint8_t *cursor = fde->fields; + size_t size; + + fde->address = reader_->ReadEncodedPointer(cursor, fde->cie->pointer_encoding, + &size); + if (size > size_t(fde->end - cursor)) + return ReportIncomplete(fde); + cursor += size; + reader_->SetFunctionBase(fde->address); + + // For the length, we strip off the upper nybble of the encoding used for + // the starting address. + DwarfPointerEncoding length_encoding = + DwarfPointerEncoding(fde->cie->pointer_encoding & 0x0f); + fde->size = reader_->ReadEncodedPointer(cursor, length_encoding, &size); + if (size > size_t(fde->end - cursor)) + return ReportIncomplete(fde); + cursor += size; + + // If the CIE has a 'z' augmentation string, then augmentation data + // appears here. + if (fde->cie->has_z_augmentation) { + uint64_t data_size = reader_->ReadUnsignedLEB128(cursor, &size); + if (size_t(fde->end - cursor) < size + data_size) + return ReportIncomplete(fde); + cursor += size; + + // In the abstract, we should walk the augmentation string, and extract + // items from the FDE's augmentation data as we encounter augmentation + // string characters that specify their presence: the ordering of items + // in the augmentation string determines the arrangement of values in + // the augmentation data. + // + // In practice, there's only ever one value in FDE augmentation data + // that we support --- the LSDA pointer --- and we have to bail if we + // see any unrecognized augmentation string characters. So if there is + // anything here at all, we know what it is, and where it starts. + if (fde->cie->has_z_lsda) { + // Check whether the LSDA's pointer encoding is usable now: only once + // we've parsed the FDE's starting address do we call reader_-> + // SetFunctionBase, so that the DW_EH_PE_funcrel encoding becomes + // usable. + if (!reader_->UsableEncoding(fde->cie->lsda_encoding)) { + reporter_->UnusablePointerEncoding(fde->cie->offset, + fde->cie->lsda_encoding); + return false; + } + + fde->lsda_address = + reader_->ReadEncodedPointer(cursor, fde->cie->lsda_encoding, &size); + if (size > data_size) + return ReportIncomplete(fde); + // Ideally, we would also complain here if there were unconsumed + // augmentation data. + } + + cursor += data_size; + } + + // The FDE's instructions start after those. + fde->instructions = cursor; + + return true; +} + +bool CallFrameInfo::Start() { + const uint8_t *buffer_end = buffer_ + buffer_length_; + const uint8_t *cursor; + bool all_ok = true; + const uint8_t *entry_end; + bool ok; + + // Traverse all the entries in buffer_, skipping CIEs and offering + // FDEs to the handler. + for (cursor = buffer_; cursor < buffer_end; + cursor = entry_end, all_ok = all_ok && ok) { + FDE fde; + + // Make it easy to skip this entry with 'continue': assume that + // things are not okay until we've checked all the data, and + // prepare the address of the next entry. + ok = false; + + // Read the entry's prologue. + if (!ReadEntryPrologue(cursor, &fde)) { + if (!fde.end) { + // If we couldn't even figure out this entry's extent, then we + // must stop processing entries altogether. + all_ok = false; + break; + } + entry_end = fde.end; + continue; + } + + // The next iteration picks up after this entry. + entry_end = fde.end; + + // Did we see an .eh_frame terminating mark? + if (fde.kind == kTerminator) { + // If there appears to be more data left in the section after the + // terminating mark, warn the user. But this is just a warning; + // we leave all_ok true. + if (fde.end < buffer_end) reporter_->EarlyEHTerminator(fde.offset); + break; + } + + // In this loop, we skip CIEs. We only parse them fully when we + // parse an FDE that refers to them. This limits our memory + // consumption (beyond the buffer itself) to that needed to + // process the largest single entry. + if (fde.kind != kFDE) { + ok = true; + continue; + } + + // Validate the CIE pointer. + if (fde.id > buffer_length_) { + reporter_->CIEPointerOutOfRange(fde.offset, fde.id); + continue; + } + + CIE cie; + + // Parse this FDE's CIE header. + if (!ReadEntryPrologue(buffer_ + fde.id, &cie)) + continue; + // This had better be an actual CIE. + if (cie.kind != kCIE) { + reporter_->BadCIEId(fde.offset, fde.id); + continue; + } + if (!ReadCIEFields(&cie)) + continue; + + // TODO(nbilling): This could lead to strange behavior if a single buffer + // contained a mixture of DWARF versions as well as address sizes. Not + // sure if it's worth handling such a case. + + // DWARF4 CIE specifies address_size, so use it for this call frame. + if (cie.version >= 4) { + reader_->SetAddressSize(cie.address_size); + } + + // We now have the values that govern both the CIE and the FDE. + cie.cie = &cie; + fde.cie = &cie; + + // Parse the FDE's header. + if (!ReadFDEFields(&fde)) + continue; + + // Call Entry to ask the consumer if they're interested. + if (!handler_->Entry(fde.offset, fde.address, fde.size, + cie.version, cie.augmentation, + cie.return_address_register)) { + // The handler isn't interested in this entry. That's not an error. + ok = true; + continue; + } + + if (cie.has_z_augmentation) { + // Report the personality routine address, if we have one. + if (cie.has_z_personality) { + if (!handler_ + ->PersonalityRoutine(cie.personality_address, + IsIndirectEncoding(cie.personality_encoding))) + continue; + } + + // Report the language-specific data area address, if we have one. + if (cie.has_z_lsda) { + if (!handler_ + ->LanguageSpecificDataArea(fde.lsda_address, + IsIndirectEncoding(cie.lsda_encoding))) + continue; + } + + // If this is a signal-handling frame, report that. + if (cie.has_z_signal_frame) { + if (!handler_->SignalHandler()) + continue; + } + } + + // Interpret the CIE's instructions, and then the FDE's instructions. + State state(reader_, handler_, reporter_, fde.address); + ok = state.InterpretCIE(cie) && state.InterpretFDE(fde); + + // Tell the ByteReader that the function start address from the + // FDE header is no longer valid. + reader_->ClearFunctionBase(); + + // Report the end of the entry. + handler_->End(); + } + + return all_ok; +} + +const char *CallFrameInfo::KindName(EntryKind kind) { + if (kind == CallFrameInfo::kUnknown) + return "entry"; + else if (kind == CallFrameInfo::kCIE) + return "common information entry"; + else if (kind == CallFrameInfo::kFDE) + return "frame description entry"; + else { + assert (kind == CallFrameInfo::kTerminator); + return ".eh_frame sequence terminator"; + } +} + +bool CallFrameInfo::ReportIncomplete(Entry *entry) { + reporter_->Incomplete(entry->offset, entry->kind); + return false; +} + +void CallFrameInfo::Reporter::Incomplete(uint64 offset, + CallFrameInfo::EntryKind kind) { + fprintf(stderr, + "%s: CFI %s at offset 0x%llx in '%s': entry ends early\n", + filename_.c_str(), CallFrameInfo::KindName(kind), offset, + section_.c_str()); +} + +void CallFrameInfo::Reporter::EarlyEHTerminator(uint64 offset) { + fprintf(stderr, + "%s: CFI at offset 0x%llx in '%s': saw end-of-data marker" + " before end of section contents\n", + filename_.c_str(), offset, section_.c_str()); +} + +void CallFrameInfo::Reporter::CIEPointerOutOfRange(uint64 offset, + uint64 cie_offset) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%llx in '%s':" + " CIE pointer is out of range: 0x%llx\n", + filename_.c_str(), offset, section_.c_str(), cie_offset); +} + +void CallFrameInfo::Reporter::BadCIEId(uint64 offset, uint64 cie_offset) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%llx in '%s':" + " CIE pointer does not point to a CIE: 0x%llx\n", + filename_.c_str(), offset, section_.c_str(), cie_offset); +} + +void CallFrameInfo::Reporter::UnexpectedAddressSize(uint64 offset, + uint8_t address_size) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%llx in '%s':" + " CIE specifies unexpected address size: %d\n", + filename_.c_str(), offset, section_.c_str(), address_size); +} + +void CallFrameInfo::Reporter::UnexpectedSegmentSize(uint64 offset, + uint8_t segment_size) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%llx in '%s':" + " CIE specifies unexpected segment size: %d\n", + filename_.c_str(), offset, section_.c_str(), segment_size); +} + +void CallFrameInfo::Reporter::UnrecognizedVersion(uint64 offset, int version) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%llx in '%s':" + " CIE specifies unrecognized version: %d\n", + filename_.c_str(), offset, section_.c_str(), version); +} + +void CallFrameInfo::Reporter::UnrecognizedAugmentation(uint64 offset, + const string &aug) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%llx in '%s':" + " CIE specifies unrecognized augmentation: '%s'\n", + filename_.c_str(), offset, section_.c_str(), aug.c_str()); +} + +void CallFrameInfo::Reporter::InvalidPointerEncoding(uint64 offset, + uint8 encoding) { + fprintf(stderr, + "%s: CFI common information entry at offset 0x%llx in '%s':" + " 'z' augmentation specifies invalid pointer encoding: 0x%02x\n", + filename_.c_str(), offset, section_.c_str(), encoding); +} + +void CallFrameInfo::Reporter::UnusablePointerEncoding(uint64 offset, + uint8 encoding) { + fprintf(stderr, + "%s: CFI common information entry at offset 0x%llx in '%s':" + " 'z' augmentation specifies a pointer encoding for which" + " we have no base address: 0x%02x\n", + filename_.c_str(), offset, section_.c_str(), encoding); +} + +void CallFrameInfo::Reporter::RestoreInCIE(uint64 offset, uint64 insn_offset) { + fprintf(stderr, + "%s: CFI common information entry at offset 0x%llx in '%s':" + " the DW_CFA_restore instruction at offset 0x%llx" + " cannot be used in a common information entry\n", + filename_.c_str(), offset, section_.c_str(), insn_offset); +} + +void CallFrameInfo::Reporter::BadInstruction(uint64 offset, + CallFrameInfo::EntryKind kind, + uint64 insn_offset) { + fprintf(stderr, + "%s: CFI %s at offset 0x%llx in section '%s':" + " the instruction at offset 0x%llx is unrecognized\n", + filename_.c_str(), CallFrameInfo::KindName(kind), + offset, section_.c_str(), insn_offset); +} + +void CallFrameInfo::Reporter::NoCFARule(uint64 offset, + CallFrameInfo::EntryKind kind, + uint64 insn_offset) { + fprintf(stderr, + "%s: CFI %s at offset 0x%llx in section '%s':" + " the instruction at offset 0x%llx assumes that a CFA rule has" + " been set, but none has been set\n", + filename_.c_str(), CallFrameInfo::KindName(kind), offset, + section_.c_str(), insn_offset); +} + +void CallFrameInfo::Reporter::EmptyStateStack(uint64 offset, + CallFrameInfo::EntryKind kind, + uint64 insn_offset) { + fprintf(stderr, + "%s: CFI %s at offset 0x%llx in section '%s':" + " the DW_CFA_restore_state instruction at offset 0x%llx" + " should pop a saved state from the stack, but the stack is empty\n", + filename_.c_str(), CallFrameInfo::KindName(kind), offset, + section_.c_str(), insn_offset); +} + +void CallFrameInfo::Reporter::ClearingCFARule(uint64 offset, + CallFrameInfo::EntryKind kind, + uint64 insn_offset) { + fprintf(stderr, + "%s: CFI %s at offset 0x%llx in section '%s':" + " the DW_CFA_restore_state instruction at offset 0x%llx" + " would clear the CFA rule in effect\n", + filename_.c_str(), CallFrameInfo::KindName(kind), offset, + section_.c_str(), insn_offset); +} + +} // namespace dwarf2reader diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.h new file mode 100644 index 0000000000..902d9ef1f4 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.h @@ -0,0 +1,1331 @@ +// -*- mode: C++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// CFI reader author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// This file contains definitions related to the DWARF2/3 reader and +// it's handler interfaces. +// The DWARF2/3 specification can be found at +// http://dwarf.freestandards.org and should be considered required +// reading if you wish to modify the implementation. +// Only a cursory attempt is made to explain terminology that is +// used here, as it is much better explained in the standard documents +#ifndef COMMON_DWARF_DWARF2READER_H__ +#define COMMON_DWARF_DWARF2READER_H__ + +#include <stdint.h> + +#include <list> +#include <map> +#include <string> +#include <utility> +#include <vector> +#include <memory> + +#include "common/dwarf/bytereader.h" +#include "common/dwarf/dwarf2enums.h" +#include "common/dwarf/types.h" +#include "common/using_std_string.h" +#include "common/dwarf/elf_reader.h" + +namespace dwarf2reader { +struct LineStateMachine; +class Dwarf2Handler; +class LineInfoHandler; +class DwpReader; + +// This maps from a string naming a section to a pair containing a +// the data for the section, and the size of the section. +typedef std::map<string, std::pair<const uint8_t *, uint64> > SectionMap; +typedef std::list<std::pair<enum DwarfAttribute, enum DwarfForm> > + AttributeList; +typedef AttributeList::iterator AttributeIterator; +typedef AttributeList::const_iterator ConstAttributeIterator; + +struct LineInfoHeader { + uint64 total_length; + uint16 version; + uint64 prologue_length; + uint8 min_insn_length; // insn stands for instructin + bool default_is_stmt; // stmt stands for statement + int8 line_base; + uint8 line_range; + uint8 opcode_base; + // Use a pointer so that signalsafe_addr2line is able to use this structure + // without heap allocation problem. + std::vector<unsigned char> *std_opcode_lengths; +}; + +class LineInfo { + public: + + // Initializes a .debug_line reader. Buffer and buffer length point + // to the beginning and length of the line information to read. + // Reader is a ByteReader class that has the endianness set + // properly. + LineInfo(const uint8_t *buffer_, uint64 buffer_length, + ByteReader* reader, LineInfoHandler* handler); + + virtual ~LineInfo() { + if (header_.std_opcode_lengths) { + delete header_.std_opcode_lengths; + } + } + + // Start processing line info, and calling callbacks in the handler. + // Consumes the line number information for a single compilation unit. + // Returns the number of bytes processed. + uint64 Start(); + + // Process a single line info opcode at START using the state + // machine at LSM. Return true if we should define a line using the + // current state of the line state machine. Place the length of the + // opcode in LEN. + // If LSM_PASSES_PC is non-NULL, this function also checks if the lsm + // passes the address of PC. In other words, LSM_PASSES_PC will be + // set to true, if the following condition is met. + // + // lsm's old address < PC <= lsm's new address + static bool ProcessOneOpcode(ByteReader* reader, + LineInfoHandler* handler, + const struct LineInfoHeader &header, + const uint8_t *start, + struct LineStateMachine* lsm, + size_t* len, + uintptr pc, + bool *lsm_passes_pc); + + private: + // Reads the DWARF2/3 header for this line info. + void ReadHeader(); + + // Reads the DWARF2/3 line information + void ReadLines(); + + // The associated handler to call processing functions in + LineInfoHandler* handler_; + + // The associated ByteReader that handles endianness issues for us + ByteReader* reader_; + + // A DWARF2/3 line info header. This is not the same size as + // in the actual file, as the one in the file may have a 32 bit or + // 64 bit lengths + + struct LineInfoHeader header_; + + // buffer is the buffer for our line info, starting at exactly where + // the line info to read is. after_header is the place right after + // the end of the line information header. + const uint8_t *buffer_; +#ifndef NDEBUG + uint64 buffer_length_; +#endif + const uint8_t *after_header_; +}; + +// This class is the main interface between the line info reader and +// the client. The virtual functions inside this get called for +// interesting events that happen during line info reading. The +// default implementation does nothing + +class LineInfoHandler { + public: + LineInfoHandler() { } + + virtual ~LineInfoHandler() { } + + // Called when we define a directory. NAME is the directory name, + // DIR_NUM is the directory number + virtual void DefineDir(const string& name, uint32 dir_num) { } + + // Called when we define a filename. NAME is the filename, FILE_NUM + // is the file number which is -1 if the file index is the next + // index after the last numbered index (this happens when files are + // dynamically defined by the line program), DIR_NUM is the + // directory index for the directory name of this file, MOD_TIME is + // the modification time of the file, and LENGTH is the length of + // the file + virtual void DefineFile(const string& name, int32 file_num, + uint32 dir_num, uint64 mod_time, + uint64 length) { } + + // Called when the line info reader has a new line, address pair + // ready for us. ADDRESS is the address of the code, LENGTH is the + // length of its machine code in bytes, FILE_NUM is the file number + // containing the code, LINE_NUM is the line number in that file for + // the code, and COLUMN_NUM is the column number the code starts at, + // if we know it (0 otherwise). + virtual void AddLine(uint64 address, uint64 length, + uint32 file_num, uint32 line_num, uint32 column_num) { } +}; + +class RangeListHandler { + public: + RangeListHandler() { } + + virtual ~RangeListHandler() { } + + // Add a range. + virtual void AddRange(uint64 begin, uint64 end) { }; + + // A new base address must be set for computing the ranges' addresses. + virtual void SetBaseAddress(uint64 base_address) { }; + + // Finish processing the range list. + virtual void Finish() { }; +}; + +class RangeListReader { + public: + RangeListReader(const uint8_t *buffer, uint64 size, ByteReader *reader, + RangeListHandler *handler); + + bool ReadRangeList(uint64 offset); + + private: + const uint8_t *buffer_; + uint64 size_; + ByteReader* reader_; + RangeListHandler *handler_; +}; + +// This class is the main interface between the reader and the +// client. The virtual functions inside this get called for +// interesting events that happen during DWARF2 reading. +// The default implementation skips everything. +class Dwarf2Handler { + public: + Dwarf2Handler() { } + + virtual ~Dwarf2Handler() { } + + // Start to process a compilation unit at OFFSET from the beginning of the + // .debug_info section. Return false if you would like to skip this + // compilation unit. + virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version) { return false; } + + // When processing a skeleton compilation unit, resulting from a split + // DWARF compilation, once the skeleton debug info has been read, + // the reader will call this function to ask the client if it needs + // the full debug info from the .dwo or .dwp file. Return true if + // you need it, or false to skip processing the split debug info. + virtual bool NeedSplitDebugInfo() { return true; } + + // Start to process a split compilation unit at OFFSET from the beginning of + // the debug_info section in the .dwp/.dwo file. Return false if you would + // like to skip this compilation unit. + virtual bool StartSplitCompilationUnit(uint64 offset, + uint64 cu_length) { return false; } + + // Start to process a DIE at OFFSET from the beginning of the .debug_info + // section. Return false if you would like to skip this DIE. + virtual bool StartDIE(uint64 offset, enum DwarfTag tag) { return false; } + + // Called when we have an attribute with unsigned data to give to our + // handler. The attribute is for the DIE at OFFSET from the beginning of the + // .debug_info section. Its name is ATTR, its form is FORM, and its value is + // DATA. + virtual void ProcessAttributeUnsigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { } + + // Called when we have an attribute with signed data to give to our handler. + // The attribute is for the DIE at OFFSET from the beginning of the + // .debug_info section. Its name is ATTR, its form is FORM, and its value is + // DATA. + virtual void ProcessAttributeSigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data) { } + + // Called when we have an attribute whose value is a reference to + // another DIE. The attribute belongs to the DIE at OFFSET from the + // beginning of the .debug_info section. Its name is ATTR, its form + // is FORM, and the offset of the DIE being referred to from the + // beginning of the .debug_info section is DATA. + virtual void ProcessAttributeReference(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { } + + // Called when we have an attribute with a buffer of data to give to our + // handler. The attribute is for the DIE at OFFSET from the beginning of the + // .debug_info section. Its name is ATTR, its form is FORM, DATA points to + // the buffer's contents, and its length in bytes is LENGTH. The buffer is + // owned by the caller, not the callee, and may not persist for very long. + // If you want the data to be available later, it needs to be copied. + virtual void ProcessAttributeBuffer(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const uint8_t *data, + uint64 len) { } + + // Called when we have an attribute with string data to give to our handler. + // The attribute is for the DIE at OFFSET from the beginning of the + // .debug_info section. Its name is ATTR, its form is FORM, and its value is + // DATA. + virtual void ProcessAttributeString(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const string& data) { } + + // Called when we have an attribute whose value is the 64-bit signature + // of a type unit in the .debug_types section. OFFSET is the offset of + // the DIE whose attribute we're reporting. ATTR and FORM are the + // attribute's name and form. SIGNATURE is the type unit's signature. + virtual void ProcessAttributeSignature(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 signature) { } + + // Called when finished processing the DIE at OFFSET. + // Because DWARF2/3 specifies a tree of DIEs, you may get starts + // before ends of the previous DIE, as we process children before + // ending the parent. + virtual void EndDIE(uint64 offset) { } + +}; + +// The base of DWARF2/3 debug info is a DIE (Debugging Information +// Entry. +// DWARF groups DIE's into a tree and calls the root of this tree a +// "compilation unit". Most of the time, there is one compilation +// unit in the .debug_info section for each file that had debug info +// generated. +// Each DIE consists of + +// 1. a tag specifying a thing that is being described (ie +// DW_TAG_subprogram for functions, DW_TAG_variable for variables, etc +// 2. attributes (such as DW_AT_location for location in memory, +// DW_AT_name for name), and data for each attribute. +// 3. A flag saying whether the DIE has children or not + +// In order to gain some amount of compression, the format of +// each DIE (tag name, attributes and data forms for the attributes) +// are stored in a separate table called the "abbreviation table". +// This is done because a large number of DIEs have the exact same tag +// and list of attributes, but different data for those attributes. +// As a result, the .debug_info section is just a stream of data, and +// requires reading of the .debug_abbrev section to say what the data +// means. + +// As a warning to the user, it should be noted that the reason for +// using absolute offsets from the beginning of .debug_info is that +// DWARF2/3 supports referencing DIE's from other DIE's by their offset +// from either the current compilation unit start, *or* the beginning +// of the .debug_info section. This means it is possible to reference +// a DIE in one compilation unit from a DIE in another compilation +// unit. This style of reference is usually used to eliminate +// duplicated information that occurs across compilation +// units, such as base types, etc. GCC 3.4+ support this with +// -feliminate-dwarf2-dups. Other toolchains will sometimes do +// duplicate elimination in the linker. + +class CompilationUnit { + public: + + // Initialize a compilation unit. This requires a map of sections, + // the offset of this compilation unit in the .debug_info section, a + // ByteReader, and a Dwarf2Handler class to call callbacks in. + CompilationUnit(const string& path, const SectionMap& sections, uint64 offset, + ByteReader* reader, Dwarf2Handler* handler); + virtual ~CompilationUnit() { + if (abbrevs_) delete abbrevs_; + } + + // Initialize a compilation unit from a .dwo or .dwp file. + // In this case, we need the .debug_addr section from the + // executable file that contains the corresponding skeleton + // compilation unit. We also inherit the Dwarf2Handler from + // the executable file, and call it as if we were still + // processing the original compilation unit. + void SetSplitDwarf(const uint8_t* addr_buffer, uint64 addr_buffer_length, + uint64 addr_base, uint64 ranges_base, uint64 dwo_id); + + // Begin reading a Dwarf2 compilation unit, and calling the + // callbacks in the Dwarf2Handler + + // Return the full length of the compilation unit, including + // headers. This plus the starting offset passed to the constructor + // is the offset of the end of the compilation unit --- and the + // start of the next compilation unit, if there is one. + uint64 Start(); + + private: + + // This struct represents a single DWARF2/3 abbreviation + // The abbreviation tells how to read a DWARF2/3 DIE, and consist of a + // tag and a list of attributes, as well as the data form of each attribute. + struct Abbrev { + uint64 number; + enum DwarfTag tag; + bool has_children; + AttributeList attributes; + }; + + // A DWARF2/3 compilation unit header. This is not the same size as + // in the actual file, as the one in the file may have a 32 bit or + // 64 bit length. + struct CompilationUnitHeader { + uint64 length; + uint16 version; + uint64 abbrev_offset; + uint8 address_size; + } header_; + + // Reads the DWARF2/3 header for this compilation unit. + void ReadHeader(); + + // Reads the DWARF2/3 abbreviations for this compilation unit + void ReadAbbrevs(); + + // Processes a single DIE for this compilation unit and return a new + // pointer just past the end of it + const uint8_t *ProcessDIE(uint64 dieoffset, + const uint8_t *start, + const Abbrev& abbrev); + + // Processes a single attribute and return a new pointer just past the + // end of it + const uint8_t *ProcessAttribute(uint64 dieoffset, + const uint8_t *start, + enum DwarfAttribute attr, + enum DwarfForm form); + + // Called when we have an attribute with unsigned data to give to + // our handler. The attribute is for the DIE at OFFSET from the + // beginning of compilation unit, has a name of ATTR, a form of + // FORM, and the actual data of the attribute is in DATA. + // If we see a DW_AT_GNU_dwo_id attribute, save the value so that + // we can find the debug info in a .dwo or .dwp file. + void ProcessAttributeUnsigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + if (attr == DW_AT_GNU_dwo_id) { + dwo_id_ = data; + } + else if (attr == DW_AT_GNU_addr_base) { + addr_base_ = data; + } + else if (attr == DW_AT_GNU_ranges_base) { + ranges_base_ = data; + } + // TODO(yunlian): When we add DW_AT_ranges_base from DWARF-5, + // that base will apply to DW_AT_ranges attributes in the + // skeleton CU as well as in the .dwo/.dwp files. + else if (attr == DW_AT_ranges && is_split_dwarf_) { + data += ranges_base_; + } + handler_->ProcessAttributeUnsigned(offset, attr, form, data); + } + + // Called when we have an attribute with signed data to give to + // our handler. The attribute is for the DIE at OFFSET from the + // beginning of compilation unit, has a name of ATTR, a form of + // FORM, and the actual data of the attribute is in DATA. + void ProcessAttributeSigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data) { + handler_->ProcessAttributeSigned(offset, attr, form, data); + } + + // Called when we have an attribute with a buffer of data to give to + // our handler. The attribute is for the DIE at OFFSET from the + // beginning of compilation unit, has a name of ATTR, a form of + // FORM, and the actual data of the attribute is in DATA, and the + // length of the buffer is LENGTH. + void ProcessAttributeBuffer(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const uint8_t* data, + uint64 len) { + handler_->ProcessAttributeBuffer(offset, attr, form, data, len); + } + + // Called when we have an attribute with string data to give to + // our handler. The attribute is for the DIE at OFFSET from the + // beginning of compilation unit, has a name of ATTR, a form of + // FORM, and the actual data of the attribute is in DATA. + // If we see a DW_AT_GNU_dwo_name attribute, save the value so + // that we can find the debug info in a .dwo or .dwp file. + void ProcessAttributeString(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const char* data) { + if (attr == DW_AT_GNU_dwo_name) + dwo_name_ = data; + handler_->ProcessAttributeString(offset, attr, form, data); + } + + // Processes all DIEs for this compilation unit + void ProcessDIEs(); + + // Skips the die with attributes specified in ABBREV starting at + // START, and return the new place to position the stream to. + const uint8_t *SkipDIE(const uint8_t *start, const Abbrev& abbrev); + + // Skips the attribute starting at START, with FORM, and return the + // new place to position the stream to. + const uint8_t *SkipAttribute(const uint8_t *start, enum DwarfForm form); + + // Process the actual debug information in a split DWARF file. + void ProcessSplitDwarf(); + + // Read the debug sections from a .dwo file. + void ReadDebugSectionsFromDwo(ElfReader* elf_reader, + SectionMap* sections); + + // Path of the file containing the debug information. + const string path_; + + // Offset from section start is the offset of this compilation unit + // from the beginning of the .debug_info section. + uint64 offset_from_section_start_; + + // buffer is the buffer for our CU, starting at .debug_info + offset + // passed in from constructor. + // after_header points to right after the compilation unit header. + const uint8_t *buffer_; + uint64 buffer_length_; + const uint8_t *after_header_; + + // The associated ByteReader that handles endianness issues for us + ByteReader* reader_; + + // The map of sections in our file to buffers containing their data + const SectionMap& sections_; + + // The associated handler to call processing functions in + Dwarf2Handler* handler_; + + // Set of DWARF2/3 abbreviations for this compilation unit. Indexed + // by abbreviation number, which means that abbrevs_[0] is not + // valid. + std::vector<Abbrev>* abbrevs_; + + // String section buffer and length, if we have a string section. + // This is here to avoid doing a section lookup for strings in + // ProcessAttribute, which is in the hot path for DWARF2 reading. + const uint8_t *string_buffer_; + uint64 string_buffer_length_; + + // String offsets section buffer and length, if we have a string offsets + // section (.debug_str_offsets or .debug_str_offsets.dwo). + const uint8_t* str_offsets_buffer_; + uint64 str_offsets_buffer_length_; + + // Address section buffer and length, if we have an address section + // (.debug_addr). + const uint8_t* addr_buffer_; + uint64 addr_buffer_length_; + + // Flag indicating whether this compilation unit is part of a .dwo + // or .dwp file. If true, we are reading this unit because a + // skeleton compilation unit in an executable file had a + // DW_AT_GNU_dwo_name or DW_AT_GNU_dwo_id attribute. + // In a .dwo file, we expect the string offsets section to + // have a ".dwo" suffix, and we will use the ".debug_addr" section + // associated with the skeleton compilation unit. + bool is_split_dwarf_; + + // The value of the DW_AT_GNU_dwo_id attribute, if any. + uint64 dwo_id_; + + // The value of the DW_AT_GNU_dwo_name attribute, if any. + const char* dwo_name_; + + // If this is a split DWARF CU, the value of the DW_AT_GNU_dwo_id attribute + // from the skeleton CU. + uint64 skeleton_dwo_id_; + + // The value of the DW_AT_GNU_ranges_base attribute, if any. + uint64 ranges_base_; + + // The value of the DW_AT_GNU_addr_base attribute, if any. + uint64 addr_base_; + + // True if we have already looked for a .dwp file. + bool have_checked_for_dwp_; + + // Path to the .dwp file. + string dwp_path_; + + // ByteReader for the DWP file. + std::unique_ptr<ByteReader> dwp_byte_reader_; + + // DWP reader. + std::unique_ptr<DwpReader> dwp_reader_; +}; + +// A Reader for a .dwp file. Supports the fetching of DWARF debug +// info for a given dwo_id. +// +// There are two versions of .dwp files. In both versions, the +// .dwp file is an ELF file containing only debug sections. +// In Version 1, the file contains many copies of each debug +// section, one for each .dwo file that is packaged in the .dwp +// file, and the .debug_cu_index section maps from the dwo_id +// to a set of section indexes. In Version 2, the file contains +// one of each debug section, and the .debug_cu_index section +// maps from the dwo_id to a set of offsets and lengths that +// identify each .dwo file's contribution to the larger sections. + +class DwpReader { + public: + DwpReader(const ByteReader& byte_reader, ElfReader* elf_reader); + + ~DwpReader(); + + // Read the CU index and initialize data members. + void Initialize(); + + // Read the debug sections for the given dwo_id. + void ReadDebugSectionsForCU(uint64 dwo_id, SectionMap* sections); + + private: + // Search a v1 hash table for "dwo_id". Returns the slot index + // where the dwo_id was found, or -1 if it was not found. + int LookupCU(uint64 dwo_id); + + // Search a v2 hash table for "dwo_id". Returns the row index + // in the offsets and sizes tables, or 0 if it was not found. + uint32 LookupCUv2(uint64 dwo_id); + + // The ELF reader for the .dwp file. + ElfReader* elf_reader_; + + // The ByteReader for the .dwp file. + const ByteReader& byte_reader_; + + // Pointer to the .debug_cu_index section. + const char* cu_index_; + + // Size of the .debug_cu_index section. + size_t cu_index_size_; + + // Pointer to the .debug_str.dwo section. + const char* string_buffer_; + + // Size of the .debug_str.dwo section. + size_t string_buffer_size_; + + // Version of the .dwp file. We support versions 1 and 2 currently. + int version_; + + // Number of columns in the section tables (version 2). + unsigned int ncolumns_; + + // Number of units in the section tables (version 2). + unsigned int nunits_; + + // Number of slots in the hash table. + unsigned int nslots_; + + // Pointer to the beginning of the hash table. + const char* phash_; + + // Pointer to the beginning of the index table. + const char* pindex_; + + // Pointer to the beginning of the section index pool (version 1). + const char* shndx_pool_; + + // Pointer to the beginning of the section offset table (version 2). + const char* offset_table_; + + // Pointer to the beginning of the section size table (version 2). + const char* size_table_; + + // Contents of the sections of interest (version 2). + const char* abbrev_data_; + size_t abbrev_size_; + const char* info_data_; + size_t info_size_; + const char* str_offsets_data_; + size_t str_offsets_size_; +}; + +// This class is a reader for DWARF's Call Frame Information. CFI +// describes how to unwind stack frames --- even for functions that do +// not follow fixed conventions for saving registers, whose frame size +// varies as they execute, etc. +// +// CFI describes, at each machine instruction, how to compute the +// stack frame's base address, how to find the return address, and +// where to find the saved values of the caller's registers (if the +// callee has stashed them somewhere to free up the registers for its +// own use). +// +// For example, suppose we have a function whose machine code looks +// like this (imagine an assembly language that looks like C, for a +// machine with 32-bit registers, and a stack that grows towards lower +// addresses): +// +// func: ; entry point; return address at sp +// func+0: sp = sp - 16 ; allocate space for stack frame +// func+1: sp[12] = r0 ; save r0 at sp+12 +// ... ; other code, not frame-related +// func+10: sp -= 4; *sp = x ; push some x on the stack +// ... ; other code, not frame-related +// func+20: r0 = sp[16] ; restore saved r0 +// func+21: sp += 20 ; pop whole stack frame +// func+22: pc = *sp; sp += 4 ; pop return address and jump to it +// +// DWARF CFI is (a very compressed representation of) a table with a +// row for each machine instruction address and a column for each +// register showing how to restore it, if possible. +// +// A special column named "CFA", for "Canonical Frame Address", tells how +// to compute the base address of the frame; registers' entries may +// refer to the CFA in describing where the registers are saved. +// +// Another special column, named "RA", represents the return address. +// +// For example, here is a complete (uncompressed) table describing the +// function above: +// +// insn cfa r0 r1 ... ra +// ======================================= +// func+0: sp cfa[0] +// func+1: sp+16 cfa[0] +// func+2: sp+16 cfa[-4] cfa[0] +// func+11: sp+20 cfa[-4] cfa[0] +// func+21: sp+20 cfa[0] +// func+22: sp cfa[0] +// +// Some things to note here: +// +// - Each row describes the state of affairs *before* executing the +// instruction at the given address. Thus, the row for func+0 +// describes the state before we allocate the stack frame. In the +// next row, the formula for computing the CFA has changed, +// reflecting that allocation. +// +// - The other entries are written in terms of the CFA; this allows +// them to remain unchanged as the stack pointer gets bumped around. +// For example, the rule for recovering the return address (the "ra" +// column) remains unchanged throughout the function, even as the +// stack pointer takes on three different offsets from the return +// address. +// +// - Although we haven't shown it, most calling conventions designate +// "callee-saves" and "caller-saves" registers. The callee must +// preserve the values of callee-saves registers; if it uses them, +// it must save their original values somewhere, and restore them +// before it returns. In contrast, the callee is free to trash +// caller-saves registers; if the callee uses these, it will +// probably not bother to save them anywhere, and the CFI will +// probably mark their values as "unrecoverable". +// +// (However, since the caller cannot assume the callee was going to +// save them, caller-saves registers are probably dead in the caller +// anyway, so compilers usually don't generate CFA for caller-saves +// registers.) +// +// - Exactly where the CFA points is a matter of convention that +// depends on the architecture and ABI in use. In the example, the +// CFA is the value the stack pointer had upon entry to the +// function, pointing at the saved return address. But on the x86, +// the call frame information generated by GCC follows the +// convention that the CFA is the address *after* the saved return +// address. +// +// But by definition, the CFA remains constant throughout the +// lifetime of the frame. This makes it a useful value for other +// columns to refer to. It is also gives debuggers a useful handle +// for identifying a frame. +// +// If you look at the table above, you'll notice that a given entry is +// often the same as the one immediately above it: most instructions +// change only one or two aspects of the stack frame, if they affect +// it at all. The DWARF format takes advantage of this fact, and +// reduces the size of the data by mentioning only the addresses and +// columns at which changes take place. So for the above, DWARF CFI +// data would only actually mention the following: +// +// insn cfa r0 r1 ... ra +// ======================================= +// func+0: sp cfa[0] +// func+1: sp+16 +// func+2: cfa[-4] +// func+11: sp+20 +// func+21: r0 +// func+22: sp +// +// In fact, this is the way the parser reports CFI to the consumer: as +// a series of statements of the form, "At address X, column Y changed +// to Z," and related conventions for describing the initial state. +// +// Naturally, it would be impractical to have to scan the entire +// program's CFI, noting changes as we go, just to recover the +// unwinding rules in effect at one particular instruction. To avoid +// this, CFI data is grouped into "entries", each of which covers a +// specified range of addresses and begins with a complete statement +// of the rules for all recoverable registers at that starting +// address. Each entry typically covers a single function. +// +// Thus, to compute the contents of a given row of the table --- that +// is, rules for recovering the CFA, RA, and registers at a given +// instruction --- the consumer should find the entry that covers that +// instruction's address, start with the initial state supplied at the +// beginning of the entry, and work forward until it has processed all +// the changes up to and including those for the present instruction. +// +// There are seven kinds of rules that can appear in an entry of the +// table: +// +// - "undefined": The given register is not preserved by the callee; +// its value cannot be recovered. +// +// - "same value": This register has the same value it did in the callee. +// +// - offset(N): The register is saved at offset N from the CFA. +// +// - val_offset(N): The value the register had in the caller is the +// CFA plus offset N. (This is usually only useful for describing +// the stack pointer.) +// +// - register(R): The register's value was saved in another register R. +// +// - expression(E): Evaluating the DWARF expression E using the +// current frame's registers' values yields the address at which the +// register was saved. +// +// - val_expression(E): Evaluating the DWARF expression E using the +// current frame's registers' values yields the value the register +// had in the caller. + +class CallFrameInfo { + public: + // The different kinds of entries one finds in CFI. Used internally, + // and for error reporting. + enum EntryKind { kUnknown, kCIE, kFDE, kTerminator }; + + // The handler class to which the parser hands the parsed call frame + // information. Defined below. + class Handler; + + // A reporter class, which CallFrameInfo uses to report errors + // encountered while parsing call frame information. Defined below. + class Reporter; + + // Create a DWARF CFI parser. BUFFER points to the contents of the + // .debug_frame section to parse; BUFFER_LENGTH is its length in bytes. + // REPORTER is an error reporter the parser should use to report + // problems. READER is a ByteReader instance that has the endianness and + // address size set properly. Report the data we find to HANDLER. + // + // This class can also parse Linux C++ exception handling data, as found + // in '.eh_frame' sections. This data is a variant of DWARF CFI that is + // placed in loadable segments so that it is present in the program's + // address space, and is interpreted by the C++ runtime to search the + // call stack for a handler interested in the exception being thrown, + // actually pop the frames, and find cleanup code to run. + // + // There are two differences between the call frame information described + // in the DWARF standard and the exception handling data Linux places in + // the .eh_frame section: + // + // - Exception handling data uses uses a different format for call frame + // information entry headers. The distinguished CIE id, the way FDEs + // refer to their CIEs, and the way the end of the series of entries is + // determined are all slightly different. + // + // If the constructor's EH_FRAME argument is true, then the + // CallFrameInfo parses the entry headers as Linux C++ exception + // handling data. If EH_FRAME is false or omitted, the CallFrameInfo + // parses standard DWARF call frame information. + // + // - Linux C++ exception handling data uses CIE augmentation strings + // beginning with 'z' to specify the presence of additional data after + // the CIE and FDE headers and special encodings used for addresses in + // frame description entries. + // + // CallFrameInfo can handle 'z' augmentations in either DWARF CFI or + // exception handling data if you have supplied READER with the base + // addresses needed to interpret the pointer encodings that 'z' + // augmentations can specify. See the ByteReader interface for details + // about the base addresses. See the CallFrameInfo::Handler interface + // for details about the additional information one might find in + // 'z'-augmented data. + // + // Thus: + // + // - If you are parsing standard DWARF CFI, as found in a .debug_frame + // section, you should pass false for the EH_FRAME argument, or omit + // it, and you need not worry about providing READER with the + // additional base addresses. + // + // - If you want to parse Linux C++ exception handling data from a + // .eh_frame section, you should pass EH_FRAME as true, and call + // READER's Set*Base member functions before calling our Start method. + // + // - If you want to parse DWARF CFI that uses the 'z' augmentations + // (although I don't think any toolchain ever emits such data), you + // could pass false for EH_FRAME, but call READER's Set*Base members. + // + // The extensions the Linux C++ ABI makes to DWARF for exception + // handling are described here, rather poorly: + // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html + // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html + // + // The mechanics of C++ exception handling, personality routines, + // and language-specific data areas are described here, rather nicely: + // http://www.codesourcery.com/public/cxx-abi/abi-eh.html + CallFrameInfo(const uint8_t *buffer, size_t buffer_length, + ByteReader *reader, Handler *handler, Reporter *reporter, + bool eh_frame = false) + : buffer_(buffer), buffer_length_(buffer_length), + reader_(reader), handler_(handler), reporter_(reporter), + eh_frame_(eh_frame) { } + + ~CallFrameInfo() { } + + // Parse the entries in BUFFER, reporting what we find to HANDLER. + // Return true if we reach the end of the section successfully, or + // false if we encounter an error. + bool Start(); + + // Return the textual name of KIND. For error reporting. + static const char *KindName(EntryKind kind); + + private: + + struct CIE; + + // A CFI entry, either an FDE or a CIE. + struct Entry { + // The starting offset of the entry in the section, for error + // reporting. + size_t offset; + + // The start of this entry in the buffer. + const uint8_t *start; + + // Which kind of entry this is. + // + // We want to be able to use this for error reporting even while we're + // in the midst of parsing. Error reporting code may assume that kind, + // offset, and start fields are valid, although kind may be kUnknown. + EntryKind kind; + + // The end of this entry's common prologue (initial length and id), and + // the start of this entry's kind-specific fields. + const uint8_t *fields; + + // The start of this entry's instructions. + const uint8_t *instructions; + + // The address past the entry's last byte in the buffer. (Note that + // since offset points to the entry's initial length field, and the + // length field is the number of bytes after that field, this is not + // simply buffer_ + offset + length.) + const uint8_t *end; + + // For both DWARF CFI and .eh_frame sections, this is the CIE id in a + // CIE, and the offset of the associated CIE in an FDE. + uint64 id; + + // The CIE that applies to this entry, if we've parsed it. If this is a + // CIE, then this field points to this structure. + CIE *cie; + }; + + // A common information entry (CIE). + struct CIE: public Entry { + uint8 version; // CFI data version number + string augmentation; // vendor format extension markers + uint64 code_alignment_factor; // scale for code address adjustments + int data_alignment_factor; // scale for stack pointer adjustments + unsigned return_address_register; // which register holds the return addr + + // True if this CIE includes Linux C++ ABI 'z' augmentation data. + bool has_z_augmentation; + + // Parsed 'z' augmentation data. These are meaningful only if + // has_z_augmentation is true. + bool has_z_lsda; // The 'z' augmentation included 'L'. + bool has_z_personality; // The 'z' augmentation included 'P'. + bool has_z_signal_frame; // The 'z' augmentation included 'S'. + + // If has_z_lsda is true, this is the encoding to be used for language- + // specific data area pointers in FDEs. + DwarfPointerEncoding lsda_encoding; + + // If has_z_personality is true, this is the encoding used for the + // personality routine pointer in the augmentation data. + DwarfPointerEncoding personality_encoding; + + // If has_z_personality is true, this is the address of the personality + // routine --- or, if personality_encoding & DW_EH_PE_indirect, the + // address where the personality routine's address is stored. + uint64 personality_address; + + // This is the encoding used for addresses in the FDE header and + // in DW_CFA_set_loc instructions. This is always valid, whether + // or not we saw a 'z' augmentation string; its default value is + // DW_EH_PE_absptr, which is what normal DWARF CFI uses. + DwarfPointerEncoding pointer_encoding; + + // These were only introduced in DWARF4, so will not be set in older + // versions. + uint8 address_size; + uint8 segment_size; + }; + + // A frame description entry (FDE). + struct FDE: public Entry { + uint64 address; // start address of described code + uint64 size; // size of described code, in bytes + + // If cie->has_z_lsda is true, then this is the language-specific data + // area's address --- or its address's address, if cie->lsda_encoding + // has the DW_EH_PE_indirect bit set. + uint64 lsda_address; + }; + + // Internal use. + class Rule; + class UndefinedRule; + class SameValueRule; + class OffsetRule; + class ValOffsetRule; + class RegisterRule; + class ExpressionRule; + class ValExpressionRule; + class RuleMap; + class State; + + // Parse the initial length and id of a CFI entry, either a CIE, an FDE, + // or a .eh_frame end-of-data mark. CURSOR points to the beginning of the + // data to parse. On success, populate ENTRY as appropriate, and return + // true. On failure, report the problem, and return false. Even if we + // return false, set ENTRY->end to the first byte after the entry if we + // were able to figure that out, or NULL if we weren't. + bool ReadEntryPrologue(const uint8_t *cursor, Entry *entry); + + // Parse the fields of a CIE after the entry prologue, including any 'z' + // augmentation data. Assume that the 'Entry' fields of CIE are + // populated; use CIE->fields and CIE->end as the start and limit for + // parsing. On success, populate the rest of *CIE, and return true; on + // failure, report the problem and return false. + bool ReadCIEFields(CIE *cie); + + // Parse the fields of an FDE after the entry prologue, including any 'z' + // augmentation data. Assume that the 'Entry' fields of *FDE are + // initialized; use FDE->fields and FDE->end as the start and limit for + // parsing. Assume that FDE->cie is fully initialized. On success, + // populate the rest of *FDE, and return true; on failure, report the + // problem and return false. + bool ReadFDEFields(FDE *fde); + + // Report that ENTRY is incomplete, and return false. This is just a + // trivial wrapper for invoking reporter_->Incomplete; it provides a + // little brevity. + bool ReportIncomplete(Entry *entry); + + // Return true if ENCODING has the DW_EH_PE_indirect bit set. + static bool IsIndirectEncoding(DwarfPointerEncoding encoding) { + return encoding & DW_EH_PE_indirect; + } + + // The contents of the DWARF .debug_info section we're parsing. + const uint8_t *buffer_; + size_t buffer_length_; + + // For reading multi-byte values with the appropriate endianness. + ByteReader *reader_; + + // The handler to which we should report the data we find. + Handler *handler_; + + // For reporting problems in the info we're parsing. + Reporter *reporter_; + + // True if we are processing .eh_frame-format data. + bool eh_frame_; +}; + +// The handler class for CallFrameInfo. The a CFI parser calls the +// member functions of a handler object to report the data it finds. +class CallFrameInfo::Handler { + public: + // The pseudo-register number for the canonical frame address. + enum { kCFARegister = -1 }; + + Handler() { } + virtual ~Handler() { } + + // The parser has found CFI for the machine code at ADDRESS, + // extending for LENGTH bytes. OFFSET is the offset of the frame + // description entry in the section, for use in error messages. + // VERSION is the version number of the CFI format. AUGMENTATION is + // a string describing any producer-specific extensions present in + // the data. RETURN_ADDRESS is the number of the register that holds + // the address to which the function should return. + // + // Entry should return true to process this CFI, or false to skip to + // the next entry. + // + // The parser invokes Entry for each Frame Description Entry (FDE) + // it finds. The parser doesn't report Common Information Entries + // to the handler explicitly; instead, if the handler elects to + // process a given FDE, the parser reiterates the appropriate CIE's + // contents at the beginning of the FDE's rules. + virtual bool Entry(size_t offset, uint64 address, uint64 length, + uint8 version, const string &augmentation, + unsigned return_address) = 0; + + // When the Entry function returns true, the parser calls these + // handler functions repeatedly to describe the rules for recovering + // registers at each instruction in the given range of machine code. + // Immediately after a call to Entry, the handler should assume that + // the rule for each callee-saves register is "unchanged" --- that + // is, that the register still has the value it had in the caller. + // + // If a *Rule function returns true, we continue processing this entry's + // instructions. If a *Rule function returns false, we stop evaluating + // instructions, and skip to the next entry. Either way, we call End + // before going on to the next entry. + // + // In all of these functions, if the REG parameter is kCFARegister, then + // the rule describes how to find the canonical frame address. + // kCFARegister may be passed as a BASE_REGISTER argument, meaning that + // the canonical frame address should be used as the base address for the + // computation. All other REG values will be positive. + + // At ADDRESS, register REG's value is not recoverable. + virtual bool UndefinedRule(uint64 address, int reg) = 0; + + // At ADDRESS, register REG's value is the same as that it had in + // the caller. + virtual bool SameValueRule(uint64 address, int reg) = 0; + + // At ADDRESS, register REG has been saved at offset OFFSET from + // BASE_REGISTER. + virtual bool OffsetRule(uint64 address, int reg, + int base_register, long offset) = 0; + + // At ADDRESS, the caller's value of register REG is the current + // value of BASE_REGISTER plus OFFSET. (This rule doesn't provide an + // address at which the register's value is saved.) + virtual bool ValOffsetRule(uint64 address, int reg, + int base_register, long offset) = 0; + + // At ADDRESS, register REG has been saved in BASE_REGISTER. This differs + // from ValOffsetRule(ADDRESS, REG, BASE_REGISTER, 0), in that + // BASE_REGISTER is the "home" for REG's saved value: if you want to + // assign to a variable whose home is REG in the calling frame, you + // should put the value in BASE_REGISTER. + virtual bool RegisterRule(uint64 address, int reg, int base_register) = 0; + + // At ADDRESS, the DWARF expression EXPRESSION yields the address at + // which REG was saved. + virtual bool ExpressionRule(uint64 address, int reg, + const string &expression) = 0; + + // At ADDRESS, the DWARF expression EXPRESSION yields the caller's + // value for REG. (This rule doesn't provide an address at which the + // register's value is saved.) + virtual bool ValExpressionRule(uint64 address, int reg, + const string &expression) = 0; + + // Indicate that the rules for the address range reported by the + // last call to Entry are complete. End should return true if + // everything is okay, or false if an error has occurred and parsing + // should stop. + virtual bool End() = 0; + + // Handler functions for Linux C++ exception handling data. These are + // only called if the data includes 'z' augmentation strings. + + // The Linux C++ ABI uses an extension of the DWARF CFI format to + // walk the stack to propagate exceptions from the throw to the + // appropriate catch, and do the appropriate cleanups along the way. + // CFI entries used for exception handling have two additional data + // associated with them: + // + // - The "language-specific data area" describes which exception + // types the function has 'catch' clauses for, and indicates how + // to go about re-entering the function at the appropriate catch + // clause. If the exception is not caught, it describes the + // destructors that must run before the frame is popped. + // + // - The "personality routine" is responsible for interpreting the + // language-specific data area's contents, and deciding whether + // the exception should continue to propagate down the stack, + // perhaps after doing some cleanup for this frame, or whether the + // exception will be caught here. + // + // In principle, the language-specific data area is opaque to + // everybody but the personality routine. In practice, these values + // may be useful or interesting to readers with extra context, and + // we have to at least skip them anyway, so we might as well report + // them to the handler. + + // This entry's exception handling personality routine's address is + // ADDRESS. If INDIRECT is true, then ADDRESS is the address at + // which the routine's address is stored. The default definition for + // this handler function simply returns true, allowing parsing of + // the entry to continue. + virtual bool PersonalityRoutine(uint64 address, bool indirect) { + return true; + } + + // This entry's language-specific data area (LSDA) is located at + // ADDRESS. If INDIRECT is true, then ADDRESS is the address at + // which the area's address is stored. The default definition for + // this handler function simply returns true, allowing parsing of + // the entry to continue. + virtual bool LanguageSpecificDataArea(uint64 address, bool indirect) { + return true; + } + + // This entry describes a signal trampoline --- this frame is the + // caller of a signal handler. The default definition for this + // handler function simply returns true, allowing parsing of the + // entry to continue. + // + // The best description of the rationale for and meaning of signal + // trampoline CFI entries seems to be in the GCC bug database: + // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=26208 + virtual bool SignalHandler() { return true; } +}; + +// The CallFrameInfo class makes calls on an instance of this class to +// report errors or warn about problems in the data it is parsing. The +// default definitions of these methods print a message to stderr, but +// you can make a derived class that overrides them. +class CallFrameInfo::Reporter { + public: + // Create an error reporter which attributes troubles to the section + // named SECTION in FILENAME. + // + // Normally SECTION would be .debug_frame, but the Mac puts CFI data + // in a Mach-O section named __debug_frame. If we support + // Linux-style exception handling data, we could be reading an + // .eh_frame section. + Reporter(const string &filename, + const string §ion = ".debug_frame") + : filename_(filename), section_(section) { } + virtual ~Reporter() { } + + // The CFI entry at OFFSET ends too early to be well-formed. KIND + // indicates what kind of entry it is; KIND can be kUnknown if we + // haven't parsed enough of the entry to tell yet. + virtual void Incomplete(uint64 offset, CallFrameInfo::EntryKind kind); + + // The .eh_frame data has a four-byte zero at OFFSET where the next + // entry's length would be; this is a terminator. However, the buffer + // length as given to the CallFrameInfo constructor says there should be + // more data. + virtual void EarlyEHTerminator(uint64 offset); + + // The FDE at OFFSET refers to the CIE at CIE_OFFSET, but the + // section is not that large. + virtual void CIEPointerOutOfRange(uint64 offset, uint64 cie_offset); + + // The FDE at OFFSET refers to the CIE at CIE_OFFSET, but the entry + // there is not a CIE. + virtual void BadCIEId(uint64 offset, uint64 cie_offset); + + // The FDE at OFFSET refers to a CIE with an address size we don't know how + // to handle. + virtual void UnexpectedAddressSize(uint64 offset, uint8_t address_size); + + // The FDE at OFFSET refers to a CIE with an segment descriptor size we + // don't know how to handle. + virtual void UnexpectedSegmentSize(uint64 offset, uint8_t segment_size); + + // The FDE at OFFSET refers to a CIE with version number VERSION, + // which we don't recognize. We cannot parse DWARF CFI if it uses + // a version number we don't recognize. + virtual void UnrecognizedVersion(uint64 offset, int version); + + // The FDE at OFFSET refers to a CIE with augmentation AUGMENTATION, + // which we don't recognize. We cannot parse DWARF CFI if it uses + // augmentations we don't recognize. + virtual void UnrecognizedAugmentation(uint64 offset, + const string &augmentation); + + // The pointer encoding ENCODING, specified by the CIE at OFFSET, is not + // a valid encoding. + virtual void InvalidPointerEncoding(uint64 offset, uint8 encoding); + + // The pointer encoding ENCODING, specified by the CIE at OFFSET, depends + // on a base address which has not been supplied. + virtual void UnusablePointerEncoding(uint64 offset, uint8 encoding); + + // The CIE at OFFSET contains a DW_CFA_restore instruction at + // INSN_OFFSET, which may not appear in a CIE. + virtual void RestoreInCIE(uint64 offset, uint64 insn_offset); + + // The entry at OFFSET, of kind KIND, has an unrecognized + // instruction at INSN_OFFSET. + virtual void BadInstruction(uint64 offset, CallFrameInfo::EntryKind kind, + uint64 insn_offset); + + // The instruction at INSN_OFFSET in the entry at OFFSET, of kind + // KIND, establishes a rule that cites the CFA, but we have not + // established a CFA rule yet. + virtual void NoCFARule(uint64 offset, CallFrameInfo::EntryKind kind, + uint64 insn_offset); + + // The instruction at INSN_OFFSET in the entry at OFFSET, of kind + // KIND, is a DW_CFA_restore_state instruction, but the stack of + // saved states is empty. + virtual void EmptyStateStack(uint64 offset, CallFrameInfo::EntryKind kind, + uint64 insn_offset); + + // The DW_CFA_remember_state instruction at INSN_OFFSET in the entry + // at OFFSET, of kind KIND, would restore a state that has no CFA + // rule, whereas the current state does have a CFA rule. This is + // bogus input, which the CallFrameInfo::Handler interface doesn't + // (and shouldn't) have any way to report. + virtual void ClearingCFARule(uint64 offset, CallFrameInfo::EntryKind kind, + uint64 insn_offset); + + protected: + // The name of the file whose CFI we're reading. + string filename_; + + // The name of the CFI section in that file. + string section_; +}; + +} // namespace dwarf2reader + +#endif // UTIL_DEBUGINFO_DWARF2READER_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_cfi_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_cfi_unittest.cc new file mode 100644 index 0000000000..35d4f340d7 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_cfi_unittest.cc @@ -0,0 +1,2582 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// dwarf2reader_cfi_unittest.cc: Unit tests for dwarf2reader::CallFrameInfo + +#include <stdint.h> +#include <stdlib.h> + +#include <string> +#include <vector> + +// The '.eh_frame' format, used by the Linux C++ ABI for exception +// handling, is poorly specified. To help test our support for .eh_frame, +// if you #define WRITE_ELF while compiling this file, and add the +// 'include' directory from the binutils, gcc, or gdb source tree to the +// #include path, then each test that calls the +// PERHAPS_WRITE_DEBUG_FRAME_FILE or PERHAPS_WRITE_EH_FRAME_FILE will write +// an ELF file containing a .debug_frame or .eh_frame section; you can then +// use tools like readelf to examine the test data, and check the tools' +// interpretation against the test's intentions. Each ELF file is named +// "cfitest-TEST", where TEST identifies the particular test. +#ifdef WRITE_ELF +#include <errno.h> +#include <stdio.h> +#include <string.h> +extern "C" { +// To compile with WRITE_ELF, you should add the 'include' directory +// of the binutils, gcc, or gdb source tree to your #include path; +// that directory contains this header. +#include "elf/common.h" +} +#endif + +#include "breakpad_googletest_includes.h" +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/cfi_assembler.h" +#include "common/dwarf/dwarf2reader.h" +#include "common/using_std_string.h" +#include "google_breakpad/common/breakpad_types.h" + +using google_breakpad::CFISection; +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; +using google_breakpad::test_assembler::Section; + +using dwarf2reader::DwarfPointerEncoding; +using dwarf2reader::ENDIANNESS_BIG; +using dwarf2reader::ENDIANNESS_LITTLE; +using dwarf2reader::ByteReader; +using dwarf2reader::CallFrameInfo; + +using std::vector; +using testing::InSequence; +using testing::Return; +using testing::Sequence; +using testing::Test; +using testing::_; + +#ifdef WRITE_ELF +void WriteELFFrameSection(const char *filename, const char *section_name, + const CFISection §ion); +#define PERHAPS_WRITE_DEBUG_FRAME_FILE(name, section) \ + WriteELFFrameSection("cfitest-" name, ".debug_frame", section); +#define PERHAPS_WRITE_EH_FRAME_FILE(name, section) \ + WriteELFFrameSection("cfitest-" name, ".eh_frame", section); +#else +#define PERHAPS_WRITE_DEBUG_FRAME_FILE(name, section) +#define PERHAPS_WRITE_EH_FRAME_FILE(name, section) +#endif + +class MockCallFrameInfoHandler: public CallFrameInfo::Handler { + public: + MOCK_METHOD6(Entry, bool(size_t offset, uint64 address, uint64 length, + uint8 version, const string &augmentation, + unsigned return_address)); + MOCK_METHOD2(UndefinedRule, bool(uint64 address, int reg)); + MOCK_METHOD2(SameValueRule, bool(uint64 address, int reg)); + MOCK_METHOD4(OffsetRule, bool(uint64 address, int reg, int base_register, + long offset)); + MOCK_METHOD4(ValOffsetRule, bool(uint64 address, int reg, int base_register, + long offset)); + MOCK_METHOD3(RegisterRule, bool(uint64 address, int reg, int base_register)); + MOCK_METHOD3(ExpressionRule, bool(uint64 address, int reg, + const string &expression)); + MOCK_METHOD3(ValExpressionRule, bool(uint64 address, int reg, + const string &expression)); + MOCK_METHOD0(End, bool()); + MOCK_METHOD2(PersonalityRoutine, bool(uint64 address, bool indirect)); + MOCK_METHOD2(LanguageSpecificDataArea, bool(uint64 address, bool indirect)); + MOCK_METHOD0(SignalHandler, bool()); +}; + +class MockCallFrameErrorReporter: public CallFrameInfo::Reporter { + public: + MockCallFrameErrorReporter() : Reporter("mock filename", "mock section") { } + MOCK_METHOD2(Incomplete, void(uint64, CallFrameInfo::EntryKind)); + MOCK_METHOD1(EarlyEHTerminator, void(uint64)); + MOCK_METHOD2(CIEPointerOutOfRange, void(uint64, uint64)); + MOCK_METHOD2(BadCIEId, void(uint64, uint64)); + MOCK_METHOD2(UnexpectedAddressSize, void(uint64, uint8_t)); + MOCK_METHOD2(UnexpectedSegmentSize, void(uint64, uint8_t)); + MOCK_METHOD2(UnrecognizedVersion, void(uint64, int version)); + MOCK_METHOD2(UnrecognizedAugmentation, void(uint64, const string &)); + MOCK_METHOD2(InvalidPointerEncoding, void(uint64, uint8)); + MOCK_METHOD2(UnusablePointerEncoding, void(uint64, uint8)); + MOCK_METHOD2(RestoreInCIE, void(uint64, uint64)); + MOCK_METHOD3(BadInstruction, void(uint64, CallFrameInfo::EntryKind, uint64)); + MOCK_METHOD3(NoCFARule, void(uint64, CallFrameInfo::EntryKind, uint64)); + MOCK_METHOD3(EmptyStateStack, void(uint64, CallFrameInfo::EntryKind, uint64)); +}; + +struct CFIFixture { + + enum { kCFARegister = CallFrameInfo::Handler::kCFARegister }; + + CFIFixture() { + // Default expectations for the data handler. + // + // - Leave Entry and End without expectations, as it's probably a + // good idea to set those explicitly in each test. + // + // - Expect the *Rule functions to not be called, + // so that each test can simply list the calls they expect. + // + // I gather I could use StrictMock for this, but the manual seems + // to suggest using that only as a last resort, and this isn't so + // bad. + EXPECT_CALL(handler, UndefinedRule(_, _)).Times(0); + EXPECT_CALL(handler, SameValueRule(_, _)).Times(0); + EXPECT_CALL(handler, OffsetRule(_, _, _, _)).Times(0); + EXPECT_CALL(handler, ValOffsetRule(_, _, _, _)).Times(0); + EXPECT_CALL(handler, RegisterRule(_, _, _)).Times(0); + EXPECT_CALL(handler, ExpressionRule(_, _, _)).Times(0); + EXPECT_CALL(handler, ValExpressionRule(_, _, _)).Times(0); + EXPECT_CALL(handler, PersonalityRoutine(_, _)).Times(0); + EXPECT_CALL(handler, LanguageSpecificDataArea(_, _)).Times(0); + EXPECT_CALL(handler, SignalHandler()).Times(0); + + // Default expectations for the error/warning reporer. + EXPECT_CALL(reporter, Incomplete(_, _)).Times(0); + EXPECT_CALL(reporter, EarlyEHTerminator(_)).Times(0); + EXPECT_CALL(reporter, CIEPointerOutOfRange(_, _)).Times(0); + EXPECT_CALL(reporter, BadCIEId(_, _)).Times(0); + EXPECT_CALL(reporter, UnrecognizedVersion(_, _)).Times(0); + EXPECT_CALL(reporter, UnrecognizedAugmentation(_, _)).Times(0); + EXPECT_CALL(reporter, InvalidPointerEncoding(_, _)).Times(0); + EXPECT_CALL(reporter, UnusablePointerEncoding(_, _)).Times(0); + EXPECT_CALL(reporter, RestoreInCIE(_, _)).Times(0); + EXPECT_CALL(reporter, BadInstruction(_, _, _)).Times(0); + EXPECT_CALL(reporter, NoCFARule(_, _, _)).Times(0); + EXPECT_CALL(reporter, EmptyStateStack(_, _, _)).Times(0); + } + + MockCallFrameInfoHandler handler; + MockCallFrameErrorReporter reporter; +}; + +class CFI: public CFIFixture, public Test { }; + +TEST_F(CFI, EmptyRegion) { + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + static const uint8_t data[] = { 42 }; + + ByteReader byte_reader(ENDIANNESS_BIG); + CallFrameInfo parser(data, 0, &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +TEST_F(CFI, IncompleteLength32) { + CFISection section(kBigEndian, 8); + section + // Not even long enough for an initial length. + .D16(0xa0f) + // Padding to keep valgrind happy. We subtract these off when we + // construct the parser. + .D16(0); + + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + + EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown)) + .WillOnce(Return()); + + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(8); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size() - 2, + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +TEST_F(CFI, IncompleteLength64) { + CFISection section(kLittleEndian, 4); + section + // An incomplete 64-bit DWARF initial length. + .D32(0xffffffff).D32(0x71fbaec2) + // Padding to keep valgrind happy. We subtract these off when we + // construct the parser. + .D32(0); + + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + + EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown)) + .WillOnce(Return()); + + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + + ByteReader byte_reader(ENDIANNESS_LITTLE); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size() - 4, + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +TEST_F(CFI, IncompleteId32) { + CFISection section(kBigEndian, 8); + section + .D32(3) // Initial length, not long enough for id + .D8(0xd7).D8(0xe5).D8(0xf1) // incomplete id + .CIEHeader(8727, 3983, 8889, 3, "") + .FinishEntry(); + + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + + EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown)) + .WillOnce(Return()); + + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(8); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +TEST_F(CFI, BadId32) { + CFISection section(kBigEndian, 8); + section + .D32(0x100) // Initial length + .D32(0xe802fade) // bogus ID + .Append(0x100 - 4, 0x42); // make the length true + section + .CIEHeader(1672, 9872, 8529, 3, "") + .FinishEntry(); + + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + + EXPECT_CALL(reporter, CIEPointerOutOfRange(_, 0xe802fade)) + .WillOnce(Return()); + + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(8); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +// A lone CIE shouldn't cause any handler calls. +TEST_F(CFI, SingleCIE) { + CFISection section(kLittleEndian, 4); + section.CIEHeader(0xffe799a8, 0x3398dcdd, 0x6e9683de, 3, ""); + section.Append(10, dwarf2reader::DW_CFA_nop); + section.FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("SingleCIE", section); + + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_LITTLE); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +// One FDE, one CIE. +TEST_F(CFI, OneFDE) { + CFISection section(kBigEndian, 4); + Label cie; + section + .Mark(&cie) + .CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 3, "") + .FinishEntry() + .FDEHeader(cie, 0x7714740d, 0x3d5a10cd) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("OneFDE", section); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, 0x7714740d, 0x3d5a10cd, 3, "", 0x6b6efb87)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +// Two FDEs share a CIE. +TEST_F(CFI, TwoFDEsOneCIE) { + CFISection section(kBigEndian, 4); + Label cie; + section + // First FDE. readelf complains about this one because it makes + // a forward reference to its CIE. + .FDEHeader(cie, 0xa42744df, 0xa3b42121) + .FinishEntry() + // CIE. + .Mark(&cie) + .CIEHeader(0x04f7dc7b, 0x3d00c05f, 0xbd43cb59, 3, "") + .FinishEntry() + // Second FDE. + .FDEHeader(cie, 0x6057d391, 0x700f608d) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("TwoFDEsOneCIE", section); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, 0xa42744df, 0xa3b42121, 3, "", 0xbd43cb59)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, 0x6057d391, 0x700f608d, 3, "", 0xbd43cb59)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +// Two FDEs, two CIEs. +TEST_F(CFI, TwoFDEsTwoCIEs) { + CFISection section(kLittleEndian, 8); + Label cie1, cie2; + section + // First CIE. + .Mark(&cie1) + .CIEHeader(0x694d5d45, 0x4233221b, 0xbf45e65a, 3, "") + .FinishEntry() + // First FDE which cites second CIE. readelf complains about + // this one because it makes a forward reference to its CIE. + .FDEHeader(cie2, 0x778b27dfe5871f05ULL, 0x324ace3448070926ULL) + .FinishEntry() + // Second FDE, which cites first CIE. + .FDEHeader(cie1, 0xf6054ca18b10bf5fULL, 0x45fdb970d8bca342ULL) + .FinishEntry() + // Second CIE. + .Mark(&cie2) + .CIEHeader(0xfba3fad7, 0x6287e1fd, 0x61d2c581, 2, "") + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("TwoFDEsTwoCIEs", section); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, 0x778b27dfe5871f05ULL, 0x324ace3448070926ULL, 2, + "", 0x61d2c581)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, 0xf6054ca18b10bf5fULL, 0x45fdb970d8bca342ULL, 3, + "", 0xbf45e65a)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_LITTLE); + byte_reader.SetAddressSize(8); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +// An FDE whose CIE specifies a version we don't recognize. +TEST_F(CFI, BadVersion) { + CFISection section(kBigEndian, 4); + Label cie1, cie2; + section + .Mark(&cie1) + .CIEHeader(0xca878cf0, 0x7698ec04, 0x7b616f54, 0x52, "") + .FinishEntry() + // We should skip this entry, as its CIE specifies a version we + // don't recognize. + .FDEHeader(cie1, 0x08852292, 0x2204004a) + .FinishEntry() + // Despite the above, we should visit this entry. + .Mark(&cie2) + .CIEHeader(0x7c3ae7c9, 0xb9b9a512, 0x96cb3264, 3, "") + .FinishEntry() + .FDEHeader(cie2, 0x2094735a, 0x6e875501) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("BadVersion", section); + + EXPECT_CALL(reporter, UnrecognizedVersion(_, 0x52)) + .WillOnce(Return()); + + { + InSequence s; + // We should see no mention of the first FDE, but we should get + // a call to Entry for the second. + EXPECT_CALL(handler, Entry(_, 0x2094735a, 0x6e875501, 3, "", + 0x96cb3264)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +// An FDE whose CIE specifies an augmentation we don't recognize. +TEST_F(CFI, BadAugmentation) { + CFISection section(kBigEndian, 4); + Label cie1, cie2; + section + .Mark(&cie1) + .CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 3, "spaniels!") + .FinishEntry() + // We should skip this entry, as its CIE specifies an + // augmentation we don't recognize. + .FDEHeader(cie1, 0x7714740d, 0x3d5a10cd) + .FinishEntry() + // Despite the above, we should visit this entry. + .Mark(&cie2) + .CIEHeader(0xf8bc4399, 0x8cf09931, 0xf2f519b2, 3, "") + .FinishEntry() + .FDEHeader(cie2, 0x7bf0fda0, 0xcbcd28d8) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("BadAugmentation", section); + + EXPECT_CALL(reporter, UnrecognizedAugmentation(_, "spaniels!")) + .WillOnce(Return()); + + { + InSequence s; + // We should see no mention of the first FDE, but we should get + // a call to Entry for the second. + EXPECT_CALL(handler, Entry(_, 0x7bf0fda0, 0xcbcd28d8, 3, "", + 0xf2f519b2)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +// The return address column field is a byte in CFI version 1 +// (DWARF2), but a ULEB128 value in version 3 (DWARF3). +TEST_F(CFI, CIEVersion1ReturnColumn) { + CFISection section(kBigEndian, 4); + Label cie; + section + // CIE, using the version 1 format: return column is a ubyte. + .Mark(&cie) + // Use a value for the return column that is parsed differently + // as a ubyte and as a ULEB128. + .CIEHeader(0xbcdea24f, 0x5be28286, 0x9f, 1, "") + .FinishEntry() + // FDE, citing that CIE. + .FDEHeader(cie, 0xb8d347b5, 0x825e55dc) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion1ReturnColumn", section); + + { + InSequence s; + EXPECT_CALL(handler, Entry(_, 0xb8d347b5, 0x825e55dc, 1, "", 0x9f)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +// The return address column field is a byte in CFI version 1 +// (DWARF2), but a ULEB128 value in version 3 (DWARF3). +TEST_F(CFI, CIEVersion3ReturnColumn) { + CFISection section(kBigEndian, 4); + Label cie; + section + // CIE, using the version 3 format: return column is a ULEB128. + .Mark(&cie) + // Use a value for the return column that is parsed differently + // as a ubyte and as a ULEB128. + .CIEHeader(0x0ab4758d, 0xc010fdf7, 0x89, 3, "") + .FinishEntry() + // FDE, citing that CIE. + .FDEHeader(cie, 0x86763f2b, 0x2a66dc23) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion3ReturnColumn", section); + + { + InSequence s; + EXPECT_CALL(handler, Entry(_, 0x86763f2b, 0x2a66dc23, 3, "", 0x89)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +TEST_F(CFI, CIEVersion4AdditionalFields) { + CFISection section(kBigEndian, 8); + Label cie; + section + .Mark(&cie) + // CIE version 4 with expected address (64bit) and segment size. + .CIEHeader(0x0ab4758d, 0xc010fdf7, 0x89, 4, "", true, 8, 0) + .FinishEntry() + // FDE, citing that CIE. + .FDEHeader(cie, 0x86763f2b, 0x2a66dc23) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion3ReturnColumn", section); + + { + InSequence s; + EXPECT_CALL(handler, Entry(_, 0x86763f2b, 0x2a66dc23, 4, "", 0x89)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +TEST_F(CFI, CIEVersion4AdditionalFields32BitAddress) { + CFISection section(kBigEndian, 4); + Label cie; + section + .Mark(&cie) + // CIE version 4 with expected address (32bit) and segment size. + .CIEHeader(0x0ab4758d, 0xc010fdf7, 0x89, 4, "", true, 4, 0) + .FinishEntry() + // FDE, citing that CIE. + .FDEHeader(cie, 0x86763f2b, 0x2a66dc23) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion3ReturnColumn", section); + + { + InSequence s; + EXPECT_CALL(handler, Entry(_, 0x86763f2b, 0x2a66dc23, 4, "", 0x89)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +TEST_F(CFI, CIEVersion4AdditionalFieldsUnexpectedAddressSize) { + CFISection section(kBigEndian, 4); + Label cie; + + section + .Mark(&cie) + // Unexpected address size. + .CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 4, "", true, 3, 0) + .FinishEntry() + // FDE, citing that CIE. + .FDEHeader(cie, 0x86763f2b, 0x2a66dc23) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("AdditionalFieldsUnexpectedAddress", section); + + EXPECT_CALL(reporter, UnexpectedAddressSize(_, 3)) + .WillOnce(Return()); + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +TEST_F(CFI, CIEVersion4AdditionalFieldsUnexpectedSegmentSize) { + CFISection section(kBigEndian, 8); + Label cie; + + section + .Mark(&cie) + .CIEHeader(0xf8bc4399, 0x8cf09931, 0xf2f519b2, 4, "", true, 8, 7) + .FinishEntry() + .FDEHeader(cie, 0x7bf0fda0, 0xcbcd28d8) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("AdditionalFieldsUnexpectedSegment", section); + + EXPECT_CALL(reporter, UnexpectedSegmentSize(_, 7)) + .WillOnce(Return()); + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +struct CFIInsnFixture: public CFIFixture { + CFIInsnFixture() : CFIFixture() { + data_factor = 0xb6f; + return_register = 0x9be1ed9f; + version = 3; + cfa_base_register = 0x383a3aa; + cfa_offset = 0xf748; + } + + // Prepare SECTION to receive FDE instructions. + // + // - Append a stock CIE header that establishes the fixture's + // code_factor, data_factor, return_register, version, and + // augmentation values. + // - Have the CIE set up a CFA rule using cfa_base_register and + // cfa_offset. + // - Append a stock FDE header, referring to the above CIE, for the + // fde_size bytes at fde_start. Choose fde_start and fde_size + // appropriately for the section's address size. + // - Set appropriate expectations on handler in sequence s for the + // frame description entry and the CIE's CFA rule. + // + // On return, SECTION is ready to have FDE instructions appended to + // it, and its FinishEntry member called. + void StockCIEAndFDE(CFISection *section) { + // Choose appropriate constants for our address size. + if (section->AddressSize() == 4) { + fde_start = 0xc628ecfbU; + fde_size = 0x5dee04a2; + code_factor = 0x60b; + } else { + assert(section->AddressSize() == 8); + fde_start = 0x0005c57ce7806bd3ULL; + fde_size = 0x2699521b5e333100ULL; + code_factor = 0x01008e32855274a8ULL; + } + + // Create the CIE. + (*section) + .Mark(&cie_label) + .CIEHeader(code_factor, data_factor, return_register, version, + "") + .D8(dwarf2reader::DW_CFA_def_cfa) + .ULEB128(cfa_base_register) + .ULEB128(cfa_offset) + .FinishEntry(); + + // Create the FDE. + section->FDEHeader(cie_label, fde_start, fde_size); + + // Expect an Entry call for the FDE and a ValOffsetRule call for the + // CIE's CFA rule. + EXPECT_CALL(handler, Entry(_, fde_start, fde_size, version, "", + return_register)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(fde_start, kCFARegister, + cfa_base_register, cfa_offset)) + .InSequence(s) + .WillOnce(Return(true)); + } + + // Run the contents of SECTION through a CallFrameInfo parser, + // expecting parser.Start to return SUCCEEDS + void ParseSection(CFISection *section, bool succeeds = true) { + string contents; + EXPECT_TRUE(section->GetContents(&contents)); + dwarf2reader::Endianness endianness; + if (section->endianness() == kBigEndian) + endianness = ENDIANNESS_BIG; + else { + assert(section->endianness() == kLittleEndian); + endianness = ENDIANNESS_LITTLE; + } + ByteReader byte_reader(endianness); + byte_reader.SetAddressSize(section->AddressSize()); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size(), + &byte_reader, &handler, &reporter); + if (succeeds) + EXPECT_TRUE(parser.Start()); + else + EXPECT_FALSE(parser.Start()); + } + + Label cie_label; + Sequence s; + uint64 code_factor; + int data_factor; + unsigned return_register; + unsigned version; + unsigned cfa_base_register; + int cfa_offset; + uint64 fde_start, fde_size; +}; + +class CFIInsn: public CFIInsnFixture, public Test { }; + +TEST_F(CFIInsn, DW_CFA_set_loc) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_set_loc).D32(0xb1ee3e7a) + // Use DW_CFA_def_cfa to force a handler call that we can use to + // check the effect of the DW_CFA_set_loc. + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x4defb431).ULEB128(0x6d17b0ee) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_set_loc", section); + + EXPECT_CALL(handler, + ValOffsetRule(0xb1ee3e7a, kCFARegister, 0x4defb431, 0x6d17b0ee)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_advance_loc) { + CFISection section(kBigEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_advance_loc | 0x2a) + // Use DW_CFA_def_cfa to force a handler call that we can use to + // check the effect of the DW_CFA_advance_loc. + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x5bbb3715).ULEB128(0x0186c7bf) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc", section); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start + 0x2a * code_factor, + kCFARegister, 0x5bbb3715, 0x0186c7bf)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_advance_loc1) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_advance_loc1).D8(0xd8) + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x69d5696a).ULEB128(0x1eb7fc93) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc1", section); + + EXPECT_CALL(handler, + ValOffsetRule((fde_start + 0xd8 * code_factor), + kCFARegister, 0x69d5696a, 0x1eb7fc93)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_advance_loc2) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_advance_loc2).D16(0x3adb) + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x3a368bed).ULEB128(0x3194ee37) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc2", section); + + EXPECT_CALL(handler, + ValOffsetRule((fde_start + 0x3adb * code_factor), + kCFARegister, 0x3a368bed, 0x3194ee37)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_advance_loc4) { + CFISection section(kBigEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_advance_loc4).D32(0x15813c88) + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x135270c5).ULEB128(0x24bad7cb) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc4", section); + + EXPECT_CALL(handler, + ValOffsetRule((fde_start + 0x15813c88ULL * code_factor), + kCFARegister, 0x135270c5, 0x24bad7cb)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_MIPS_advance_loc8) { + code_factor = 0x2d; + CFISection section(kBigEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_MIPS_advance_loc8).D64(0x3c4f3945b92c14ULL) + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0xe17ed602).ULEB128(0x3d162e7f) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc8", section); + + EXPECT_CALL(handler, + ValOffsetRule((fde_start + 0x3c4f3945b92c14ULL * code_factor), + kCFARegister, 0xe17ed602, 0x3d162e7f)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x4e363a85).ULEB128(0x815f9aa7) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_def_cfa", section); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x4e363a85, 0x815f9aa7)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa_sf) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_sf).ULEB128(0x8ccb32b7).LEB128(0x9ea) + .D8(dwarf2reader::DW_CFA_def_cfa_sf).ULEB128(0x9b40f5da).LEB128(-0x40a2) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x8ccb32b7, + 0x9ea * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x9b40f5da, + -0x40a2 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa_register) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_register).ULEB128(0x3e7e9363) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x3e7e9363, cfa_offset)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +// DW_CFA_def_cfa_register should have no effect when applied to a +// non-base/offset rule. +TEST_F(CFIInsn, DW_CFA_def_cfa_registerBadRule) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("needle in a haystack") + .D8(dwarf2reader::DW_CFA_def_cfa_register).ULEB128(0xf1b49e49) + .FinishEntry(); + + EXPECT_CALL(handler, + ValExpressionRule(fde_start, kCFARegister, + "needle in a haystack")) + .WillRepeatedly(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa_offset) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x1e8e3b9b) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, cfa_base_register, + 0x1e8e3b9b)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa_offset_sf) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_offset_sf).LEB128(0x970) + .D8(dwarf2reader::DW_CFA_def_cfa_offset_sf).LEB128(-0x2cd) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, cfa_base_register, + 0x970 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, cfa_base_register, + -0x2cd * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +// DW_CFA_def_cfa_offset should have no effect when applied to a +// non-base/offset rule. +TEST_F(CFIInsn, DW_CFA_def_cfa_offsetBadRule) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("six ways to Sunday") + .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x1e8e3b9b) + .FinishEntry(); + + EXPECT_CALL(handler, + ValExpressionRule(fde_start, kCFARegister, "six ways to Sunday")) + .WillRepeatedly(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa_expression) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("eating crow") + .FinishEntry(); + + EXPECT_CALL(handler, ValExpressionRule(fde_start, kCFARegister, + "eating crow")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_undefined) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x300ce45d) + .FinishEntry(); + + EXPECT_CALL(handler, UndefinedRule(fde_start, 0x300ce45d)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_same_value) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x3865a760) + .FinishEntry(); + + EXPECT_CALL(handler, SameValueRule(fde_start, 0x3865a760)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_offset) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset | 0x2c).ULEB128(0x9f6) + .FinishEntry(); + + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x2c, kCFARegister, 0x9f6 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_offset_extended) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset_extended).ULEB128(0x402b).ULEB128(0xb48) + .FinishEntry(); + + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x402b, kCFARegister, 0xb48 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_offset_extended_sf) { + CFISection section(kBigEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset_extended_sf) + .ULEB128(0x997c23ee).LEB128(0x2d00) + .D8(dwarf2reader::DW_CFA_offset_extended_sf) + .ULEB128(0x9519eb82).LEB128(-0xa77) + .FinishEntry(); + + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x997c23ee, + kCFARegister, 0x2d00 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x9519eb82, + kCFARegister, -0xa77 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_val_offset) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x623562fe).ULEB128(0x673) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, 0x623562fe, + kCFARegister, 0x673 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_val_offset_sf) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_offset_sf).ULEB128(0x6f4f).LEB128(0xaab) + .D8(dwarf2reader::DW_CFA_val_offset_sf).ULEB128(0x2483).LEB128(-0x8a2) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, 0x6f4f, + kCFARegister, 0xaab * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, + ValOffsetRule(fde_start, 0x2483, + kCFARegister, -0x8a2 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_register) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_register).ULEB128(0x278d18f9).ULEB128(0x1a684414) + .FinishEntry(); + + EXPECT_CALL(handler, RegisterRule(fde_start, 0x278d18f9, 0x1a684414)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_expression) { + CFISection section(kBigEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0xa1619fb2) + .Block("plus ça change, plus c'est la même chose") + .FinishEntry(); + + EXPECT_CALL(handler, + ExpressionRule(fde_start, 0xa1619fb2, + "plus ça change, plus c'est la même chose")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_val_expression) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0xc5e4a9e3) + .Block("he who has the gold makes the rules") + .FinishEntry(); + + EXPECT_CALL(handler, + ValExpressionRule(fde_start, 0xc5e4a9e3, + "he who has the gold makes the rules")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_restore) { + CFISection section(kLittleEndian, 8); + code_factor = 0x01bd188a9b1fa083ULL; + data_factor = -0x1ac8; + return_register = 0x8c35b049; + version = 2; + fde_start = 0x2d70fe998298bbb1ULL; + fde_size = 0x46ccc2e63cf0b108ULL; + Label cie; + section + .Mark(&cie) + .CIEHeader(code_factor, data_factor, return_register, version, + "") + // Provide a CFA rule, because register rules require them. + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x6ca1d50e).ULEB128(0x372e38e8) + // Provide an offset(N) rule for register 0x3c. + .D8(dwarf2reader::DW_CFA_offset | 0x3c).ULEB128(0xb348) + .FinishEntry() + // In the FDE... + .FDEHeader(cie, fde_start, fde_size) + // At a second address, provide a new offset(N) rule for register 0x3c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0x13) + .D8(dwarf2reader::DW_CFA_offset | 0x3c).ULEB128(0x9a50) + // At a third address, restore the original rule for register 0x3c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0x01) + .D8(dwarf2reader::DW_CFA_restore | 0x3c) + .FinishEntry(); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, fde_start, fde_size, version, "", return_register)) + .WillOnce(Return(true)); + // CIE's CFA rule. + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x6ca1d50e, 0x372e38e8)) + .WillOnce(Return(true)); + // CIE's rule for register 0x3c. + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x3c, kCFARegister, 0xb348 * data_factor)) + .WillOnce(Return(true)); + // FDE's rule for register 0x3c. + EXPECT_CALL(handler, + OffsetRule(fde_start + 0x13 * code_factor, 0x3c, + kCFARegister, 0x9a50 * data_factor)) + .WillOnce(Return(true)); + // Restore CIE's rule for register 0x3c. + EXPECT_CALL(handler, + OffsetRule(fde_start + (0x13 + 0x01) * code_factor, 0x3c, + kCFARegister, 0xb348 * data_factor)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_restoreNoRule) { + CFISection section(kBigEndian, 4); + code_factor = 0x005f78143c1c3b82ULL; + data_factor = 0x25d0; + return_register = 0xe8; + version = 1; + fde_start = 0x4062e30f; + fde_size = 0x5302a389; + Label cie; + section + .Mark(&cie) + .CIEHeader(code_factor, data_factor, return_register, version, "") + // Provide a CFA rule, because register rules require them. + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x470aa334).ULEB128(0x099ef127) + .FinishEntry() + // In the FDE... + .FDEHeader(cie, fde_start, fde_size) + // At a second address, provide an offset(N) rule for register 0x2c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0x7) + .D8(dwarf2reader::DW_CFA_offset | 0x2c).ULEB128(0x1f47) + // At a third address, restore the (missing) CIE rule for register 0x2c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0xb) + .D8(dwarf2reader::DW_CFA_restore | 0x2c) + .FinishEntry(); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, fde_start, fde_size, version, "", return_register)) + .WillOnce(Return(true)); + // CIE's CFA rule. + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x470aa334, 0x099ef127)) + .WillOnce(Return(true)); + // FDE's rule for register 0x2c. + EXPECT_CALL(handler, + OffsetRule(fde_start + 0x7 * code_factor, 0x2c, + kCFARegister, 0x1f47 * data_factor)) + .WillOnce(Return(true)); + // Restore CIE's (missing) rule for register 0x2c. + EXPECT_CALL(handler, + SameValueRule(fde_start + (0x7 + 0xb) * code_factor, 0x2c)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_restore_extended) { + CFISection section(kBigEndian, 4); + code_factor = 0x126e; + data_factor = -0xd8b; + return_register = 0x77711787; + version = 3; + fde_start = 0x01f55a45; + fde_size = 0x452adb80; + Label cie; + section + .Mark(&cie) + .CIEHeader(code_factor, data_factor, return_register, version, + "", true /* dwarf64 */ ) + // Provide a CFA rule, because register rules require them. + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x56fa0edd).ULEB128(0x097f78a5) + // Provide an offset(N) rule for register 0x0f9b8a1c. + .D8(dwarf2reader::DW_CFA_offset_extended) + .ULEB128(0x0f9b8a1c).ULEB128(0xc979) + .FinishEntry() + // In the FDE... + .FDEHeader(cie, fde_start, fde_size) + // At a second address, provide a new offset(N) rule for reg 0x0f9b8a1c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0x3) + .D8(dwarf2reader::DW_CFA_offset_extended) + .ULEB128(0x0f9b8a1c).ULEB128(0x3b7b) + // At a third address, restore the original rule for register 0x0f9b8a1c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0x04) + .D8(dwarf2reader::DW_CFA_restore_extended).ULEB128(0x0f9b8a1c) + .FinishEntry(); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, fde_start, fde_size, version, "", return_register)) + .WillOnce(Return(true)); + // CIE's CFA rule. + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x56fa0edd, 0x097f78a5)) + .WillOnce(Return(true)); + // CIE's rule for register 0x0f9b8a1c. + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x0f9b8a1c, kCFARegister, + 0xc979 * data_factor)) + .WillOnce(Return(true)); + // FDE's rule for register 0x0f9b8a1c. + EXPECT_CALL(handler, + OffsetRule(fde_start + 0x3 * code_factor, 0x0f9b8a1c, + kCFARegister, 0x3b7b * data_factor)) + .WillOnce(Return(true)); + // Restore CIE's rule for register 0x0f9b8a1c. + EXPECT_CALL(handler, + OffsetRule(fde_start + (0x3 + 0x4) * code_factor, 0x0f9b8a1c, + kCFARegister, 0xc979 * data_factor)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_remember_and_restore_state) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + + // We create a state, save it, modify it, and then restore. We + // refer to the state that is overridden the restore as the + // "outgoing" state, and the restored state the "incoming" state. + // + // Register outgoing incoming expect + // 1 offset(N) no rule new "same value" rule + // 2 register(R) offset(N) report changed rule + // 3 offset(N) offset(M) report changed offset + // 4 offset(N) offset(N) no report + // 5 offset(N) no rule new "same value" rule + section + // Create the "incoming" state, which we will save and later restore. + .D8(dwarf2reader::DW_CFA_offset | 2).ULEB128(0x9806) + .D8(dwarf2reader::DW_CFA_offset | 3).ULEB128(0x995d) + .D8(dwarf2reader::DW_CFA_offset | 4).ULEB128(0x7055) + .D8(dwarf2reader::DW_CFA_remember_state) + // Advance to a new instruction; an implementation could legitimately + // ignore all but the final rule for a given register at a given address. + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + // Create the "outgoing" state, which we will discard. + .D8(dwarf2reader::DW_CFA_offset | 1).ULEB128(0xea1a) + .D8(dwarf2reader::DW_CFA_register).ULEB128(2).ULEB128(0x1d2a3767) + .D8(dwarf2reader::DW_CFA_offset | 3).ULEB128(0xdd29) + .D8(dwarf2reader::DW_CFA_offset | 5).ULEB128(0xf1ce) + // At a third address, restore the incoming state. + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + uint64 addr = fde_start; + + // Expect the incoming rules to be reported. + EXPECT_CALL(handler, OffsetRule(addr, 2, kCFARegister, 0x9806 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0x995d * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 4, kCFARegister, 0x7055 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + + addr += code_factor; + + // After the save, we establish the outgoing rule set. + EXPECT_CALL(handler, OffsetRule(addr, 1, kCFARegister, 0xea1a * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(addr, 2, 0x1d2a3767)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0xdd29 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 5, kCFARegister, 0xf1ce * data_factor)) + .InSequence(s).WillOnce(Return(true)); + + addr += code_factor; + + // Finally, after the restore, expect to see the differences from + // the outgoing to the incoming rules reported. + EXPECT_CALL(handler, SameValueRule(addr, 1)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 2, kCFARegister, 0x9806 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0x995d * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, SameValueRule(addr, 5)) + .InSequence(s).WillOnce(Return(true)); + + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +// Check that restoring a rule set reports changes to the CFA rule. +TEST_F(CFIInsn, DW_CFA_remember_and_restore_stateCFA) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + + section + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x90481102) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor, kCFARegister, + cfa_base_register, 0x90481102)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor * 2, kCFARegister, + cfa_base_register, cfa_offset)) + .InSequence(s).WillOnce(Return(true)); + + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_nop) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_nop) + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x3fb8d4f1).ULEB128(0x078dc67b) + .D8(dwarf2reader::DW_CFA_nop) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x3fb8d4f1, 0x078dc67b)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_GNU_window_save) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_GNU_window_save) + .FinishEntry(); + + // Don't include all the rules in any particular sequence. + + // The caller's %o0-%o7 have become the callee's %i0-%i7. This is + // the GCC register numbering. + for (int i = 8; i < 16; i++) + EXPECT_CALL(handler, RegisterRule(fde_start, i, i + 16)) + .WillOnce(Return(true)); + // The caller's %l0-%l7 and %i0-%i7 have been saved at the top of + // its frame. + for (int i = 16; i < 32; i++) + EXPECT_CALL(handler, OffsetRule(fde_start, i, kCFARegister, (i-16) * 4)) + .WillOnce(Return(true)); + + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_GNU_args_size) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_GNU_args_size).ULEB128(0xeddfa520) + // Verify that we see this, meaning we parsed the above properly. + .D8(dwarf2reader::DW_CFA_offset | 0x23).ULEB128(0x269) + .FinishEntry(); + + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x23, kCFARegister, 0x269 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_GNU_negative_offset_extended) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_GNU_negative_offset_extended) + .ULEB128(0x430cc87a).ULEB128(0x613) + .FinishEntry(); + + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x430cc87a, + kCFARegister, -0x613 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +// Three FDEs: skip the second +TEST_F(CFIInsn, SkipFDE) { + CFISection section(kBigEndian, 4); + Label cie; + section + // CIE, used by all FDEs. + .Mark(&cie) + .CIEHeader(0x010269f2, 0x9177, 0xedca5849, 2, "") + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x42ed390b).ULEB128(0x98f43aad) + .FinishEntry() + // First FDE. + .FDEHeader(cie, 0xa870ebdd, 0x60f6aa4) + .D8(dwarf2reader::DW_CFA_register).ULEB128(0x3a860351).ULEB128(0x6c9a6bcf) + .FinishEntry() + // Second FDE. + .FDEHeader(cie, 0xc534f7c0, 0xf6552e9, true /* dwarf64 */) + .D8(dwarf2reader::DW_CFA_register).ULEB128(0x1b62c234).ULEB128(0x26586b18) + .FinishEntry() + // Third FDE. + .FDEHeader(cie, 0xf681cfc8, 0x7e4594e) + .D8(dwarf2reader::DW_CFA_register).ULEB128(0x26c53934).ULEB128(0x18eeb8a4) + .FinishEntry(); + + { + InSequence s; + + // Process the first FDE. + EXPECT_CALL(handler, Entry(_, 0xa870ebdd, 0x60f6aa4, 2, "", 0xedca5849)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(0xa870ebdd, kCFARegister, + 0x42ed390b, 0x98f43aad)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(0xa870ebdd, 0x3a860351, 0x6c9a6bcf)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .WillOnce(Return(true)); + + // Skip the second FDE. + EXPECT_CALL(handler, Entry(_, 0xc534f7c0, 0xf6552e9, 2, "", 0xedca5849)) + .WillOnce(Return(false)); + + // Process the third FDE. + EXPECT_CALL(handler, Entry(_, 0xf681cfc8, 0x7e4594e, 2, "", 0xedca5849)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(0xf681cfc8, kCFARegister, + 0x42ed390b, 0x98f43aad)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(0xf681cfc8, 0x26c53934, 0x18eeb8a4)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .WillOnce(Return(true)); + } + + ParseSection(§ion); +} + +// Quit processing in the middle of an entry's instructions. +TEST_F(CFIInsn, QuitMidentry) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_register).ULEB128(0xe0cf850d).ULEB128(0x15aab431) + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x46750aa5).Block("meat") + .FinishEntry(); + + EXPECT_CALL(handler, RegisterRule(fde_start, 0xe0cf850d, 0x15aab431)) + .InSequence(s).WillOnce(Return(false)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion, false); +} + +class CFIRestore: public CFIInsnFixture, public Test { }; + +TEST_F(CFIRestore, RestoreUndefinedRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x0bac878e) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, UndefinedRule(fde_start, 0x0bac878e)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreUndefinedRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x7dedff5f) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x7dedff5f) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, UndefinedRule(fde_start, 0x7dedff5f)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, SameValueRule(fde_start + code_factor, 0x7dedff5f)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + 2 * code_factor, 0x7dedff5f)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreSameValueRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0xadbc9b3a) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, SameValueRule(fde_start, 0xadbc9b3a)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreSameValueRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x3d90dcb5) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x3d90dcb5) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, SameValueRule(fde_start, 0x3d90dcb5)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0x3d90dcb5)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, SameValueRule(fde_start + 2 * code_factor, 0x3d90dcb5)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreOffsetRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset | 0x14).ULEB128(0xb6f) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, OffsetRule(fde_start, 0x14, + kCFARegister, 0xb6f * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreOffsetRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0xeb7) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x21) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, OffsetRule(fde_start, 0x21, + kCFARegister, 0xeb7 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0x21)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(fde_start + 2 * code_factor, 0x21, + kCFARegister, 0xeb7 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreOffsetRuleChangedOffset) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0x134) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0xf4f) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, OffsetRule(fde_start, 0x21, + kCFARegister, 0x134 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(fde_start + code_factor, 0x21, + kCFARegister, 0xf4f * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(fde_start + 2 * code_factor, 0x21, + kCFARegister, 0x134 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValOffsetRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x829caee6).ULEB128(0xe4c) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ValOffsetRule(fde_start, 0x829caee6, + kCFARegister, 0xe4c * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValOffsetRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0xf17c36d6).ULEB128(0xeb7) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xf17c36d6) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ValOffsetRule(fde_start, 0xf17c36d6, + kCFARegister, 0xeb7 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xf17c36d6)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(fde_start + 2 * code_factor, 0xf17c36d6, + kCFARegister, 0xeb7 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValOffsetRuleChangedValOffset) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x2cf0ab1b).ULEB128(0x562) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x2cf0ab1b).ULEB128(0xe88) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ValOffsetRule(fde_start, 0x2cf0ab1b, + kCFARegister, 0x562 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor, 0x2cf0ab1b, + kCFARegister, 0xe88 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(fde_start + 2 * code_factor, 0x2cf0ab1b, + kCFARegister, 0x562 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreRegisterRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_register).ULEB128(0x77514acc).ULEB128(0x464de4ce) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, RegisterRule(fde_start, 0x77514acc, 0x464de4ce)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreRegisterRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_register).ULEB128(0xe39acce5).ULEB128(0x095f1559) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xe39acce5) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, RegisterRule(fde_start, 0xe39acce5, 0x095f1559)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xe39acce5)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(fde_start + 2 * code_factor, 0xe39acce5, + 0x095f1559)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreRegisterRuleChangedRegister) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_register).ULEB128(0xd40e21b1).ULEB128(0x16607d6a) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_register).ULEB128(0xd40e21b1).ULEB128(0xbabb4742) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, RegisterRule(fde_start, 0xd40e21b1, 0x16607d6a)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(fde_start + code_factor, 0xd40e21b1, + 0xbabb4742)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(fde_start + 2 * code_factor, 0xd40e21b1, + 0x16607d6a)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreExpressionRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x666ae152).Block("dwarf") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ExpressionRule(fde_start, 0x666ae152, "dwarf")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreExpressionRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0xb5ca5c46).Block("elf") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xb5ca5c46) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ExpressionRule(fde_start, 0xb5ca5c46, "elf")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xb5ca5c46)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ExpressionRule(fde_start + 2 * code_factor, 0xb5ca5c46, + "elf")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreExpressionRuleChangedExpression) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x500f5739).Block("smurf") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x500f5739).Block("orc") + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ExpressionRule(fde_start, 0x500f5739, "smurf")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ExpressionRule(fde_start + code_factor, 0x500f5739, + "orc")) + .InSequence(s).WillOnce(Return(true)); + // Expectations are not wishes. + EXPECT_CALL(handler, ExpressionRule(fde_start + 2 * code_factor, 0x500f5739, + "smurf")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValExpressionRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x666ae152) + .Block("hideous") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ValExpressionRule(fde_start, 0x666ae152, "hideous")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValExpressionRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0xb5ca5c46) + .Block("revolting") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xb5ca5c46) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("RestoreValExpressionRuleChanged", section); + + EXPECT_CALL(handler, ValExpressionRule(fde_start, 0xb5ca5c46, "revolting")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xb5ca5c46)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValExpressionRule(fde_start + 2 * code_factor, 0xb5ca5c46, + "revolting")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValExpressionRuleChangedValExpression) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x500f5739) + .Block("repulsive") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x500f5739) + .Block("nauseous") + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("RestoreValExpressionRuleChangedValExpression", + section); + + EXPECT_CALL(handler, ValExpressionRule(fde_start, 0x500f5739, "repulsive")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValExpressionRule(fde_start + code_factor, 0x500f5739, + "nauseous")) + .InSequence(s).WillOnce(Return(true)); + // Expectations are not wishes. + EXPECT_CALL(handler, ValExpressionRule(fde_start + 2 * code_factor, 0x500f5739, + "repulsive")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +struct EHFrameFixture: public CFIInsnFixture { + EHFrameFixture() + : CFIInsnFixture(), section(kBigEndian, 4, true) { + encoded_pointer_bases.cfi = 0x7f496cb2; + encoded_pointer_bases.text = 0x540f67b6; + encoded_pointer_bases.data = 0xe3eab768; + section.SetEncodedPointerBases(encoded_pointer_bases); + } + CFISection section; + CFISection::EncodedPointerBases encoded_pointer_bases; + + // Parse CFIInsnFixture::ParseSection, but parse the section as + // .eh_frame data, supplying stock base addresses. + void ParseEHFrameSection(CFISection *section, bool succeeds = true) { + EXPECT_TRUE(section->ContainsEHFrame()); + string contents; + EXPECT_TRUE(section->GetContents(&contents)); + dwarf2reader::Endianness endianness; + if (section->endianness() == kBigEndian) + endianness = ENDIANNESS_BIG; + else { + assert(section->endianness() == kLittleEndian); + endianness = ENDIANNESS_LITTLE; + } + ByteReader byte_reader(endianness); + byte_reader.SetAddressSize(section->AddressSize()); + byte_reader.SetCFIDataBase(encoded_pointer_bases.cfi, + reinterpret_cast<const uint8_t *>(contents.data())); + byte_reader.SetTextBase(encoded_pointer_bases.text); + byte_reader.SetDataBase(encoded_pointer_bases.data); + CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()), + contents.size(), + &byte_reader, &handler, &reporter, true); + if (succeeds) + EXPECT_TRUE(parser.Start()); + else + EXPECT_FALSE(parser.Start()); + } + +}; + +class EHFrame: public EHFrameFixture, public Test { }; + +// A simple CIE, an FDE, and a terminator. +TEST_F(EHFrame, Terminator) { + Label cie; + section + .Mark(&cie) + .CIEHeader(9968, 2466, 67, 1, "") + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(3772).ULEB128(1372) + .FinishEntry() + .FDEHeader(cie, 0x848037a1, 0x7b30475e) + .D8(dwarf2reader::DW_CFA_set_loc).D32(0x17713850) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(5721) + .FinishEntry() + .D32(0) // Terminate the sequence. + // This FDE should be ignored. + .FDEHeader(cie, 0xf19629fe, 0x439fb09b) + .FinishEntry(); + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.Terminator", section); + + EXPECT_CALL(handler, Entry(_, 0x848037a1, 0x7b30475e, 1, "", 67)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(0x848037a1, kCFARegister, 3772, 1372)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(0x17713850, 5721)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(reporter, EarlyEHTerminator(_)) + .InSequence(s).WillOnce(Return()); + + ParseEHFrameSection(§ion); +} + +// The parser should recognize the Linux Standards Base 'z' augmentations. +TEST_F(EHFrame, SimpleFDE) { + DwarfPointerEncoding lsda_encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect + | dwarf2reader::DW_EH_PE_datarel + | dwarf2reader::DW_EH_PE_sdata2); + DwarfPointerEncoding fde_encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel + | dwarf2reader::DW_EH_PE_udata2); + + section.SetPointerEncoding(fde_encoding); + section.SetEncodedPointerBases(encoded_pointer_bases); + Label cie; + section + .Mark(&cie) + .CIEHeader(4873, 7012, 100, 1, "zSLPR") + .ULEB128(7) // Augmentation data length + .D8(lsda_encoding) // LSDA pointer format + .D8(dwarf2reader::DW_EH_PE_pcrel) // personality pointer format + .EncodedPointer(0x97baa00, dwarf2reader::DW_EH_PE_pcrel) // and value + .D8(fde_encoding) // FDE pointer format + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(6706).ULEB128(31) + .FinishEntry() + .FDEHeader(cie, 0x540f6b56, 0xf686) + .ULEB128(2) // Augmentation data length + .EncodedPointer(0xe3eab475, lsda_encoding) // LSDA pointer, signed + .D8(dwarf2reader::DW_CFA_set_loc) + .EncodedPointer(0x540fa4ce, fde_encoding) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x675e) + .FinishEntry() + .D32(0); // terminator + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.SimpleFDE", section); + + EXPECT_CALL(handler, Entry(_, 0x540f6b56, 0xf686, 1, "zSLPR", 100)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, PersonalityRoutine(0x97baa00, false)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, LanguageSpecificDataArea(0xe3eab475, true)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, SignalHandler()) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(0x540f6b56, kCFARegister, 6706, 31)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(0x540fa4ce, 0x675e)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +// Check that we can handle an empty 'z' augmentation. +TEST_F(EHFrame, EmptyZ) { + Label cie; + section + .Mark(&cie) + .CIEHeader(5955, 5805, 228, 1, "z") + .ULEB128(0) // Augmentation data length + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(3629).ULEB128(247) + .FinishEntry() + .FDEHeader(cie, 0xda007738, 0xfb55c641) + .ULEB128(0) // Augmentation data length + .D8(dwarf2reader::DW_CFA_advance_loc1).D8(11) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(3769) + .FinishEntry(); + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.EmptyZ", section); + + EXPECT_CALL(handler, Entry(_, 0xda007738, 0xfb55c641, 1, "z", 228)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(0xda007738, kCFARegister, 3629, 247)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(0xda007738 + 11 * 5955, 3769)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +// Check that we recognize bad 'z' augmentation characters. +TEST_F(EHFrame, BadZ) { + Label cie; + section + .Mark(&cie) + .CIEHeader(6937, 1045, 142, 1, "zQ") + .ULEB128(0) // Augmentation data length + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(9006).ULEB128(7725) + .FinishEntry() + .FDEHeader(cie, 0x1293efa8, 0x236f53f2) + .ULEB128(0) // Augmentation data length + .D8(dwarf2reader::DW_CFA_advance_loc | 12) + .D8(dwarf2reader::DW_CFA_register).ULEB128(5667).ULEB128(3462) + .FinishEntry(); + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.BadZ", section); + + EXPECT_CALL(reporter, UnrecognizedAugmentation(_, "zQ")) + .WillOnce(Return()); + + ParseEHFrameSection(§ion, false); +} + +TEST_F(EHFrame, zL) { + Label cie; + DwarfPointerEncoding lsda_encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_funcrel + | dwarf2reader::DW_EH_PE_udata2); + section + .Mark(&cie) + .CIEHeader(9285, 9959, 54, 1, "zL") + .ULEB128(1) // Augmentation data length + .D8(lsda_encoding) // encoding for LSDA pointer in FDE + + .FinishEntry() + .FDEHeader(cie, 0xd40091aa, 0x9aa6e746) + .ULEB128(2) // Augmentation data length + .EncodedPointer(0xd40099cd, lsda_encoding) // LSDA pointer + .FinishEntry() + .D32(0); // terminator + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zL", section); + + EXPECT_CALL(handler, Entry(_, 0xd40091aa, 0x9aa6e746, 1, "zL", 54)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, LanguageSpecificDataArea(0xd40099cd, false)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +TEST_F(EHFrame, zP) { + Label cie; + DwarfPointerEncoding personality_encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_datarel + | dwarf2reader::DW_EH_PE_udata2); + section + .Mark(&cie) + .CIEHeader(1097, 6313, 17, 1, "zP") + .ULEB128(3) // Augmentation data length + .D8(personality_encoding) // encoding for personality routine + .EncodedPointer(0xe3eaccac, personality_encoding) // value + .FinishEntry() + .FDEHeader(cie, 0x0c8350c9, 0xbef11087) + .ULEB128(0) // Augmentation data length + .FinishEntry() + .D32(0); // terminator + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zP", section); + + EXPECT_CALL(handler, Entry(_, 0x0c8350c9, 0xbef11087, 1, "zP", 17)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, PersonalityRoutine(0xe3eaccac, false)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +TEST_F(EHFrame, zR) { + Label cie; + DwarfPointerEncoding pointer_encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel + | dwarf2reader::DW_EH_PE_sdata2); + section.SetPointerEncoding(pointer_encoding); + section + .Mark(&cie) + .CIEHeader(8011, 5496, 75, 1, "zR") + .ULEB128(1) // Augmentation data length + .D8(pointer_encoding) // encoding for FDE addresses + .FinishEntry() + .FDEHeader(cie, 0x540f9431, 0xbd0) + .ULEB128(0) // Augmentation data length + .FinishEntry() + .D32(0); // terminator + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zR", section); + + EXPECT_CALL(handler, Entry(_, 0x540f9431, 0xbd0, 1, "zR", 75)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +TEST_F(EHFrame, zS) { + Label cie; + section + .Mark(&cie) + .CIEHeader(9217, 7694, 57, 1, "zS") + .ULEB128(0) // Augmentation data length + .FinishEntry() + .FDEHeader(cie, 0xd40091aa, 0x9aa6e746) + .ULEB128(0) // Augmentation data length + .FinishEntry() + .D32(0); // terminator + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zS", section); + + EXPECT_CALL(handler, Entry(_, 0xd40091aa, 0x9aa6e746, 1, "zS", 57)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, SignalHandler()) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +// These tests require manual inspection of the test output. +struct CFIReporterFixture { + CFIReporterFixture() : reporter("test file name", "test section name") { } + CallFrameInfo::Reporter reporter; +}; + +class CFIReporter: public CFIReporterFixture, public Test { }; + +TEST_F(CFIReporter, Incomplete) { + reporter.Incomplete(0x0102030405060708ULL, CallFrameInfo::kUnknown); +} + +TEST_F(CFIReporter, EarlyEHTerminator) { + reporter.EarlyEHTerminator(0x0102030405060708ULL); +} + +TEST_F(CFIReporter, CIEPointerOutOfRange) { + reporter.CIEPointerOutOfRange(0x0123456789abcdefULL, 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, BadCIEId) { + reporter.BadCIEId(0x0123456789abcdefULL, 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, UnrecognizedVersion) { + reporter.UnrecognizedVersion(0x0123456789abcdefULL, 43); +} + +TEST_F(CFIReporter, UnrecognizedAugmentation) { + reporter.UnrecognizedAugmentation(0x0123456789abcdefULL, "poodles"); +} + +TEST_F(CFIReporter, InvalidPointerEncoding) { + reporter.InvalidPointerEncoding(0x0123456789abcdefULL, 0x42); +} + +TEST_F(CFIReporter, UnusablePointerEncoding) { + reporter.UnusablePointerEncoding(0x0123456789abcdefULL, 0x42); +} + +TEST_F(CFIReporter, RestoreInCIE) { + reporter.RestoreInCIE(0x0123456789abcdefULL, 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, BadInstruction) { + reporter.BadInstruction(0x0123456789abcdefULL, CallFrameInfo::kFDE, + 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, NoCFARule) { + reporter.NoCFARule(0x0123456789abcdefULL, CallFrameInfo::kCIE, + 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, EmptyStateStack) { + reporter.EmptyStateStack(0x0123456789abcdefULL, CallFrameInfo::kTerminator, + 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, ClearingCFARule) { + reporter.ClearingCFARule(0x0123456789abcdefULL, CallFrameInfo::kFDE, + 0xfedcba9876543210ULL); +} + +#ifdef WRITE_ELF +// See comments at the top of the file mentioning WRITE_ELF for details. + +using google_breakpad::test_assembler::Section; + +struct ELFSectionHeader { + ELFSectionHeader(unsigned int set_type) + : type(set_type), flags(0), address(0), link(0), info(0), + alignment(1), entry_size(0) { } + Label name; + unsigned int type; + uint64_t flags; + uint64_t address; + Label file_offset; + Label file_size; + unsigned int link; + unsigned int info; + uint64_t alignment; + uint64_t entry_size; +}; + +void AppendSectionHeader(CFISection *table, const ELFSectionHeader &header) { + (*table) + .D32(header.name) // name, index in string tbl + .D32(header.type) // type + .Address(header.flags) // flags + .Address(header.address) // address in memory + .Address(header.file_offset) // offset in ELF file + .Address(header.file_size) // length in bytes + .D32(header.link) // link to related section + .D32(header.info) // miscellaneous + .Address(header.alignment) // alignment + .Address(header.entry_size); // entry size +} + +void WriteELFFrameSection(const char *filename, const char *cfi_name, + const CFISection &cfi) { + int elf_class = cfi.AddressSize() == 4 ? ELFCLASS32 : ELFCLASS64; + int elf_data = (cfi.endianness() == kBigEndian + ? ELFDATA2MSB : ELFDATA2LSB); + CFISection elf(cfi.endianness(), cfi.AddressSize()); + Label elf_header_size, section_table_offset; + elf + .Append("\x7f" "ELF") + .D8(elf_class) // 32-bit or 64-bit ELF + .D8(elf_data) // endianness + .D8(1) // ELF version + .D8(ELFOSABI_LINUX) // Operating System/ABI indication + .D8(0) // ABI version + .Append(7, 0xda) // padding + .D16(ET_EXEC) // file type: executable file + .D16(EM_386) // architecture: Intel IA-32 + .D32(EV_CURRENT); // ELF version + elf + .Address(0x0123456789abcdefULL) // program entry point + .Address(0) // program header offset + .Address(section_table_offset) // section header offset + .D32(0) // processor-specific flags + .D16(elf_header_size) // ELF header size in bytes */ + .D16(elf_class == ELFCLASS32 ? 32 : 56) // program header entry size + .D16(0) // program header table entry count + .D16(elf_class == ELFCLASS32 ? 40 : 64) // section header entry size + .D16(3) // section count + .D16(1) // section name string table + .Mark(&elf_header_size); + + // The null section. Every ELF file has one, as the first entry in + // the section header table. + ELFSectionHeader null_header(SHT_NULL); + null_header.file_offset = 0; + null_header.file_size = 0; + + // The CFI section. The whole reason for writing out this ELF file + // is to put this in it so that we can run other dumping programs on + // it to check its contents. + ELFSectionHeader cfi_header(SHT_PROGBITS); + cfi_header.file_size = cfi.Size(); + + // The section holding the names of the sections. This is the + // section whose index appears in the e_shstrndx member of the ELF + // header. + ELFSectionHeader section_names_header(SHT_STRTAB); + CFISection section_names(cfi.endianness(), cfi.AddressSize()); + section_names + .Mark(&null_header.name) + .AppendCString("") + .Mark(§ion_names_header.name) + .AppendCString(".shstrtab") + .Mark(&cfi_header.name) + .AppendCString(cfi_name) + .Mark(§ion_names_header.file_size); + + // Create the section table. The ELF header's e_shoff member refers + // to this, and the e_shnum member gives the number of entries it + // contains. + CFISection section_table(cfi.endianness(), cfi.AddressSize()); + AppendSectionHeader(§ion_table, null_header); + AppendSectionHeader(§ion_table, section_names_header); + AppendSectionHeader(§ion_table, cfi_header); + + // Append the section table and the section contents to the ELF file. + elf + .Mark(§ion_table_offset) + .Append(section_table) + .Mark(§ion_names_header.file_offset) + .Append(section_names) + .Mark(&cfi_header.file_offset) + .Append(cfi); + + string contents; + if (!elf.GetContents(&contents)) { + fprintf(stderr, "failed to get ELF file contents\n"); + exit(1); + } + + FILE *out = fopen(filename, "w"); + if (!out) { + fprintf(stderr, "error opening ELF file '%s': %s\n", + filename, strerror(errno)); + exit(1); + } + + if (fwrite(contents.data(), 1, contents.size(), out) != contents.size()) { + fprintf(stderr, "error writing ELF data to '%s': %s\n", + filename, strerror(errno)); + exit(1); + } + + if (fclose(out) == EOF) { + fprintf(stderr, "error closing ELF file '%s': %s\n", + filename, strerror(errno)); + exit(1); + } +} +#endif diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc new file mode 100644 index 0000000000..71418eb8d7 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc @@ -0,0 +1,487 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// dwarf2reader_die_unittest.cc: Unit tests for dwarf2reader::CompilationUnit + +#include <stdint.h> +#include <stdlib.h> + +#include <iostream> +#include <string> +#include <vector> + +#include "breakpad_googletest_includes.h" +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/dwarf2reader_test_common.h" +#include "common/dwarf/dwarf2reader.h" +#include "common/using_std_string.h" +#include "google_breakpad/common/breakpad_types.h" + +using google_breakpad::test_assembler::Endianness; +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::Section; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; + +using dwarf2reader::ByteReader; +using dwarf2reader::CompilationUnit; +using dwarf2reader::Dwarf2Handler; +using dwarf2reader::DwarfAttribute; +using dwarf2reader::DwarfForm; +using dwarf2reader::DwarfHasChild; +using dwarf2reader::DwarfTag; +using dwarf2reader::ENDIANNESS_BIG; +using dwarf2reader::ENDIANNESS_LITTLE; +using dwarf2reader::SectionMap; + +using std::vector; +using testing::InSequence; +using testing::Pointee; +using testing::Return; +using testing::Sequence; +using testing::Test; +using testing::TestWithParam; +using testing::_; + +class MockDwarf2Handler: public Dwarf2Handler { + public: + MOCK_METHOD5(StartCompilationUnit, bool(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version)); + MOCK_METHOD2(StartDIE, bool(uint64 offset, enum DwarfTag tag)); + MOCK_METHOD4(ProcessAttributeUnsigned, void(uint64 offset, + DwarfAttribute attr, + enum DwarfForm form, + uint64 data)); + MOCK_METHOD4(ProcessAttributeSigned, void(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data)); + MOCK_METHOD4(ProcessAttributeReference, void(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data)); + MOCK_METHOD5(ProcessAttributeBuffer, void(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const uint8_t *data, + uint64 len)); + MOCK_METHOD4(ProcessAttributeString, void(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const string& data)); + MOCK_METHOD4(ProcessAttributeSignature, void(uint64 offset, + DwarfAttribute attr, + enum DwarfForm form, + uint64 signature)); + MOCK_METHOD1(EndDIE, void(uint64 offset)); +}; + +struct DIEFixture { + + DIEFixture() { + // Fix the initial offset of the .debug_info and .debug_abbrev sections. + info.start() = 0; + abbrevs.start() = 0; + + // Default expectations for the data handler. + EXPECT_CALL(handler, StartCompilationUnit(_, _, _, _, _)).Times(0); + EXPECT_CALL(handler, StartDIE(_, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeUnsigned(_, _, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeSigned(_, _, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeReference(_, _, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, _, _, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeString(_, _, _, _)).Times(0); + EXPECT_CALL(handler, EndDIE(_)).Times(0); + } + + // Return a reference to a section map whose .debug_info section refers + // to |info|, and whose .debug_abbrev section refers to |abbrevs|. This + // function returns a reference to the same SectionMap each time; new + // calls wipe out maps established by earlier calls. + const SectionMap &MakeSectionMap() { + // Copy the sections' contents into strings that will live as long as + // the map itself. + assert(info.GetContents(&info_contents)); + assert(abbrevs.GetContents(&abbrevs_contents)); + section_map.clear(); + section_map[".debug_info"].first + = reinterpret_cast<const uint8_t *>(info_contents.data()); + section_map[".debug_info"].second = info_contents.size(); + section_map[".debug_abbrev"].first + = reinterpret_cast<const uint8_t *>(abbrevs_contents.data()); + section_map[".debug_abbrev"].second = abbrevs_contents.size(); + return section_map; + } + + TestCompilationUnit info; + TestAbbrevTable abbrevs; + MockDwarf2Handler handler; + string abbrevs_contents, info_contents; + SectionMap section_map; +}; + +struct DwarfHeaderParams { + DwarfHeaderParams(Endianness endianness, size_t format_size, + int version, size_t address_size) + : endianness(endianness), format_size(format_size), + version(version), address_size(address_size) { } + Endianness endianness; + size_t format_size; // 4-byte or 8-byte DWARF offsets + int version; + size_t address_size; +}; + +class DwarfHeader: public DIEFixture, + public TestWithParam<DwarfHeaderParams> { }; + +TEST_P(DwarfHeader, Header) { + Label abbrev_table = abbrevs.Here(); + abbrevs.Abbrev(1, dwarf2reader::DW_TAG_compile_unit, + dwarf2reader::DW_children_yes) + .Attribute(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_string) + .EndAbbrev() + .EndTable(); + + info.set_format_size(GetParam().format_size); + info.set_endianness(GetParam().endianness); + + info.Header(GetParam().version, abbrev_table, GetParam().address_size) + .ULEB128(1) // DW_TAG_compile_unit, with children + .AppendCString("sam") // DW_AT_name, DW_FORM_string + .D8(0); // end of children + info.Finish(); + + { + InSequence s; + EXPECT_CALL(handler, + StartCompilationUnit(0, GetParam().address_size, + GetParam().format_size, _, + GetParam().version)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, StartDIE(_, dwarf2reader::DW_TAG_compile_unit)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, ProcessAttributeString(_, dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_string, + "sam")) + .WillOnce(Return()); + EXPECT_CALL(handler, EndDIE(_)) + .WillOnce(Return()); + } + + ByteReader byte_reader(GetParam().endianness == kLittleEndian ? + ENDIANNESS_LITTLE : ENDIANNESS_BIG); + CompilationUnit parser("", MakeSectionMap(), 0, &byte_reader, &handler); + EXPECT_EQ(parser.Start(), info_contents.size()); +} + +INSTANTIATE_TEST_CASE_P( + HeaderVariants, DwarfHeader, + ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4), + DwarfHeaderParams(kLittleEndian, 4, 2, 8), + DwarfHeaderParams(kLittleEndian, 4, 3, 4), + DwarfHeaderParams(kLittleEndian, 4, 3, 8), + DwarfHeaderParams(kLittleEndian, 4, 4, 4), + DwarfHeaderParams(kLittleEndian, 4, 4, 8), + DwarfHeaderParams(kLittleEndian, 8, 2, 4), + DwarfHeaderParams(kLittleEndian, 8, 2, 8), + DwarfHeaderParams(kLittleEndian, 8, 3, 4), + DwarfHeaderParams(kLittleEndian, 8, 3, 8), + DwarfHeaderParams(kLittleEndian, 8, 4, 4), + DwarfHeaderParams(kLittleEndian, 8, 4, 8), + DwarfHeaderParams(kBigEndian, 4, 2, 4), + DwarfHeaderParams(kBigEndian, 4, 2, 8), + DwarfHeaderParams(kBigEndian, 4, 3, 4), + DwarfHeaderParams(kBigEndian, 4, 3, 8), + DwarfHeaderParams(kBigEndian, 4, 4, 4), + DwarfHeaderParams(kBigEndian, 4, 4, 8), + DwarfHeaderParams(kBigEndian, 8, 2, 4), + DwarfHeaderParams(kBigEndian, 8, 2, 8), + DwarfHeaderParams(kBigEndian, 8, 3, 4), + DwarfHeaderParams(kBigEndian, 8, 3, 8), + DwarfHeaderParams(kBigEndian, 8, 4, 4), + DwarfHeaderParams(kBigEndian, 8, 4, 8))); + +struct DwarfFormsFixture: public DIEFixture { + // Start a compilation unit, as directed by |params|, containing one + // childless DIE of the given tag, with one attribute of the given name + // and form. The 'info' fixture member is left just after the abbrev + // code, waiting for the attribute value to be appended. + void StartSingleAttributeDIE(const DwarfHeaderParams ¶ms, + DwarfTag tag, DwarfAttribute name, + DwarfForm form) { + // Create the abbreviation table. + Label abbrev_table = abbrevs.Here(); + abbrevs.Abbrev(1, tag, dwarf2reader::DW_children_no) + .Attribute(name, form) + .EndAbbrev() + .EndTable(); + + // Create the compilation unit, up to the attribute value. + info.set_format_size(params.format_size); + info.set_endianness(params.endianness); + info.Header(params.version, abbrev_table, params.address_size) + .ULEB128(1); // abbrev code + } + + // Set up handler to expect a compilation unit matching |params|, + // containing one childless DIE of the given tag, in the sequence s. Stop + // just before the expectations. + void ExpectBeginCompilationUnit(const DwarfHeaderParams ¶ms, + DwarfTag tag, uint64 offset=0) { + EXPECT_CALL(handler, + StartCompilationUnit(offset, params.address_size, + params.format_size, _, + params.version)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, StartDIE(_, tag)) + .InSequence(s) + .WillOnce(Return(true)); + } + + void ExpectEndCompilationUnit() { + EXPECT_CALL(handler, EndDIE(_)) + .InSequence(s) + .WillOnce(Return()); + } + + void ParseCompilationUnit(const DwarfHeaderParams ¶ms, uint64 offset=0) { + ByteReader byte_reader(params.endianness == kLittleEndian ? + ENDIANNESS_LITTLE : ENDIANNESS_BIG); + CompilationUnit parser("", MakeSectionMap(), offset, &byte_reader, &handler); + EXPECT_EQ(offset + parser.Start(), info_contents.size()); + } + + // The sequence to which the fixture's methods append expectations. + Sequence s; +}; + +struct DwarfForms: public DwarfFormsFixture, + public TestWithParam<DwarfHeaderParams> { }; + +TEST_P(DwarfForms, addr) { + StartSingleAttributeDIE(GetParam(), dwarf2reader::DW_TAG_compile_unit, + dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr); + uint64_t value; + if (GetParam().address_size == 4) { + value = 0xc8e9ffcc; + info.D32(value); + } else { + value = 0xe942517fc2768564ULL; + info.D64(value); + } + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), dwarf2reader::DW_TAG_compile_unit); + EXPECT_CALL(handler, ProcessAttributeUnsigned(_, dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr, + value)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, block2_empty) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7, + (DwarfAttribute) 0xe52c4463, + dwarf2reader::DW_FORM_block2); + info.D16(0); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463, + dwarf2reader::DW_FORM_block2, + _, 0)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, block2) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7, + (DwarfAttribute) 0xe52c4463, + dwarf2reader::DW_FORM_block2); + unsigned char data[258]; + memset(data, '*', sizeof(data)); + info.D16(sizeof(data)) + .Append(data, sizeof(data)); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463, + dwarf2reader::DW_FORM_block2, + Pointee('*'), 258)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, flag_present) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x3e449ac2, + (DwarfAttribute) 0x359d1972, + dwarf2reader::DW_FORM_flag_present); + // DW_FORM_flag_present occupies no space in the DIE. + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x3e449ac2); + EXPECT_CALL(handler, + ProcessAttributeUnsigned(_, (DwarfAttribute) 0x359d1972, + dwarf2reader::DW_FORM_flag_present, + 1)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, sec_offset) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x1d971689, + (DwarfAttribute) 0xa060bfd1, + dwarf2reader::DW_FORM_sec_offset); + uint64_t value; + if (GetParam().format_size == 4) { + value = 0xacc9c388; + info.D32(value); + } else { + value = 0xcffe5696ffe3ed0aULL; + info.D64(value); + } + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x1d971689); + EXPECT_CALL(handler, ProcessAttributeUnsigned(_, (DwarfAttribute) 0xa060bfd1, + dwarf2reader::DW_FORM_sec_offset, + value)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, exprloc) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0xb6d167bb, + (DwarfAttribute) 0xba3ae5cb, + dwarf2reader::DW_FORM_exprloc); + info.ULEB128(29) + .Append(29, 173); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0xb6d167bb); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xba3ae5cb, + dwarf2reader::DW_FORM_exprloc, + Pointee(173), 29)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, ref_sig8) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b, + (DwarfAttribute) 0xd708d908, + dwarf2reader::DW_FORM_ref_sig8); + info.D64(0xf72fa0cb6ddcf9d6ULL); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b); + EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908, + dwarf2reader::DW_FORM_ref_sig8, + 0xf72fa0cb6ddcf9d6ULL)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +// A value passed to ProcessAttributeSignature is just an absolute number, +// not an offset within the compilation unit as most of the other +// DW_FORM_ref forms are. Check that the reader doesn't try to apply any +// offset to the signature, by reading it from a compilation unit that does +// not start at the beginning of the section. +TEST_P(DwarfForms, ref_sig8_not_first) { + info.Append(98, '*'); + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b, + (DwarfAttribute) 0xd708d908, + dwarf2reader::DW_FORM_ref_sig8); + info.D64(0xf72fa0cb6ddcf9d6ULL); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b, 98); + EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908, + dwarf2reader::DW_FORM_ref_sig8, + 0xf72fa0cb6ddcf9d6ULL)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam(), 98); +} + +// Tests for the other attribute forms could go here. + +INSTANTIATE_TEST_CASE_P( + HeaderVariants, DwarfForms, + ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4), + DwarfHeaderParams(kLittleEndian, 4, 2, 8), + DwarfHeaderParams(kLittleEndian, 4, 3, 4), + DwarfHeaderParams(kLittleEndian, 4, 3, 8), + DwarfHeaderParams(kLittleEndian, 4, 4, 4), + DwarfHeaderParams(kLittleEndian, 4, 4, 8), + DwarfHeaderParams(kLittleEndian, 8, 2, 4), + DwarfHeaderParams(kLittleEndian, 8, 2, 8), + DwarfHeaderParams(kLittleEndian, 8, 3, 4), + DwarfHeaderParams(kLittleEndian, 8, 3, 8), + DwarfHeaderParams(kLittleEndian, 8, 4, 4), + DwarfHeaderParams(kLittleEndian, 8, 4, 8), + DwarfHeaderParams(kBigEndian, 4, 2, 4), + DwarfHeaderParams(kBigEndian, 4, 2, 8), + DwarfHeaderParams(kBigEndian, 4, 3, 4), + DwarfHeaderParams(kBigEndian, 4, 3, 8), + DwarfHeaderParams(kBigEndian, 4, 4, 4), + DwarfHeaderParams(kBigEndian, 4, 4, 8), + DwarfHeaderParams(kBigEndian, 8, 2, 4), + DwarfHeaderParams(kBigEndian, 8, 2, 8), + DwarfHeaderParams(kBigEndian, 8, 3, 4), + DwarfHeaderParams(kBigEndian, 8, 3, 8), + DwarfHeaderParams(kBigEndian, 8, 4, 4), + DwarfHeaderParams(kBigEndian, 8, 4, 8))); diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_test_common.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_test_common.h new file mode 100644 index 0000000000..e91de90610 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_test_common.h @@ -0,0 +1,149 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// dwarf2reader_test_common.h: Define TestCompilationUnit and +// TestAbbrevTable, classes for creating properly (and improperly) +// formatted DWARF compilation unit data for unit tests. + +#ifndef COMMON_DWARF_DWARF2READER_TEST_COMMON_H__ +#define COMMON_DWARF_DWARF2READER_TEST_COMMON_H__ + +#include "common/test_assembler.h" +#include "common/dwarf/dwarf2enums.h" + +// A subclass of test_assembler::Section, specialized for constructing +// DWARF compilation units. +class TestCompilationUnit: public google_breakpad::test_assembler::Section { + public: + typedef dwarf2reader::DwarfTag DwarfTag; + typedef dwarf2reader::DwarfAttribute DwarfAttribute; + typedef dwarf2reader::DwarfForm DwarfForm; + typedef google_breakpad::test_assembler::Label Label; + + // Set the section's DWARF format size (the 32-bit DWARF format or the + // 64-bit DWARF format, for lengths and section offsets --- not the + // address size) to format_size. + void set_format_size(size_t format_size) { + assert(format_size == 4 || format_size == 8); + format_size_ = format_size; + } + + // Append a DWARF section offset value, of the appropriate size for this + // compilation unit. + template<typename T> + void SectionOffset(T offset) { + if (format_size_ == 4) + D32(offset); + else + D64(offset); + } + + // Append a DWARF compilation unit header to the section, with the given + // DWARF version, abbrev table offset, and address size. + TestCompilationUnit &Header(int version, const Label &abbrev_offset, + size_t address_size) { + if (format_size_ == 4) { + D32(length_); + } else { + D32(0xffffffff); + D64(length_); + } + post_length_offset_ = Size(); + D16(version); + SectionOffset(abbrev_offset); + D8(address_size); + return *this; + } + + // Mark the end of this header's DIEs. + TestCompilationUnit &Finish() { + length_ = Size() - post_length_offset_; + return *this; + } + + private: + // The DWARF format size for this compilation unit. + size_t format_size_; + + // The offset of the point in the compilation unit header immediately + // after the initial length field. + uint64_t post_length_offset_; + + // The length of the compilation unit, not including the initial length field. + Label length_; +}; + +// A subclass of test_assembler::Section specialized for constructing DWARF +// abbreviation tables. +class TestAbbrevTable: public google_breakpad::test_assembler::Section { + public: + typedef dwarf2reader::DwarfTag DwarfTag; + typedef dwarf2reader::DwarfAttribute DwarfAttribute; + typedef dwarf2reader::DwarfForm DwarfForm; + typedef dwarf2reader::DwarfHasChild DwarfHasChild; + typedef google_breakpad::test_assembler::Label Label; + + // Start a new abbreviation table entry for abbreviation code |code|, + // encoding a DIE whose tag is |tag|, and which has children if and only + // if |has_children| is true. + TestAbbrevTable &Abbrev(int code, DwarfTag tag, DwarfHasChild has_children) { + assert(code != 0); + ULEB128(code); + ULEB128(static_cast<unsigned>(tag)); + D8(static_cast<unsigned>(has_children)); + return *this; + }; + + // Add an attribute to the current abbreviation code whose name is |name| + // and whose form is |form|. + TestAbbrevTable &Attribute(DwarfAttribute name, DwarfForm form) { + ULEB128(static_cast<unsigned>(name)); + ULEB128(static_cast<unsigned>(form)); + return *this; + } + + // Finish the current abbreviation code. + TestAbbrevTable &EndAbbrev() { + ULEB128(0); + ULEB128(0); + return *this; + } + + // Finish the current abbreviation table. + TestAbbrevTable &EndTable() { + ULEB128(0); + return *this; + } +}; + +#endif // COMMON_DWARF_DWARF2READER_TEST_COMMON_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/elf_reader.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf/elf_reader.cc new file mode 100644 index 0000000000..7e4be639a7 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/elf_reader.cc @@ -0,0 +1,1274 @@ +// Copyright 2005 Google Inc. All Rights Reserved. +// Author: chatham@google.com (Andrew Chatham) +// Author: satorux@google.com (Satoru Takabayashi) +// +// Code for reading in ELF files. +// +// For information on the ELF format, see +// http://www.x86.org/ftp/manuals/tools/elf.pdf +// +// I also liked: +// http://www.caldera.com/developers/gabi/1998-04-29/contents.html +// +// A note about types: When dealing with the file format, we use types +// like Elf32_Word, but in the public interfaces we treat all +// addresses as uint64. As a result, we should be able to symbolize +// 64-bit binaries from a 32-bit process (which we don't do, +// anyway). size_t should therefore be avoided, except where required +// by things like mmap(). +// +// Although most of this code can deal with arbitrary ELF files of +// either word size, the public ElfReader interface only examines +// files loaded into the current address space, which must all match +// the machine's native word size. This code cannot handle ELF files +// with a non-native byte ordering. +// +// TODO(chatham): It would be nice if we could accomplish this task +// without using malloc(), so we could use it as the process is dying. + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // needed for pread() +#endif + +#include <fcntl.h> +#include <limits.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <algorithm> +#include <map> +#include <string> +#include <vector> +// TODO(saugustine): Add support for compressed debug. +// Also need to add configure tests for zlib. +//#include "zlib.h" + +#include "third_party/musl/include/elf.h" +#include "elf_reader.h" +#include "common/using_std_string.h" + +// EM_AARCH64 is not defined by elf.h of GRTE v3 on x86. +// TODO(dougkwan): Remove this when v17 is retired. +#if !defined(EM_AARCH64) +#define EM_AARCH64 183 /* ARM AARCH64 */ +#endif + +// Map Linux macros to their Apple equivalents. +#if __APPLE__ || __FreeBSD__ +#ifndef __LITTLE_ENDIAN +#define __LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ +#endif // __LITTLE_ENDIAN +#ifndef __BIG_ENDIAN +#define __BIG_ENDIAN __ORDER_BIG_ENDIAN__ +#endif // __BIG_ENDIAN +#ifndef __BYTE_ORDER +#define __BYTE_ORDER __BYTE_ORDER__ +#endif // __BYTE_ORDER +#endif // __APPLE__ + +// TODO(dthomson): Can be removed once all Java code is using the Google3 +// launcher. We need to avoid processing PLT functions as it causes memory +// fragmentation in malloc, which is fixed in tcmalloc - and if the Google3 +// launcher is used the JVM will then use tcmalloc. b/13735638 +//DEFINE_bool(elfreader_process_dynsyms, true, +// "Activate PLT function processing"); + +using std::vector; + +namespace { + +// The lowest bit of an ARM symbol value is used to indicate a Thumb address. +const int kARMThumbBitOffset = 0; + +// Converts an ARM Thumb symbol value to a true aligned address value. +template <typename T> +T AdjustARMThumbSymbolValue(const T& symbol_table_value) { + return symbol_table_value & ~(1 << kARMThumbBitOffset); +} + +// Names of PLT-related sections. +const char kElfPLTRelSectionName[] = ".rel.plt"; // Use Rel struct. +const char kElfPLTRelaSectionName[] = ".rela.plt"; // Use Rela struct. +const char kElfPLTSectionName[] = ".plt"; +const char kElfDynSymSectionName[] = ".dynsym"; + +const int kX86PLTCodeSize = 0x10; // Size of one x86 PLT function in bytes. +const int kARMPLTCodeSize = 0xc; +const int kAARCH64PLTCodeSize = 0x10; + +const int kX86PLT0Size = 0x10; // Size of the special PLT0 entry. +const int kARMPLT0Size = 0x14; +const int kAARCH64PLT0Size = 0x20; + +// Suffix for PLT functions when it needs to be explicitly identified as such. +const char kPLTFunctionSuffix[] = "@plt"; + +} // namespace + +namespace dwarf2reader { + +template <class ElfArch> class ElfReaderImpl; + +// 32-bit and 64-bit ELF files are processed exactly the same, except +// for various field sizes. Elf32 and Elf64 encompass all of the +// differences between the two formats, and all format-specific code +// in this file is templated on one of them. +class Elf32 { + public: + typedef Elf32_Ehdr Ehdr; + typedef Elf32_Shdr Shdr; + typedef Elf32_Phdr Phdr; + typedef Elf32_Word Word; + typedef Elf32_Sym Sym; + typedef Elf32_Rel Rel; + typedef Elf32_Rela Rela; + + // What should be in the EI_CLASS header. + static const int kElfClass = ELFCLASS32; + + // Given a symbol pointer, return the binding type (eg STB_WEAK). + static char Bind(const Elf32_Sym *sym) { + return ELF32_ST_BIND(sym->st_info); + } + // Given a symbol pointer, return the symbol type (eg STT_FUNC). + static char Type(const Elf32_Sym *sym) { + return ELF32_ST_TYPE(sym->st_info); + } + + // Extract the symbol index from the r_info field of a relocation. + static int r_sym(const Elf32_Word r_info) { + return ELF32_R_SYM(r_info); + } +}; + + +class Elf64 { + public: + typedef Elf64_Ehdr Ehdr; + typedef Elf64_Shdr Shdr; + typedef Elf64_Phdr Phdr; + typedef Elf64_Word Word; + typedef Elf64_Sym Sym; + typedef Elf64_Rel Rel; + typedef Elf64_Rela Rela; + + // What should be in the EI_CLASS header. + static const int kElfClass = ELFCLASS64; + + static char Bind(const Elf64_Sym *sym) { + return ELF64_ST_BIND(sym->st_info); + } + static char Type(const Elf64_Sym *sym) { + return ELF64_ST_TYPE(sym->st_info); + } + static int r_sym(const Elf64_Xword r_info) { + return ELF64_R_SYM(r_info); + } +}; + + +// ElfSectionReader mmaps a section of an ELF file ("section" is ELF +// terminology). The ElfReaderImpl object providing the section header +// must exist for the lifetime of this object. +// +// The motivation for mmaping individual sections of the file is that +// many Google executables are large enough when unstripped that we +// have to worry about running out of virtual address space. +// +// For compressed sections we have no choice but to allocate memory. +template<class ElfArch> +class ElfSectionReader { + public: + ElfSectionReader(const char *name, const string &path, int fd, + const typename ElfArch::Shdr §ion_header) + : contents_aligned_(NULL), + contents_(NULL), + header_(section_header) { + // Back up to the beginning of the page we're interested in. + const size_t additional = header_.sh_offset % getpagesize(); + const size_t offset_aligned = header_.sh_offset - additional; + section_size_ = header_.sh_size; + size_aligned_ = section_size_ + additional; + // If the section has been stripped or is empty, do not attempt + // to process its contents. + if (header_.sh_type == SHT_NOBITS || header_.sh_size == 0) + return; + contents_aligned_ = mmap(NULL, size_aligned_, PROT_READ, MAP_SHARED, + fd, offset_aligned); + // Set where the offset really should begin. + contents_ = reinterpret_cast<char *>(contents_aligned_) + + (header_.sh_offset - offset_aligned); + + // Check for and handle any compressed contents. + //if (strncmp(name, ".zdebug_", strlen(".zdebug_")) == 0) + // DecompressZlibContents(); + // TODO(saugustine): Add support for proposed elf-section flag + // "SHF_COMPRESS". + } + + ~ElfSectionReader() { + if (contents_aligned_ != NULL) + munmap(contents_aligned_, size_aligned_); + else + delete[] contents_; + } + + // Return the section header for this section. + typename ElfArch::Shdr const &header() const { return header_; } + + // Return memory at the given offset within this section. + const char *GetOffset(typename ElfArch::Word bytes) const { + return contents_ + bytes; + } + + const char *contents() const { return contents_; } + size_t section_size() const { return section_size_; } + + private: + // page-aligned file contents + void *contents_aligned_; + // contents as usable by the client. For non-compressed sections, + // pointer within contents_aligned_ to where the section data + // begins; for compressed sections, pointer to the decompressed + // data. + char *contents_; + // size of contents_aligned_ + size_t size_aligned_; + // size of contents. + size_t section_size_; + const typename ElfArch::Shdr header_; +}; + +// An iterator over symbols in a given section. It handles walking +// through the entries in the specified section and mapping symbol +// entries to their names in the appropriate string table (in +// another section). +template<class ElfArch> +class SymbolIterator { + public: + SymbolIterator(ElfReaderImpl<ElfArch> *reader, + typename ElfArch::Word section_type) + : symbol_section_(reader->GetSectionByType(section_type)), + string_section_(NULL), + num_symbols_in_section_(0), + symbol_within_section_(0) { + + // If this section type doesn't exist, leave + // num_symbols_in_section_ as zero, so this iterator is already + // done(). + if (symbol_section_ != NULL) { + num_symbols_in_section_ = symbol_section_->header().sh_size / + symbol_section_->header().sh_entsize; + + // Symbol sections have sh_link set to the section number of + // the string section containing the symbol names. + string_section_ = reader->GetSection(symbol_section_->header().sh_link); + } + } + + // Return true iff we have passed all symbols in this section. + bool done() const { + return symbol_within_section_ >= num_symbols_in_section_; + } + + // Advance to the next symbol in this section. + // REQUIRES: !done() + void Next() { ++symbol_within_section_; } + + // Return a pointer to the current symbol. + // REQUIRES: !done() + const typename ElfArch::Sym *GetSymbol() const { + return reinterpret_cast<const typename ElfArch::Sym*>( + symbol_section_->GetOffset(symbol_within_section_ * + symbol_section_->header().sh_entsize)); + } + + // Return the name of the current symbol, NULL if it has none. + // REQUIRES: !done() + const char *GetSymbolName() const { + int name_offset = GetSymbol()->st_name; + if (name_offset == 0) + return NULL; + return string_section_->GetOffset(name_offset); + } + + int GetCurrentSymbolIndex() const { + return symbol_within_section_; + } + + private: + const ElfSectionReader<ElfArch> *const symbol_section_; + const ElfSectionReader<ElfArch> *string_section_; + int num_symbols_in_section_; + int symbol_within_section_; +}; + + +// Copied from strings/strutil.h. Per chatham, +// this library should not depend on strings. + +static inline bool MyHasSuffixString(const string& str, const string& suffix) { + int len = str.length(); + int suflen = suffix.length(); + return (suflen <= len) && (str.compare(len-suflen, suflen, suffix) == 0); +} + + +// ElfReader loads an ELF binary and can provide information about its +// contents. It is most useful for matching addresses to function +// names. It does not understand debugging formats (eg dwarf2), so it +// can't print line numbers. It takes a path to an elf file and a +// readable file descriptor for that file, which it does not assume +// ownership of. +template<class ElfArch> +class ElfReaderImpl { + public: + explicit ElfReaderImpl(const string &path, int fd) + : path_(path), + fd_(fd), + section_headers_(NULL), + program_headers_(NULL), + opd_section_(NULL), + base_for_text_(0), + plts_supported_(false), + plt_code_size_(0), + plt0_size_(0), + visited_relocation_entries_(false) { + string error; + is_dwp_ = MyHasSuffixString(path, ".dwp"); + ParseHeaders(fd, path); + // Currently we need some extra information for PowerPC64 binaries + // including a way to read the .opd section for function descriptors and a + // way to find the linked base for function symbols. + if (header_.e_machine == EM_PPC64) { + // "opd_section_" must always be checked for NULL before use. + opd_section_ = GetSectionInfoByName(".opd", &opd_info_); + for (unsigned int k = 0u; k < GetNumSections(); ++k) { + const char *name = GetSectionName(section_headers_[k].sh_name); + if (strncmp(name, ".text", strlen(".text")) == 0) { + base_for_text_ = + section_headers_[k].sh_addr - section_headers_[k].sh_offset; + break; + } + } + } + // Turn on PLTs. + if (header_.e_machine == EM_386 || header_.e_machine == EM_X86_64) { + plt_code_size_ = kX86PLTCodeSize; + plt0_size_ = kX86PLT0Size; + plts_supported_ = true; + } else if (header_.e_machine == EM_ARM) { + plt_code_size_ = kARMPLTCodeSize; + plt0_size_ = kARMPLT0Size; + plts_supported_ = true; + } else if (header_.e_machine == EM_AARCH64) { + plt_code_size_ = kAARCH64PLTCodeSize; + plt0_size_ = kAARCH64PLT0Size; + plts_supported_ = true; + } + } + + ~ElfReaderImpl() { + for (unsigned int i = 0u; i < sections_.size(); ++i) + delete sections_[i]; + delete [] section_headers_; + delete [] program_headers_; + } + + // Examine the headers of the file and return whether the file looks + // like an ELF file for this architecture. Takes an already-open + // file descriptor for the candidate file, reading in the prologue + // to see if the ELF file appears to match the current + // architecture. If error is non-NULL, it will be set with a reason + // in case of failure. + static bool IsArchElfFile(int fd, string *error) { + unsigned char header[EI_NIDENT]; + if (pread(fd, header, sizeof(header), 0) != sizeof(header)) { + if (error != NULL) *error = "Could not read header"; + return false; + } + + if (memcmp(header, ELFMAG, SELFMAG) != 0) { + if (error != NULL) *error = "Missing ELF magic"; + return false; + } + + if (header[EI_CLASS] != ElfArch::kElfClass) { + if (error != NULL) *error = "Different word size"; + return false; + } + + int endian = 0; + if (header[EI_DATA] == ELFDATA2LSB) + endian = __LITTLE_ENDIAN; + else if (header[EI_DATA] == ELFDATA2MSB) + endian = __BIG_ENDIAN; + if (endian != __BYTE_ORDER) { + if (error != NULL) *error = "Different byte order"; + return false; + } + + return true; + } + + // Return true if we can use this symbol in Address-to-Symbol map. + bool CanUseSymbol(const char *name, const typename ElfArch::Sym *sym) { + // For now we only save FUNC and NOTYPE symbols. For now we just + // care about functions, but some functions written in assembler + // don't have a proper ELF type attached to them, so we store + // NOTYPE symbols as well. The remaining significant type is + // OBJECT (eg global variables), which represent about 25% of + // the symbols in a typical google3 binary. + if (ElfArch::Type(sym) != STT_FUNC && + ElfArch::Type(sym) != STT_NOTYPE) { + return false; + } + + // Target specific filtering. + switch (header_.e_machine) { + case EM_AARCH64: + case EM_ARM: + // Filter out '$x' special local symbols used by tools + return name[0] != '$' || ElfArch::Bind(sym) != STB_LOCAL; + case EM_X86_64: + // Filter out read-only constants like .LC123. + return name[0] != '.' || ElfArch::Bind(sym) != STB_LOCAL; + default: + return true; + } + } + + // Iterate over the symbols in a section, either SHT_DYNSYM or + // SHT_SYMTAB. Add all symbols to the given SymbolMap. + /* + void GetSymbolPositions(SymbolMap *symbols, + typename ElfArch::Word section_type, + uint64 mem_offset, + uint64 file_offset) { + // This map is used to filter out "nested" functions. + // See comment below. + AddrToSymMap addr_to_sym_map; + for (SymbolIterator<ElfArch> it(this, section_type); + !it.done(); it.Next()) { + const char *name = it.GetSymbolName(); + if (name == NULL) + continue; + const typename ElfArch::Sym *sym = it.GetSymbol(); + if (CanUseSymbol(name, sym)) { + const int sec = sym->st_shndx; + + // We don't support special section indices. The most common + // is SHN_ABS, for absolute symbols used deep in the bowels of + // glibc. Also ignore any undefined symbols. + if (sec == SHN_UNDEF || + (sec >= SHN_LORESERVE && sec <= SHN_HIRESERVE)) { + continue; + } + + const typename ElfArch::Shdr& hdr = section_headers_[sec]; + + // Adjust for difference between where we expected to mmap + // this section, and where it was actually mmapped. + const int64 expected_base = hdr.sh_addr - hdr.sh_offset; + const int64 real_base = mem_offset - file_offset; + const int64 adjust = real_base - expected_base; + + uint64 start = sym->st_value + adjust; + + // Adjust function symbols for PowerPC64 by dereferencing and adjusting + // the function descriptor to get the function address. + if (header_.e_machine == EM_PPC64 && ElfArch::Type(sym) == STT_FUNC) { + const uint64 opd_addr = + AdjustPPC64FunctionDescriptorSymbolValue(sym->st_value); + // Only adjust the returned value if the function address was found. + if (opd_addr != sym->st_value) { + const int64 adjust_function_symbols = + real_base - base_for_text_; + start = opd_addr + adjust_function_symbols; + } + } + + addr_to_sym_map.push_back(std::make_pair(start, sym)); + } + } + std::sort(addr_to_sym_map.begin(), addr_to_sym_map.end(), &AddrToSymSorter); + addr_to_sym_map.erase(std::unique(addr_to_sym_map.begin(), + addr_to_sym_map.end(), &AddrToSymEquals), + addr_to_sym_map.end()); + + // Squeeze out any "nested functions". + // Nested functions are not allowed in C, but libc plays tricks. + // + // For example, here is disassembly of /lib64/tls/libc-2.3.5.so: + // 0x00000000000aa380 <read+0>: cmpl $0x0,0x2781b9(%rip) + // 0x00000000000aa387 <read+7>: jne 0xaa39b <read+27> + // 0x00000000000aa389 <__read_nocancel+0>: mov $0x0,%rax + // 0x00000000000aa390 <__read_nocancel+7>: syscall + // 0x00000000000aa392 <__read_nocancel+9>: cmp $0xfffffffffffff001,%rax + // 0x00000000000aa398 <__read_nocancel+15>: jae 0xaa3ef <read+111> + // 0x00000000000aa39a <__read_nocancel+17>: retq + // 0x00000000000aa39b <read+27>: sub $0x28,%rsp + // 0x00000000000aa39f <read+31>: mov %rdi,0x8(%rsp) + // ... + // Without removing __read_nocancel, symbolizer will return NULL + // given e.g. 0xaa39f (because the lower bound is __read_nocancel, + // but 0xaa39f is beyond its end. + if (addr_to_sym_map.empty()) { + return; + } + const ElfSectionReader<ElfArch> *const symbol_section = + this->GetSectionByType(section_type); + const ElfSectionReader<ElfArch> *const string_section = + this->GetSection(symbol_section->header().sh_link); + + typename AddrToSymMap::iterator curr = addr_to_sym_map.begin(); + // Always insert the first symbol. + symbols->AddSymbol(string_section->GetOffset(curr->second->st_name), + curr->first, curr->second->st_size); + typename AddrToSymMap::iterator prev = curr++; + for (; curr != addr_to_sym_map.end(); ++curr) { + const uint64 prev_addr = prev->first; + const uint64 curr_addr = curr->first; + const typename ElfArch::Sym *const prev_sym = prev->second; + const typename ElfArch::Sym *const curr_sym = curr->second; + if (prev_addr + prev_sym->st_size <= curr_addr || + // The next condition is true if two symbols overlap like this: + // + // Previous symbol |----------------------------| + // Current symbol |-------------------------------| + // + // These symbols are not found in google3 codebase, but in + // jdk1.6.0_01_gg1/jre/lib/i386/server/libjvm.so. + // + // 0619e040 00000046 t CardTableModRefBS::write_region_work() + // 0619e070 00000046 t CardTableModRefBS::write_ref_array_work() + // + // We allow overlapped symbols rather than ignore these. + // Due to the way SymbolMap::GetSymbolAtPosition() works, + // lookup for any address in [curr_addr, curr_addr + its size) + // (e.g. 0619e071) will produce the current symbol, + // which is the desired outcome. + prev_addr + prev_sym->st_size < curr_addr + curr_sym->st_size) { + const char *name = string_section->GetOffset(curr_sym->st_name); + symbols->AddSymbol(name, curr_addr, curr_sym->st_size); + prev = curr; + } else { + // Current symbol is "nested" inside previous one like this: + // + // Previous symbol |----------------------------| + // Current symbol |---------------------| + // + // This happens within glibc, e.g. __read_nocancel is nested + // "inside" __read. Ignore "inner" symbol. + //DCHECK_LE(curr_addr + curr_sym->st_size, + // prev_addr + prev_sym->st_size); + ; + } + } + } +*/ + + void VisitSymbols(typename ElfArch::Word section_type, + ElfReader::SymbolSink *sink) { + VisitSymbols(section_type, sink, -1, -1, false); + } + + void VisitSymbols(typename ElfArch::Word section_type, + ElfReader::SymbolSink *sink, + int symbol_binding, + int symbol_type, + bool get_raw_symbol_values) { + for (SymbolIterator<ElfArch> it(this, section_type); + !it.done(); it.Next()) { + const char *name = it.GetSymbolName(); + if (!name) continue; + const typename ElfArch::Sym *sym = it.GetSymbol(); + if ((symbol_binding < 0 || ElfArch::Bind(sym) == symbol_binding) && + (symbol_type < 0 || ElfArch::Type(sym) == symbol_type)) { + typename ElfArch::Sym symbol = *sym; + // Add a PLT symbol in addition to the main undefined symbol. + // Only do this for SHT_DYNSYM, because PLT symbols are dynamic. + int symbol_index = it.GetCurrentSymbolIndex(); + // TODO(dthomson): Can be removed once all Java code is using the + // Google3 launcher. + if (section_type == SHT_DYNSYM && + static_cast<unsigned int>(symbol_index) < symbols_plt_offsets_.size() && + symbols_plt_offsets_[symbol_index] != 0) { + string plt_name = string(name) + kPLTFunctionSuffix; + if (plt_function_names_[symbol_index].empty()) { + plt_function_names_[symbol_index] = plt_name; + } else if (plt_function_names_[symbol_index] != plt_name) { + ; + } + sink->AddSymbol(plt_function_names_[symbol_index].c_str(), + symbols_plt_offsets_[it.GetCurrentSymbolIndex()], + plt_code_size_); + } + if (!get_raw_symbol_values) + AdjustSymbolValue(&symbol); + sink->AddSymbol(name, symbol.st_value, symbol.st_size); + } + } + } + + void VisitRelocationEntries() { + if (visited_relocation_entries_) { + return; + } + visited_relocation_entries_ = true; + + if (!plts_supported_) { + return; + } + // First determine if PLTs exist. If not, then there is nothing to do. + ElfReader::SectionInfo plt_section_info; + const char* plt_section = + GetSectionInfoByName(kElfPLTSectionName, &plt_section_info); + if (!plt_section) { + return; + } + if (plt_section_info.size == 0) { + return; + } + + // The PLTs could be referenced by either a Rel or Rela (Rel with Addend) + // section. + ElfReader::SectionInfo rel_section_info; + ElfReader::SectionInfo rela_section_info; + const char* rel_section = + GetSectionInfoByName(kElfPLTRelSectionName, &rel_section_info); + const char* rela_section = + GetSectionInfoByName(kElfPLTRelaSectionName, &rela_section_info); + + const typename ElfArch::Rel* rel = + reinterpret_cast<const typename ElfArch::Rel*>(rel_section); + const typename ElfArch::Rela* rela = + reinterpret_cast<const typename ElfArch::Rela*>(rela_section); + + if (!rel_section && !rela_section) { + return; + } + + // Use either Rel or Rela section, depending on which one exists. + size_t section_size = rel_section ? rel_section_info.size + : rela_section_info.size; + size_t entry_size = rel_section ? sizeof(typename ElfArch::Rel) + : sizeof(typename ElfArch::Rela); + + // Determine the number of entries in the dynamic symbol table. + ElfReader::SectionInfo dynsym_section_info; + const char* dynsym_section = + GetSectionInfoByName(kElfDynSymSectionName, &dynsym_section_info); + // The dynsym section might not exist, or it might be empty. In either case + // there is nothing to be done so return. + if (!dynsym_section || dynsym_section_info.size == 0) { + return; + } + size_t num_dynamic_symbols = + dynsym_section_info.size / dynsym_section_info.entsize; + symbols_plt_offsets_.resize(num_dynamic_symbols, 0); + + // TODO(dthomson): Can be removed once all Java code is using the + // Google3 launcher. + // Make storage room for PLT function name strings. + plt_function_names_.resize(num_dynamic_symbols); + + for (size_t i = 0; i < section_size / entry_size; ++i) { + // Determine symbol index from the |r_info| field. + int sym_index = ElfArch::r_sym(rel_section ? rel[i].r_info + : rela[i].r_info); + if (static_cast<unsigned int>(sym_index) >= symbols_plt_offsets_.size()) { + continue; + } + symbols_plt_offsets_[sym_index] = + plt_section_info.addr + plt0_size_ + i * plt_code_size_; + } + } + + // Return an ElfSectionReader for the first section of the given + // type by iterating through all section headers. Returns NULL if + // the section type is not found. + const ElfSectionReader<ElfArch> *GetSectionByType( + typename ElfArch::Word section_type) { + for (unsigned int k = 0u; k < GetNumSections(); ++k) { + if (section_headers_[k].sh_type == section_type) { + return GetSection(k); + } + } + return NULL; + } + + // Return the name of section "shndx". Returns NULL if the section + // is not found. + const char *GetSectionNameByIndex(int shndx) { + return GetSectionName(section_headers_[shndx].sh_name); + } + + // Return a pointer to section "shndx", and store the size in + // "size". Returns NULL if the section is not found. + const char *GetSectionContentsByIndex(int shndx, size_t *size) { + const ElfSectionReader<ElfArch> *section = GetSection(shndx); + if (section != NULL) { + *size = section->section_size(); + return section->contents(); + } + return NULL; + } + + // Return a pointer to the first section of the given name by + // iterating through all section headers, and store the size in + // "size". Returns NULL if the section name is not found. + const char *GetSectionContentsByName(const string §ion_name, + size_t *size) { + for (unsigned int k = 0u; k < GetNumSections(); ++k) { + // When searching for sections in a .dwp file, the sections + // we're looking for will always be at the end of the section + // table, so reverse the direction of iteration. + int shndx = is_dwp_ ? GetNumSections() - k - 1 : k; + const char *name = GetSectionName(section_headers_[shndx].sh_name); + if (name != NULL && ElfReader::SectionNamesMatch(section_name, name)) { + const ElfSectionReader<ElfArch> *section = GetSection(shndx); + if (section == NULL) { + return NULL; + } else { + *size = section->section_size(); + return section->contents(); + } + } + } + return NULL; + } + + // This is like GetSectionContentsByName() but it returns a lot of extra + // information about the section. + const char *GetSectionInfoByName(const string §ion_name, + ElfReader::SectionInfo *info) { + for (unsigned int k = 0u; k < GetNumSections(); ++k) { + // When searching for sections in a .dwp file, the sections + // we're looking for will always be at the end of the section + // table, so reverse the direction of iteration. + int shndx = is_dwp_ ? GetNumSections() - k - 1 : k; + const char *name = GetSectionName(section_headers_[shndx].sh_name); + if (name != NULL && ElfReader::SectionNamesMatch(section_name, name)) { + const ElfSectionReader<ElfArch> *section = GetSection(shndx); + if (section == NULL) { + return NULL; + } else { + info->type = section->header().sh_type; + info->flags = section->header().sh_flags; + info->addr = section->header().sh_addr; + info->offset = section->header().sh_offset; + info->size = section->header().sh_size; + info->link = section->header().sh_link; + info->info = section->header().sh_info; + info->addralign = section->header().sh_addralign; + info->entsize = section->header().sh_entsize; + return section->contents(); + } + } + } + return NULL; + } + + // p_vaddr of the first PT_LOAD segment (if any), or 0 if no PT_LOAD + // segments are present. This is the address an ELF image was linked + // (by static linker) to be loaded at. Usually (but not always) 0 for + // shared libraries and position-independent executables. + uint64 VaddrOfFirstLoadSegment() const { + // Relocatable objects (of type ET_REL) do not have LOAD segments. + if (header_.e_type == ET_REL) { + return 0; + } + for (int i = 0; i < GetNumProgramHeaders(); ++i) { + if (program_headers_[i].p_type == PT_LOAD) { + return program_headers_[i].p_vaddr; + } + } + return 0; + } + + // According to the LSB ("ELF special sections"), sections with debug + // info are prefixed by ".debug". The names are not specified, but they + // look like ".debug_line", ".debug_info", etc. + bool HasDebugSections() { + // Debug sections are likely to be near the end, so reverse the + // direction of iteration. + for (int k = GetNumSections() - 1; k >= 0; --k) { + const char *name = GetSectionName(section_headers_[k].sh_name); + if (strncmp(name, ".debug", strlen(".debug")) == 0) return true; + if (strncmp(name, ".zdebug", strlen(".zdebug")) == 0) return true; + } + return false; + } + + bool IsDynamicSharedObject() const { + return header_.e_type == ET_DYN; + } + + // Return the number of sections. + uint64_t GetNumSections() const { + if (HasManySections()) + return first_section_header_.sh_size; + return header_.e_shnum; + } + + private: + typedef vector<pair<uint64, const typename ElfArch::Sym *> > AddrToSymMap; + + static bool AddrToSymSorter(const typename AddrToSymMap::value_type& lhs, + const typename AddrToSymMap::value_type& rhs) { + return lhs.first < rhs.first; + } + + static bool AddrToSymEquals(const typename AddrToSymMap::value_type& lhs, + const typename AddrToSymMap::value_type& rhs) { + return lhs.first == rhs.first; + } + + // Does this ELF file have too many sections to fit in the program header? + bool HasManySections() const { + return header_.e_shnum == SHN_UNDEF; + } + + // Return the number of program headers. + int GetNumProgramHeaders() const { + if (HasManySections() && header_.e_phnum == 0xffff && + first_section_header_.sh_info != 0) + return first_section_header_.sh_info; + return header_.e_phnum; + } + + // Return the index of the string table. + int GetStringTableIndex() const { + if (HasManySections()) { + if (header_.e_shstrndx == 0xffff) + return first_section_header_.sh_link; + else if (header_.e_shstrndx >= GetNumSections()) + return 0; + } + return header_.e_shstrndx; + } + + // Given an offset into the section header string table, return the + // section name. + const char *GetSectionName(typename ElfArch::Word sh_name) { + const ElfSectionReader<ElfArch> *shstrtab = + GetSection(GetStringTableIndex()); + if (shstrtab != NULL) { + return shstrtab->GetOffset(sh_name); + } + return NULL; + } + + // Return an ElfSectionReader for the given section. The reader will + // be freed when this object is destroyed. + const ElfSectionReader<ElfArch> *GetSection(int num) { + const char *name; + // Hard-coding the name for the section-name string table prevents + // infinite recursion. + if (num == GetStringTableIndex()) + name = ".shstrtab"; + else + name = GetSectionNameByIndex(num); + ElfSectionReader<ElfArch> *& reader = sections_[num]; + if (reader == NULL) + reader = new ElfSectionReader<ElfArch>(name, path_, fd_, + section_headers_[num]); + return reader; + } + + // Parse out the overall header information from the file and assert + // that it looks sane. This contains information like the magic + // number and target architecture. + bool ParseHeaders(int fd, const string &path) { + // Read in the global ELF header. + if (pread(fd, &header_, sizeof(header_), 0) != sizeof(header_)) { + return false; + } + + // Must be an executable, dynamic shared object or relocatable object + if (header_.e_type != ET_EXEC && + header_.e_type != ET_DYN && + header_.e_type != ET_REL) { + return false; + } + // Need a section header. + if (header_.e_shoff == 0) { + return false; + } + + if (header_.e_shnum == SHN_UNDEF) { + // The number of sections in the program header is only a 16-bit value. In + // the event of overflow (greater than SHN_LORESERVE sections), e_shnum + // will read SHN_UNDEF and the true number of section header table entries + // is found in the sh_size field of the first section header. + // See: http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html + if (pread(fd, &first_section_header_, sizeof(first_section_header_), + header_.e_shoff) != sizeof(first_section_header_)) { + return false; + } + } + + // Dynamically allocate enough space to store the section headers + // and read them out of the file. + const int section_headers_size = + GetNumSections() * sizeof(*section_headers_); + section_headers_ = new typename ElfArch::Shdr[section_headers_size]; + if (pread(fd, section_headers_, section_headers_size, header_.e_shoff) != + section_headers_size) { + return false; + } + + // Dynamically allocate enough space to store the program headers + // and read them out of the file. + //const int program_headers_size = + // GetNumProgramHeaders() * sizeof(*program_headers_); + program_headers_ = new typename ElfArch::Phdr[GetNumProgramHeaders()]; + + // Presize the sections array for efficiency. + sections_.resize(GetNumSections(), NULL); + return true; + } + + // Given the "value" of a function descriptor return the address of the + // function (i.e. the dereferenced value). Otherwise return "value". + uint64 AdjustPPC64FunctionDescriptorSymbolValue(uint64 value) { + if (opd_section_ != NULL && + opd_info_.addr <= value && + value < opd_info_.addr + opd_info_.size) { + uint64 offset = value - opd_info_.addr; + return (*reinterpret_cast<const uint64*>(opd_section_ + offset)); + } + return value; + } + + void AdjustSymbolValue(typename ElfArch::Sym* sym) { + switch (header_.e_machine) { + case EM_ARM: + // For ARM architecture, if the LSB of the function symbol offset is set, + // it indicates a Thumb function. This bit should not be taken literally. + // Clear it. + if (ElfArch::Type(sym) == STT_FUNC) + sym->st_value = AdjustARMThumbSymbolValue(sym->st_value); + break; + case EM_386: + // No adjustment needed for Intel x86 architecture. However, explicitly + // define this case as we use it quite often. + break; + case EM_PPC64: + // PowerPC64 currently has function descriptors as part of the ABI. + // Function symbols need to be adjusted accordingly. + if (ElfArch::Type(sym) == STT_FUNC) + sym->st_value = AdjustPPC64FunctionDescriptorSymbolValue(sym->st_value); + break; + default: + break; + } + } + + friend class SymbolIterator<ElfArch>; + + // The file we're reading. + const string path_; + // Open file descriptor for path_. Not owned by this object. + const int fd_; + + // The global header of the ELF file. + typename ElfArch::Ehdr header_; + + // The header of the first section. This may be used to supplement the ELF + // file header. + typename ElfArch::Shdr first_section_header_; + + // Array of GetNumSections() section headers, allocated when we read + // in the global header. + typename ElfArch::Shdr *section_headers_; + + // Array of GetNumProgramHeaders() program headers, allocated when we read + // in the global header. + typename ElfArch::Phdr *program_headers_; + + // An array of pointers to ElfSectionReaders. Sections are + // mmaped as they're needed and not released until this object is + // destroyed. + vector<ElfSectionReader<ElfArch>*> sections_; + + // For PowerPC64 we need to keep track of function descriptors when looking up + // values for funtion symbols values. Function descriptors are kept in the + // .opd section and are dereferenced to find the function address. + ElfReader::SectionInfo opd_info_; + const char *opd_section_; // Must be checked for NULL before use. + int64 base_for_text_; + + // Read PLT-related sections for the current architecture. + bool plts_supported_; + // Code size of each PLT function for the current architecture. + size_t plt_code_size_; + // Size of the special first entry in the .plt section that calls the runtime + // loader resolution routine, and that all other entries jump to when doing + // lazy symbol binding. + size_t plt0_size_; + + // Maps a dynamic symbol index to a PLT offset. + // The vector entry index is the dynamic symbol index. + std::vector<uint64> symbols_plt_offsets_; + + // Container for PLT function name strings. These strings are passed by + // reference to SymbolSink::AddSymbol() so they need to be stored somewhere. + std::vector<string> plt_function_names_; + + bool visited_relocation_entries_; + + // True if this is a .dwp file. + bool is_dwp_; +}; + +ElfReader::ElfReader(const string &path) + : path_(path), fd_(-1), impl32_(NULL), impl64_(NULL) { + // linux 2.6.XX kernel can show deleted files like this: + // /var/run/nscd/dbYLJYaE (deleted) + // and the kernel-supplied vdso and vsyscall mappings like this: + // [vdso] + // [vsyscall] + if (MyHasSuffixString(path, " (deleted)")) + return; + if (path == "[vdso]") + return; + if (path == "[vsyscall]") + return; + + fd_ = open(path.c_str(), O_RDONLY); +} + +ElfReader::~ElfReader() { + if (fd_ != -1) + close(fd_); + if (impl32_ != NULL) + delete impl32_; + if (impl64_ != NULL) + delete impl64_; +} + + +// The only word-size specific part of this file is IsNativeElfFile(). +#if ULONG_MAX == 0xffffffff +#define NATIVE_ELF_ARCH Elf32 +#elif ULONG_MAX == 0xffffffffffffffff +#define NATIVE_ELF_ARCH Elf64 +#else +#error "Invalid word size" +#endif + +template <typename ElfArch> +static bool IsElfFile(const int fd, const string &path) { + if (fd < 0) + return false; + if (!ElfReaderImpl<ElfArch>::IsArchElfFile(fd, NULL)) { + // No error message here. IsElfFile gets called many times. + return false; + } + return true; +} + +bool ElfReader::IsNativeElfFile() const { + return IsElfFile<NATIVE_ELF_ARCH>(fd_, path_); +} + +bool ElfReader::IsElf32File() const { + return IsElfFile<Elf32>(fd_, path_); +} + +bool ElfReader::IsElf64File() const { + return IsElfFile<Elf64>(fd_, path_); +} + +/* +void ElfReader::AddSymbols(SymbolMap *symbols, + uint64 mem_offset, uint64 file_offset, + uint64 length) { + if (fd_ < 0) + return; + // TODO(chatham): Actually use the information about file offset and + // the length of the mapped section. On some machines the data + // section gets mapped as executable, and we'll end up reading the + // file twice and getting some of the offsets wrong. + if (IsElf32File()) { + GetImpl32()->GetSymbolPositions(symbols, SHT_SYMTAB, + mem_offset, file_offset); + GetImpl32()->GetSymbolPositions(symbols, SHT_DYNSYM, + mem_offset, file_offset); + } else if (IsElf64File()) { + GetImpl64()->GetSymbolPositions(symbols, SHT_SYMTAB, + mem_offset, file_offset); + GetImpl64()->GetSymbolPositions(symbols, SHT_DYNSYM, + mem_offset, file_offset); + } +} +*/ + +void ElfReader::VisitSymbols(ElfReader::SymbolSink *sink) { + VisitSymbols(sink, -1, -1); +} + +void ElfReader::VisitSymbols(ElfReader::SymbolSink *sink, + int symbol_binding, + int symbol_type) { + VisitSymbols(sink, symbol_binding, symbol_type, false); +} + +void ElfReader::VisitSymbols(ElfReader::SymbolSink *sink, + int symbol_binding, + int symbol_type, + bool get_raw_symbol_values) { + if (IsElf32File()) { + GetImpl32()->VisitRelocationEntries(); + GetImpl32()->VisitSymbols(SHT_SYMTAB, sink, symbol_binding, symbol_type, + get_raw_symbol_values); + GetImpl32()->VisitSymbols(SHT_DYNSYM, sink, symbol_binding, symbol_type, + get_raw_symbol_values); + } else if (IsElf64File()) { + GetImpl64()->VisitRelocationEntries(); + GetImpl64()->VisitSymbols(SHT_SYMTAB, sink, symbol_binding, symbol_type, + get_raw_symbol_values); + GetImpl64()->VisitSymbols(SHT_DYNSYM, sink, symbol_binding, symbol_type, + get_raw_symbol_values); + } +} + +uint64 ElfReader::VaddrOfFirstLoadSegment() { + if (IsElf32File()) { + return GetImpl32()->VaddrOfFirstLoadSegment(); + } else if (IsElf64File()) { + return GetImpl64()->VaddrOfFirstLoadSegment(); + } else { + return 0; + } +} + +const char *ElfReader::GetSectionName(int shndx) { + if (shndx < 0 || static_cast<unsigned int>(shndx) >= GetNumSections()) return NULL; + if (IsElf32File()) { + return GetImpl32()->GetSectionNameByIndex(shndx); + } else if (IsElf64File()) { + return GetImpl64()->GetSectionNameByIndex(shndx); + } else { + return NULL; + } +} + +uint64 ElfReader::GetNumSections() { + if (IsElf32File()) { + return GetImpl32()->GetNumSections(); + } else if (IsElf64File()) { + return GetImpl64()->GetNumSections(); + } else { + return 0; + } +} + +const char *ElfReader::GetSectionByIndex(int shndx, size_t *size) { + if (IsElf32File()) { + return GetImpl32()->GetSectionContentsByIndex(shndx, size); + } else if (IsElf64File()) { + return GetImpl64()->GetSectionContentsByIndex(shndx, size); + } else { + return NULL; + } +} + +const char *ElfReader::GetSectionByName(const string §ion_name, + size_t *size) { + if (IsElf32File()) { + return GetImpl32()->GetSectionContentsByName(section_name, size); + } else if (IsElf64File()) { + return GetImpl64()->GetSectionContentsByName(section_name, size); + } else { + return NULL; + } +} + +const char *ElfReader::GetSectionInfoByName(const string §ion_name, + SectionInfo *info) { + if (IsElf32File()) { + return GetImpl32()->GetSectionInfoByName(section_name, info); + } else if (IsElf64File()) { + return GetImpl64()->GetSectionInfoByName(section_name, info); + } else { + return NULL; + } +} + +bool ElfReader::SectionNamesMatch(const string &name, const string &sh_name) { + if ((name.find(".debug_", 0) == 0) && (sh_name.find(".zdebug_", 0) == 0)) { + const string name_suffix(name, strlen(".debug_")); + const string sh_name_suffix(sh_name, strlen(".zdebug_")); + return name_suffix == sh_name_suffix; + } + return name == sh_name; +} + +bool ElfReader::IsDynamicSharedObject() { + if (IsElf32File()) { + return GetImpl32()->IsDynamicSharedObject(); + } else if (IsElf64File()) { + return GetImpl64()->IsDynamicSharedObject(); + } else { + return false; + } +} + +ElfReaderImpl<Elf32> *ElfReader::GetImpl32() { + if (impl32_ == NULL) { + impl32_ = new ElfReaderImpl<Elf32>(path_, fd_); + } + return impl32_; +} + +ElfReaderImpl<Elf64> *ElfReader::GetImpl64() { + if (impl64_ == NULL) { + impl64_ = new ElfReaderImpl<Elf64>(path_, fd_); + } + return impl64_; +} + +// Return true if file is an ELF binary of ElfArch, with unstripped +// debug info (debug_only=true) or symbol table (debug_only=false). +// Otherwise, return false. +template <typename ElfArch> +static bool IsNonStrippedELFBinaryImpl(const string &path, const int fd, + bool debug_only) { + if (!ElfReaderImpl<ElfArch>::IsArchElfFile(fd, NULL)) return false; + ElfReaderImpl<ElfArch> elf_reader(path, fd); + return debug_only ? + elf_reader.HasDebugSections() + : (elf_reader.GetSectionByType(SHT_SYMTAB) != NULL); +} + +// Helper for the IsNon[Debug]StrippedELFBinary functions. +static bool IsNonStrippedELFBinaryHelper(const string &path, + bool debug_only) { + const int fd = open(path.c_str(), O_RDONLY); + if (fd == -1) { + return false; + } + + if (IsNonStrippedELFBinaryImpl<Elf32>(path, fd, debug_only) || + IsNonStrippedELFBinaryImpl<Elf64>(path, fd, debug_only)) { + close(fd); + return true; + } + close(fd); + return false; +} + +bool ElfReader::IsNonStrippedELFBinary(const string &path) { + return IsNonStrippedELFBinaryHelper(path, false); +} + +bool ElfReader::IsNonDebugStrippedELFBinary(const string &path) { + return IsNonStrippedELFBinaryHelper(path, true); +} +} // namespace dwarf2reader diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/elf_reader.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf/elf_reader.h new file mode 100644 index 0000000000..b1bb67a882 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/elf_reader.h @@ -0,0 +1,166 @@ +// Copyright 2005 Google Inc. All Rights Reserved. +// Author: chatham@google.com (Andrew Chatham) +// Author: satorux@google.com (Satoru Takabayashi) +// +// ElfReader handles reading in ELF. It can extract symbols from the +// current process, which may be used to symbolize stack traces +// without having to make a potentially dangerous call to fork(). +// +// ElfReader dynamically allocates memory, so it is not appropriate to +// use once the address space might be corrupted, such as during +// process death. +// +// ElfReader supports both 32-bit and 64-bit ELF binaries. + +#ifndef COMMON_DWARF_ELF_READER_H__ +#define COMMON_DWARF_ELF_READER_H__ + +#include <string> +#include <vector> + +#include "common/dwarf/types.h" +#include "common/using_std_string.h" + +using std::vector; +using std::pair; + +namespace dwarf2reader { + +class SymbolMap; +class Elf32; +class Elf64; +template<typename ElfArch> +class ElfReaderImpl; + +class ElfReader { + public: + explicit ElfReader(const string &path); + ~ElfReader(); + + // Parse the ELF prologue of this file and return whether it was + // successfully parsed and matches the word size and byte order of + // the current process. + bool IsNativeElfFile() const; + + // Similar to IsNativeElfFile but checks if it's a 32-bit ELF file. + bool IsElf32File() const; + + // Similar to IsNativeElfFile but checks if it's a 64-bit ELF file. + bool IsElf64File() const; + + // Checks if it's an ELF file of type ET_DYN (shared object file). + bool IsDynamicSharedObject(); + + // Add symbols in the given ELF file into the provided SymbolMap, + // assuming that the file has been loaded into the specified + // offset. + // + // The remaining arguments are typically taken from a + // ProcMapsIterator (base/sysinfo.h) and describe which portions of + // the ELF file are mapped into which parts of memory: + // + // mem_offset - position at which the segment is mapped into memory + // file_offset - offset in the file where the mapping begins + // length - length of the mapped segment + void AddSymbols(SymbolMap *symbols, + uint64 mem_offset, uint64 file_offset, + uint64 length); + + class SymbolSink { + public: + virtual ~SymbolSink() {} + virtual void AddSymbol(const char *name, uint64 address, uint64 size) = 0; + }; + + // Like AddSymbols above, but with no address correction. + // Processes any SHT_SYMTAB section, followed by any SHT_DYNSYM section. + void VisitSymbols(SymbolSink *sink); + + // Like VisitSymbols above, but for a specific symbol binding/type. + // A negative value for the binding and type parameters means any + // binding or type. + void VisitSymbols(SymbolSink *sink, int symbol_binding, int symbol_type); + + // Like VisitSymbols above but can optionally export raw symbol values instead + // of adjusted ones. + void VisitSymbols(SymbolSink *sink, int symbol_binding, int symbol_type, + bool get_raw_symbol_values); + + // p_vaddr of the first PT_LOAD segment (if any), or 0 if no PT_LOAD + // segments are present. This is the address an ELF image was linked + // (by static linker) to be loaded at. Usually (but not always) 0 for + // shared libraries and position-independent executables. + uint64 VaddrOfFirstLoadSegment(); + + // Return the name of section "shndx". Returns NULL if the section + // is not found. + const char *GetSectionName(int shndx); + + // Return the number of sections in the given ELF file. + uint64 GetNumSections(); + + // Get section "shndx" from the given ELF file. On success, return + // the pointer to the section and store the size in "size". + // On error, return NULL. The returned section data is only valid + // until the ElfReader gets destroyed. + const char *GetSectionByIndex(int shndx, size_t *size); + + // Get section with "section_name" (ex. ".text", ".symtab") in the + // given ELF file. On success, return the pointer to the section + // and store the size in "size". On error, return NULL. The + // returned section data is only valid until the ElfReader gets + // destroyed. + const char *GetSectionByName(const string §ion_name, size_t *size); + + // This is like GetSectionByName() but it returns a lot of extra information + // about the section. The SectionInfo structure is almost identical to + // the typedef struct Elf64_Shdr defined in <elf.h>, but is redefined + // here so that the many short macro names in <elf.h> don't have to be + // added to our already cluttered namespace. + struct SectionInfo { + uint32 type; // Section type (SHT_xxx constant from elf.h). + uint64 flags; // Section flags (SHF_xxx constants from elf.h). + uint64 addr; // Section virtual address at execution. + uint64 offset; // Section file offset. + uint64 size; // Section size in bytes. + uint32 link; // Link to another section. + uint32 info; // Additional section information. + uint64 addralign; // Section alignment. + uint64 entsize; // Entry size if section holds a table. + }; + const char *GetSectionInfoByName(const string §ion_name, + SectionInfo *info); + + // Check if "path" is an ELF binary that has not been stripped of symbol + // tables. This function supports both 32-bit and 64-bit ELF binaries. + static bool IsNonStrippedELFBinary(const string &path); + + // Check if "path" is an ELF binary that has not been stripped of debug + // info. Unlike IsNonStrippedELFBinary, this function will return + // false for binaries passed through "strip -S". + static bool IsNonDebugStrippedELFBinary(const string &path); + + // Match a requested section name with the section name as it + // appears in the elf-file, adjusting for compressed debug section + // names. For example, returns true if name == ".debug_abbrev" and + // sh_name == ".zdebug_abbrev" + static bool SectionNamesMatch(const string &name, const string &sh_name); + + private: + // Lazily initialize impl32_ and return it. + ElfReaderImpl<Elf32> *GetImpl32(); + // Ditto for impl64_. + ElfReaderImpl<Elf64> *GetImpl64(); + + // Path of the file we're reading. + const string path_; + // Read-only file descriptor for the file. May be -1 if there was an + // error during open. + int fd_; + ElfReaderImpl<Elf32> *impl32_; + ElfReaderImpl<Elf64> *impl64_; +}; + +} // namespace dwarf2reader + +#endif // COMMON_DWARF_ELF_READER_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/functioninfo.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf/functioninfo.cc new file mode 100644 index 0000000000..ee198fc5d2 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/functioninfo.cc @@ -0,0 +1,234 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// This is a client for the dwarf2reader to extract function and line +// information from the debug info. + +#include <assert.h> +#include <limits.h> +#include <stdio.h> + +#include <map> +#include <queue> +#include <vector> + +#include "common/dwarf/functioninfo.h" +#include "common/dwarf/bytereader.h" +#include "common/scoped_ptr.h" +#include "common/using_std_string.h" + +using google_breakpad::scoped_ptr; + +namespace dwarf2reader { + +CULineInfoHandler::CULineInfoHandler(std::vector<SourceFileInfo>* files, + std::vector<string>* dirs, + LineMap* linemap):linemap_(linemap), + files_(files), + dirs_(dirs) { + // The dirs and files are 1 indexed, so just make sure we put + // nothing in the 0 vector. + assert(dirs->size() == 0); + assert(files->size() == 0); + dirs->push_back(""); + SourceFileInfo s; + s.name = ""; + s.lowpc = ULLONG_MAX; + files->push_back(s); +} + +void CULineInfoHandler::DefineDir(const string& name, uint32 dir_num) { + // These should never come out of order, actually + assert(dir_num == dirs_->size()); + dirs_->push_back(name); +} + +void CULineInfoHandler::DefineFile(const string& name, + int32 file_num, uint32 dir_num, + uint64 mod_time, uint64 length) { + assert(dir_num >= 0); + assert(dir_num < dirs_->size()); + + // These should never come out of order, actually. + if (file_num == (int32)files_->size() || file_num == -1) { + string dir = dirs_->at(dir_num); + + SourceFileInfo s; + s.lowpc = ULLONG_MAX; + + if (dir == "") { + s.name = name; + } else { + s.name = dir + "/" + name; + } + + files_->push_back(s); + } else { + fprintf(stderr, "error in DefineFile"); + } +} + +void CULineInfoHandler::AddLine(uint64 address, uint64 length, uint32 file_num, + uint32 line_num, uint32 column_num) { + if (file_num < files_->size()) { + linemap_->insert( + std::make_pair(address, + std::make_pair(files_->at(file_num).name.c_str(), + line_num))); + + if (address < files_->at(file_num).lowpc) { + files_->at(file_num).lowpc = address; + } + } else { + fprintf(stderr, "error in AddLine"); + } +} + +bool CUFunctionInfoHandler::StartCompilationUnit(uint64 offset, + uint8 address_size, + uint8 offset_size, + uint64 cu_length, + uint8 dwarf_version) { + current_compilation_unit_offset_ = offset; + return true; +} + + +// For function info, we only care about subprograms and inlined +// subroutines. For line info, the DW_AT_stmt_list lives in the +// compile unit tag. + +bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag) { + switch (tag) { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: { + current_function_info_ = new FunctionInfo; + current_function_info_->lowpc = current_function_info_->highpc = 0; + current_function_info_->name = ""; + current_function_info_->line = 0; + current_function_info_->file = ""; + offset_to_funcinfo_->insert(std::make_pair(offset, + current_function_info_)); + }; + // FALLTHROUGH + case DW_TAG_compile_unit: + return true; + default: + return false; + } + return false; +} + +// Only care about the name attribute for functions + +void CUFunctionInfoHandler::ProcessAttributeString(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const string &data) { + if (current_function_info_) { + if (attr == DW_AT_name) + current_function_info_->name = data; + else if (attr == DW_AT_MIPS_linkage_name) + current_function_info_->mangled_name = data; + } +} + +void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + if (attr == DW_AT_stmt_list) { + SectionMap::const_iterator iter = sections_.find("__debug_line"); + assert(iter != sections_.end()); + + scoped_ptr<LineInfo> lireader(new LineInfo(iter->second.first + data, + iter->second.second - data, + reader_, linehandler_)); + lireader->Start(); + } else if (current_function_info_) { + switch (attr) { + case DW_AT_low_pc: + current_function_info_->lowpc = data; + break; + case DW_AT_high_pc: + current_function_info_->highpc = data; + break; + case DW_AT_decl_line: + current_function_info_->line = data; + break; + case DW_AT_decl_file: + current_function_info_->file = files_->at(data).name; + break; + case DW_AT_ranges: + current_function_info_->ranges = data; + break; + default: + break; + } + } +} + +void CUFunctionInfoHandler::ProcessAttributeReference(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + if (current_function_info_) { + switch (attr) { + case DW_AT_specification: { + // Some functions have a "specification" attribute + // which means they were defined elsewhere. The name + // attribute is not repeated, and must be taken from + // the specification DIE. Here we'll assume that + // any DIE referenced in this manner will already have + // been seen, but that's not really required by the spec. + FunctionMap::iterator iter = offset_to_funcinfo_->find(data); + if (iter != offset_to_funcinfo_->end()) { + current_function_info_->name = iter->second->name; + current_function_info_->mangled_name = iter->second->mangled_name; + } else { + // If you hit this, this code probably needs to be rewritten. + fprintf(stderr, + "Error: DW_AT_specification was seen before the referenced " + "DIE! (Looking for DIE at offset %08llx, in DIE at " + "offset %08llx)\n", data, offset); + } + break; + } + default: + break; + } + } +} + +void CUFunctionInfoHandler::EndDIE(uint64 offset) { + if (current_function_info_ && current_function_info_->lowpc) + address_to_funcinfo_->insert(std::make_pair(current_function_info_->lowpc, + current_function_info_)); +} + +} // namespace dwarf2reader diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/functioninfo.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf/functioninfo.h new file mode 100644 index 0000000000..9efae6d4ee --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/functioninfo.h @@ -0,0 +1,190 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + + +// This file contains the definitions for a DWARF2/3 information +// collector that uses the DWARF2/3 reader interface to build a mapping +// of addresses to files, lines, and functions. + +#ifndef COMMON_DWARF_FUNCTIONINFO_H__ +#define COMMON_DWARF_FUNCTIONINFO_H__ + +#include <map> +#include <string> +#include <utility> +#include <vector> + +#include "common/dwarf/dwarf2reader.h" +#include "common/using_std_string.h" + + +namespace dwarf2reader { + +struct FunctionInfo { + // Name of the function + string name; + // Mangled name of the function + string mangled_name; + // File containing this function + string file; + // Line number for start of function. + uint32 line; + // Beginning address for this function + uint64 lowpc; + // End address for this function. + uint64 highpc; + // Ranges offset + uint64 ranges; +}; + +struct SourceFileInfo { + // Name of the source file name + string name; + // Low address of source file name + uint64 lowpc; +}; + +typedef std::map<uint64, FunctionInfo*> FunctionMap; +typedef std::map<uint64, std::pair<string, uint32> > LineMap; + +// This class is a basic line info handler that fills in the dirs, +// file, and linemap passed into it with the data produced from the +// LineInfoHandler. +class CULineInfoHandler: public LineInfoHandler { + public: + + // + CULineInfoHandler(std::vector<SourceFileInfo>* files, + std::vector<string>* dirs, + LineMap* linemap); + virtual ~CULineInfoHandler() { } + + // Called when we define a directory. We just place NAME into dirs_ + // at position DIR_NUM. + virtual void DefineDir(const string& name, uint32 dir_num); + + // Called when we define a filename. We just place + // concat(dirs_[DIR_NUM], NAME) into files_ at position FILE_NUM. + virtual void DefineFile(const string& name, int32 file_num, + uint32 dir_num, uint64 mod_time, uint64 length); + + + // Called when the line info reader has a new line, address pair + // ready for us. ADDRESS is the address of the code, LENGTH is the + // length of its machine code in bytes, FILE_NUM is the file number + // containing the code, LINE_NUM is the line number in that file for + // the code, and COLUMN_NUM is the column number the code starts at, + // if we know it (0 otherwise). + virtual void AddLine(uint64 address, uint64 length, + uint32 file_num, uint32 line_num, uint32 column_num); + + private: + LineMap* linemap_; + std::vector<SourceFileInfo>* files_; + std::vector<string>* dirs_; +}; + +class CUFunctionInfoHandler: public Dwarf2Handler { + public: + CUFunctionInfoHandler(std::vector<SourceFileInfo>* files, + std::vector<string>* dirs, + LineMap* linemap, + FunctionMap* offset_to_funcinfo, + FunctionMap* address_to_funcinfo, + CULineInfoHandler* linehandler, + const SectionMap& sections, + ByteReader* reader) + : files_(files), dirs_(dirs), linemap_(linemap), + offset_to_funcinfo_(offset_to_funcinfo), + address_to_funcinfo_(address_to_funcinfo), + linehandler_(linehandler), sections_(sections), + reader_(reader), current_function_info_(NULL) { } + + virtual ~CUFunctionInfoHandler() { } + + // Start to process a compilation unit at OFFSET from the beginning of the + // .debug_info section. We want to see all compilation units, so we + // always return true. + + virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version); + + // Start to process a DIE at OFFSET from the beginning of the + // .debug_info section. We only care about function related DIE's. + virtual bool StartDIE(uint64 offset, enum DwarfTag tag); + + // Called when we have an attribute with unsigned data to give to + // our handler. The attribute is for the DIE at OFFSET from the + // beginning of the .debug_info section, has a name of ATTR, a form of + // FORM, and the actual data of the attribute is in DATA. + virtual void ProcessAttributeUnsigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + + // Called when we have an attribute with a DIE reference to give to + // our handler. The attribute is for the DIE at OFFSET from the + // beginning of the .debug_info section, has a name of ATTR, a form of + // FORM, and the offset of the referenced DIE from the start of the + // .debug_info section is in DATA. + virtual void ProcessAttributeReference(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + + // Called when we have an attribute with string data to give to + // our handler. The attribute is for the DIE at OFFSET from the + // beginning of the .debug_info section, has a name of ATTR, a form of + // FORM, and the actual data of the attribute is in DATA. + virtual void ProcessAttributeString(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const string& data); + + // Called when finished processing the DIE at OFFSET. + // Because DWARF2/3 specifies a tree of DIEs, you may get starts + // before ends of the previous DIE, as we process children before + // ending the parent. + virtual void EndDIE(uint64 offset); + + private: + std::vector<SourceFileInfo>* files_; + std::vector<string>* dirs_; + LineMap* linemap_; + FunctionMap* offset_to_funcinfo_; + FunctionMap* address_to_funcinfo_; + CULineInfoHandler* linehandler_; + const SectionMap& sections_; + ByteReader* reader_; + FunctionInfo* current_function_info_; + uint64 current_compilation_unit_offset_; +}; + +} // namespace dwarf2reader +#endif // COMMON_DWARF_FUNCTIONINFO_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/line_state_machine.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf/line_state_machine.h new file mode 100644 index 0000000000..0ff72abcfc --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/line_state_machine.h @@ -0,0 +1,61 @@ +// Copyright 2008 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + + +#ifndef COMMON_DWARF_LINE_STATE_MACHINE_H__ +#define COMMON_DWARF_LINE_STATE_MACHINE_H__ + +namespace dwarf2reader { + +// This is the format of a DWARF2/3 line state machine that we process +// opcodes using. There is no need for anything outside the lineinfo +// processor to know how this works. +struct LineStateMachine { + void Reset(bool default_is_stmt) { + file_num = 1; + address = 0; + line_num = 1; + column_num = 0; + is_stmt = default_is_stmt; + basic_block = false; + end_sequence = false; + } + + uint32 file_num; + uint64 address; + uint32 line_num; + uint32 column_num; + bool is_stmt; // stmt means statement. + bool basic_block; + bool end_sequence; +}; + +} // namespace dwarf2reader + + +#endif // COMMON_DWARF_LINE_STATE_MACHINE_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/types.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf/types.h new file mode 100644 index 0000000000..59dda31600 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/types.h @@ -0,0 +1,51 @@ +// Copyright 2008 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + + +// This file contains some typedefs for basic types + + +#ifndef _COMMON_DWARF_TYPES_H__ +#define _COMMON_DWARF_TYPES_H__ + +#include <stdint.h> + +typedef signed char int8; +typedef short int16; +typedef int int32; +typedef long long int64; + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned long long uint64; + +typedef intptr_t intptr; +typedef uintptr_t uintptr; + +#endif // _COMMON_DWARF_TYPES_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf_cfi_to_module.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf_cfi_to_module.cc new file mode 100644 index 0000000000..bd298a2f79 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf_cfi_to_module.cc @@ -0,0 +1,295 @@ +// -*- mode: c++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// Implementation of google_breakpad::DwarfCFIToModule. +// See dwarf_cfi_to_module.h for details. + +#include <sstream> + +#include "common/dwarf_cfi_to_module.h" + +namespace google_breakpad { + +using std::ostringstream; + +vector<string> DwarfCFIToModule::RegisterNames::MakeVector( + const char * const *strings, + size_t size) { + vector<string> names(strings, strings + size); + return names; +} + +vector<string> DwarfCFIToModule::RegisterNames::I386() { + static const char *const names[] = { + "$eax", "$ecx", "$edx", "$ebx", "$esp", "$ebp", "$esi", "$edi", + "$eip", "$eflags", "$unused1", + "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", + "$unused2", "$unused3", + "$xmm0", "$xmm1", "$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", + "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", + "$fcw", "$fsw", "$mxcsr", + "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused4", "$unused5", + "$tr", "$ldtr" + }; + + return MakeVector(names, sizeof(names) / sizeof(names[0])); +} + +vector<string> DwarfCFIToModule::RegisterNames::X86_64() { + static const char *const names[] = { + "$rax", "$rdx", "$rcx", "$rbx", "$rsi", "$rdi", "$rbp", "$rsp", + "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", + "$rip", + "$xmm0","$xmm1","$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", + "$xmm8","$xmm9","$xmm10","$xmm11","$xmm12","$xmm13","$xmm14","$xmm15", + "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", + "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", + "$rflags", + "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused1", "$unused2", + "$fs.base", "$gs.base", "$unused3", "$unused4", + "$tr", "$ldtr", + "$mxcsr", "$fcw", "$fsw" + }; + + return MakeVector(names, sizeof(names) / sizeof(names[0])); +} + +// Per ARM IHI 0040A, section 3.1 +vector<string> DwarfCFIToModule::RegisterNames::ARM() { + static const char *const names[] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "fps", "cpsr", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15", + "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23", + "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7" + }; + + return MakeVector(names, sizeof(names) / sizeof(names[0])); +} + +// Per ARM IHI 0057A, section 3.1 +vector<string> DwarfCFIToModule::RegisterNames::ARM64() { + static const char *const names[] = { + "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", + "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", + "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", + "x24", "x25", "x26", "x27", "x28", "x29", "x30", "sp", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", + "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", + "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", + "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31" + }; + + return MakeVector(names, sizeof(names) / sizeof(names[0])); +} + +vector<string> DwarfCFIToModule::RegisterNames::MIPS() { + static const char* const kRegisterNames[] = { + "$zero", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3", + "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", + "$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7", + "$t8", "$t9", "$k0", "$k1", "$gp", "$sp", "$fp", "$ra", + "$lo", "$hi", "$pc", "$f0", "$f2", "$f3", "$f4", "$f5", + "$f6", "$f7", "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", + "$f14", "$f15", "$f16", "$f17", "$f18", "$f19", "$f20", + "$f21", "$f22", "$f23", "$f24", "$f25", "$f26", "$f27", + "$f28", "$f29", "$f30", "$f31", "$fcsr", "$fir" + }; + + return MakeVector(kRegisterNames, + sizeof(kRegisterNames) / sizeof(kRegisterNames[0])); +} + +bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length, + uint8 version, const string &augmentation, + unsigned return_address) { + assert(!entry_); + + // If dwarf2reader::CallFrameInfo can handle this version and + // augmentation, then we should be okay with that, so there's no + // need to check them here. + + // Get ready to collect entries. + entry_ = new Module::StackFrameEntry; + entry_->address = address; + entry_->size = length; + entry_offset_ = offset; + return_address_ = return_address; + + // Breakpad STACK CFI records must provide a .ra rule, but DWARF CFI + // may not establish any rule for .ra if the return address column + // is an ordinary register, and that register holds the return + // address on entry to the function. So establish an initial .ra + // rule citing the return address register. + if (return_address_ < register_names_.size()) + entry_->initial_rules[ra_name_] = register_names_[return_address_]; + + return true; +} + +string DwarfCFIToModule::RegisterName(int i) { + assert(entry_); + if (i < 0) { + assert(i == kCFARegister); + return cfa_name_; + } + unsigned reg = i; + if (reg == return_address_) + return ra_name_; + + // Ensure that a non-empty name exists for this register value. + if (reg < register_names_.size() && !register_names_[reg].empty()) + return register_names_[reg]; + + reporter_->UnnamedRegister(entry_offset_, reg); + char buf[30]; + sprintf(buf, "unnamed_register%u", reg); + return buf; +} + +void DwarfCFIToModule::Record(Module::Address address, int reg, + const string &rule) { + assert(entry_); + + // Place the name in our global set of strings, and then use the string + // from the set. Even though the assignment looks like a copy, all the + // major string implementations use reference counting internally, + // so the effect is to have all our data structures share copies of rules + // whenever possible. Since register names are drawn from a + // vector<string>, register names are already shared. + string shared_rule = *common_strings_.insert(rule).first; + + // Is this one of this entry's initial rules? + if (address == entry_->address) + entry_->initial_rules[RegisterName(reg)] = shared_rule; + // File it under the appropriate address. + else + entry_->rule_changes[address][RegisterName(reg)] = shared_rule; +} + +bool DwarfCFIToModule::UndefinedRule(uint64 address, int reg) { + reporter_->UndefinedNotSupported(entry_offset_, RegisterName(reg)); + // Treat this as a non-fatal error. + return true; +} + +bool DwarfCFIToModule::SameValueRule(uint64 address, int reg) { + ostringstream s; + s << RegisterName(reg); + Record(address, reg, s.str()); + return true; +} + +bool DwarfCFIToModule::OffsetRule(uint64 address, int reg, + int base_register, long offset) { + ostringstream s; + s << RegisterName(base_register) << " " << offset << " + ^"; + Record(address, reg, s.str()); + return true; +} + +bool DwarfCFIToModule::ValOffsetRule(uint64 address, int reg, + int base_register, long offset) { + ostringstream s; + s << RegisterName(base_register) << " " << offset << " +"; + Record(address, reg, s.str()); + return true; +} + +bool DwarfCFIToModule::RegisterRule(uint64 address, int reg, + int base_register) { + ostringstream s; + s << RegisterName(base_register); + Record(address, reg, s.str()); + return true; +} + +bool DwarfCFIToModule::ExpressionRule(uint64 address, int reg, + const string &expression) { + reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg)); + // Treat this as a non-fatal error. + return true; +} + +bool DwarfCFIToModule::ValExpressionRule(uint64 address, int reg, + const string &expression) { + reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg)); + // Treat this as a non-fatal error. + return true; +} + +bool DwarfCFIToModule::End() { + module_->AddStackFrameEntry(entry_); + entry_ = NULL; + return true; +} + +void DwarfCFIToModule::Reporter::UnnamedRegister(size_t offset, int reg) { + fprintf(stderr, "%s, section '%s': " + "the call frame entry at offset 0x%zx refers to register %d," + " whose name we don't know\n", + file_.c_str(), section_.c_str(), offset, reg); +} + +void DwarfCFIToModule::Reporter::UndefinedNotSupported(size_t offset, + const string ®) { + fprintf(stderr, "%s, section '%s': " + "the call frame entry at offset 0x%zx sets the rule for " + "register '%s' to 'undefined', but the Breakpad symbol file format" + " cannot express this\n", + file_.c_str(), section_.c_str(), offset, reg.c_str()); +} + +void DwarfCFIToModule::Reporter::ExpressionsNotSupported(size_t offset, + const string ®) { + fprintf(stderr, "%s, section '%s': " + "the call frame entry at offset 0x%zx uses a DWARF expression to" + " describe how to recover register '%s', " + " but this translator cannot yet translate DWARF expressions to" + " Breakpad postfix expressions\n", + file_.c_str(), section_.c_str(), offset, reg.c_str()); +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf_cfi_to_module.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf_cfi_to_module.h new file mode 100644 index 0000000000..a5302e15eb --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf_cfi_to_module.h @@ -0,0 +1,202 @@ +// -*- mode: c++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// dwarf_cfi_to_module.h: Define the DwarfCFIToModule class, which +// accepts parsed DWARF call frame info and adds it to a +// google_breakpad::Module object, which can write that information to +// a Breakpad symbol file. + +#ifndef COMMON_LINUX_DWARF_CFI_TO_MODULE_H +#define COMMON_LINUX_DWARF_CFI_TO_MODULE_H + +#include <assert.h> +#include <stdio.h> + +#include <set> +#include <string> +#include <vector> + +#include "common/module.h" +#include "common/dwarf/dwarf2reader.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +using dwarf2reader::CallFrameInfo; +using google_breakpad::Module; +using std::set; +using std::vector; + +// A class that accepts parsed call frame information from the DWARF +// CFI parser and populates a google_breakpad::Module object with the +// contents. +class DwarfCFIToModule: public CallFrameInfo::Handler { + public: + + // DwarfCFIToModule uses an instance of this class to report errors + // detected while converting DWARF CFI to Breakpad STACK CFI records. + class Reporter { + public: + // Create a reporter that writes messages to the standard error + // stream. FILE is the name of the file we're processing, and + // SECTION is the name of the section within that file that we're + // looking at (.debug_frame, .eh_frame, etc.). + Reporter(const string &file, const string §ion) + : file_(file), section_(section) { } + virtual ~Reporter() { } + + // The DWARF CFI entry at OFFSET cites register REG, but REG is not + // covered by the vector of register names passed to the + // DwarfCFIToModule constructor, nor does it match the return + // address column number for this entry. + virtual void UnnamedRegister(size_t offset, int reg); + + // The DWARF CFI entry at OFFSET says that REG is undefined, but the + // Breakpad symbol file format cannot express this. + virtual void UndefinedNotSupported(size_t offset, const string ®); + + // The DWARF CFI entry at OFFSET says that REG uses a DWARF + // expression to find its value, but DwarfCFIToModule is not + // capable of translating DWARF expressions to Breakpad postfix + // expressions. + virtual void ExpressionsNotSupported(size_t offset, const string ®); + + protected: + string file_, section_; + }; + + // Register name tables. If TABLE is a vector returned by one of these + // functions, then TABLE[R] is the name of the register numbered R in + // DWARF call frame information. + class RegisterNames { + public: + // Intel's "x86" or IA-32. + static vector<string> I386(); + + // AMD x86_64, AMD64, Intel EM64T, or Intel 64 + static vector<string> X86_64(); + + // ARM. + static vector<string> ARM(); + + // ARM64, aka AARCH64. + static vector<string> ARM64(); + + // MIPS. + static vector<string> MIPS(); + + private: + // Given STRINGS, an array of C strings with SIZE elements, return an + // equivalent vector<string>. + static vector<string> MakeVector(const char * const *strings, size_t size); + }; + + // Create a handler for the dwarf2reader::CallFrameInfo parser that + // records the stack unwinding information it receives in MODULE. + // + // Use REGISTER_NAMES[I] as the name of register number I; *this + // keeps a reference to the vector, so the vector should remain + // alive for as long as the DwarfCFIToModule does. + // + // Use REPORTER for reporting problems encountered in the conversion + // process. + DwarfCFIToModule(Module *module, const vector<string> ®ister_names, + Reporter *reporter) + : module_(module), register_names_(register_names), reporter_(reporter), + entry_(NULL), return_address_(-1), cfa_name_(".cfa"), ra_name_(".ra") { + } + virtual ~DwarfCFIToModule() { delete entry_; } + + virtual bool Entry(size_t offset, uint64 address, uint64 length, + uint8 version, const string &augmentation, + unsigned return_address); + virtual bool UndefinedRule(uint64 address, int reg); + virtual bool SameValueRule(uint64 address, int reg); + virtual bool OffsetRule(uint64 address, int reg, + int base_register, long offset); + virtual bool ValOffsetRule(uint64 address, int reg, + int base_register, long offset); + virtual bool RegisterRule(uint64 address, int reg, int base_register); + virtual bool ExpressionRule(uint64 address, int reg, + const string &expression); + virtual bool ValExpressionRule(uint64 address, int reg, + const string &expression); + virtual bool End(); + + private: + // Return the name to use for register REG. + string RegisterName(int i); + + // Record RULE for register REG at ADDRESS. + void Record(Module::Address address, int reg, const string &rule); + + // The module to which we should add entries. + Module *module_; + + // Map from register numbers to register names. + const vector<string> ®ister_names_; + + // The reporter to use to report problems. + Reporter *reporter_; + + // The current entry we're constructing. + Module::StackFrameEntry *entry_; + + // The section offset of the current frame description entry, for + // use in error messages. + size_t entry_offset_; + + // The return address column for that entry. + unsigned return_address_; + + // The names of the return address and canonical frame address. Putting + // these here instead of using string literals allows us to share their + // texts in reference-counted string implementations (all the + // popular ones). Many, many rules cite these strings. + string cfa_name_, ra_name_; + + // A set of strings used by this CFI. Before storing a string in one of + // our data structures, insert it into this set, and then use the string + // from the set. + // + // Because string uses reference counting internally, simply using + // strings from this set, even if passed by value, assigned, or held + // directly in structures and containers (map<string, ...>, for example), + // causes those strings to share a single instance of each distinct piece + // of text. + set<string> common_strings_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_DWARF_CFI_TO_MODULE_H diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf_cfi_to_module_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf_cfi_to_module_unittest.cc new file mode 100644 index 0000000000..807d1b20c3 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf_cfi_to_module_unittest.cc @@ -0,0 +1,306 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// dwarf_cfi_to_module_unittest.cc: Tests for google_breakpad::DwarfCFIToModule. + +#include <string> +#include <vector> + +#include "breakpad_googletest_includes.h" +#include "common/dwarf_cfi_to_module.h" +#include "common/using_std_string.h" + +using std::vector; + +using google_breakpad::Module; +using google_breakpad::DwarfCFIToModule; +using testing::ContainerEq; +using testing::Test; +using testing::_; + +struct MockCFIReporter: public DwarfCFIToModule::Reporter { + MockCFIReporter(const string &file, const string §ion) + : Reporter(file, section) { } + MOCK_METHOD2(UnnamedRegister, void(size_t offset, int reg)); + MOCK_METHOD2(UndefinedNotSupported, void(size_t offset, const string ®)); + MOCK_METHOD2(ExpressionsNotSupported, void(size_t offset, const string ®)); +}; + +struct DwarfCFIToModuleFixture { + DwarfCFIToModuleFixture() + : module("module name", "module os", "module arch", "module id"), + reporter("reporter file", "reporter section"), + handler(&module, register_names, &reporter) { + register_names.push_back("reg0"); + register_names.push_back("reg1"); + register_names.push_back("reg2"); + register_names.push_back("reg3"); + register_names.push_back("reg4"); + register_names.push_back("reg5"); + register_names.push_back("reg6"); + register_names.push_back("reg7"); + register_names.push_back("sp"); + register_names.push_back("pc"); + register_names.push_back(""); + + EXPECT_CALL(reporter, UnnamedRegister(_, _)).Times(0); + EXPECT_CALL(reporter, UndefinedNotSupported(_, _)).Times(0); + EXPECT_CALL(reporter, ExpressionsNotSupported(_, _)).Times(0); + } + + Module module; + vector<string> register_names; + MockCFIReporter reporter; + DwarfCFIToModule handler; + vector<Module::StackFrameEntry *> entries; +}; + +class Entry: public DwarfCFIToModuleFixture, public Test { }; + +TEST_F(Entry, Accept) { + ASSERT_TRUE(handler.Entry(0x3b8961b8, 0xa21069698096fc98ULL, + 0xb440ce248169c8d6ULL, 3, "", 0xea93c106)); + ASSERT_TRUE(handler.End()); + module.GetStackFrameEntries(&entries); + EXPECT_EQ(1U, entries.size()); + EXPECT_EQ(0xa21069698096fc98ULL, entries[0]->address); + EXPECT_EQ(0xb440ce248169c8d6ULL, entries[0]->size); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Entry, AcceptOldVersion) { + ASSERT_TRUE(handler.Entry(0xeb60e0fc, 0x75b8806bb09eab78ULL, + 0xc771f44958d40bbcULL, 1, "", 0x093c945e)); + ASSERT_TRUE(handler.End()); + module.GetStackFrameEntries(&entries); + EXPECT_EQ(1U, entries.size()); + EXPECT_EQ(0x75b8806bb09eab78ULL, entries[0]->address); + EXPECT_EQ(0xc771f44958d40bbcULL, entries[0]->size); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +struct RuleFixture: public DwarfCFIToModuleFixture { + RuleFixture() : DwarfCFIToModuleFixture() { + entry_address = 0x89327ebf86b47492ULL; + entry_size = 0x2f8cd573072fe02aULL; + return_reg = 0x7886a346; + } + void StartEntry() { + ASSERT_TRUE(handler.Entry(0x4445c05c, entry_address, entry_size, + 3, "", return_reg)); + } + void CheckEntry() { + module.GetStackFrameEntries(&entries); + EXPECT_EQ(1U, entries.size()); + EXPECT_EQ(entry_address, entries[0]->address); + EXPECT_EQ(entry_size, entries[0]->size); + } + uint64 entry_address, entry_size; + unsigned return_reg; +}; + +class Rule: public RuleFixture, public Test { }; + +TEST_F(Rule, UndefinedRule) { + EXPECT_CALL(reporter, UndefinedNotSupported(_, "reg7")); + StartEntry(); + ASSERT_TRUE(handler.UndefinedRule(entry_address, 7)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, RegisterWithEmptyName) { + EXPECT_CALL(reporter, UnnamedRegister(_, 10)); + EXPECT_CALL(reporter, UndefinedNotSupported(_, "unnamed_register10")); + StartEntry(); + ASSERT_TRUE(handler.UndefinedRule(entry_address, 10)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, SameValueRule) { + StartEntry(); + ASSERT_TRUE(handler.SameValueRule(entry_address, 6)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + Module::RuleMap expected_initial; + expected_initial["reg6"] = "reg6"; + EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, OffsetRule) { + StartEntry(); + ASSERT_TRUE(handler.OffsetRule(entry_address + 1, return_reg, + DwarfCFIToModule::kCFARegister, + 16927065)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + Module::RuleChangeMap expected_changes; + expected_changes[entry_address + 1][".ra"] = ".cfa 16927065 + ^"; + EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); +} + +TEST_F(Rule, OffsetRuleNegative) { + StartEntry(); + ASSERT_TRUE(handler.OffsetRule(entry_address + 1, + DwarfCFIToModule::kCFARegister, 4, -34530721)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + Module::RuleChangeMap expected_changes; + expected_changes[entry_address + 1][".cfa"] = "reg4 -34530721 + ^"; + EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); +} + +TEST_F(Rule, ValOffsetRule) { + // Use an unnamed register number, to exercise that branch of RegisterName. + EXPECT_CALL(reporter, UnnamedRegister(_, 11)); + StartEntry(); + ASSERT_TRUE(handler.ValOffsetRule(entry_address + 0x5ab7, + DwarfCFIToModule::kCFARegister, + 11, 61812979)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + Module::RuleChangeMap expected_changes; + expected_changes[entry_address + 0x5ab7][".cfa"] = + "unnamed_register11 61812979 +"; + EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); +} + +TEST_F(Rule, RegisterRule) { + StartEntry(); + ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 3)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + Module::RuleMap expected_initial; + expected_initial[".ra"] = "reg3"; + EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, ExpressionRule) { + EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg2")); + StartEntry(); + ASSERT_TRUE(handler.ExpressionRule(entry_address + 0xf326, 2, + "it takes two to tango")); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, ValExpressionRule) { + EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg0")); + StartEntry(); + ASSERT_TRUE(handler.ValExpressionRule(entry_address + 0x6367, 0, + "bit off more than he could chew")); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, DefaultReturnAddressRule) { + return_reg = 2; + StartEntry(); + ASSERT_TRUE(handler.RegisterRule(entry_address, 0, 1)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + Module::RuleMap expected_initial; + expected_initial[".ra"] = "reg2"; + expected_initial["reg0"] = "reg1"; + EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, DefaultReturnAddressRuleOverride) { + return_reg = 2; + StartEntry(); + ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 1)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + Module::RuleMap expected_initial; + expected_initial[".ra"] = "reg1"; + EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, DefaultReturnAddressRuleLater) { + return_reg = 2; + StartEntry(); + ASSERT_TRUE(handler.RegisterRule(entry_address + 1, return_reg, 1)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + Module::RuleMap expected_initial; + expected_initial[".ra"] = "reg2"; + EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); + Module::RuleChangeMap expected_changes; + expected_changes[entry_address + 1][".ra"] = "reg1"; + EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); +} + +TEST(RegisterNames, I386) { + vector<string> names = DwarfCFIToModule::RegisterNames::I386(); + + EXPECT_EQ("$eax", names[0]); + EXPECT_EQ("$ecx", names[1]); + EXPECT_EQ("$esp", names[4]); + EXPECT_EQ("$eip", names[8]); +} + +TEST(RegisterNames, ARM) { + vector<string> names = DwarfCFIToModule::RegisterNames::ARM(); + + EXPECT_EQ("r0", names[0]); + EXPECT_EQ("r10", names[10]); + EXPECT_EQ("sp", names[13]); + EXPECT_EQ("lr", names[14]); + EXPECT_EQ("pc", names[15]); +} + +TEST(RegisterNames, X86_64) { + vector<string> names = DwarfCFIToModule::RegisterNames::X86_64(); + + EXPECT_EQ("$rax", names[0]); + EXPECT_EQ("$rdx", names[1]); + EXPECT_EQ("$rbp", names[6]); + EXPECT_EQ("$rsp", names[7]); + EXPECT_EQ("$rip", names[16]); +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf_cu_to_module.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf_cu_to_module.cc new file mode 100644 index 0000000000..56399c3a0b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf_cu_to_module.cc @@ -0,0 +1,1243 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// Implement the DwarfCUToModule class; see dwarf_cu_to_module.h. + +// For <inttypes.h> PRI* macros, before anything else might #include it. +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif /* __STDC_FORMAT_MACROS */ + +#include "common/dwarf_cu_to_module.h" + +#include <assert.h> +#include <inttypes.h> +#include <stdint.h> +#include <stdio.h> + +#include <algorithm> +#include <numeric> +#include <utility> + +#include "common/dwarf_line_to_module.h" +#include "common/unordered.h" + +namespace google_breakpad { + +using std::accumulate; +using std::map; +using std::pair; +using std::sort; +using std::vector; + +// Data provided by a DWARF specification DIE. +// +// In DWARF, the DIE for a definition may contain a DW_AT_specification +// attribute giving the offset of the corresponding declaration DIE, and +// the definition DIE may omit information given in the declaration. For +// example, it's common for a function's address range to appear only in +// its definition DIE, but its name to appear only in its declaration +// DIE. +// +// The dumper needs to be able to follow DW_AT_specification links to +// bring all this information together in a FUNC record. Conveniently, +// DIEs that are the target of such links have a DW_AT_declaration flag +// set, so we can identify them when we first see them, and record their +// contents for later reference. +// +// A Specification holds information gathered from a declaration DIE that +// we may need if we find a DW_AT_specification link pointing to it. +struct DwarfCUToModule::Specification { + // The qualified name that can be found by demangling DW_AT_MIPS_linkage_name. + string qualified_name; + + // The name of the enclosing scope, or the empty string if there is none. + string enclosing_name; + + // The name for the specification DIE itself, without any enclosing + // name components. + string unqualified_name; +}; + +// An abstract origin -- base definition of an inline function. +struct AbstractOrigin { + AbstractOrigin() : name() {} + explicit AbstractOrigin(const string& name) : name(name) {} + + string name; +}; + +typedef map<uint64, AbstractOrigin> AbstractOriginByOffset; + +// Data global to the DWARF-bearing file that is private to the +// DWARF-to-Module process. +struct DwarfCUToModule::FilePrivate { + // A set of strings used in this CU. Before storing a string in one of + // our data structures, insert it into this set, and then use the string + // from the set. + // + // In some STL implementations, strings are reference-counted internally, + // meaning that simply using strings from this set, even if passed by + // value, assigned, or held directly in structures and containers + // (map<string, ...>, for example), causes those strings to share a + // single instance of each distinct piece of text. GNU's libstdc++ uses + // reference counts, and I believe MSVC did as well, at some point. + // However, C++ '11 implementations are moving away from reference + // counting. + // + // In other implementations, string assignments copy the string's text, + // so this set will actually hold yet another copy of the string (although + // everything will still work). To improve memory consumption portably, + // we will probably need to use pointers to strings held in this set. + unordered_set<string> common_strings; + + // A map from offsets of DIEs within the .debug_info section to + // Specifications describing those DIEs. Specification references can + // cross compilation unit boundaries. + SpecificationByOffset specifications; + + AbstractOriginByOffset origins; +}; + +DwarfCUToModule::FileContext::FileContext(const string &filename, + Module *module, + bool handle_inter_cu_refs) + : filename_(filename), + module_(module), + handle_inter_cu_refs_(handle_inter_cu_refs), + file_private_(new FilePrivate()) { +} + +DwarfCUToModule::FileContext::~FileContext() { +} + +void DwarfCUToModule::FileContext::AddSectionToSectionMap( + const string& name, const uint8_t *contents, uint64 length) { + section_map_[name] = std::make_pair(contents, length); +} + +void DwarfCUToModule::FileContext::ClearSectionMapForTest() { + section_map_.clear(); +} + +const dwarf2reader::SectionMap& +DwarfCUToModule::FileContext::section_map() const { + return section_map_; +} + +void DwarfCUToModule::FileContext::ClearSpecifications() { + if (!handle_inter_cu_refs_) + file_private_->specifications.clear(); +} + +bool DwarfCUToModule::FileContext::IsUnhandledInterCUReference( + uint64 offset, uint64 compilation_unit_start) const { + if (handle_inter_cu_refs_) + return false; + return offset < compilation_unit_start; +} + +// Information global to the particular compilation unit we're +// parsing. This is for data shared across the CU's entire DIE tree, +// and parameters from the code invoking the CU parser. +struct DwarfCUToModule::CUContext { + CUContext(FileContext *file_context_arg, WarningReporter *reporter_arg, + RangesHandler *ranges_handler_arg) + : file_context(file_context_arg), + reporter(reporter_arg), + ranges_handler(ranges_handler_arg), + language(Language::CPlusPlus), + low_pc(0), + high_pc(0), + ranges(0) {} + + ~CUContext() { + for (vector<Module::Function *>::iterator it = functions.begin(); + it != functions.end(); ++it) { + delete *it; + } + }; + + // The DWARF-bearing file into which this CU was incorporated. + FileContext *file_context; + + // For printing error messages. + WarningReporter *reporter; + + // For reading ranges from the .debug_ranges section + RangesHandler *ranges_handler; + + // The source language of this compilation unit. + const Language *language; + + // Addresses covered by this CU. If high_pc_ is non-zero then the CU covers + // low_pc to high_pc, otherwise ranges is non-zero and low_pc represents + // the base address of the ranges covered by the CU. + uint64 low_pc; + uint64 high_pc; + uint64 ranges; + + // The functions defined in this compilation unit. We accumulate + // them here during parsing. Then, in DwarfCUToModule::Finish, we + // assign them lines and add them to file_context->module. + // + // Destroying this destroys all the functions this vector points to. + vector<Module::Function *> functions; + + // Keep a list of forward references from DW_AT_abstract_origin and + // DW_AT_specification attributes so names can be fixed up. + std::map<uint64_t, Module::Function *> forward_ref_die_to_func; +}; + +// Information about the context of a particular DIE. This is for +// information that changes as we descend the tree towards the leaves: +// the containing classes/namespaces, etc. +struct DwarfCUToModule::DIEContext { + // The fully-qualified name of the context. For example, for a + // tree like: + // + // DW_TAG_namespace Foo + // DW_TAG_class Bar + // DW_TAG_subprogram Baz + // + // in a C++ compilation unit, the DIEContext's name for the + // DW_TAG_subprogram DIE would be "Foo::Bar". The DIEContext's + // name for the DW_TAG_namespace DIE would be "". + string name; +}; + +// An abstract base class for all the dumper's DIE handlers. +class DwarfCUToModule::GenericDIEHandler: public dwarf2reader::DIEHandler { + public: + // Create a handler for the DIE at OFFSET whose compilation unit is + // described by CU_CONTEXT, and whose immediate context is described + // by PARENT_CONTEXT. + GenericDIEHandler(CUContext *cu_context, DIEContext *parent_context, + uint64 offset) + : cu_context_(cu_context), + parent_context_(parent_context), + offset_(offset), + declaration_(false), + specification_(NULL), + forward_ref_die_offset_(0) { } + + // Derived classes' ProcessAttributeUnsigned can defer to this to + // handle DW_AT_declaration, or simply not override it. + void ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + + // Derived classes' ProcessAttributeReference can defer to this to + // handle DW_AT_specification, or simply not override it. + void ProcessAttributeReference(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + + // Derived classes' ProcessAttributeReference can defer to this to + // handle DW_AT_specification, or simply not override it. + void ProcessAttributeString(enum DwarfAttribute attr, + enum DwarfForm form, + const string &data); + + protected: + // Compute and return the fully-qualified name of the DIE. If this + // DIE is a declaration DIE, to be cited by other DIEs' + // DW_AT_specification attributes, record its enclosing name and + // unqualified name in the specification table. + // + // Use this from EndAttributes member functions, not ProcessAttribute* + // functions; only the former can be sure that all the DIE's attributes + // have been seen. + string ComputeQualifiedName(); + + CUContext *cu_context_; + DIEContext *parent_context_; + uint64 offset_; + + // Place the name in the global set of strings. Even though this looks + // like a copy, all the major string implementations use reference + // counting internally, so the effect is to have all the data structures + // share copies of strings whenever possible. + // FIXME: Should this return something like a string_ref to avoid the + // assumption about how strings are implemented? + string AddStringToPool(const string &str); + + // If this DIE has a DW_AT_declaration attribute, this is its value. + // It is false on DIEs with no DW_AT_declaration attribute. + bool declaration_; + + // If this DIE has a DW_AT_specification attribute, this is the + // Specification structure for the DIE the attribute refers to. + // Otherwise, this is NULL. + Specification *specification_; + + // If this DIE has a DW_AT_specification or DW_AT_abstract_origin and it is a + // forward reference, no Specification will be available. Track the reference + // to be fixed up when the DIE is parsed. + uint64_t forward_ref_die_offset_; + + // The value of the DW_AT_name attribute, or the empty string if the + // DIE has no such attribute. + string name_attribute_; + + // The demangled value of the DW_AT_MIPS_linkage_name attribute, or the empty + // string if the DIE has no such attribute or its content could not be + // demangled. + string demangled_name_; + + // The non-demangled value of the DW_AT_MIPS_linkage_name attribute, + // it its content count not be demangled. + string raw_name_; +}; + +void DwarfCUToModule::GenericDIEHandler::ProcessAttributeUnsigned( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + switch (attr) { + case dwarf2reader::DW_AT_declaration: declaration_ = (data != 0); break; + default: break; + } +} + +void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + switch (attr) { + case dwarf2reader::DW_AT_specification: { + FileContext *file_context = cu_context_->file_context; + if (file_context->IsUnhandledInterCUReference( + data, cu_context_->reporter->cu_offset())) { + cu_context_->reporter->UnhandledInterCUReference(offset_, data); + break; + } + // Find the Specification to which this attribute refers, and + // set specification_ appropriately. We could do more processing + // here, but it's better to leave the real work to our + // EndAttribute member function, at which point we know we have + // seen all the DIE's attributes. + SpecificationByOffset *specifications = + &file_context->file_private_->specifications; + SpecificationByOffset::iterator spec = specifications->find(data); + if (spec != specifications->end()) { + specification_ = &spec->second; + } else if (data > offset_) { + forward_ref_die_offset_ = data; + } else { + cu_context_->reporter->UnknownSpecification(offset_, data); + } + break; + } + default: break; + } +} + +string DwarfCUToModule::GenericDIEHandler::AddStringToPool(const string &str) { + pair<unordered_set<string>::iterator, bool> result = + cu_context_->file_context->file_private_->common_strings.insert(str); + return *result.first; +} + +void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString( + enum DwarfAttribute attr, + enum DwarfForm form, + const string &data) { + switch (attr) { + case dwarf2reader::DW_AT_name: + name_attribute_ = AddStringToPool(data); + break; + case dwarf2reader::DW_AT_MIPS_linkage_name: + case dwarf2reader::DW_AT_linkage_name: { + string demangled; + Language::DemangleResult result = + cu_context_->language->DemangleName(data, &demangled); + switch (result) { + case Language::kDemangleSuccess: + demangled_name_ = AddStringToPool(demangled); + break; + + case Language::kDemangleFailure: + cu_context_->reporter->DemangleError(data); + // fallthrough + case Language::kDontDemangle: + demangled_name_.clear(); + raw_name_ = AddStringToPool(data); + break; + } + break; + } + default: break; + } +} + +string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() { + // Use the demangled name, if one is available. Demangled names are + // preferable to those inferred from the DWARF structure because they + // include argument types. + const string *qualified_name = NULL; + if (!demangled_name_.empty()) { + // Found it is this DIE. + qualified_name = &demangled_name_; + } else if (specification_ && !specification_->qualified_name.empty()) { + // Found it on the specification. + qualified_name = &specification_->qualified_name; + } + + const string *unqualified_name = NULL; + const string *enclosing_name; + if (!qualified_name) { + // Find the unqualified name. If the DIE has its own DW_AT_name + // attribute, then use that; otherwise, check the specification. + if (!name_attribute_.empty()) + unqualified_name = &name_attribute_; + else if (specification_) + unqualified_name = &specification_->unqualified_name; + else if (!raw_name_.empty()) + unqualified_name = &raw_name_; + + // Find the name of the enclosing context. If this DIE has a + // specification, it's the specification's enclosing context that + // counts; otherwise, use this DIE's context. + if (specification_) + enclosing_name = &specification_->enclosing_name; + else + enclosing_name = &parent_context_->name; + } + + // Prepare the return value before upcoming mutations possibly invalidate the + // existing pointers. + string return_value; + if (qualified_name) { + return_value = *qualified_name; + } else if (unqualified_name && enclosing_name) { + // Combine the enclosing name and unqualified name to produce our + // own fully-qualified name. + return_value = cu_context_->language->MakeQualifiedName(*enclosing_name, + *unqualified_name); + } + + // If this DIE was marked as a declaration, record its names in the + // specification table. + if ((declaration_ && qualified_name) || + (unqualified_name && enclosing_name)) { + Specification spec; + if (qualified_name) { + spec.qualified_name = *qualified_name; + } else { + spec.enclosing_name = *enclosing_name; + spec.unqualified_name = *unqualified_name; + } + cu_context_->file_context->file_private_->specifications[offset_] = spec; + } + + return return_value; +} + +// A handler class for DW_TAG_subprogram DIEs. +class DwarfCUToModule::FuncHandler: public GenericDIEHandler { + public: + FuncHandler(CUContext *cu_context, DIEContext *parent_context, + uint64 offset) + : GenericDIEHandler(cu_context, parent_context, offset), + low_pc_(0), high_pc_(0), high_pc_form_(dwarf2reader::DW_FORM_addr), + ranges_(0), abstract_origin_(NULL), inline_(false) { } + void ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + void ProcessAttributeSigned(enum DwarfAttribute attr, + enum DwarfForm form, + int64 data); + void ProcessAttributeReference(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + + bool EndAttributes(); + void Finish(); + + private: + // The fully-qualified name, as derived from name_attribute_, + // specification_, parent_context_. Computed in EndAttributes. + string name_; + uint64 low_pc_, high_pc_; // DW_AT_low_pc, DW_AT_high_pc + DwarfForm high_pc_form_; // DW_AT_high_pc can be length or address. + uint64 ranges_; // DW_AT_ranges + const AbstractOrigin* abstract_origin_; + bool inline_; +}; + +void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + switch (attr) { + // If this attribute is present at all --- even if its value is + // DW_INL_not_inlined --- then GCC may cite it as someone else's + // DW_AT_abstract_origin attribute. + case dwarf2reader::DW_AT_inline: inline_ = true; break; + + case dwarf2reader::DW_AT_low_pc: low_pc_ = data; break; + case dwarf2reader::DW_AT_high_pc: + high_pc_form_ = form; + high_pc_ = data; + break; + case dwarf2reader::DW_AT_ranges: + ranges_ = data; + break; + + default: + GenericDIEHandler::ProcessAttributeUnsigned(attr, form, data); + break; + } +} + +void DwarfCUToModule::FuncHandler::ProcessAttributeSigned( + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data) { + switch (attr) { + // If this attribute is present at all --- even if its value is + // DW_INL_not_inlined --- then GCC may cite it as someone else's + // DW_AT_abstract_origin attribute. + case dwarf2reader::DW_AT_inline: inline_ = true; break; + + default: + break; + } +} + +void DwarfCUToModule::FuncHandler::ProcessAttributeReference( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + switch (attr) { + case dwarf2reader::DW_AT_abstract_origin: { + const AbstractOriginByOffset& origins = + cu_context_->file_context->file_private_->origins; + AbstractOriginByOffset::const_iterator origin = origins.find(data); + if (origin != origins.end()) { + abstract_origin_ = &(origin->second); + } else if (data > offset_) { + forward_ref_die_offset_ = data; + } else { + cu_context_->reporter->UnknownAbstractOrigin(offset_, data); + } + break; + } + default: + GenericDIEHandler::ProcessAttributeReference(attr, form, data); + break; + } +} + +bool DwarfCUToModule::FuncHandler::EndAttributes() { + // Compute our name, and record a specification, if appropriate. + name_ = ComputeQualifiedName(); + if (name_.empty() && abstract_origin_) { + name_ = abstract_origin_->name; + } + return true; +} + +static bool IsEmptyRange(const vector<Module::Range>& ranges) { + uint64 size = accumulate(ranges.cbegin(), ranges.cend(), 0, + [](uint64 total, Module::Range entry) { + return total + entry.size; + } + ); + + return size == 0; +} + +void DwarfCUToModule::FuncHandler::Finish() { + vector<Module::Range> ranges; + + // Check if this DIE was one of the forward references that was not able + // to be processed, and fix up the name of the appropriate Module::Function. + // "name_" will have already been fixed up in EndAttributes(). + if (!name_.empty()) { + auto iter = cu_context_->forward_ref_die_to_func.find(offset_); + if (iter != cu_context_->forward_ref_die_to_func.end()) + iter->second->name = name_; + } + + if (!ranges_) { + // Make high_pc_ an address, if it isn't already. + if (high_pc_form_ != dwarf2reader::DW_FORM_addr && + high_pc_form_ != dwarf2reader::DW_FORM_GNU_addr_index) { + high_pc_ += low_pc_; + } + + Module::Range range(low_pc_, high_pc_ - low_pc_); + ranges.push_back(range); + } else { + RangesHandler *ranges_handler = cu_context_->ranges_handler; + + if (ranges_handler) { + if (!ranges_handler->ReadRanges(ranges_, cu_context_->low_pc, &ranges)) { + ranges.clear(); + cu_context_->reporter->MalformedRangeList(ranges_); + } + } else { + cu_context_->reporter->MissingRanges(); + } + } + + // Did we collect the information we need? Not all DWARF function + // entries are non-empty (for example, inlined functions that were never + // used), but all the ones we're interested in cover a non-empty range of + // bytes. + if (!IsEmptyRange(ranges)) { + low_pc_ = ranges.front().address; + + // Malformed DWARF may omit the name, but all Module::Functions must + // have names. + string name; + if (!name_.empty()) { + name = name_; + } else { + // If we have a forward reference to a DW_AT_specification or + // DW_AT_abstract_origin, then don't warn, the name will be fixed up + // later + if (forward_ref_die_offset_ == 0) + cu_context_->reporter->UnnamedFunction(offset_); + name = "<name omitted>"; + } + + // Create a Module::Function based on the data we've gathered, and + // add it to the functions_ list. + scoped_ptr<Module::Function> func(new Module::Function(name, low_pc_)); + func->ranges = ranges; + func->parameter_size = 0; + if (func->address) { + // If the function address is zero this is a sign that this function + // description is just empty debug data and should just be discarded. + cu_context_->functions.push_back(func.release()); + if (forward_ref_die_offset_ != 0) { + auto iter = + cu_context_->forward_ref_die_to_func.find(forward_ref_die_offset_); + if (iter == cu_context_->forward_ref_die_to_func.end()) { + cu_context_->reporter->UnknownSpecification(offset_, + forward_ref_die_offset_); + } else { + iter->second = cu_context_->functions.back(); + } + } + } + } else if (inline_) { + AbstractOrigin origin(name_); + cu_context_->file_context->file_private_->origins[offset_] = origin; + } +} + +// A handler for DIEs that contain functions and contribute a +// component to their names: namespaces, classes, etc. +class DwarfCUToModule::NamedScopeHandler: public GenericDIEHandler { + public: + NamedScopeHandler(CUContext *cu_context, DIEContext *parent_context, + uint64 offset) + : GenericDIEHandler(cu_context, parent_context, offset) { } + bool EndAttributes(); + DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag); + + private: + DIEContext child_context_; // A context for our children. +}; + +bool DwarfCUToModule::NamedScopeHandler::EndAttributes() { + child_context_.name = ComputeQualifiedName(); + return true; +} + +dwarf2reader::DIEHandler *DwarfCUToModule::NamedScopeHandler::FindChildHandler( + uint64 offset, + enum DwarfTag tag) { + switch (tag) { + case dwarf2reader::DW_TAG_subprogram: + return new FuncHandler(cu_context_, &child_context_, offset); + case dwarf2reader::DW_TAG_namespace: + case dwarf2reader::DW_TAG_class_type: + case dwarf2reader::DW_TAG_structure_type: + case dwarf2reader::DW_TAG_union_type: + return new NamedScopeHandler(cu_context_, &child_context_, offset); + default: + return NULL; + } +} + +void DwarfCUToModule::WarningReporter::CUHeading() { + if (printed_cu_header_) + return; + fprintf(stderr, "%s: in compilation unit '%s' (offset 0x%llx):\n", + filename_.c_str(), cu_name_.c_str(), cu_offset_); + printed_cu_header_ = true; +} + +void DwarfCUToModule::WarningReporter::UnknownSpecification(uint64 offset, + uint64 target) { + CUHeading(); + fprintf(stderr, "%s: the DIE at offset 0x%llx has a DW_AT_specification" + " attribute referring to the DIE at offset 0x%llx, which was not" + " marked as a declaration\n", + filename_.c_str(), offset, target); +} + +void DwarfCUToModule::WarningReporter::UnknownAbstractOrigin(uint64 offset, + uint64 target) { + CUHeading(); + fprintf(stderr, "%s: the DIE at offset 0x%llx has a DW_AT_abstract_origin" + " attribute referring to the DIE at offset 0x%llx, which was not" + " marked as an inline\n", + filename_.c_str(), offset, target); +} + +void DwarfCUToModule::WarningReporter::MissingSection(const string &name) { + CUHeading(); + fprintf(stderr, "%s: warning: couldn't find DWARF '%s' section\n", + filename_.c_str(), name.c_str()); +} + +void DwarfCUToModule::WarningReporter::BadLineInfoOffset(uint64 offset) { + CUHeading(); + fprintf(stderr, "%s: warning: line number data offset beyond end" + " of '.debug_line' section\n", + filename_.c_str()); +} + +void DwarfCUToModule::WarningReporter::UncoveredHeading() { + if (printed_unpaired_header_) + return; + CUHeading(); + fprintf(stderr, "%s: warning: skipping unpaired lines/functions:\n", + filename_.c_str()); + printed_unpaired_header_ = true; +} + +void DwarfCUToModule::WarningReporter::UncoveredFunction( + const Module::Function &function) { + if (!uncovered_warnings_enabled_) + return; + UncoveredHeading(); + fprintf(stderr, " function%s: %s\n", + IsEmptyRange(function.ranges) ? " (zero-length)" : "", + function.name.c_str()); +} + +void DwarfCUToModule::WarningReporter::UncoveredLine(const Module::Line &line) { + if (!uncovered_warnings_enabled_) + return; + UncoveredHeading(); + fprintf(stderr, " line%s: %s:%d at 0x%" PRIx64 "\n", + (line.size == 0 ? " (zero-length)" : ""), + line.file->name.c_str(), line.number, line.address); +} + +void DwarfCUToModule::WarningReporter::UnnamedFunction(uint64 offset) { + CUHeading(); + fprintf(stderr, "%s: warning: function at offset 0x%llx has no name\n", + filename_.c_str(), offset); +} + +void DwarfCUToModule::WarningReporter::DemangleError(const string &input) { + CUHeading(); + fprintf(stderr, "%s: warning: failed to demangle %s\n", + filename_.c_str(), input.c_str()); +} + +void DwarfCUToModule::WarningReporter::UnhandledInterCUReference( + uint64 offset, uint64 target) { + CUHeading(); + fprintf(stderr, "%s: warning: the DIE at offset 0x%llx has a " + "DW_FORM_ref_addr attribute with an inter-CU reference to " + "0x%llx, but inter-CU reference handling is turned off.\n", + filename_.c_str(), offset, target); +} + +void DwarfCUToModule::WarningReporter::MalformedRangeList(uint64 offset) { + CUHeading(); + fprintf(stderr, "%s: warning: the range list at offset 0x%llx falls out of " + "the .debug_ranges section.\n", + filename_.c_str(), offset); +} + +void DwarfCUToModule::WarningReporter::MissingRanges() { + CUHeading(); + fprintf(stderr, "%s: warning: A DW_AT_ranges attribute was encountered but " + "the .debug_ranges section is missing.\n", filename_.c_str()); +} + +DwarfCUToModule::DwarfCUToModule(FileContext *file_context, + LineToModuleHandler *line_reader, + RangesHandler *ranges_handler, + WarningReporter *reporter) + : line_reader_(line_reader), + cu_context_(new CUContext(file_context, reporter, ranges_handler)), + child_context_(new DIEContext()), + has_source_line_info_(false) { +} + +DwarfCUToModule::~DwarfCUToModule() { +} + +void DwarfCUToModule::ProcessAttributeSigned(enum DwarfAttribute attr, + enum DwarfForm form, + int64 data) { + switch (attr) { + case dwarf2reader::DW_AT_language: // source language of this CU + SetLanguage(static_cast<DwarfLanguage>(data)); + break; + default: + break; + } +} + +void DwarfCUToModule::ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + switch (attr) { + case dwarf2reader::DW_AT_stmt_list: // Line number information. + has_source_line_info_ = true; + source_line_offset_ = data; + break; + case dwarf2reader::DW_AT_language: // source language of this CU + SetLanguage(static_cast<DwarfLanguage>(data)); + break; + case dwarf2reader::DW_AT_low_pc: + cu_context_->low_pc = data; + break; + case dwarf2reader::DW_AT_high_pc: + cu_context_->high_pc = data; + break; + case dwarf2reader::DW_AT_ranges: + cu_context_->ranges = data; + break; + + default: + break; + } +} + +void DwarfCUToModule::ProcessAttributeString(enum DwarfAttribute attr, + enum DwarfForm form, + const string &data) { + switch (attr) { + case dwarf2reader::DW_AT_name: + cu_context_->reporter->SetCUName(data); + break; + case dwarf2reader::DW_AT_comp_dir: + line_reader_->StartCompilationUnit(data); + break; + default: + break; + } +} + +bool DwarfCUToModule::EndAttributes() { + return true; +} + +dwarf2reader::DIEHandler *DwarfCUToModule::FindChildHandler( + uint64 offset, + enum DwarfTag tag) { + switch (tag) { + case dwarf2reader::DW_TAG_subprogram: + return new FuncHandler(cu_context_.get(), child_context_.get(), offset); + case dwarf2reader::DW_TAG_namespace: + case dwarf2reader::DW_TAG_class_type: + case dwarf2reader::DW_TAG_structure_type: + case dwarf2reader::DW_TAG_union_type: + case dwarf2reader::DW_TAG_module: + return new NamedScopeHandler(cu_context_.get(), child_context_.get(), + offset); + default: + return NULL; + } +} + +void DwarfCUToModule::SetLanguage(DwarfLanguage language) { + switch (language) { + case dwarf2reader::DW_LANG_Java: + cu_context_->language = Language::Java; + break; + + case dwarf2reader::DW_LANG_Swift: + cu_context_->language = Language::Swift; + break; + + case dwarf2reader::DW_LANG_Rust: + cu_context_->language = Language::Rust; + break; + + // DWARF has no generic language code for assembly language; this is + // what the GNU toolchain uses. + case dwarf2reader::DW_LANG_Mips_Assembler: + cu_context_->language = Language::Assembler; + break; + + // C++ covers so many cases that it probably has some way to cope + // with whatever the other languages throw at us. So make it the + // default. + // + // Objective C and Objective C++ seem to create entries for + // methods whose DW_AT_name values are already fully-qualified: + // "-[Classname method:]". These appear at the top level. + // + // DWARF data for C should never include namespaces or functions + // nested in struct types, but if it ever does, then C++'s + // notation is probably not a bad choice for that. + default: + case dwarf2reader::DW_LANG_ObjC: + case dwarf2reader::DW_LANG_ObjC_plus_plus: + case dwarf2reader::DW_LANG_C: + case dwarf2reader::DW_LANG_C89: + case dwarf2reader::DW_LANG_C99: + case dwarf2reader::DW_LANG_C_plus_plus: + cu_context_->language = Language::CPlusPlus; + break; + } +} + +void DwarfCUToModule::ReadSourceLines(uint64 offset) { + const dwarf2reader::SectionMap §ion_map + = cu_context_->file_context->section_map(); + dwarf2reader::SectionMap::const_iterator map_entry + = section_map.find(".debug_line"); + // Mac OS X puts DWARF data in sections whose names begin with "__" + // instead of ".". + if (map_entry == section_map.end()) + map_entry = section_map.find("__debug_line"); + if (map_entry == section_map.end()) { + cu_context_->reporter->MissingSection(".debug_line"); + return; + } + const uint8_t *section_start = map_entry->second.first; + uint64 section_length = map_entry->second.second; + if (offset >= section_length) { + cu_context_->reporter->BadLineInfoOffset(offset); + return; + } + line_reader_->ReadProgram(section_start + offset, section_length - offset, + cu_context_->file_context->module_, &lines_); +} + +namespace { +class FunctionRange { + public: + FunctionRange(const Module::Range &range, Module::Function *function) : + address(range.address), size(range.size), function(function) { } + + void AddLine(Module::Line &line) { + function->lines.push_back(line); + } + + Module::Address address; + Module::Address size; + Module::Function *function; +}; + +// Fills an array of ranges with pointers to the functions which owns +// them. The array is sorted in ascending order and the ranges are non +// empty and non-overlapping. + +static void FillSortedFunctionRanges(vector<FunctionRange> &dest_ranges, + vector<Module::Function *> *functions) { + for (vector<Module::Function *>::const_iterator func_it = functions->cbegin(); + func_it != functions->cend(); + func_it++) + { + Module::Function *func = *func_it; + vector<Module::Range> &ranges = func->ranges; + for (vector<Module::Range>::const_iterator ranges_it = ranges.cbegin(); + ranges_it != ranges.cend(); + ++ranges_it) { + FunctionRange range(*ranges_it, func); + if (range.size != 0) { + dest_ranges.push_back(range); + } + } + } + + sort(dest_ranges.begin(), dest_ranges.end(), + [](const FunctionRange &fr1, const FunctionRange &fr2) { + return fr1.address < fr2.address; + } + ); +} + +// Return true if ADDRESS falls within the range of ITEM. +template <class T> +inline bool within(const T &item, Module::Address address) { + // Because Module::Address is unsigned, and unsigned arithmetic + // wraps around, this will be false if ADDRESS falls before the + // start of ITEM, or if it falls after ITEM's end. + return address - item.address < item.size; +} +} + +void DwarfCUToModule::AssignLinesToFunctions() { + vector<Module::Function *> *functions = &cu_context_->functions; + WarningReporter *reporter = cu_context_->reporter; + + // This would be simpler if we assumed that source line entries + // don't cross function boundaries. However, there's no real reason + // to assume that (say) a series of function definitions on the same + // line wouldn't get coalesced into one line number entry. The + // DWARF spec certainly makes no such promises. + // + // So treat the functions and lines as peers, and take the trouble + // to compute their ranges' intersections precisely. In any case, + // the hair here is a constant factor for performance; the + // complexity from here on out is linear. + + // Put both our functions and lines in order by address. + std::sort(functions->begin(), functions->end(), + Module::Function::CompareByAddress); + std::sort(lines_.begin(), lines_.end(), Module::Line::CompareByAddress); + + // The last line that we used any piece of. We use this only for + // generating warnings. + const Module::Line *last_line_used = NULL; + + // The last function and line we warned about --- so we can avoid + // doing so more than once. + const Module::Function *last_function_cited = NULL; + const Module::Line *last_line_cited = NULL; + + // Prepare a sorted list of ranges with range-to-function mapping + vector<FunctionRange> sorted_ranges; + FillSortedFunctionRanges(sorted_ranges, functions); + + // Make a single pass through both the range and line vectors from lower to + // higher addresses, populating each range's function lines vector with lines + // from our lines_ vector that fall within the range. + vector<FunctionRange>::iterator range_it = sorted_ranges.begin(); + vector<Module::Line>::const_iterator line_it = lines_.begin(); + + Module::Address current; + + // Pointers to the referents of func_it and line_it, or NULL if the + // iterator is at the end of the sequence. + FunctionRange *range; + const Module::Line *line; + + // Start current at the beginning of the first line or function, + // whichever is earlier. + if (range_it != sorted_ranges.end() && line_it != lines_.end()) { + range = &*range_it; + line = &*line_it; + current = std::min(range->address, line->address); + } else if (line_it != lines_.end()) { + range = NULL; + line = &*line_it; + current = line->address; + } else if (range_it != sorted_ranges.end()) { + range = &*range_it; + line = NULL; + current = range->address; + } else { + return; + } + + while (range || line) { + // This loop has two invariants that hold at the top. + // + // First, at least one of the iterators is not at the end of its + // sequence, and those that are not refer to the earliest + // range or line that contains or starts after CURRENT. + // + // Note that every byte is in one of four states: it is covered + // or not covered by a range, and, independently, it is + // covered or not covered by a line. + // + // The second invariant is that CURRENT refers to a byte whose + // state is different from its predecessor, or it refers to the + // first byte in the address space. In other words, CURRENT is + // always the address of a transition. + // + // Note that, although each iteration advances CURRENT from one + // transition address to the next in each iteration, it might + // not advance the iterators. Suppose we have a range that + // starts with a line, has a gap, and then a second line, and + // suppose that we enter an iteration with CURRENT at the end of + // the first line. The next transition address is the start of + // the second line, after the gap, so the iteration should + // advance CURRENT to that point. At the head of that iteration, + // the invariants require that the line iterator be pointing at + // the second line. But this is also true at the head of the + // next. And clearly, the iteration must not change the range + // iterator. So neither iterator moves. + + // Assert the first invariant (see above). + assert(!range || current < range->address || within(*range, current)); + assert(!line || current < line->address || within(*line, current)); + + // The next transition after CURRENT. + Module::Address next_transition; + + // Figure out which state we're in, add lines or warn, and compute + // the next transition address. + if (range && current >= range->address) { + if (line && current >= line->address) { + // Covered by both a line and a range. + Module::Address range_left = range->size - (current - range->address); + Module::Address line_left = line->size - (current - line->address); + // This may overflow, but things work out. + next_transition = current + std::min(range_left, line_left); + Module::Line l = *line; + l.address = current; + l.size = next_transition - current; + range->AddLine(l); + last_line_used = line; + } else { + // Covered by a range, but no line. + if (range->function != last_function_cited) { + reporter->UncoveredFunction(*(range->function)); + last_function_cited = range->function; + } + if (line && within(*range, line->address)) + next_transition = line->address; + else + // If this overflows, we'll catch it below. + next_transition = range->address + range->size; + } + } else { + if (line && current >= line->address) { + // Covered by a line, but no range. + // + // If GCC emits padding after one function to align the start + // of the next, then it will attribute the padding + // instructions to the last source line of function (to reduce + // the size of the line number info), but omit it from the + // DW_AT_{low,high}_pc range given in .debug_info (since it + // costs nothing to be precise there). If we did use at least + // some of the line we're about to skip, and it ends at the + // start of the next function, then assume this is what + // happened, and don't warn. + if (line != last_line_cited + && !(range + && line == last_line_used + && range->address - line->address == line->size)) { + reporter->UncoveredLine(*line); + last_line_cited = line; + } + if (range && within(*line, range->address)) + next_transition = range->address; + else + // If this overflows, we'll catch it below. + next_transition = line->address + line->size; + } else { + // Covered by neither a range nor a line. By the invariant, + // both range and line begin after CURRENT. The next transition + // is the start of the next range or next line, whichever + // is earliest. + assert(range || line); + if (range && line) + next_transition = std::min(range->address, line->address); + else if (range) + next_transition = range->address; + else + next_transition = line->address; + } + } + + // If a function or line abuts the end of the address space, then + // next_transition may end up being zero, in which case we've completed + // our pass. Handle that here, instead of trying to deal with it in + // each place we compute next_transition. + if (!next_transition) + break; + + // Advance iterators as needed. If lines overlap or functions overlap, + // then we could go around more than once. We don't worry too much + // about what result we produce in that case, just as long as we don't + // hang or crash. + while (range_it != sorted_ranges.end() + && next_transition >= range_it->address + && !within(*range_it, next_transition)) + range_it++; + range = (range_it != sorted_ranges.end()) ? &(*range_it) : NULL; + while (line_it != lines_.end() + && next_transition >= line_it->address + && !within(*line_it, next_transition)) + line_it++; + line = (line_it != lines_.end()) ? &*line_it : NULL; + + // We must make progress. + assert(next_transition > current); + current = next_transition; + } +} + +void DwarfCUToModule::Finish() { + // Assembly language files have no function data, and that gives us + // no place to store our line numbers (even though the GNU toolchain + // will happily produce source line info for assembly language + // files). To avoid spurious warnings about lines we can't assign + // to functions, skip CUs in languages that lack functions. + if (!cu_context_->language->HasFunctions()) + return; + + // Read source line info, if we have any. + if (has_source_line_info_) + ReadSourceLines(source_line_offset_); + + vector<Module::Function *> *functions = &cu_context_->functions; + + // Dole out lines to the appropriate functions. + AssignLinesToFunctions(); + + // Add our functions, which now have source lines assigned to them, + // to module_. + cu_context_->file_context->module_->AddFunctions(functions->begin(), + functions->end()); + + // Ownership of the function objects has shifted from cu_context to + // the Module. + functions->clear(); + + cu_context_->file_context->ClearSpecifications(); +} + +bool DwarfCUToModule::StartCompilationUnit(uint64 offset, + uint8 address_size, + uint8 offset_size, + uint64 cu_length, + uint8 dwarf_version) { + return dwarf_version >= 2; +} + +bool DwarfCUToModule::StartRootDIE(uint64 offset, enum DwarfTag tag) { + // We don't deal with partial compilation units (the only other tag + // likely to be used for root DIE). + return tag == dwarf2reader::DW_TAG_compile_unit; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf_cu_to_module.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf_cu_to_module.h new file mode 100644 index 0000000000..f73a3c933b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf_cu_to_module.h @@ -0,0 +1,345 @@ +// -*- mode: c++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// Add DWARF debugging information to a Breakpad symbol file. This +// file defines the DwarfCUToModule class, which accepts parsed DWARF +// data and populates a google_breakpad::Module with the results; the +// Module can then write its contents as a Breakpad symbol file. + +#ifndef COMMON_LINUX_DWARF_CU_TO_MODULE_H__ +#define COMMON_LINUX_DWARF_CU_TO_MODULE_H__ + +#include <stdint.h> + +#include <string> + +#include "common/language.h" +#include "common/module.h" +#include "common/dwarf/bytereader.h" +#include "common/dwarf/dwarf2diehandler.h" +#include "common/dwarf/dwarf2reader.h" +#include "common/scoped_ptr.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +using dwarf2reader::DwarfAttribute; +using dwarf2reader::DwarfForm; +using dwarf2reader::DwarfLanguage; +using dwarf2reader::DwarfTag; + +// Populate a google_breakpad::Module with DWARF debugging information. +// +// An instance of this class can be provided as a handler to a +// dwarf2reader::DIEDispatcher, which can in turn be a handler for a +// dwarf2reader::CompilationUnit DWARF parser. The handler uses the results +// of parsing to populate a google_breakpad::Module with source file, +// function, and source line information. +class DwarfCUToModule: public dwarf2reader::RootDIEHandler { + struct FilePrivate; + public: + // Information global to the DWARF-bearing file we are processing, + // for use by DwarfCUToModule. Each DwarfCUToModule instance deals + // with a single compilation unit within the file, but information + // global to the whole file is held here. The client is responsible + // for filling it in appropriately (except for the 'file_private' + // field, which the constructor and destructor take care of), and + // then providing it to the DwarfCUToModule instance for each + // compilation unit we process in that file. Set HANDLE_INTER_CU_REFS + // to true to handle debugging symbols with DW_FORM_ref_addr entries. + class FileContext { + public: + FileContext(const string &filename, + Module *module, + bool handle_inter_cu_refs); + ~FileContext(); + + // Add CONTENTS of size LENGTH to the section map as NAME. + void AddSectionToSectionMap(const string& name, + const uint8_t *contents, + uint64 length); + + // Clear the section map for testing. + void ClearSectionMapForTest(); + + const dwarf2reader::SectionMap& section_map() const; + + private: + friend class DwarfCUToModule; + + // Clears all the Specifications if HANDLE_INTER_CU_REFS_ is false. + void ClearSpecifications(); + + // Given an OFFSET and a CU that starts at COMPILATION_UNIT_START, returns + // true if this is an inter-compilation unit reference that is not being + // handled. + bool IsUnhandledInterCUReference(uint64 offset, + uint64 compilation_unit_start) const; + + // The name of this file, for use in error messages. + const string filename_; + + // A map of this file's sections, used for finding other DWARF + // sections that the .debug_info section may refer to. + dwarf2reader::SectionMap section_map_; + + // The Module to which we're contributing definitions. + Module *module_; + + // True if we are handling references between compilation units. + const bool handle_inter_cu_refs_; + + // Inter-compilation unit data used internally by the handlers. + scoped_ptr<FilePrivate> file_private_; + }; + + // An abstract base class for handlers that handle DWARF range lists for + // DwarfCUToModule. + class RangesHandler { + public: + RangesHandler() { } + virtual ~RangesHandler() { } + + // Called when finishing a function to populate the function's ranges. + // The ranges' entries are read starting from offset in the .debug_ranges + // section, base_address holds the base PC the range list values are + // offsets off. Return false if the rangelist falls out of the + // .debug_ranges section. + virtual bool ReadRanges(uint64 offset, Module::Address base_address, + vector<Module::Range>* ranges) = 0; + }; + + // An abstract base class for handlers that handle DWARF line data + // for DwarfCUToModule. DwarfCUToModule could certainly just use + // dwarf2reader::LineInfo itself directly, but decoupling things + // this way makes unit testing a little easier. + class LineToModuleHandler { + public: + LineToModuleHandler() { } + virtual ~LineToModuleHandler() { } + + // Called at the beginning of a new compilation unit, prior to calling + // ReadProgram(). compilation_dir will indicate the path that the + // current compilation unit was compiled in, consistent with the + // DW_AT_comp_dir DIE. + virtual void StartCompilationUnit(const string& compilation_dir) = 0; + + // Populate MODULE and LINES with source file names and code/line + // mappings, given a pointer to some DWARF line number data + // PROGRAM, and an overestimate of its size. Add no zero-length + // lines to LINES. + virtual void ReadProgram(const uint8_t *program, uint64 length, + Module *module, vector<Module::Line> *lines) = 0; + }; + + // The interface DwarfCUToModule uses to report warnings. The member + // function definitions for this class write messages to stderr, but + // you can override them if you'd like to detect or report these + // conditions yourself. + class WarningReporter { + public: + // Warn about problems in the DWARF file FILENAME, in the + // compilation unit at OFFSET. + WarningReporter(const string &filename, uint64 cu_offset) + : filename_(filename), cu_offset_(cu_offset), printed_cu_header_(false), + printed_unpaired_header_(false), + uncovered_warnings_enabled_(false) { } + virtual ~WarningReporter() { } + + // Set the name of the compilation unit we're processing to NAME. + virtual void SetCUName(const string &name) { cu_name_ = name; } + + // Accessor and setter for uncovered_warnings_enabled_. + // UncoveredFunction and UncoveredLine only report a problem if that is + // true. By default, these warnings are disabled, because those + // conditions occur occasionally in healthy code. + virtual bool uncovered_warnings_enabled() const { + return uncovered_warnings_enabled_; + } + virtual void set_uncovered_warnings_enabled(bool value) { + uncovered_warnings_enabled_ = value; + } + + // A DW_AT_specification in the DIE at OFFSET refers to a DIE we + // haven't processed yet, or that wasn't marked as a declaration, + // at TARGET. + virtual void UnknownSpecification(uint64 offset, uint64 target); + + // A DW_AT_abstract_origin in the DIE at OFFSET refers to a DIE we + // haven't processed yet, or that wasn't marked as inline, at TARGET. + virtual void UnknownAbstractOrigin(uint64 offset, uint64 target); + + // We were unable to find the DWARF section named SECTION_NAME. + virtual void MissingSection(const string §ion_name); + + // The CU's DW_AT_stmt_list offset OFFSET is bogus. + virtual void BadLineInfoOffset(uint64 offset); + + // FUNCTION includes code covered by no line number data. + virtual void UncoveredFunction(const Module::Function &function); + + // Line number NUMBER in LINE_FILE, of length LENGTH, includes code + // covered by no function. + virtual void UncoveredLine(const Module::Line &line); + + // The DW_TAG_subprogram DIE at OFFSET has no name specified directly + // in the DIE, nor via a DW_AT_specification or DW_AT_abstract_origin + // link. + virtual void UnnamedFunction(uint64 offset); + + // __cxa_demangle() failed to demangle INPUT. + virtual void DemangleError(const string &input); + + // The DW_FORM_ref_addr at OFFSET to TARGET was not handled because + // FilePrivate did not retain the inter-CU specification data. + virtual void UnhandledInterCUReference(uint64 offset, uint64 target); + + // The DW_AT_ranges at offset is malformed (truncated or outside of the + // .debug_ranges section's bound). + virtual void MalformedRangeList(uint64 offset); + + // A DW_AT_ranges attribute was encountered but the no .debug_ranges + // section was found. + virtual void MissingRanges(); + + uint64 cu_offset() const { + return cu_offset_; + } + + protected: + const string filename_; + const uint64 cu_offset_; + string cu_name_; + bool printed_cu_header_; + bool printed_unpaired_header_; + bool uncovered_warnings_enabled_; + + private: + // Print a per-CU heading, once. + void CUHeading(); + // Print an unpaired function/line heading, once. + void UncoveredHeading(); + }; + + // Create a DWARF debugging info handler for a compilation unit + // within FILE_CONTEXT. This uses information received from the + // dwarf2reader::CompilationUnit DWARF parser to populate + // FILE_CONTEXT->module. Use LINE_READER to handle the compilation + // unit's line number data. Use REPORTER to report problems with the + // data we find. + DwarfCUToModule(FileContext *file_context, + LineToModuleHandler *line_reader, + RangesHandler *ranges_handler, + WarningReporter *reporter); + ~DwarfCUToModule(); + + void ProcessAttributeSigned(enum DwarfAttribute attr, + enum DwarfForm form, + int64 data); + void ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + void ProcessAttributeString(enum DwarfAttribute attr, + enum DwarfForm form, + const string &data); + bool EndAttributes(); + DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag); + + // Assign all our source Lines to the Functions that cover their + // addresses, and then add them to module_. + void Finish(); + + bool StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version); + bool StartRootDIE(uint64 offset, enum DwarfTag tag); + + private: + // Used internally by the handler. Full definitions are in + // dwarf_cu_to_module.cc. + struct CUContext; + struct DIEContext; + struct Specification; + class GenericDIEHandler; + class FuncHandler; + class NamedScopeHandler; + + // A map from section offsets to specifications. + typedef map<uint64, Specification> SpecificationByOffset; + + // Set this compilation unit's source language to LANGUAGE. + void SetLanguage(DwarfLanguage language); + + // Read source line information at OFFSET in the .debug_line + // section. Record source files in module_, but record source lines + // in lines_; we apportion them to functions in + // AssignLinesToFunctions. + void ReadSourceLines(uint64 offset); + + // Assign the lines in lines_ to the individual line lists of the + // functions in functions_. (DWARF line information maps an entire + // compilation unit at a time, and gives no indication of which + // lines belong to which functions, beyond their addresses.) + void AssignLinesToFunctions(); + + // The only reason cu_context_ and child_context_ are pointers is + // that we want to keep their definitions private to + // dwarf_cu_to_module.cc, instead of listing them all here. They are + // owned by this DwarfCUToModule: the constructor sets them, and the + // destructor deletes them. + + // The handler to use to handle line number data. + LineToModuleHandler *line_reader_; + + // This compilation unit's context. + scoped_ptr<CUContext> cu_context_; + + // A context for our children. + scoped_ptr<DIEContext> child_context_; + + // True if this compilation unit has source line information. + bool has_source_line_info_; + + // The offset of this compilation unit's line number information in + // the .debug_line section. + uint64 source_line_offset_; + + // The line numbers we have seen thus far. We accumulate these here + // during parsing. Then, in Finish, we call AssignLinesToFunctions + // to dole them out to the appropriate functions. + vector<Module::Line> lines_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_DWARF_CU_TO_MODULE_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf_cu_to_module_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf_cu_to_module_unittest.cc new file mode 100644 index 0000000000..aef69221ce --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf_cu_to_module_unittest.cc @@ -0,0 +1,1854 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// dwarf_cu_to_module.cc: Unit tests for google_breakpad::DwarfCUToModule. + +#include <stdint.h> + +#include <string> +#include <utility> +#include <vector> + +#include "breakpad_googletest_includes.h" +#include "common/dwarf_cu_to_module.h" +#include "common/using_std_string.h" + +using std::make_pair; +using std::vector; + +using dwarf2reader::DIEHandler; +using dwarf2reader::DwarfTag; +using dwarf2reader::DwarfAttribute; +using dwarf2reader::DwarfForm; +using dwarf2reader::DwarfInline; +using dwarf2reader::RootDIEHandler; +using google_breakpad::DwarfCUToModule; +using google_breakpad::Module; + +using ::testing::_; +using ::testing::AtMost; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::Test; +using ::testing::TestWithParam; +using ::testing::Values; +using ::testing::ValuesIn; + +// Mock classes. + +class MockLineToModuleHandler: public DwarfCUToModule::LineToModuleHandler { + public: + MOCK_METHOD1(StartCompilationUnit, void(const string& compilation_dir)); + MOCK_METHOD4(ReadProgram, void(const uint8_t *program, uint64 length, + Module *module, vector<Module::Line> *lines)); +}; + +class MockWarningReporter: public DwarfCUToModule::WarningReporter { + public: + MockWarningReporter(const string &filename, uint64 cu_offset) + : DwarfCUToModule::WarningReporter(filename, cu_offset) { } + MOCK_METHOD1(SetCUName, void(const string &name)); + MOCK_METHOD2(UnknownSpecification, void(uint64 offset, uint64 target)); + MOCK_METHOD2(UnknownAbstractOrigin, void(uint64 offset, uint64 target)); + MOCK_METHOD1(MissingSection, void(const string §ion_name)); + MOCK_METHOD1(BadLineInfoOffset, void(uint64 offset)); + MOCK_METHOD1(UncoveredFunction, void(const Module::Function &function)); + MOCK_METHOD1(UncoveredLine, void(const Module::Line &line)); + MOCK_METHOD1(UnnamedFunction, void(uint64 offset)); + MOCK_METHOD1(DemangleError, void(const string &input)); + MOCK_METHOD2(UnhandledInterCUReference, void(uint64 offset, uint64 target)); +}; + +// A fixture class including all the objects needed to handle a +// compilation unit, and their entourage. It includes member functions +// for doing common kinds of setup and tests. +class CUFixtureBase { + public: + // If we have: + // + // vector<Module::Line> lines; + // AppendLinesFunctor appender(lines); + // + // then doing: + // + // appender(line_program, length, module, line_vector); + // + // will append lines to the end of line_vector. We can use this with + // MockLineToModuleHandler like this: + // + // MockLineToModuleHandler l2m; + // EXPECT_CALL(l2m, ReadProgram(_,_,_,_)) + // .WillOnce(DoAll(Invoke(appender), Return())); + // + // in which case calling l2m with some line vector will append lines. + class AppendLinesFunctor { + public: + explicit AppendLinesFunctor( + const vector<Module::Line> *lines) : lines_(lines) { } + void operator()(const uint8_t *program, uint64 length, + Module *module, vector<Module::Line> *lines) { + lines->insert(lines->end(), lines_->begin(), lines_->end()); + } + private: + const vector<Module::Line> *lines_; + }; + + CUFixtureBase() + : module_("module-name", "module-os", "module-arch", "module-id"), + file_context_("dwarf-filename", &module_, true), + language_(dwarf2reader::DW_LANG_none), + language_signed_(false), + appender_(&lines_), + reporter_("dwarf-filename", 0xcf8f9bb6443d29b5LL), + root_handler_(&file_context_, &line_reader_, + /* ranges_reader */ nullptr, &reporter_), + functions_filled_(false) { + // By default, expect no warnings to be reported, and expect the + // compilation unit's name to be provided. The test can override + // these expectations. + EXPECT_CALL(reporter_, SetCUName("compilation-unit-name")).Times(1); + EXPECT_CALL(reporter_, UnknownSpecification(_, _)).Times(0); + EXPECT_CALL(reporter_, UnknownAbstractOrigin(_, _)).Times(0); + EXPECT_CALL(reporter_, MissingSection(_)).Times(0); + EXPECT_CALL(reporter_, BadLineInfoOffset(_)).Times(0); + EXPECT_CALL(reporter_, UncoveredFunction(_)).Times(0); + EXPECT_CALL(reporter_, UncoveredLine(_)).Times(0); + EXPECT_CALL(reporter_, UnnamedFunction(_)).Times(0); + EXPECT_CALL(reporter_, UnhandledInterCUReference(_, _)).Times(0); + + // By default, expect the line program reader not to be invoked. We + // may override this in StartCU. + EXPECT_CALL(line_reader_, StartCompilationUnit(_)).Times(0); + EXPECT_CALL(line_reader_, ReadProgram(_,_,_,_)).Times(0); + + // The handler will consult this section map to decide what to + // pass to our line reader. + file_context_.AddSectionToSectionMap(".debug_line", + dummy_line_program_, + dummy_line_size_); + } + + // Add a line with the given address, size, filename, and line + // number to the end of the statement list the handler will receive + // when it invokes its LineToModuleHandler. Call this before calling + // StartCU. + void PushLine(Module::Address address, Module::Address size, + const string &filename, int line_number); + + // Use LANGUAGE for the compilation unit. More precisely, arrange + // for StartCU to pass the compilation unit's root DIE a + // DW_AT_language attribute whose value is LANGUAGE. + void SetLanguage(dwarf2reader::DwarfLanguage language) { + language_ = language; + } + + // If SIGNED true, have StartCU report DW_AT_language as a signed + // attribute; if false, have it report it as unsigned. + void SetLanguageSigned(bool is_signed) { language_signed_ = is_signed; } + + // Call the handler this.root_handler_'s StartCompilationUnit and + // StartRootDIE member functions, passing it appropriate attributes as + // determined by prior calls to PushLine and SetLanguage. Leave + // this.root_handler_ ready to hear about children: call + // this.root_handler_.EndAttributes, but not this.root_handler_.Finish. + void StartCU(); + + // Have HANDLER process some strange attribute/form/value triples. + void ProcessStrangeAttributes(dwarf2reader::DIEHandler *handler); + + // Start a child DIE of PARENT with the given tag and name. Leave + // the handler ready to hear about children: call EndAttributes, but + // not Finish. + DIEHandler *StartNamedDIE(DIEHandler *parent, DwarfTag tag, + const string &name); + + // Start a child DIE of PARENT with the given tag and a + // DW_AT_specification attribute whose value is SPECIFICATION. Leave + // the handler ready to hear about children: call EndAttributes, but + // not Finish. If NAME is non-zero, use it as the DW_AT_name + // attribute. + DIEHandler *StartSpecifiedDIE(DIEHandler *parent, DwarfTag tag, + uint64 specification, const char *name = NULL); + + // Define a function as a child of PARENT with the given name, address, and + // size. If high_pc_form is DW_FORM_addr then the DW_AT_high_pc attribute + // will be written as an address; otherwise it will be written as the + // function's size. Call EndAttributes and Finish; one cannot define + // children of the defined function's DIE. + void DefineFunction(DIEHandler *parent, const string &name, + Module::Address address, Module::Address size, + const char* mangled_name, + DwarfForm high_pc_form = dwarf2reader::DW_FORM_addr); + + // Create a declaration DIE as a child of PARENT with the given + // offset, tag and name. If NAME is the empty string, don't provide + // a DW_AT_name attribute. Call EndAttributes and Finish. + void DeclarationDIE(DIEHandler *parent, uint64 offset, + DwarfTag tag, const string &name, + const string &mangled_name); + + // Create a definition DIE as a child of PARENT with the given tag + // that refers to the declaration DIE at offset SPECIFICATION as its + // specification. If NAME is non-empty, pass it as the DW_AT_name + // attribute. If SIZE is non-zero, record ADDRESS and SIZE as + // low_pc/high_pc attributes. + void DefinitionDIE(DIEHandler *parent, DwarfTag tag, + uint64 specification, const string &name, + Module::Address address = 0, Module::Address size = 0); + + // Create an inline DW_TAG_subprogram DIE as a child of PARENT. If + // SPECIFICATION is non-zero, then the DIE refers to the declaration DIE at + // offset SPECIFICATION as its specification. If Name is non-empty, pass it + // as the DW_AT_name attribute. + void AbstractInstanceDIE(DIEHandler *parent, uint64 offset, + DwarfInline type, uint64 specification, + const string &name, + DwarfForm form = dwarf2reader::DW_FORM_data1); + + // Create a DW_TAG_subprogram DIE as a child of PARENT that refers to + // ORIGIN in its DW_AT_abstract_origin attribute. If NAME is the empty + // string, don't provide a DW_AT_name attribute. + void DefineInlineInstanceDIE(DIEHandler *parent, const string &name, + uint64 origin, Module::Address address, + Module::Address size); + + // The following Test* functions should be called after calling + // this.root_handler_.Finish. After that point, no further calls + // should be made on the handler. + + // Test that the number of functions defined in the module this.module_ is + // equal to EXPECTED. + void TestFunctionCount(size_t expected); + + // Test that the I'th function (ordered by address) in the module + // this.module_ has the given name, address, and size, and that its + // parameter size is zero. + void TestFunction(int i, const string &name, + Module::Address address, Module::Address size); + + // Test that the number of source lines owned by the I'th function + // in the module this.module_ is equal to EXPECTED. + void TestLineCount(int i, size_t expected); + + // Test that the J'th line (ordered by address) of the I'th function + // (again, by address) has the given address, size, filename, and + // line number. + void TestLine(int i, int j, Module::Address address, Module::Address size, + const string &filename, int number); + + // Actual objects under test. + Module module_; + DwarfCUToModule::FileContext file_context_; + + // If this is not DW_LANG_none, we'll pass it as a DW_AT_language + // attribute to the compilation unit. This defaults to DW_LANG_none. + dwarf2reader::DwarfLanguage language_; + + // If this is true, report DW_AT_language as a signed value; if false, + // report it as an unsigned value. + bool language_signed_; + + // If this is not empty, we'll give the CU a DW_AT_comp_dir attribute that + // indicates the path that this compilation unit was compiled in. + string compilation_dir_; + + // If this is not empty, we'll give the CU a DW_AT_stmt_list + // attribute that, when passed to line_reader_, adds these lines to the + // provided lines array. + vector<Module::Line> lines_; + + // Mock line program reader. + MockLineToModuleHandler line_reader_; + AppendLinesFunctor appender_; + static const uint8_t dummy_line_program_[]; + static const size_t dummy_line_size_; + + MockWarningReporter reporter_; + DwarfCUToModule root_handler_; + + private: + // Fill functions_, if we haven't already. + void FillFunctions(); + + // If functions_filled_ is true, this is a table of functions we've + // extracted from module_, sorted by address. + vector<Module::Function *> functions_; + // True if we have filled the above vector with this.module_'s function list. + bool functions_filled_; +}; + +const uint8_t CUFixtureBase::dummy_line_program_[] = "lots of fun data"; +const size_t CUFixtureBase::dummy_line_size_ = + sizeof(CUFixtureBase::dummy_line_program_); + +void CUFixtureBase::PushLine(Module::Address address, Module::Address size, + const string &filename, int line_number) { + Module::Line l; + l.address = address; + l.size = size; + l.file = module_.FindFile(filename); + l.number = line_number; + lines_.push_back(l); +} + +void CUFixtureBase::StartCU() { + if (!compilation_dir_.empty()) + EXPECT_CALL(line_reader_, + StartCompilationUnit(compilation_dir_)).Times(1); + + // If we have lines, make the line reader expect to be invoked at + // most once. (Hey, if the handler can pass its tests without + // bothering to read the line number data, that's great.) + // Have it add the lines passed to PushLine. Otherwise, leave the + // initial expectation (no calls) in force. + if (!lines_.empty()) + EXPECT_CALL(line_reader_, + ReadProgram(&dummy_line_program_[0], dummy_line_size_, + &module_, _)) + .Times(AtMost(1)) + .WillOnce(DoAll(Invoke(appender_), Return())); + + ASSERT_TRUE(root_handler_ + .StartCompilationUnit(0x51182ec307610b51ULL, 0x81, 0x44, + 0x4241b4f33720dd5cULL, 3)); + { + ASSERT_TRUE(root_handler_.StartRootDIE(0x02e56bfbda9e7337ULL, + dwarf2reader::DW_TAG_compile_unit)); + } + root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + "compilation-unit-name"); + if (!compilation_dir_.empty()) + root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_comp_dir, + dwarf2reader::DW_FORM_strp, + compilation_dir_); + if (!lines_.empty()) + root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_stmt_list, + dwarf2reader::DW_FORM_ref4, + 0); + if (language_ != dwarf2reader::DW_LANG_none) { + if (language_signed_) + root_handler_.ProcessAttributeSigned(dwarf2reader::DW_AT_language, + dwarf2reader::DW_FORM_sdata, + language_); + else + root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_language, + dwarf2reader::DW_FORM_udata, + language_); + } + ASSERT_TRUE(root_handler_.EndAttributes()); +} + +void CUFixtureBase::ProcessStrangeAttributes( + dwarf2reader::DIEHandler *handler) { + handler->ProcessAttributeUnsigned((DwarfAttribute) 0xf560dead, + (DwarfForm) 0x4106e4db, + 0xa592571997facda1ULL); + handler->ProcessAttributeSigned((DwarfAttribute) 0x85380095, + (DwarfForm) 0x0f16fe87, + 0x12602a4e3bf1f446LL); + handler->ProcessAttributeReference((DwarfAttribute) 0xf7f7480f, + (DwarfForm) 0x829e038a, + 0x50fddef44734fdecULL); + static const uint8_t buffer[10] = "frobynode"; + handler->ProcessAttributeBuffer((DwarfAttribute) 0xa55ffb51, + (DwarfForm) 0x2f43b041, + buffer, sizeof(buffer)); + handler->ProcessAttributeString((DwarfAttribute) 0x2f43b041, + (DwarfForm) 0x895ffa23, + "strange string"); +} + +DIEHandler *CUFixtureBase::StartNamedDIE(DIEHandler *parent, + DwarfTag tag, + const string &name) { + dwarf2reader::DIEHandler *handler + = parent->FindChildHandler(0x8f4c783c0467c989ULL, tag); + if (!handler) + return NULL; + handler->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + ProcessStrangeAttributes(handler); + if (!handler->EndAttributes()) { + handler->Finish(); + delete handler; + return NULL; + } + + return handler; +} + +DIEHandler *CUFixtureBase::StartSpecifiedDIE(DIEHandler *parent, + DwarfTag tag, + uint64 specification, + const char *name) { + dwarf2reader::DIEHandler *handler + = parent->FindChildHandler(0x8f4c783c0467c989ULL, tag); + if (!handler) + return NULL; + if (name) + handler->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + handler->ProcessAttributeReference(dwarf2reader::DW_AT_specification, + dwarf2reader::DW_FORM_ref4, + specification); + if (!handler->EndAttributes()) { + handler->Finish(); + delete handler; + return NULL; + } + + return handler; +} + +void CUFixtureBase::DefineFunction(dwarf2reader::DIEHandler *parent, + const string &name, Module::Address address, + Module::Address size, + const char* mangled_name, + DwarfForm high_pc_form) { + dwarf2reader::DIEHandler *func + = parent->FindChildHandler(0xe34797c7e68590a8LL, + dwarf2reader::DW_TAG_subprogram); + ASSERT_TRUE(func != NULL); + func->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr, + address); + + Module::Address high_pc = size; + if (high_pc_form == dwarf2reader::DW_FORM_addr) { + high_pc += address; + } + func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc, + high_pc_form, + high_pc); + + if (mangled_name) + func->ProcessAttributeString(dwarf2reader::DW_AT_MIPS_linkage_name, + dwarf2reader::DW_FORM_strp, + mangled_name); + + ProcessStrangeAttributes(func); + EXPECT_TRUE(func->EndAttributes()); + func->Finish(); + delete func; +} + +void CUFixtureBase::DeclarationDIE(DIEHandler *parent, uint64 offset, + DwarfTag tag, + const string &name, + const string &mangled_name) { + dwarf2reader::DIEHandler *die = parent->FindChildHandler(offset, tag); + ASSERT_TRUE(die != NULL); + if (!name.empty()) + die->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + if (!mangled_name.empty()) + die->ProcessAttributeString(dwarf2reader::DW_AT_MIPS_linkage_name, + dwarf2reader::DW_FORM_strp, + mangled_name); + + die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_declaration, + dwarf2reader::DW_FORM_flag, + 1); + EXPECT_TRUE(die->EndAttributes()); + die->Finish(); + delete die; +} + +void CUFixtureBase::DefinitionDIE(DIEHandler *parent, + DwarfTag tag, + uint64 specification, + const string &name, + Module::Address address, + Module::Address size) { + dwarf2reader::DIEHandler *die + = parent->FindChildHandler(0x6ccfea031a9e6cc9ULL, tag); + ASSERT_TRUE(die != NULL); + die->ProcessAttributeReference(dwarf2reader::DW_AT_specification, + dwarf2reader::DW_FORM_ref4, + specification); + if (!name.empty()) + die->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + if (size) { + die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr, + address); + die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc, + dwarf2reader::DW_FORM_addr, + address + size); + } + EXPECT_TRUE(die->EndAttributes()); + die->Finish(); + delete die; +} + +void CUFixtureBase::AbstractInstanceDIE(DIEHandler *parent, + uint64 offset, + DwarfInline type, + uint64 specification, + const string &name, + DwarfForm form) { + dwarf2reader::DIEHandler *die + = parent->FindChildHandler(offset, dwarf2reader::DW_TAG_subprogram); + ASSERT_TRUE(die != NULL); + if (specification != 0ULL) + die->ProcessAttributeReference(dwarf2reader::DW_AT_specification, + dwarf2reader::DW_FORM_ref4, + specification); + if (form == dwarf2reader::DW_FORM_sdata) { + die->ProcessAttributeSigned(dwarf2reader::DW_AT_inline, form, type); + } else { + die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_inline, form, type); + } + if (!name.empty()) + die->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + + EXPECT_TRUE(die->EndAttributes()); + die->Finish(); + delete die; +} + +void CUFixtureBase::DefineInlineInstanceDIE(DIEHandler *parent, + const string &name, + uint64 origin, + Module::Address address, + Module::Address size) { + dwarf2reader::DIEHandler *func + = parent->FindChildHandler(0x11c70f94c6e87ccdLL, + dwarf2reader::DW_TAG_subprogram); + ASSERT_TRUE(func != NULL); + if (!name.empty()) { + func->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + } + func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr, + address); + func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc, + dwarf2reader::DW_FORM_addr, + address + size); + func->ProcessAttributeReference(dwarf2reader::DW_AT_abstract_origin, + dwarf2reader::DW_FORM_ref4, + origin); + ProcessStrangeAttributes(func); + EXPECT_TRUE(func->EndAttributes()); + func->Finish(); + delete func; +} + +void CUFixtureBase::FillFunctions() { + if (functions_filled_) + return; + module_.GetFunctions(&functions_, functions_.end()); + sort(functions_.begin(), functions_.end(), + Module::Function::CompareByAddress); + functions_filled_ = true; +} + +void CUFixtureBase::TestFunctionCount(size_t expected) { + FillFunctions(); + ASSERT_EQ(expected, functions_.size()); +} + +void CUFixtureBase::TestFunction(int i, const string &name, + Module::Address address, + Module::Address size) { + FillFunctions(); + ASSERT_LT((size_t) i, functions_.size()); + + Module::Function *function = functions_[i]; + EXPECT_EQ(name, function->name); + EXPECT_EQ(address, function->address); + EXPECT_EQ(size, function->ranges[0].size); + EXPECT_EQ(0U, function->parameter_size); +} + +void CUFixtureBase::TestLineCount(int i, size_t expected) { + FillFunctions(); + ASSERT_LT((size_t) i, functions_.size()); + + ASSERT_EQ(expected, functions_[i]->lines.size()); +} + +void CUFixtureBase::TestLine(int i, int j, + Module::Address address, Module::Address size, + const string &filename, int number) { + FillFunctions(); + ASSERT_LT((size_t) i, functions_.size()); + ASSERT_LT((size_t) j, functions_[i]->lines.size()); + + Module::Line *line = &functions_[i]->lines[j]; + EXPECT_EQ(address, line->address); + EXPECT_EQ(size, line->size); + EXPECT_EQ(filename, line->file->name.c_str()); + EXPECT_EQ(number, line->number); +} + +// Include caller locations for our test subroutines. +#define TRACE(call) do { SCOPED_TRACE("called from here"); call; } while (0) +#define PushLine(a,b,c,d) TRACE(PushLine((a),(b),(c),(d))) +#define SetLanguage(a) TRACE(SetLanguage(a)) +#define StartCU() TRACE(StartCU()) +#define DefineFunction(a,b,c,d,e) TRACE(DefineFunction((a),(b),(c),(d),(e))) +// (DefineFunction) instead of DefineFunction to avoid macro expansion. +#define DefineFunction6(a,b,c,d,e,f) \ + TRACE((DefineFunction)((a),(b),(c),(d),(e),(f))) +#define DeclarationDIE(a,b,c,d,e) TRACE(DeclarationDIE((a),(b),(c),(d),(e))) +#define DefinitionDIE(a,b,c,d,e,f) \ + TRACE(DefinitionDIE((a),(b),(c),(d),(e),(f))) +#define TestFunctionCount(a) TRACE(TestFunctionCount(a)) +#define TestFunction(a,b,c,d) TRACE(TestFunction((a),(b),(c),(d))) +#define TestLineCount(a,b) TRACE(TestLineCount((a),(b))) +#define TestLine(a,b,c,d,e,f) TRACE(TestLine((a),(b),(c),(d),(e),(f))) + +class SimpleCU: public CUFixtureBase, public Test { +}; + +TEST_F(SimpleCU, CompilationDir) { + compilation_dir_ = "/src/build/"; + + StartCU(); + root_handler_.Finish(); +} + +TEST_F(SimpleCU, OneFunc) { + PushLine(0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", 246571772); + + StartCU(); + DefineFunction(&root_handler_, "function1", + 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, NULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "function1", 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL); + TestLineCount(0, 1); + TestLine(0, 0, 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", + 246571772); +} + +// As above, only DW_AT_high_pc is a length rather than an address. +TEST_F(SimpleCU, OneFuncHighPcIsLength) { + PushLine(0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", 246571772); + + StartCU(); + DefineFunction6(&root_handler_, "function1", + 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, NULL, + dwarf2reader::DW_FORM_udata); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "function1", 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL); + TestLineCount(0, 1); + TestLine(0, 0, 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", + 246571772); +} + +TEST_F(SimpleCU, MangledName) { + PushLine(0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", 246571772); + + StartCU(); + DefineFunction(&root_handler_, "function1", + 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "_ZN1n1fEi"); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "n::f(int)", 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL); +} + +TEST_F(SimpleCU, IrrelevantRootChildren) { + StartCU(); + EXPECT_FALSE(root_handler_ + .FindChildHandler(0x7db32bff4e2dcfb1ULL, + dwarf2reader::DW_TAG_lexical_block)); +} + +TEST_F(SimpleCU, IrrelevantNamedScopeChildren) { + StartCU(); + DIEHandler *class_A_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "class_A"); + EXPECT_TRUE(class_A_handler != NULL); + EXPECT_FALSE(class_A_handler + ->FindChildHandler(0x02e55999b865e4e9ULL, + dwarf2reader::DW_TAG_lexical_block)); + delete class_A_handler; +} + +// Verify that FileContexts can safely be deleted unused. +TEST_F(SimpleCU, UnusedFileContext) { + Module m("module-name", "module-os", "module-arch", "module-id"); + DwarfCUToModule::FileContext fc("dwarf-filename", &m, true); + + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); +} + +TEST_F(SimpleCU, InlineFunction) { + PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); + + StartCU(); + AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL, + dwarf2reader::DW_INL_inlined, 0, "inline-name"); + DefineInlineInstanceDIE(&root_handler_, "", 0x1e8dac5d507ed7abULL, + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "inline-name", + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); +} + +TEST_F(SimpleCU, InlineFunctionSignedAttribute) { + PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); + + StartCU(); + AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL, + dwarf2reader::DW_INL_inlined, 0, "inline-name", + dwarf2reader::DW_FORM_sdata); + DefineInlineInstanceDIE(&root_handler_, "", 0x1e8dac5d507ed7abULL, + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "inline-name", + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); +} + +// Any DIE with an DW_AT_inline attribute can be cited by +// DW_AT_abstract_origin attributes --- even if the value of the +// DW_AT_inline attribute is DW_INL_not_inlined. +TEST_F(SimpleCU, AbstractOriginNotInlined) { + PushLine(0x2805c4531be6ca0eULL, 0x686b52155a8d4d2cULL, "line-file", 6111581); + + StartCU(); + AbstractInstanceDIE(&root_handler_, 0x93e9cdad52826b39ULL, + dwarf2reader::DW_INL_not_inlined, 0, "abstract-instance"); + DefineInlineInstanceDIE(&root_handler_, "", 0x93e9cdad52826b39ULL, + 0x2805c4531be6ca0eULL, 0x686b52155a8d4d2cULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "abstract-instance", + 0x2805c4531be6ca0eULL, 0x686b52155a8d4d2cULL); +} + +TEST_F(SimpleCU, UnknownAbstractOrigin) { + EXPECT_CALL(reporter_, UnknownAbstractOrigin(_, 1ULL)).WillOnce(Return()); + EXPECT_CALL(reporter_, UnnamedFunction(0x11c70f94c6e87ccdLL)) + .WillOnce(Return()); + PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); + + StartCU(); + AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL, + dwarf2reader::DW_INL_inlined, 0, "inline-name"); + DefineInlineInstanceDIE(&root_handler_, "", 1ULL, + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "<name omitted>", + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); +} + +TEST_F(SimpleCU, UnnamedFunction) { + EXPECT_CALL(reporter_, UnnamedFunction(0xe34797c7e68590a8LL)) + .WillOnce(Return()); + PushLine(0x72b80e41a0ac1d40ULL, 0x537174f231ee181cULL, "line-file", 14044850); + + StartCU(); + DefineFunction(&root_handler_, "", + 0x72b80e41a0ac1d40ULL, 0x537174f231ee181cULL, NULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "<name omitted>", + 0x72b80e41a0ac1d40ULL, 0x537174f231ee181cULL); +} + +// An address range. +struct Range { + Module::Address start, end; +}; + +// Test data for pairing functions and lines. +struct Situation { + // Two function intervals, and two line intervals. + Range functions[2], lines[2]; + + // The number of lines we expect to be assigned to each of the + // functions, and the address ranges. + int paired_count[2]; + Range paired[2][2]; + + // The number of functions that are not entirely covered by lines, + // and vice versa. + int uncovered_functions, uncovered_lines; +}; + +#define PAIRING(func1_start, func1_end, func2_start, func2_end, \ + line1_start, line1_end, line2_start, line2_end, \ + func1_num_lines, func2_num_lines, \ + func1_line1_start, func1_line1_end, \ + func1_line2_start, func1_line2_end, \ + func2_line1_start, func2_line1_end, \ + func2_line2_start, func2_line2_end, \ + uncovered_functions, uncovered_lines) \ + { { { func1_start, func1_end }, { func2_start, func2_end } }, \ + { { line1_start, line1_end }, { line2_start, line2_end } }, \ + { func1_num_lines, func2_num_lines }, \ + { { { func1_line1_start, func1_line1_end }, \ + { func1_line2_start, func1_line2_end } }, \ + { { func2_line1_start, func2_line1_end }, \ + { func2_line2_start, func2_line2_end } } }, \ + uncovered_functions, uncovered_lines }, + +Situation situations[] = { +#include "common/testdata/func-line-pairing.h" +}; + +#undef PAIRING + +class FuncLinePairing: public CUFixtureBase, + public TestWithParam<Situation> { }; + +INSTANTIATE_TEST_CASE_P(AllSituations, FuncLinePairing, + ValuesIn(situations)); + +TEST_P(FuncLinePairing, Pairing) { + const Situation &s = GetParam(); + PushLine(s.lines[0].start, + s.lines[0].end - s.lines[0].start, + "line-file", 67636963); + PushLine(s.lines[1].start, + s.lines[1].end - s.lines[1].start, + "line-file", 67636963); + if (s.uncovered_functions) + EXPECT_CALL(reporter_, UncoveredFunction(_)) + .Times(s.uncovered_functions) + .WillRepeatedly(Return()); + if (s.uncovered_lines) + EXPECT_CALL(reporter_, UncoveredLine(_)) + .Times(s.uncovered_lines) + .WillRepeatedly(Return()); + + StartCU(); + DefineFunction(&root_handler_, "function1", + s.functions[0].start, + s.functions[0].end - s.functions[0].start, NULL); + DefineFunction(&root_handler_, "function2", + s.functions[1].start, + s.functions[1].end - s.functions[1].start, NULL); + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "function1", + s.functions[0].start, + s.functions[0].end - s.functions[0].start); + TestLineCount(0, s.paired_count[0]); + for (int i = 0; i < s.paired_count[0]; i++) + TestLine(0, i, s.paired[0][i].start, + s.paired[0][i].end - s.paired[0][i].start, + "line-file", 67636963); + TestFunction(1, "function2", + s.functions[1].start, + s.functions[1].end - s.functions[1].start); + TestLineCount(1, s.paired_count[1]); + for (int i = 0; i < s.paired_count[1]; i++) + TestLine(1, i, s.paired[1][i].start, + s.paired[1][i].end - s.paired[1][i].start, + "line-file", 67636963); +} + +TEST_F(FuncLinePairing, EmptyCU) { + StartCU(); + root_handler_.Finish(); + + TestFunctionCount(0); +} + +TEST_F(FuncLinePairing, LinesNoFuncs) { + PushLine(40, 2, "line-file", 82485646); + EXPECT_CALL(reporter_, UncoveredLine(_)).WillOnce(Return()); + + StartCU(); + root_handler_.Finish(); + + TestFunctionCount(0); +} + +TEST_F(FuncLinePairing, FuncsNoLines) { + EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); + + StartCU(); + DefineFunction(&root_handler_, "function1", 0x127da12ffcf5c51fULL, 0x1000U, + NULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "function1", 0x127da12ffcf5c51fULL, 0x1000U); +} + +TEST_F(FuncLinePairing, GapThenFunction) { + PushLine(20, 2, "line-file-2", 174314698); + PushLine(10, 2, "line-file-1", 263008005); + + StartCU(); + DefineFunction(&root_handler_, "function1", 10, 2, NULL); + DefineFunction(&root_handler_, "function2", 20, 2, NULL); + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "function1", 10, 2); + TestLineCount(0, 1); + TestLine(0, 0, 10, 2, "line-file-1", 263008005); + TestFunction(1, "function2", 20, 2); + TestLineCount(1, 1); + TestLine(1, 0, 20, 2, "line-file-2", 174314698); +} + +// If GCC emits padding after one function to align the start of +// the next, then it will attribute the padding instructions to +// the last source line of function (to reduce the size of the +// line number info), but omit it from the DW_AT_{low,high}_pc +// range given in .debug_info (since it costs nothing to be +// precise there). If we did use at least some of the line +// we're about to skip, then assume this is what happened, and +// don't warn. +TEST_F(FuncLinePairing, GCCAlignmentStretch) { + PushLine(10, 10, "line-file", 63351048); + PushLine(20, 10, "line-file", 61661044); + + StartCU(); + DefineFunction(&root_handler_, "function1", 10, 5, NULL); + // five-byte gap between functions, covered by line 63351048. + // This should not elicit a warning. + DefineFunction(&root_handler_, "function2", 20, 10, NULL); + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "function1", 10, 5); + TestLineCount(0, 1); + TestLine(0, 0, 10, 5, "line-file", 63351048); + TestFunction(1, "function2", 20, 10); + TestLineCount(1, 1); + TestLine(1, 0, 20, 10, "line-file", 61661044); +} + +// Unfortunately, neither the DWARF parser's handler interface nor the +// DIEHandler interface is capable of expressing a function that abuts +// the end of the address space: the high_pc value looks like zero. + +TEST_F(FuncLinePairing, LineAtEndOfAddressSpace) { + PushLine(0xfffffffffffffff0ULL, 16, "line-file", 63351048); + EXPECT_CALL(reporter_, UncoveredLine(_)).WillOnce(Return()); + + StartCU(); + DefineFunction(&root_handler_, "function1", 0xfffffffffffffff0ULL, 6, NULL); + DefineFunction(&root_handler_, "function2", 0xfffffffffffffffaULL, 5, NULL); + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "function1", 0xfffffffffffffff0ULL, 6); + TestLineCount(0, 1); + TestLine(0, 0, 0xfffffffffffffff0ULL, 6, "line-file", 63351048); + TestFunction(1, "function2", 0xfffffffffffffffaULL, 5); + TestLineCount(1, 1); + TestLine(1, 0, 0xfffffffffffffffaULL, 5, "line-file", 63351048); +} + +// A function with more than one uncovered area should only be warned +// about once. +TEST_F(FuncLinePairing, WarnOnceFunc) { + PushLine(20, 1, "line-file-2", 262951329); + PushLine(11, 1, "line-file-1", 219964021); + EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); + + StartCU(); + DefineFunction(&root_handler_, "function", 10, 11, NULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "function", 10, 11); + TestLineCount(0, 2); + TestLine(0, 0, 11, 1, "line-file-1", 219964021); + TestLine(0, 1, 20, 1, "line-file-2", 262951329); +} + +// A line with more than one uncovered area should only be warned +// about once. +TEST_F(FuncLinePairing, WarnOnceLine) { + PushLine(10, 20, "filename1", 118581871); + EXPECT_CALL(reporter_, UncoveredLine(_)).WillOnce(Return()); + + StartCU(); + DefineFunction(&root_handler_, "function1", 11, 1, NULL); + DefineFunction(&root_handler_, "function2", 13, 1, NULL); + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "function1", 11, 1); + TestLineCount(0, 1); + TestLine(0, 0, 11, 1, "filename1", 118581871); + TestFunction(1, "function2", 13, 1); + TestLineCount(1, 1); + TestLine(1, 0, 13, 1, "filename1", 118581871); +} + +class CXXQualifiedNames: public CUFixtureBase, + public TestWithParam<DwarfTag> { }; + +INSTANTIATE_TEST_CASE_P(VersusEnclosures, CXXQualifiedNames, + Values(dwarf2reader::DW_TAG_class_type, + dwarf2reader::DW_TAG_structure_type, + dwarf2reader::DW_TAG_union_type, + dwarf2reader::DW_TAG_namespace)); + +TEST_P(CXXQualifiedNames, TwoFunctions) { + DwarfTag tag = GetParam(); + + SetLanguage(dwarf2reader::DW_LANG_C_plus_plus); + PushLine(10, 1, "filename1", 69819327); + PushLine(20, 1, "filename2", 95115701); + + StartCU(); + DIEHandler *enclosure_handler = StartNamedDIE(&root_handler_, tag, + "Enclosure"); + EXPECT_TRUE(enclosure_handler != NULL); + DefineFunction(enclosure_handler, "func_B", 10, 1, NULL); + DefineFunction(enclosure_handler, "func_C", 20, 1, NULL); + enclosure_handler->Finish(); + delete enclosure_handler; + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "Enclosure::func_B", 10, 1); + TestFunction(1, "Enclosure::func_C", 20, 1); +} + +TEST_P(CXXQualifiedNames, FuncInEnclosureInNamespace) { + DwarfTag tag = GetParam(); + + SetLanguage(dwarf2reader::DW_LANG_C_plus_plus); + PushLine(10, 1, "line-file", 69819327); + + StartCU(); + DIEHandler *namespace_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, + "Namespace"); + EXPECT_TRUE(namespace_handler != NULL); + DIEHandler *enclosure_handler = StartNamedDIE(namespace_handler, tag, + "Enclosure"); + EXPECT_TRUE(enclosure_handler != NULL); + DefineFunction(enclosure_handler, "function", 10, 1, NULL); + enclosure_handler->Finish(); + delete enclosure_handler; + namespace_handler->Finish(); + delete namespace_handler; + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "Namespace::Enclosure::function", 10, 1); +} + +TEST_F(CXXQualifiedNames, FunctionInClassInStructInNamespace) { + SetLanguage(dwarf2reader::DW_LANG_C_plus_plus); + PushLine(10, 1, "filename1", 69819327); + + StartCU(); + DIEHandler *namespace_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, + "namespace_A"); + EXPECT_TRUE(namespace_handler != NULL); + DIEHandler *struct_handler + = StartNamedDIE(namespace_handler, dwarf2reader::DW_TAG_structure_type, + "struct_B"); + EXPECT_TRUE(struct_handler != NULL); + DIEHandler *class_handler + = StartNamedDIE(struct_handler, dwarf2reader::DW_TAG_class_type, + "class_C"); + DefineFunction(class_handler, "function_D", 10, 1, NULL); + class_handler->Finish(); + delete class_handler; + struct_handler->Finish(); + delete struct_handler; + namespace_handler->Finish(); + delete namespace_handler; + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "namespace_A::struct_B::class_C::function_D", 10, 1); +} + +struct LanguageAndQualifiedName { + dwarf2reader::DwarfLanguage language; + const char *name; +}; + +const LanguageAndQualifiedName LanguageAndQualifiedNameCases[] = { + { dwarf2reader::DW_LANG_none, "class_A::function_B" }, + { dwarf2reader::DW_LANG_C, "class_A::function_B" }, + { dwarf2reader::DW_LANG_C89, "class_A::function_B" }, + { dwarf2reader::DW_LANG_C99, "class_A::function_B" }, + { dwarf2reader::DW_LANG_C_plus_plus, "class_A::function_B" }, + { dwarf2reader::DW_LANG_Java, "class_A.function_B" }, + { dwarf2reader::DW_LANG_Cobol74, "class_A::function_B" }, + { dwarf2reader::DW_LANG_Mips_Assembler, NULL } +}; + +class QualifiedForLanguage + : public CUFixtureBase, + public TestWithParam<LanguageAndQualifiedName> { }; + +INSTANTIATE_TEST_CASE_P(LanguageAndQualifiedName, QualifiedForLanguage, + ValuesIn(LanguageAndQualifiedNameCases)); + +TEST_P(QualifiedForLanguage, MemberFunction) { + const LanguageAndQualifiedName ¶m = GetParam(); + + PushLine(10, 1, "line-file", 212966758); + SetLanguage(param.language); + + StartCU(); + DIEHandler *class_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + "class_A"); + DefineFunction(class_handler, "function_B", 10, 1, NULL); + class_handler->Finish(); + delete class_handler; + root_handler_.Finish(); + + if (param.name) { + TestFunctionCount(1); + TestFunction(0, param.name, 10, 1); + } else { + TestFunctionCount(0); + } +} + +TEST_P(QualifiedForLanguage, MemberFunctionSignedLanguage) { + const LanguageAndQualifiedName ¶m = GetParam(); + + PushLine(10, 1, "line-file", 212966758); + SetLanguage(param.language); + SetLanguageSigned(true); + + StartCU(); + DIEHandler *class_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + "class_A"); + DefineFunction(class_handler, "function_B", 10, 1, NULL); + class_handler->Finish(); + delete class_handler; + root_handler_.Finish(); + + if (param.name) { + TestFunctionCount(1); + TestFunction(0, param.name, 10, 1); + } else { + TestFunctionCount(0); + } +} + +class Specifications: public CUFixtureBase, public Test { }; + +TEST_F(Specifications, Function) { + PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661); + + StartCU(); + DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL, + dwarf2reader::DW_TAG_subprogram, "declaration-name", ""); + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0xcd3c51b946fb1eeeLL, "", + 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "declaration-name", + 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); +} + +TEST_F(Specifications, MangledName) { + // Language defaults to C++, so no need to set it here. + PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661); + + StartCU(); + DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL, + dwarf2reader::DW_TAG_subprogram, "declaration-name", + "_ZN1C1fEi"); + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0xcd3c51b946fb1eeeLL, "", + 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "C::f(int)", + 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); +} + +TEST_F(Specifications, MangledNameSwift) { + // Swift mangled names should pass through untouched. + SetLanguage(dwarf2reader::DW_LANG_Swift); + PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661); + StartCU(); + const string kName = "_TFC9swifttest5Shape17simpleDescriptionfS0_FT_Si"; + DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL, + dwarf2reader::DW_TAG_subprogram, "declaration-name", + kName); + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0xcd3c51b946fb1eeeLL, "", + 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, kName, + 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); +} + +TEST_F(Specifications, MangledNameRust) { + SetLanguage(dwarf2reader::DW_LANG_Rust); + PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661); + + StartCU(); + const string kName = "_ZN14rustc_demangle8demangle17h373defa94bffacdeE"; + DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL, + dwarf2reader::DW_TAG_subprogram, "declaration-name", + kName); + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0xcd3c51b946fb1eeeLL, "", + 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, +#ifndef HAVE_RUST_DEMANGLE + // Rust mangled names should pass through untouched if not + // using rust-demangle. + kName, +#else + // If rust-demangle is available this should be properly + // demangled. + "rustc_demangle::demangle", +#endif + 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); +} + +TEST_F(Specifications, MemberFunction) { + PushLine(0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL, "line-file", 18116691); + + StartCU(); + DIEHandler *class_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "class_A"); + DeclarationDIE(class_handler, 0x7d83028c431406e8ULL, + dwarf2reader::DW_TAG_subprogram, "declaration-name", ""); + class_handler->Finish(); + delete class_handler; + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0x7d83028c431406e8ULL, "", + 0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "class_A::declaration-name", + 0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL); +} + +// This case should gather the name from both the definition and the +// declaration's parent. +TEST_F(Specifications, FunctionDeclarationParent) { + PushLine(0x463c9ddf405be227ULL, 0x6a47774af5049680ULL, "line-file", 70254922); + + StartCU(); + { + DIEHandler *class_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + "class_A"); + ASSERT_TRUE(class_handler != NULL); + DeclarationDIE(class_handler, 0x0e0e877c8404544aULL, + dwarf2reader::DW_TAG_subprogram, "declaration-name", ""); + class_handler->Finish(); + delete class_handler; + } + + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0x0e0e877c8404544aULL, "definition-name", + 0x463c9ddf405be227ULL, 0x6a47774af5049680ULL); + + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "class_A::definition-name", + 0x463c9ddf405be227ULL, 0x6a47774af5049680ULL); +} + +// Named scopes should also gather enclosing name components from +// their declarations. +TEST_F(Specifications, NamedScopeDeclarationParent) { + PushLine(0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL, "line-file", 77392604); + + StartCU(); + { + DIEHandler *space_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, + "space_A"); + ASSERT_TRUE(space_handler != NULL); + DeclarationDIE(space_handler, 0x419bb1d12f9a73a2ULL, + dwarf2reader::DW_TAG_class_type, "class-declaration-name", + ""); + space_handler->Finish(); + delete space_handler; + } + + { + DIEHandler *class_handler + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + 0x419bb1d12f9a73a2ULL, "class-definition-name"); + ASSERT_TRUE(class_handler != NULL); + DefineFunction(class_handler, "function", + 0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL, NULL); + class_handler->Finish(); + delete class_handler; + } + + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "space_A::class-definition-name::function", + 0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL); +} + +// This test recreates bug 364. +TEST_F(Specifications, InlineFunction) { + PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); + + StartCU(); + DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL, + dwarf2reader::DW_TAG_subprogram, "inline-name", ""); + AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL, + dwarf2reader::DW_INL_inlined, 0xcd3c51b946fb1eeeLL, ""); + DefineInlineInstanceDIE(&root_handler_, "", 0x1e8dac5d507ed7abULL, + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "inline-name", + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); +} + +// An inline function in a namespace should correctly derive its +// name from its abstract origin, and not just the namespace name. +TEST_F(Specifications, InlineFunctionInNamespace) { + PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); + + StartCU(); + DIEHandler* space_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, + "Namespace"); + ASSERT_TRUE(space_handler != NULL); + AbstractInstanceDIE(space_handler, 0x1e8dac5d507ed7abULL, + dwarf2reader::DW_INL_inlined, 0LL, "func-name"); + DefineInlineInstanceDIE(space_handler, "", 0x1e8dac5d507ed7abULL, + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); + space_handler->Finish(); + delete space_handler; + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "Namespace::func-name", + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); +} + +// Check name construction for a long chain containing each combination of: +// - struct, union, class, namespace +// - direct and definition +TEST_F(Specifications, LongChain) { + PushLine(0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL, "line-file", 21192926); + SetLanguage(dwarf2reader::DW_LANG_C_plus_plus); + + StartCU(); + // The structure we're building here is: + // space_A full definition + // space_B declaration + // space_B definition + // struct_C full definition + // struct_D declaration + // struct_D definition + // union_E full definition + // union_F declaration + // union_F definition + // class_G full definition + // class_H declaration + // class_H definition + // func_I declaration + // func_I definition + // + // So: + // - space_A, struct_C, union_E, and class_G don't use specifications; + // - space_B, struct_D, union_F, and class_H do. + // - func_I uses a specification. + // + // The full name for func_I is thus: + // + // space_A::space_B::struct_C::struct_D::union_E::union_F:: + // class_G::class_H::func_I + { + DIEHandler *space_A_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, + "space_A"); + DeclarationDIE(space_A_handler, 0x2e111126496596e2ULL, + dwarf2reader::DW_TAG_namespace, "space_B", ""); + space_A_handler->Finish(); + delete space_A_handler; + } + + { + DIEHandler *space_B_handler + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, + 0x2e111126496596e2ULL); + DIEHandler *struct_C_handler + = StartNamedDIE(space_B_handler, dwarf2reader::DW_TAG_structure_type, + "struct_C"); + DeclarationDIE(struct_C_handler, 0x20cd423bf2a25a4cULL, + dwarf2reader::DW_TAG_structure_type, "struct_D", ""); + struct_C_handler->Finish(); + delete struct_C_handler; + space_B_handler->Finish(); + delete space_B_handler; + } + + { + DIEHandler *struct_D_handler + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_structure_type, + 0x20cd423bf2a25a4cULL); + DIEHandler *union_E_handler + = StartNamedDIE(struct_D_handler, dwarf2reader::DW_TAG_union_type, + "union_E"); + DeclarationDIE(union_E_handler, 0xe25c84805aa58c32ULL, + dwarf2reader::DW_TAG_union_type, "union_F", ""); + union_E_handler->Finish(); + delete union_E_handler; + struct_D_handler->Finish(); + delete struct_D_handler; + } + + { + DIEHandler *union_F_handler + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_union_type, + 0xe25c84805aa58c32ULL); + DIEHandler *class_G_handler + = StartNamedDIE(union_F_handler, dwarf2reader::DW_TAG_class_type, + "class_G"); + DeclarationDIE(class_G_handler, 0xb70d960dcc173b6eULL, + dwarf2reader::DW_TAG_class_type, "class_H", ""); + class_G_handler->Finish(); + delete class_G_handler; + union_F_handler->Finish(); + delete union_F_handler; + } + + { + DIEHandler *class_H_handler + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + 0xb70d960dcc173b6eULL); + DeclarationDIE(class_H_handler, 0x27ff829e3bf69f37ULL, + dwarf2reader::DW_TAG_subprogram, "func_I", ""); + class_H_handler->Finish(); + delete class_H_handler; + } + + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0x27ff829e3bf69f37ULL, "", + 0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "space_A::space_B::struct_C::struct_D::union_E::union_F" + "::class_G::class_H::func_I", + 0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL); +} + +TEST_F(Specifications, InterCU) { + Module m("module-name", "module-os", "module-arch", "module-id"); + DwarfCUToModule::FileContext fc("dwarf-filename", &m, true); + EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); + MockLineToModuleHandler lr; + EXPECT_CALL(lr, ReadProgram(_,_,_,_)).Times(0); + + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); + + // First CU. Declares class_A. + { + DwarfCUToModule root1_handler(&fc, &lr, nullptr, &reporter_); + ASSERT_TRUE(root1_handler.StartCompilationUnit(0, 1, 2, 3, 3)); + ASSERT_TRUE(root1_handler.StartRootDIE(1, + dwarf2reader::DW_TAG_compile_unit)); + ProcessStrangeAttributes(&root1_handler); + ASSERT_TRUE(root1_handler.EndAttributes()); + DeclarationDIE(&root1_handler, 0xb8fbfdd5f0b26fceULL, + dwarf2reader::DW_TAG_class_type, "class_A", ""); + root1_handler.Finish(); + } + + // Second CU. Defines class_A, declares member_func_B. + { + DwarfCUToModule root2_handler(&fc, &lr, nullptr, &reporter_); + ASSERT_TRUE(root2_handler.StartCompilationUnit(0, 1, 2, 3, 3)); + ASSERT_TRUE(root2_handler.StartRootDIE(1, + dwarf2reader::DW_TAG_compile_unit)); + ASSERT_TRUE(root2_handler.EndAttributes()); + DIEHandler *class_A_handler + = StartSpecifiedDIE(&root2_handler, dwarf2reader::DW_TAG_class_type, + 0xb8fbfdd5f0b26fceULL); + DeclarationDIE(class_A_handler, 0xb01fef8b380bd1a2ULL, + dwarf2reader::DW_TAG_subprogram, "member_func_B", ""); + class_A_handler->Finish(); + delete class_A_handler; + root2_handler.Finish(); + } + + // Third CU. Defines member_func_B. + { + DwarfCUToModule root3_handler(&fc, &lr, nullptr, &reporter_); + ASSERT_TRUE(root3_handler.StartCompilationUnit(0, 1, 2, 3, 3)); + ASSERT_TRUE(root3_handler.StartRootDIE(1, + dwarf2reader::DW_TAG_compile_unit)); + ASSERT_TRUE(root3_handler.EndAttributes()); + DefinitionDIE(&root3_handler, dwarf2reader::DW_TAG_subprogram, + 0xb01fef8b380bd1a2ULL, "", + 0x2618f00a1a711e53ULL, 0x4fd94b76d7c2caf5ULL); + root3_handler.Finish(); + } + + vector<Module::Function *> functions; + m.GetFunctions(&functions, functions.end()); + EXPECT_EQ(1U, functions.size()); + EXPECT_STREQ("class_A::member_func_B", functions[0]->name.c_str()); +} + +TEST_F(Specifications, UnhandledInterCU) { + Module m("module-name", "module-os", "module-arch", "module-id"); + DwarfCUToModule::FileContext fc("dwarf-filename", &m, false); + EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); + MockLineToModuleHandler lr; + EXPECT_CALL(lr, ReadProgram(_,_,_,_)).Times(0); + + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); + + // First CU. Declares class_A. + { + DwarfCUToModule root1_handler(&fc, &lr, nullptr, &reporter_); + ASSERT_TRUE(root1_handler.StartCompilationUnit(0, 1, 2, 3, 3)); + ASSERT_TRUE(root1_handler.StartRootDIE(1, + dwarf2reader::DW_TAG_compile_unit)); + ProcessStrangeAttributes(&root1_handler); + ASSERT_TRUE(root1_handler.EndAttributes()); + DeclarationDIE(&root1_handler, 0xb8fbfdd5f0b26fceULL, + dwarf2reader::DW_TAG_class_type, "class_A", ""); + root1_handler.Finish(); + } + + // Second CU. Defines class_A, declares member_func_B. + { + DwarfCUToModule root2_handler(&fc, &lr, nullptr, &reporter_); + ASSERT_TRUE(root2_handler.StartCompilationUnit(0, 1, 2, 3, 3)); + ASSERT_TRUE(root2_handler.StartRootDIE(1, + dwarf2reader::DW_TAG_compile_unit)); + ASSERT_TRUE(root2_handler.EndAttributes()); + EXPECT_CALL(reporter_, UnhandledInterCUReference(_, _)).Times(1); + DIEHandler *class_A_handler + = StartSpecifiedDIE(&root2_handler, dwarf2reader::DW_TAG_class_type, + 0xb8fbfdd5f0b26fceULL); + DeclarationDIE(class_A_handler, 0xb01fef8b380bd1a2ULL, + dwarf2reader::DW_TAG_subprogram, "member_func_B", ""); + class_A_handler->Finish(); + delete class_A_handler; + root2_handler.Finish(); + } + + // Third CU. Defines member_func_B. + { + DwarfCUToModule root3_handler(&fc, &lr, nullptr, &reporter_); + ASSERT_TRUE(root3_handler.StartCompilationUnit(0, 1, 2, 3, 3)); + ASSERT_TRUE(root3_handler.StartRootDIE(1, + dwarf2reader::DW_TAG_compile_unit)); + ASSERT_TRUE(root3_handler.EndAttributes()); + EXPECT_CALL(reporter_, UnhandledInterCUReference(_, _)).Times(1); + EXPECT_CALL(reporter_, UnnamedFunction(_)).Times(1); + DefinitionDIE(&root3_handler, dwarf2reader::DW_TAG_subprogram, + 0xb01fef8b380bd1a2ULL, "", + 0x2618f00a1a711e53ULL, 0x4fd94b76d7c2caf5ULL); + root3_handler.Finish(); + } +} + +TEST_F(Specifications, BadOffset) { + PushLine(0xa0277efd7ce83771ULL, 0x149554a184c730c1ULL, "line-file", 56636272); + EXPECT_CALL(reporter_, UnknownSpecification(_, 0x2be953efa6f9a996ULL)) + .WillOnce(Return()); + + StartCU(); + DeclarationDIE(&root_handler_, 0xefd7f7752c27b7e4ULL, + dwarf2reader::DW_TAG_subprogram, "", ""); + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0x2be953efa6f9a996ULL, "function", + 0xa0277efd7ce83771ULL, 0x149554a184c730c1ULL); + root_handler_.Finish(); +} + +TEST_F(Specifications, FunctionDefinitionHasOwnName) { + PushLine(0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL, "line-file", 56792403); + + StartCU(); + DeclarationDIE(&root_handler_, 0xc34ff4786cae78bdULL, + dwarf2reader::DW_TAG_subprogram, "declaration-name", ""); + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0xc34ff4786cae78bdULL, "definition-name", + 0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "definition-name", + 0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL); +} + +TEST_F(Specifications, ClassDefinitionHasOwnName) { + PushLine(0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL, "line-file", 57119241); + + StartCU(); + DeclarationDIE(&root_handler_, 0xd0fe467ec2f1a58cULL, + dwarf2reader::DW_TAG_class_type, "class-declaration-name", ""); + + dwarf2reader::DIEHandler *class_definition + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + 0xd0fe467ec2f1a58cULL, "class-definition-name"); + ASSERT_TRUE(class_definition); + DeclarationDIE(class_definition, 0x6d028229c15623dbULL, + dwarf2reader::DW_TAG_subprogram, + "function-declaration-name", ""); + class_definition->Finish(); + delete class_definition; + + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0x6d028229c15623dbULL, "function-definition-name", + 0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL); + + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "class-definition-name::function-definition-name", + 0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL); +} + +// DIEs that cite a specification should prefer the specification's +// parents over their own when choosing qualified names. In this test, +// we take the name from our definition but the enclosing scope name +// from our declaration. I don't see why they'd ever be different, but +// we want to verify what DwarfCUToModule is looking at. +TEST_F(Specifications, PreferSpecificationParents) { + PushLine(0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL, "line-file", 79488694); + + StartCU(); + { + dwarf2reader::DIEHandler *declaration_class_handler = + StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + "declaration-class"); + DeclarationDIE(declaration_class_handler, 0x9ddb35517455ef7aULL, + dwarf2reader::DW_TAG_subprogram, "function-declaration", + ""); + declaration_class_handler->Finish(); + delete declaration_class_handler; + } + { + dwarf2reader::DIEHandler *definition_class_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + "definition-class"); + DefinitionDIE(definition_class_handler, dwarf2reader::DW_TAG_subprogram, + 0x9ddb35517455ef7aULL, "function-definition", + 0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL); + definition_class_handler->Finish(); + delete definition_class_handler; + } + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "declaration-class::function-definition", + 0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL); +} + +class CUErrors: public CUFixtureBase, public Test { }; + +TEST_F(CUErrors, BadStmtList) { + EXPECT_CALL(reporter_, BadLineInfoOffset(dummy_line_size_ + 10)).Times(1); + + ASSERT_TRUE(root_handler_ + .StartCompilationUnit(0xc591d5b037543d7cULL, 0x11, 0xcd, + 0x2d7d19546cf6590cULL, 3)); + ASSERT_TRUE(root_handler_.StartRootDIE(0xae789dc102cfca54ULL, + dwarf2reader::DW_TAG_compile_unit)); + root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + "compilation-unit-name"); + root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_stmt_list, + dwarf2reader::DW_FORM_ref4, + dummy_line_size_ + 10); + root_handler_.EndAttributes(); + root_handler_.Finish(); +} + +TEST_F(CUErrors, NoLineSection) { + EXPECT_CALL(reporter_, MissingSection(".debug_line")).Times(1); + PushLine(0x88507fb678052611ULL, 0x42c8e9de6bbaa0faULL, "line-file", 64472290); + // Delete the entry for .debug_line added by the fixture class's constructor. + file_context_.ClearSectionMapForTest(); + + StartCU(); + root_handler_.Finish(); +} + +TEST_F(CUErrors, BadDwarfVersion1) { + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); + + ASSERT_FALSE(root_handler_ + .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, + 0xc9de224ccb99ac3eULL, 1)); +} + +TEST_F(CUErrors, GoodDwarfVersion2) { + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); + + ASSERT_TRUE(root_handler_ + .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, + 0xc9de224ccb99ac3eULL, 2)); +} + +TEST_F(CUErrors, GoodDwarfVersion3) { + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); + + ASSERT_TRUE(root_handler_ + .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, + 0xc9de224ccb99ac3eULL, 3)); +} + +TEST_F(CUErrors, BadCURootDIETag) { + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); + + ASSERT_TRUE(root_handler_ + .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, + 0xc9de224ccb99ac3eULL, 3)); + + ASSERT_FALSE(root_handler_.StartRootDIE(0x02e56bfbda9e7337ULL, + dwarf2reader::DW_TAG_subprogram)); +} + +// Tests for DwarfCUToModule::Reporter. These just produce (or fail to +// produce) output, so their results need to be checked by hand. +struct Reporter: public Test { + Reporter() + : reporter("filename", 0x123456789abcdef0ULL), + function("function name", 0x19c45c30770c1eb0ULL), + file("source file name") { + reporter.SetCUName("compilation-unit-name"); + + Module::Range range(0x19c45c30770c1eb0ULL, 0x89808a5bdfa0a6a3ULL); + function.ranges.push_back(range); + function.parameter_size = 0x6a329f18683dcd51ULL; + + line.address = 0x3606ac6267aebeccULL; + line.size = 0x5de482229f32556aULL; + line.file = &file; + line.number = 93400201; + } + + DwarfCUToModule::WarningReporter reporter; + Module::Function function; + Module::File file; + Module::Line line; +}; + +TEST_F(Reporter, UnknownSpecification) { + reporter.UnknownSpecification(0x123456789abcdef1ULL, 0x323456789abcdef2ULL); +} + +TEST_F(Reporter, UnknownAbstractOrigin) { + reporter.UnknownAbstractOrigin(0x123456789abcdef1ULL, 0x323456789abcdef2ULL); +} + +TEST_F(Reporter, MissingSection) { + reporter.MissingSection("section name"); +} + +TEST_F(Reporter, BadLineInfoOffset) { + reporter.BadLineInfoOffset(0x123456789abcdef1ULL); +} + +TEST_F(Reporter, UncoveredFunctionDisabled) { + reporter.UncoveredFunction(function); + EXPECT_FALSE(reporter.uncovered_warnings_enabled()); +} + +TEST_F(Reporter, UncoveredFunctionEnabled) { + reporter.set_uncovered_warnings_enabled(true); + reporter.UncoveredFunction(function); + EXPECT_TRUE(reporter.uncovered_warnings_enabled()); +} + +TEST_F(Reporter, UncoveredLineDisabled) { + reporter.UncoveredLine(line); + EXPECT_FALSE(reporter.uncovered_warnings_enabled()); +} + +TEST_F(Reporter, UncoveredLineEnabled) { + reporter.set_uncovered_warnings_enabled(true); + reporter.UncoveredLine(line); + EXPECT_TRUE(reporter.uncovered_warnings_enabled()); +} + +TEST_F(Reporter, UnnamedFunction) { + reporter.UnnamedFunction(0x90c0baff9dedb2d9ULL); +} + +// Would be nice to also test: +// - overlapping lines, functions diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf_line_to_module.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf_line_to_module.cc new file mode 100644 index 0000000000..258b0b603f --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf_line_to_module.cc @@ -0,0 +1,143 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// dwarf_line_to_module.cc: Implementation of DwarfLineToModule class. +// See dwarf_line_to_module.h for details. + +#include <stdio.h> + +#include <string> + +#include "common/dwarf_line_to_module.h" +#include "common/using_std_string.h" + +// Trying to support Windows paths in a reasonable way adds a lot of +// variations to test; it would be better to just put off dealing with +// it until we actually have to deal with DWARF on Windows. + +// Return true if PATH is an absolute path, false if it is relative. +static bool PathIsAbsolute(const string &path) { + return (path.size() >= 1 && path[0] == '/'); +} + +static bool HasTrailingSlash(const string &path) { + return (path.size() >= 1 && path[path.size() - 1] == '/'); +} + +// If PATH is an absolute path, return PATH. If PATH is a relative path, +// treat it as relative to BASE and return the combined path. +static string ExpandPath(const string &path, + const string &base) { + if (PathIsAbsolute(path) || base.empty()) + return path; + return base + (HasTrailingSlash(base) ? "" : "/") + path; +} + +namespace google_breakpad { + +void DwarfLineToModule::DefineDir(const string &name, uint32 dir_num) { + // Directory number zero is reserved to mean the compilation + // directory. Silently ignore attempts to redefine it. + if (dir_num != 0) + directories_[dir_num] = ExpandPath(name, compilation_dir_); +} + +void DwarfLineToModule::DefineFile(const string &name, int32 file_num, + uint32 dir_num, uint64 mod_time, + uint64 length) { + if (file_num == -1) + file_num = ++highest_file_number_; + else if (file_num > highest_file_number_) + highest_file_number_ = file_num; + + string dir_name; + if (dir_num == 0) { + // Directory number zero is the compilation directory, and is stored as + // an attribute on the compilation unit, rather than in the program table. + dir_name = compilation_dir_; + } else { + DirectoryTable::const_iterator directory_it = directories_.find(dir_num); + if (directory_it != directories_.end()) { + dir_name = directory_it->second; + } else { + if (!warned_bad_directory_number_) { + fprintf(stderr, "warning: DWARF line number data refers to undefined" + " directory numbers\n"); + warned_bad_directory_number_ = true; + } + } + } + + string full_name = ExpandPath(name, dir_name); + + // Find a Module::File object of the given name, and add it to the + // file table. + files_[file_num] = module_->FindFile(full_name); +} + +void DwarfLineToModule::AddLine(uint64 address, uint64 length, + uint32 file_num, uint32 line_num, + uint32 column_num) { + if (length == 0) + return; + + // Clip lines not to extend beyond the end of the address space. + if (address + length < address) + length = -address; + + // Should we omit this line? (See the comments for omitted_line_end_.) + if (address == 0 || address == omitted_line_end_) { + omitted_line_end_ = address + length; + return; + } else { + omitted_line_end_ = 0; + } + + // Find the source file being referred to. + Module::File *file = files_[file_num]; + if (!file) { + if (!warned_bad_file_number_) { + fprintf(stderr, "warning: DWARF line number data refers to " + "undefined file numbers\n"); + warned_bad_file_number_ = true; + } + return; + } + Module::Line line; + line.address = address; + // We set the size when we get the next line or the EndSequence call. + line.size = length; + line.file = file; + line.number = line_num; + lines_->push_back(line); +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf_line_to_module.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf_line_to_module.h new file mode 100644 index 0000000000..1fdd4cb716 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf_line_to_module.h @@ -0,0 +1,188 @@ +// -*- mode: c++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// The DwarfLineToModule class accepts line number information from a +// DWARF parser and adds it to a google_breakpad::Module. The Module +// can write that data out as a Breakpad symbol file. + +#ifndef COMMON_LINUX_DWARF_LINE_TO_MODULE_H +#define COMMON_LINUX_DWARF_LINE_TO_MODULE_H + +#include <string> + +#include "common/module.h" +#include "common/dwarf/dwarf2reader.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +// A class for producing a vector of google_breakpad::Module::Line +// instances from parsed DWARF line number data. +// +// An instance of this class can be provided as a handler to a +// dwarf2reader::LineInfo DWARF line number information parser. The +// handler accepts source location information from the parser and +// uses it to produce a vector of google_breakpad::Module::Line +// objects, referring to google_breakpad::Module::File objects added +// to a particular google_breakpad::Module. +// +// GNU toolchain omitted sections support: +// ====================================== +// +// Given the right options, the GNU toolchain will omit unreferenced +// functions from the final executable. Unfortunately, when it does so, it +// does not remove the associated portions of the DWARF line number +// program; instead, it gives the DW_LNE_set_address instructions referring +// to the now-deleted code addresses of zero. Given this input, the DWARF +// line parser will call AddLine with a series of lines starting at address +// zero. For example, here is the output from 'readelf -wl' for a program +// with four functions, the first three of which have been omitted: +// +// Line Number Statements: +// Extended opcode 2: set Address to 0x0 +// Advance Line by 14 to 15 +// Copy +// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 16 +// Special opcode 119: advance Address by 8 to 0xb and Line by 2 to 18 +// Advance PC by 2 to 0xd +// Extended opcode 1: End of Sequence +// +// Extended opcode 2: set Address to 0x0 +// Advance Line by 14 to 15 +// Copy +// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 16 +// Special opcode 119: advance Address by 8 to 0xb and Line by 2 to 18 +// Advance PC by 2 to 0xd +// Extended opcode 1: End of Sequence +// +// Extended opcode 2: set Address to 0x0 +// Advance Line by 19 to 20 +// Copy +// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 21 +// Special opcode 76: advance Address by 5 to 0x8 and Line by 1 to 22 +// Advance PC by 2 to 0xa +// Extended opcode 1: End of Sequence +// +// Extended opcode 2: set Address to 0x80483a4 +// Advance Line by 23 to 24 +// Copy +// Special opcode 202: advance Address by 14 to 0x80483b2 and Line by 1 to 25 +// Special opcode 76: advance Address by 5 to 0x80483b7 and Line by 1 to 26 +// Advance PC by 6 to 0x80483bd +// Extended opcode 1: End of Sequence +// +// Instead of collecting runs of lines describing code that is not there, +// we try to recognize and drop them. Since the linker doesn't explicitly +// distinguish references to dropped sections from genuine references to +// code at address zero, we must use a heuristic. We have chosen: +// +// - If a line starts at address zero, omit it. (On the platforms +// breakpad targets, it is extremely unlikely that there will be code +// at address zero.) +// +// - If a line starts immediately after an omitted line, omit it too. +class DwarfLineToModule: public dwarf2reader::LineInfoHandler { + public: + // As the DWARF line info parser passes us line records, add source + // files to MODULE, and add all lines to the end of LINES. LINES + // need not be empty. If the parser hands us a zero-length line, we + // omit it. If the parser hands us a line that extends beyond the + // end of the address space, we clip it. It's up to our client to + // sort out which lines belong to which functions; we don't add them + // to any particular function in MODULE ourselves. + DwarfLineToModule(Module *module, const string& compilation_dir, + vector<Module::Line> *lines) + : module_(module), + compilation_dir_(compilation_dir), + lines_(lines), + highest_file_number_(-1), + omitted_line_end_(0), + warned_bad_file_number_(false), + warned_bad_directory_number_(false) { } + + ~DwarfLineToModule() { } + + void DefineDir(const string &name, uint32 dir_num); + void DefineFile(const string &name, int32 file_num, + uint32 dir_num, uint64 mod_time, + uint64 length); + void AddLine(uint64 address, uint64 length, + uint32 file_num, uint32 line_num, uint32 column_num); + + private: + + typedef std::map<uint32, string> DirectoryTable; + typedef std::map<uint32, Module::File *> FileTable; + + // The module we're contributing debugging info to. Owned by our + // client. + Module *module_; + + // The compilation directory for the current compilation unit whose + // lines are being accumulated. + string compilation_dir_; + + // The vector of lines we're accumulating. Owned by our client. + // + // In a Module, as in a breakpad symbol file, lines belong to + // specific functions, but DWARF simply assigns lines to addresses; + // one must infer the line/function relationship using the + // functions' beginning and ending addresses. So we can't add these + // to the appropriate function from module_ until we've read the + // function info as well. Instead, we accumulate lines here, and let + // whoever constructed this sort it all out. + vector<Module::Line> *lines_; + + // A table mapping directory numbers to paths. + DirectoryTable directories_; + + // A table mapping file numbers to Module::File pointers. + FileTable files_; + + // The highest file number we've seen so far, or -1 if we've seen + // none. Used for dynamically defined file numbers. + int32 highest_file_number_; + + // This is the ending address of the last line we omitted, or zero if we + // didn't omit the previous line. It is zero before we have received any + // AddLine calls. + uint64 omitted_line_end_; + + // True if we've warned about: + bool warned_bad_file_number_; // bad file numbers + bool warned_bad_directory_number_; // bad directory numbers +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_DWARF_LINE_TO_MODULE_H diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf_line_to_module_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf_line_to_module_unittest.cc new file mode 100644 index 0000000000..7c0fcfd35a --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf_line_to_module_unittest.cc @@ -0,0 +1,391 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// dwarf_line_to_module.cc: Unit tests for google_breakpad::DwarfLineToModule. + +#include <vector> + +#include "breakpad_googletest_includes.h" +#include "common/dwarf_line_to_module.h" + +using std::vector; + +using google_breakpad::DwarfLineToModule; +using google_breakpad::Module; +using google_breakpad::Module; + +TEST(SimpleModule, One) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "/", &lines); + + h.DefineFile("file1", 0x30bf0f27, 0, 0, 0); + h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27, + 0x4c090cbf, 0x1cf9fe0d); + + vector<Module::File *> files; + m.GetFiles(&files); + EXPECT_EQ(1U, files.size()); + EXPECT_STREQ("/file1", files[0]->name.c_str()); + + EXPECT_EQ(1U, lines.size()); + EXPECT_EQ(0x6fd126fbf74f2680ULL, lines[0].address); + EXPECT_EQ(0x63c9a14cf556712bULL, lines[0].size); + EXPECT_TRUE(lines[0].file == files[0]); + EXPECT_EQ(0x4c090cbf, lines[0].number); +} + +TEST(SimpleModule, Many) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "/", &lines); + + h.DefineDir("directory1", 0x838299ab); + h.DefineDir("directory2", 0xf85de023); + h.DefineFile("file1", 0x2b80377a, 0x838299ab, 0, 0); + h.DefineFile("file1", 0x63beb4a4, 0xf85de023, 0, 0); + h.DefineFile("file2", 0x1d161d56, 0x838299ab, 0, 0); + h.DefineFile("file2", 0x1e7a667c, 0xf85de023, 0, 0); + h.AddLine(0x69900c5d553b7274ULL, 0x90fded183f0d0d3cULL, 0x2b80377a, + 0x15b0f0a9U, 0x3ff5abd6U); + h.AddLine(0x45811219a39b7101ULL, 0x25a5e6a924afc41fULL, 0x63beb4a4, + 0x4d259ce9U, 0x41c5ee32U); + h.AddLine(0xfa90514c1dc9704bULL, 0x0063efeabc02f313ULL, 0x1d161d56, + 0x1ee9fa4fU, 0xbf70e46aU); + h.AddLine(0x556b55fb6a647b10ULL, 0x3f3089ca2bfd80f5ULL, 0x1e7a667c, + 0x77fc280eU, 0x2c4a728cU); + h.DefineFile("file3", -1, 0, 0, 0); + h.AddLine(0xe2d72a37f8d9403aULL, 0x034dfab5b0d4d236ULL, 0x63beb4a5, + 0x75047044U, 0xb6a0016cU); + + vector<Module::File *> files; + m.GetFiles(&files); + ASSERT_EQ(5U, files.size()); + EXPECT_STREQ("/directory1/file1", files[0]->name.c_str()); + EXPECT_STREQ("/directory1/file2", files[1]->name.c_str()); + EXPECT_STREQ("/directory2/file1", files[2]->name.c_str()); + EXPECT_STREQ("/directory2/file2", files[3]->name.c_str()); + EXPECT_STREQ("/file3", files[4]->name.c_str()); + + ASSERT_EQ(5U, lines.size()); + + EXPECT_EQ(0x69900c5d553b7274ULL, lines[0].address); + EXPECT_EQ(0x90fded183f0d0d3cULL, lines[0].size); + EXPECT_TRUE(lines[0].file == files[0]); + EXPECT_EQ(0x15b0f0a9, lines[0].number); + + EXPECT_EQ(0x45811219a39b7101ULL, lines[1].address); + EXPECT_EQ(0x25a5e6a924afc41fULL, lines[1].size); + EXPECT_TRUE(lines[1].file == files[2]); + EXPECT_EQ(0x4d259ce9, lines[1].number); + + EXPECT_EQ(0xfa90514c1dc9704bULL, lines[2].address); + EXPECT_EQ(0x0063efeabc02f313ULL, lines[2].size); + EXPECT_TRUE(lines[2].file == files[1]); + EXPECT_EQ(0x1ee9fa4f, lines[2].number); + + EXPECT_EQ(0x556b55fb6a647b10ULL, lines[3].address); + EXPECT_EQ(0x3f3089ca2bfd80f5ULL, lines[3].size); + EXPECT_TRUE(lines[3].file == files[3]); + EXPECT_EQ(0x77fc280e, lines[3].number); + + EXPECT_EQ(0xe2d72a37f8d9403aULL, lines[4].address); + EXPECT_EQ(0x034dfab5b0d4d236ULL, lines[4].size); + EXPECT_TRUE(lines[4].file == files[4]); + EXPECT_EQ(0x75047044, lines[4].number); +} + +TEST(Filenames, Absolute) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "/", &lines); + + h.DefineDir("directory1", 1); + h.DefineFile("/absolute", 1, 1, 0, 0); + + h.AddLine(1, 1, 1, 0, 0); + + vector<Module::File *> files; + m.GetFiles(&files); + ASSERT_EQ(1U, files.size()); + EXPECT_STREQ("/absolute", files[0]->name.c_str()); + ASSERT_EQ(1U, lines.size()); + EXPECT_TRUE(lines[0].file == files[0]); +} + +TEST(Filenames, Relative) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "/", &lines); + + h.DefineDir("directory1", 1); + h.DefineFile("relative", 1, 1, 0, 0); + + h.AddLine(1, 1, 1, 0, 0); + + vector<Module::File *> files; + m.GetFiles(&files); + ASSERT_EQ(1U, files.size()); + EXPECT_STREQ("/directory1/relative", files[0]->name.c_str()); + ASSERT_EQ(1U, lines.size()); + EXPECT_TRUE(lines[0].file == files[0]); +} + +TEST(Filenames, StrangeFile) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "/", &lines); + + h.DefineDir("directory1", 1); + h.DefineFile("", 1, 1, 0, 0); + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("/directory1/", lines[0].file->name.c_str()); +} + +TEST(Filenames, StrangeDirectory) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "/", &lines); + + h.DefineDir("", 1); + h.DefineFile("file1", 1, 1, 0, 0); + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("/file1", lines[0].file->name.c_str()); +} + +TEST(Filenames, StrangeDirectoryAndFile) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "/", &lines); + + h.DefineDir("", 1); + h.DefineFile("", 1, 1, 0, 0); + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("/", lines[0].file->name.c_str()); +} + +// We should use the compilation directory when encountering a file for +// directory number zero. +TEST(Filenames, DirectoryZeroFileIsRelativeToCompilationDir) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "src/build", &lines); + + h.DefineDir("Dir", 1); + h.DefineFile("File", 1, 0, 0, 0); + + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("src/build/File", lines[0].file->name.c_str()); +} + +// We should treat non-absolute directories as relative to the compilation +// directory. +TEST(Filenames, IncludeDirectoryRelativeToDirectoryZero) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "src/build", &lines); + + h.DefineDir("Dir", 1); + h.DefineFile("File", 1, 1, 0, 0); + + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("src/build/Dir/File", lines[0].file->name.c_str()); +} + +// We should treat absolute directories as absolute, and not relative to +// the compilation dir. +TEST(Filenames, IncludeDirectoryAbsolute) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "src/build", &lines); + + h.DefineDir("/Dir", 1); + h.DefineFile("File", 1, 1, 0, 0); + + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("/Dir/File", lines[0].file->name.c_str()); +} + +// We should silently ignore attempts to define directory number zero, +// since that is always the compilation directory. +TEST(ModuleErrors, DirectoryZero) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "/", &lines); + + h.DefineDir("directory0", 0); // should be ignored + h.DefineFile("relative", 1, 0, 0, 0); + + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("/relative", lines[0].file->name.c_str()); +} + +// We should refuse to add lines with bogus file numbers. We should +// produce only one warning, however. +TEST(ModuleErrors, BadFileNumber) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "/", &lines); + + h.DefineFile("relative", 1, 0, 0, 0); + h.AddLine(1, 1, 2, 0, 0); // bad file number + h.AddLine(2, 1, 2, 0, 0); // bad file number (no duplicate warning) + + EXPECT_EQ(0U, lines.size()); +} + +// We should treat files with bogus directory numbers as relative to +// the compilation unit. +TEST(ModuleErrors, BadDirectoryNumber) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "/", &lines); + + h.DefineDir("directory1", 1); + h.DefineFile("baddirnumber1", 1, 2, 0, 0); // bad directory number + h.DefineFile("baddirnumber2", 2, 2, 0, 0); // bad dir number (no warning) + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("baddirnumber1", lines[0].file->name.c_str()); +} + +// We promise not to report empty lines. +TEST(ModuleErrors, EmptyLine) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "/", &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(1, 0, 1, 0, 0); + + ASSERT_EQ(0U, lines.size()); +} + +// We are supposed to clip lines that extend beyond the end of the +// address space. +TEST(ModuleErrors, BigLine) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "/", &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(0xffffffffffffffffULL, 2, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_EQ(1U, lines[0].size); +} + +// The 'Omitted' tests verify that we correctly omit line information +// for code in sections that the linker has dropped. See "GNU +// toolchain omitted sections support" at the top of the +// DwarfLineToModule class. + +TEST(Omitted, DroppedThenGood) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "/", &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(0, 10, 1, 83816211, 0); // should be omitted + h.AddLine(20, 10, 1, 13059195, 0); // should be recorded + + ASSERT_EQ(1U, lines.size()); + EXPECT_EQ(13059195, lines[0].number); +} + +TEST(Omitted, GoodThenDropped) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "/", &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(0x9dd6a372, 10, 1, 41454594, 0); // should be recorded + h.AddLine(0, 10, 1, 44793413, 0); // should be omitted + + ASSERT_EQ(1U, lines.size()); + EXPECT_EQ(41454594, lines[0].number); +} + +TEST(Omitted, Mix1) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "/", &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(0x679ed72f, 10, 1, 58932642, 0); // should be recorded + h.AddLine(0xdfb5a72d, 10, 1, 39847385, 0); // should be recorded + h.AddLine(0, 0x78, 1, 23053829, 0); // should be omitted + h.AddLine(0x78, 0x6a, 1, 65317783, 0); // should be omitted + h.AddLine(0x78 + 0x6a, 0x2a, 1, 77601423, 0); // should be omitted + h.AddLine(0x9fe0cea5, 10, 1, 91806582, 0); // should be recorded + h.AddLine(0x7e41a109, 10, 1, 56169221, 0); // should be recorded + + ASSERT_EQ(4U, lines.size()); + EXPECT_EQ(58932642, lines[0].number); + EXPECT_EQ(39847385, lines[1].number); + EXPECT_EQ(91806582, lines[2].number); + EXPECT_EQ(56169221, lines[3].number); +} + +TEST(Omitted, Mix2) { + Module m("name", "os", "architecture", "id"); + vector<Module::Line> lines; + DwarfLineToModule h(&m, "/", &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(0, 0xf2, 1, 58802211, 0); // should be omitted + h.AddLine(0xf2, 0xb9, 1, 78958222, 0); // should be omitted + h.AddLine(0xf2 + 0xb9, 0xf7, 1, 64861892, 0); // should be omitted + h.AddLine(0x4e4d271e, 9, 1, 67355743, 0); // should be recorded + h.AddLine(0xdfb5a72d, 30, 1, 23365776, 0); // should be recorded + h.AddLine(0, 0x64, 1, 76196762, 0); // should be omitted + h.AddLine(0x64, 0x33, 1, 71066611, 0); // should be omitted + h.AddLine(0x64 + 0x33, 0xe3, 1, 61749337, 0); // should be omitted + + ASSERT_EQ(2U, lines.size()); + EXPECT_EQ(67355743, lines[0].number); + EXPECT_EQ(23365776, lines[1].number); +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf_range_list_handler.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf_range_list_handler.cc new file mode 100644 index 0000000000..8733df3611 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf_range_list_handler.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2018 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Gabriele Svelto <gsvelto@mozilla.com> +// <gabriele.svelto@gmail.com> + +// dwarf_range_list_handler.cc: Implementation of DwarfRangeListHandler class. +// See dwarf_range_list_handler.h for details. + +#include <algorithm> + +#include "common/dwarf_range_list_handler.h" + +namespace google_breakpad { + +void DwarfRangeListHandler::AddRange(uint64 begin, uint64 end) { + Module::Range r(begin + base_address_, end - begin); + + ranges_->push_back(r); +} + +void DwarfRangeListHandler::SetBaseAddress(uint64 base_address) { + base_address_ = base_address; +} + +void DwarfRangeListHandler::Finish() { + std::sort(ranges_->begin(), ranges_->end(), + [](const Module::Range &a, const Module::Range &b) { + return a.address < b.address; + } + ); +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf_range_list_handler.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf_range_list_handler.h new file mode 100644 index 0000000000..bd0996863d --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf_range_list_handler.h @@ -0,0 +1,79 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2018 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Gabriele Svelto <gsvelto@mozilla.com> +// <gabriele.svelto@gmail.com> + +// The DwarfRangeListHandler class accepts rangelist data from a DWARF parser +// and adds it to a google_breakpad::Function or other objects supporting +// ranges. + +#ifndef COMMON_LINUX_DWARF_RANGE_LIST_HANDLER_H +#define COMMON_LINUX_DWARF_RANGE_LIST_HANDLER_H + +#include <vector> + +#include "common/module.h" +#include "common/dwarf/dwarf2reader.h" + +namespace google_breakpad { + +// A class for producing a vector of google_breakpad::Module::Range +// instances from a parsed DWARF range list. + +class DwarfRangeListHandler: public dwarf2reader::RangeListHandler { + public: + DwarfRangeListHandler(uint64 base_address, vector<Module::Range> *ranges) + : base_address_(base_address), ranges_(ranges) { } + + ~DwarfRangeListHandler() { } + + // Add a range to the list + void AddRange(uint64 begin, uint64 end); + + // Record the new base address and use it for the following entries + void SetBaseAddress(uint64 base_address); + + // Sort the ranges so that they are in ascending order of starting address + void Finish(); + + private: + // The current PC to add to every entry, this can be overridden by a special + // list entry + uint64 base_address_; + + // The list of ranges to be populated + vector<Module::Range> *ranges_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_DWARF_RANGE_LIST_HANDLER_H diff --git a/toolkit/crashreporter/google-breakpad/src/common/language.cc b/toolkit/crashreporter/google-breakpad/src/common/language.cc new file mode 100644 index 0000000000..978fb85567 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/language.cc @@ -0,0 +1,197 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// language.cc: Subclasses and singletons for google_breakpad::Language. +// See language.h for details. + +#include "common/language.h" + +#include <stdlib.h> + +#if !defined(__ANDROID__) +#include <cxxabi.h> +#endif + +#if defined(HAVE_RUST_DEMANGLE) +#include <rust_demangle.h> +#endif + +#include <limits> + +namespace { + +string MakeQualifiedNameWithSeparator(const string& parent_name, + const char* separator, + const string& name) { + if (parent_name.empty()) { + return name; + } + + return parent_name + separator + name; +} + +} // namespace + +namespace google_breakpad { + +// C++ language-specific operations. +class CPPLanguage: public Language { + public: + CPPLanguage() {} + + string MakeQualifiedName(const string &parent_name, + const string &name) const { + return MakeQualifiedNameWithSeparator(parent_name, "::", name); + } + + virtual DemangleResult DemangleName(const string& mangled, + string* demangled) const { +#if defined(__ANDROID__) + // Android NDK doesn't provide abi::__cxa_demangle. + demangled->clear(); + return kDontDemangle; +#else + int status; + char* demangled_c = + abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status); + + DemangleResult result; + if (status == 0) { + result = kDemangleSuccess; + demangled->assign(demangled_c); + } else { + result = kDemangleFailure; + demangled->clear(); + } + + if (demangled_c) { + free(reinterpret_cast<void*>(demangled_c)); + } + + return result; +#endif + } +}; + +CPPLanguage CPPLanguageSingleton; + +// Java language-specific operations. +class JavaLanguage: public Language { + public: + JavaLanguage() {} + + string MakeQualifiedName(const string &parent_name, + const string &name) const { + return MakeQualifiedNameWithSeparator(parent_name, ".", name); + } +}; + +JavaLanguage JavaLanguageSingleton; + +// Swift language-specific operations. +class SwiftLanguage: public Language { + public: + SwiftLanguage() {} + + string MakeQualifiedName(const string &parent_name, + const string &name) const { + return MakeQualifiedNameWithSeparator(parent_name, ".", name); + } + + virtual DemangleResult DemangleName(const string& mangled, + string* demangled) const { + // There is no programmatic interface to a Swift demangler. Pass through the + // mangled form because it encodes more information than the qualified name + // that would have been built by MakeQualifiedName(). The output can be + // post-processed by xcrun swift-demangle to transform mangled Swift names + // into something more readable. + demangled->assign(mangled); + return kDemangleSuccess; + } +}; + +SwiftLanguage SwiftLanguageSingleton; + +// Rust language-specific operations. +class RustLanguage: public Language { + public: + RustLanguage() {} + + string MakeQualifiedName(const string &parent_name, + const string &name) const { + return MakeQualifiedNameWithSeparator(parent_name, ".", name); + } + + virtual DemangleResult DemangleName(const string& mangled, + string* demangled) const { + // Rust names use GCC C++ name mangling, but demangling them with + // abi_demangle doesn't produce stellar results due to them having + // another layer of encoding. + // If callers provide rustc-demangle, use that. +#if defined(HAVE_RUST_DEMANGLE) + char* rust_demangled = rust_demangle(mangled.c_str()); + if (rust_demangled == nullptr) { + return kDemangleFailure; + } + demangled->assign(rust_demangled); + free_rust_demangled_name(rust_demangled); +#else + // Otherwise, pass through the mangled name so callers can demangle + // after the fact. + demangled->assign(mangled); +#endif + return kDemangleSuccess; + } +}; + +RustLanguage RustLanguageSingleton; + +// Assembler language-specific operations. +class AssemblerLanguage: public Language { + public: + AssemblerLanguage() {} + + bool HasFunctions() const { return false; } + string MakeQualifiedName(const string &parent_name, + const string &name) const { + return name; + } +}; + +AssemblerLanguage AssemblerLanguageSingleton; + +const Language * const Language::CPlusPlus = &CPPLanguageSingleton; +const Language * const Language::Java = &JavaLanguageSingleton; +const Language * const Language::Swift = &SwiftLanguageSingleton; +const Language * const Language::Rust = &RustLanguageSingleton; +const Language * const Language::Assembler = &AssemblerLanguageSingleton; + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/language.h b/toolkit/crashreporter/google-breakpad/src/common/language.h new file mode 100644 index 0000000000..2d2dbcd9e9 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/language.h @@ -0,0 +1,105 @@ +// -*- mode: c++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// language.h: Define google_breakpad::Language. Instances of +// subclasses of this class provide language-appropriate operations +// for the Breakpad symbol dumper. + +#ifndef COMMON_LINUX_LANGUAGE_H__ +#define COMMON_LINUX_LANGUAGE_H__ + +#include <string> + +#include "common/using_std_string.h" + +namespace google_breakpad { + +// An abstract base class for language-specific operations. We choose +// an instance of a subclass of this when we find the CU's language. +// This class's definitions are appropriate for CUs with no specified +// language. +class Language { + public: + // A base class destructor should be either public and virtual, + // or protected and nonvirtual. + virtual ~Language() {} + + // Return true if this language has functions to which we can assign + // line numbers. (Debugging info for assembly language, for example, + // can have source location information, but does not have functions + // recorded using DW_TAG_subprogram DIEs.) + virtual bool HasFunctions() const { return true; } + + // Construct a fully-qualified, language-appropriate form of NAME, + // given that PARENT_NAME is the name of the construct enclosing + // NAME. If PARENT_NAME is the empty string, then NAME is a + // top-level name. + // + // This API sort of assumes that a fully-qualified name is always + // some simple textual composition of the unqualified name and its + // parent's name, and that we don't need to know anything else about + // the parent or the child (say, their DIEs' tags) to do the job. + // This is true for the languages we support at the moment, and + // keeps things concrete. Perhaps a more refined operation would + // take into account the parent and child DIE types, allow languages + // to use their own data type for complex parent names, etc. But if + // C++ doesn't need all that, who would? + virtual string MakeQualifiedName (const string &parent_name, + const string &name) const = 0; + + enum DemangleResult { + // Demangling was not performed because it’s not appropriate to attempt. + kDontDemangle = -1, + + kDemangleSuccess, + kDemangleFailure, + }; + + // Wraps abi::__cxa_demangle() or similar for languages where appropriate. + virtual DemangleResult DemangleName(const string& mangled, + string* demangled) const { + demangled->clear(); + return kDontDemangle; + } + + // Instances for specific languages. + static const Language * const CPlusPlus, + * const Java, + * const Swift, + * const Rust, + * const Assembler; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_LANGUAGE_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/breakpad_getcontext.S b/toolkit/crashreporter/google-breakpad/src/common/linux/breakpad_getcontext.S new file mode 100644 index 0000000000..fea0109d15 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/breakpad_getcontext.S @@ -0,0 +1,486 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// A minimalistic implementation of getcontext() to be used by +// Google Breakpad when getcontext() is not available in libc. + +#include "common/linux/ucontext_constants.h" + +/* int getcontext (ucontext_t *ucp) */ + +#if defined(__arm__) + + .text + .global breakpad_getcontext + .hidden breakpad_getcontext + .type breakpad_getcontext, #function + .align 0 + .fnstart +breakpad_getcontext: + + /* First, save r4-r11 */ + add r1, r0, #(MCONTEXT_GREGS_OFFSET + 4*4) + stm r1, {r4-r11} + + /* r12 is a scratch register, don't save it */ + + /* Save sp and lr explicitly. */ + /* - sp can't be stored with stmia in Thumb-2 */ + /* - STM instructions that store sp and pc are deprecated in ARM */ + str sp, [r0, #(MCONTEXT_GREGS_OFFSET + 13*4)] + str lr, [r0, #(MCONTEXT_GREGS_OFFSET + 14*4)] + + /* Save the caller's address in 'pc' */ + str lr, [r0, #(MCONTEXT_GREGS_OFFSET + 15*4)] + + /* Save ucontext_t* pointer across next call */ + mov r4, r0 + + /* Call sigprocmask(SIG_BLOCK, NULL, &(ucontext->uc_sigmask)) */ + mov r0, #0 /* SIG_BLOCK */ + mov r1, #0 /* NULL */ + add r2, r4, #UCONTEXT_SIGMASK_OFFSET + bl sigprocmask(PLT) + + /* Intentionally do not save the FPU state here. This is because on + * Linux/ARM, one should instead use ptrace(PTRACE_GETFPREGS) or + * ptrace(PTRACE_GETVFPREGS) to get it. + * + * Note that a real implementation of getcontext() would need to save + * this here to allow setcontext()/swapcontext() to work correctly. + */ + + /* Restore the values of r4 and lr */ + mov r0, r4 + ldr lr, [r0, #(MCONTEXT_GREGS_OFFSET + 14*4)] + ldr r4, [r0, #(MCONTEXT_GREGS_OFFSET + 4*4)] + + /* Return 0 */ + mov r0, #0 + bx lr + + .fnend + .size breakpad_getcontext, . - breakpad_getcontext + +#elif defined(__aarch64__) + +#define _NSIG 64 +#define __NR_rt_sigprocmask 135 + + .text + .global breakpad_getcontext + .hidden breakpad_getcontext + .type breakpad_getcontext, #function + .align 4 + .cfi_startproc +breakpad_getcontext: + + /* The saved context will return to the getcontext() call point + with a return value of 0 */ + str xzr, [x0, MCONTEXT_GREGS_OFFSET + 0 * REGISTER_SIZE] + + stp x18, x19, [x0, MCONTEXT_GREGS_OFFSET + 18 * REGISTER_SIZE] + stp x20, x21, [x0, MCONTEXT_GREGS_OFFSET + 20 * REGISTER_SIZE] + stp x22, x23, [x0, MCONTEXT_GREGS_OFFSET + 22 * REGISTER_SIZE] + stp x24, x25, [x0, MCONTEXT_GREGS_OFFSET + 24 * REGISTER_SIZE] + stp x26, x27, [x0, MCONTEXT_GREGS_OFFSET + 26 * REGISTER_SIZE] + stp x28, x29, [x0, MCONTEXT_GREGS_OFFSET + 28 * REGISTER_SIZE] + str x30, [x0, MCONTEXT_GREGS_OFFSET + 30 * REGISTER_SIZE] + + /* Place LR into the saved PC, this will ensure that when + switching to this saved context with setcontext() control + will pass back to the caller of getcontext(), we have + already arranged to return the appropriate return value in x0 + above. */ + str x30, [x0, MCONTEXT_PC_OFFSET] + + /* Save the current SP */ + mov x2, sp + str x2, [x0, MCONTEXT_SP_OFFSET] + + /* Initialize the pstate. */ + str xzr, [x0, MCONTEXT_PSTATE_OFFSET] + + /* Figure out where to place the first context extension + block. */ + add x2, x0, #MCONTEXT_EXTENSION_OFFSET + + /* Write the context extension fpsimd header. */ + mov w3, #(FPSIMD_MAGIC & 0xffff) + movk w3, #(FPSIMD_MAGIC >> 16), lsl #16 + str w3, [x2, #FPSIMD_CONTEXT_MAGIC_OFFSET] + mov w3, #FPSIMD_CONTEXT_SIZE + str w3, [x2, #FPSIMD_CONTEXT_SIZE_OFFSET] + + /* Fill in the FP SIMD context. */ + add x3, x2, #(FPSIMD_CONTEXT_VREGS_OFFSET + 8 * SIMD_REGISTER_SIZE) + stp d8, d9, [x3], #(2 * SIMD_REGISTER_SIZE) + stp d10, d11, [x3], #(2 * SIMD_REGISTER_SIZE) + stp d12, d13, [x3], #(2 * SIMD_REGISTER_SIZE) + stp d14, d15, [x3], #(2 * SIMD_REGISTER_SIZE) + + add x3, x2, FPSIMD_CONTEXT_FPSR_OFFSET + + mrs x4, fpsr + str w4, [x3] + + mrs x4, fpcr + str w4, [x3, FPSIMD_CONTEXT_FPCR_OFFSET - FPSIMD_CONTEXT_FPSR_OFFSET] + + /* Write the termination context extension header. */ + add x2, x2, #FPSIMD_CONTEXT_SIZE + + str xzr, [x2, #FPSIMD_CONTEXT_MAGIC_OFFSET] + str xzr, [x2, #FPSIMD_CONTEXT_SIZE_OFFSET] + + /* Grab the signal mask */ + /* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8) */ + add x2, x0, #UCONTEXT_SIGMASK_OFFSET + mov x0, #0 /* SIG_BLOCK */ + mov x1, #0 /* NULL */ + mov x3, #(_NSIG / 8) + mov x8, #__NR_rt_sigprocmask + svc 0 + + /* Return x0 for success */ + mov x0, 0 + ret + + .cfi_endproc + .size breakpad_getcontext, . - breakpad_getcontext + +#elif defined(__i386__) + + .text + .global breakpad_getcontext + .hidden breakpad_getcontext + .align 4 + .type breakpad_getcontext, @function + +breakpad_getcontext: + + movl 4(%esp), %eax /* eax = uc */ + + /* Save register values */ + movl %ecx, MCONTEXT_ECX_OFFSET(%eax) + movl %edx, MCONTEXT_EDX_OFFSET(%eax) + movl %ebx, MCONTEXT_EBX_OFFSET(%eax) + movl %edi, MCONTEXT_EDI_OFFSET(%eax) + movl %esi, MCONTEXT_ESI_OFFSET(%eax) + movl %ebp, MCONTEXT_EBP_OFFSET(%eax) + + movl (%esp), %edx /* return address */ + lea 4(%esp), %ecx /* exclude return address from stack */ + mov %edx, MCONTEXT_EIP_OFFSET(%eax) + mov %ecx, MCONTEXT_ESP_OFFSET(%eax) + + xorl %ecx, %ecx + movw %fs, %cx + mov %ecx, MCONTEXT_FS_OFFSET(%eax) + + movl $0, MCONTEXT_EAX_OFFSET(%eax) + + /* Save floating point state to fpregstate, then update + * the fpregs pointer to point to it */ + leal UCONTEXT_FPREGS_MEM_OFFSET(%eax), %ecx + fnstenv (%ecx) + fldenv (%ecx) + mov %ecx, UCONTEXT_FPREGS_OFFSET(%eax) + + /* Save signal mask: sigprocmask(SIGBLOCK, NULL, &uc->uc_sigmask) */ + leal UCONTEXT_SIGMASK_OFFSET(%eax), %edx + xorl %ecx, %ecx + push %edx /* &uc->uc_sigmask */ + push %ecx /* NULL */ + push %ecx /* SIGBLOCK == 0 on i386 */ + call sigprocmask@PLT + addl $12, %esp + + movl $0, %eax + ret + + .size breakpad_getcontext, . - breakpad_getcontext + +#elif defined(__mips__) + +// This implementation is inspired by implementation of getcontext in glibc. +#include <asm-mips/asm.h> +#include <asm-mips/regdef.h> +#if _MIPS_SIM == _ABIO32 +#include <asm-mips/fpregdef.h> +#endif + +// from asm-mips/asm.h +#if _MIPS_SIM == _ABIO32 +#define ALSZ 7 +#define ALMASK ~7 +#define SZREG 4 +#else // _MIPS_SIM != _ABIO32 +#define ALSZ 15 +#define ALMASK ~15 +#define SZREG 8 +#endif + +#include <asm/unistd.h> // for __NR_rt_sigprocmask + +#define _NSIG8 128 / 8 +#define SIG_BLOCK 1 + + + .text +LOCALS_NUM = 1 // save gp on stack +FRAME_SIZE = ((LOCALS_NUM * SZREG) + ALSZ) & ALMASK + +GP_FRAME_OFFSET = FRAME_SIZE - (1 * SZREG) +MCONTEXT_REG_SIZE = 8 + +#if _MIPS_SIM == _ABIO32 + +NESTED (breakpad_getcontext, FRAME_SIZE, ra) + .mask 0x00000000, 0 + .fmask 0x00000000, 0 + + .set noreorder + .cpload t9 + .set reorder + + move a2, sp +#define _SP a2 + + addiu sp, -FRAME_SIZE + .cprestore GP_FRAME_OFFSET + + sw s0, (16 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sw s1, (17 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sw s2, (18 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sw s3, (19 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sw s4, (20 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sw s5, (21 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sw s6, (22 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sw s7, (23 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sw _SP, (29 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sw fp, (30 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sw ra, (31 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sw ra, MCONTEXT_PC_OFFSET(a0) + +#ifdef __mips_hard_float + s.d fs0, (20 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0) + s.d fs1, (22 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0) + s.d fs2, (24 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0) + s.d fs3, (26 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0) + s.d fs4, (28 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0) + s.d fs5, (30 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0) + + cfc1 v1, fcr31 + sw v1, MCONTEXT_FPC_CSR(a0) +#endif // __mips_hard_float + + /* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8) */ + li a3, _NSIG8 + addu a2, a0, UCONTEXT_SIGMASK_OFFSET + move a1, zero + li a0, SIG_BLOCK + li v0, __NR_rt_sigprocmask + syscall + + addiu sp, FRAME_SIZE + jr ra + +END (breakpad_getcontext) +#else + +#ifndef NESTED +/* + * NESTED - declare nested routine entry point + */ +#define NESTED(symbol, framesize, rpc) \ + .globl symbol; \ + .align 2; \ + .type symbol,@function; \ + .ent symbol,0; \ +symbol: .frame sp, framesize, rpc; +#endif + +/* + * END - mark end of function + */ +#ifndef END +# define END(function) \ + .end function; \ + .size function,.-function +#endif + +/* int getcontext (ucontext_t *ucp) */ + +NESTED (breakpad_getcontext, FRAME_SIZE, ra) + .mask 0x10000000, 0 + .fmask 0x00000000, 0 + + move a2, sp +#define _SP a2 + move a3, gp +#define _GP a3 + + daddiu sp, -FRAME_SIZE + .cpsetup $25, GP_FRAME_OFFSET, breakpad_getcontext + + /* Store a magic flag. */ + li v1, 1 + sd v1, (0 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) /* zero */ + + sd s0, (16 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sd s1, (17 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sd s2, (18 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sd s3, (19 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sd s4, (20 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sd s5, (21 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sd s6, (22 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sd s7, (23 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sd _GP, (28 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sd _SP, (29 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sd s8, (30 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sd ra, (31 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) + sd ra, MCONTEXT_PC_OFFSET(a0) + +#ifdef __mips_hard_float + s.d $f24, (24 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0) + s.d $f25, (25 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0) + s.d $f26, (26 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0) + s.d $f27, (27 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0) + s.d $f28, (28 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0) + s.d $f29, (29 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0) + s.d $f30, (30 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0) + s.d $f31, (31 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0) + + cfc1 v1, $31 + sw v1, MCONTEXT_FPC_CSR(a0) +#endif /* __mips_hard_float */ + +/* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8) */ + li a3, _NSIG8 + daddu a2, a0, UCONTEXT_SIGMASK_OFFSET + move a1, zero + li a0, SIG_BLOCK + + li v0, __NR_rt_sigprocmask + syscall + + .cpreturn + daddiu sp, FRAME_SIZE + move v0, zero + jr ra + +END (breakpad_getcontext) +#endif // _MIPS_SIM == _ABIO32 + +#elif defined(__x86_64__) +/* The x64 implementation of breakpad_getcontext was derived in part + from the implementation of libunwind which requires the following + notice. */ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 Google, Inc + Contributed by Paul Pluzhnikov <ppluzhnikov@google.com> + Copyright (C) 2010 Konstantin Belousov <kib@freebsd.org> + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + + .text + .global breakpad_getcontext + .hidden breakpad_getcontext + .align 4 + .type breakpad_getcontext, @function + +breakpad_getcontext: + .cfi_startproc + + /* Callee saved: RBX, RBP, R12-R15 */ + movq %r12, MCONTEXT_GREGS_R12(%rdi) + movq %r13, MCONTEXT_GREGS_R13(%rdi) + movq %r14, MCONTEXT_GREGS_R14(%rdi) + movq %r15, MCONTEXT_GREGS_R15(%rdi) + movq %rbp, MCONTEXT_GREGS_RBP(%rdi) + movq %rbx, MCONTEXT_GREGS_RBX(%rdi) + + /* Save argument registers (not strictly needed, but setcontext + restores them, so don't restore garbage). */ + movq %r8, MCONTEXT_GREGS_R8(%rdi) + movq %r9, MCONTEXT_GREGS_R9(%rdi) + movq %rdi, MCONTEXT_GREGS_RDI(%rdi) + movq %rsi, MCONTEXT_GREGS_RSI(%rdi) + movq %rdx, MCONTEXT_GREGS_RDX(%rdi) + movq %rax, MCONTEXT_GREGS_RAX(%rdi) + movq %rcx, MCONTEXT_GREGS_RCX(%rdi) + + /* Save fp state (not needed, except for setcontext not + restoring garbage). */ + leaq MCONTEXT_FPREGS_MEM(%rdi),%r8 + movq %r8, MCONTEXT_FPREGS_PTR(%rdi) + fnstenv (%r8) + stmxcsr FPREGS_OFFSET_MXCSR(%r8) + + leaq 8(%rsp), %rax /* exclude this call. */ + movq %rax, MCONTEXT_GREGS_RSP(%rdi) + + movq 0(%rsp), %rax + movq %rax, MCONTEXT_GREGS_RIP(%rdi) + + /* Save signal mask: sigprocmask(SIGBLOCK, NULL, &uc->uc_sigmask) */ + leaq UCONTEXT_SIGMASK_OFFSET(%rdi), %rdx // arg3 + xorq %rsi, %rsi // arg2 NULL + xorq %rdi, %rdi // arg1 SIGBLOCK == 0 + call sigprocmask@PLT + + /* Always return 0 for success, even if sigprocmask failed. */ + xorl %eax, %eax + ret + .cfi_endproc + .size breakpad_getcontext, . - breakpad_getcontext + +#else +#error "This file has not been ported for your CPU!" +#endif diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/breakpad_getcontext.h b/toolkit/crashreporter/google-breakpad/src/common/linux/breakpad_getcontext.h new file mode 100644 index 0000000000..1418cde621 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/breakpad_getcontext.h @@ -0,0 +1,56 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef GOOGLE_BREAKPAD_COMMON_LINUX_INCLUDE_UCONTEXT_H +#define GOOGLE_BREAKPAD_COMMON_LINUX_INCLUDE_UCONTEXT_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifndef HAVE_GETCONTEXT + +#include <signal.h> + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Provided by src/common/linux/breakpad_getcontext.S +int breakpad_getcontext(ucontext_t* ucp); + +#define getcontext(x) breakpad_getcontext(x) + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // HAVE_GETCONTEXT + +#endif // GOOGLE_BREAKPAD_COMMON_LINUX_INCLUDE_UCONTEXT_H diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/breakpad_getcontext_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/breakpad_getcontext_unittest.cc new file mode 100644 index 0000000000..a57bfedf9b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/breakpad_getcontext_unittest.cc @@ -0,0 +1,194 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// asm/sigcontext.h can't be included with signal.h on glibc or +// musl, so only compare _libc_fpstate and _fpstate on Android. +#if defined(__ANDROID__) && defined(__x86_64__) +#include <asm/sigcontext.h> +#endif + +#include <sys/ucontext.h> + +#include <type_traits> + +#include "breakpad_googletest_includes.h" +#include "common/linux/ucontext_constants.h" + +template <int left, int right> +struct CompileAssertEquals { + // a compilation error here indicates left and right are not equal. + char left_too_large[right - left]; + // a compilation error here indicates left and right are not equal. + char right_too_large[left - right]; +}; + +#define COMPILE_ASSERT_EQ(left, right, tag) \ + CompileAssertEquals<left, right> tag; + +TEST(AndroidUContext, GRegsOffset) { +#if defined(__arm__) + // There is no gregs[] array on ARM, so compare to the offset of + // first register fields, since they're stored in order. + ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.arm_r0)); +#elif defined(__aarch64__) + // There is no gregs[] array on ARM, so compare to the offset of + // first register fields, since they're stored in order. + ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.regs[0])); + ASSERT_EQ(static_cast<size_t>(MCONTEXT_SP_OFFSET), + offsetof(ucontext_t,uc_mcontext.sp)); + ASSERT_EQ(static_cast<size_t>(MCONTEXT_PC_OFFSET), + offsetof(ucontext_t,uc_mcontext.pc)); + ASSERT_EQ(static_cast<size_t>(MCONTEXT_PSTATE_OFFSET), + offsetof(ucontext_t,uc_mcontext.pstate)); + ASSERT_EQ(static_cast<size_t>(MCONTEXT_EXTENSION_OFFSET), + offsetof(ucontext_t,uc_mcontext.__reserved)); +#elif defined(__i386__) + ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.gregs)); +#define CHECK_REG(x) \ + ASSERT_EQ(static_cast<size_t>(MCONTEXT_##x##_OFFSET), \ + offsetof(ucontext_t,uc_mcontext.gregs[REG_##x])) + CHECK_REG(GS); + CHECK_REG(FS); + CHECK_REG(ES); + CHECK_REG(DS); + CHECK_REG(EDI); + CHECK_REG(ESI); + CHECK_REG(EBP); + CHECK_REG(ESP); + CHECK_REG(EBX); + CHECK_REG(EDX); + CHECK_REG(ECX); + CHECK_REG(EAX); + CHECK_REG(TRAPNO); + CHECK_REG(ERR); + CHECK_REG(EIP); + CHECK_REG(CS); + CHECK_REG(EFL); + CHECK_REG(UESP); + CHECK_REG(SS); + + ASSERT_EQ(static_cast<size_t>(UCONTEXT_FPREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.fpregs)); + + ASSERT_EQ(static_cast<size_t>(UCONTEXT_FPREGS_MEM_OFFSET), + offsetof(ucontext_t,__fpregs_mem)); +#elif defined(__mips__) + ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.gregs)); + + // PC for mips is not part of gregs. + ASSERT_EQ(static_cast<size_t>(MCONTEXT_PC_OFFSET), + offsetof(ucontext_t,uc_mcontext.pc)); + + ASSERT_EQ(static_cast<size_t>(MCONTEXT_FPREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.fpregs)); + + ASSERT_EQ(static_cast<size_t>(MCONTEXT_FPC_CSR), + offsetof(ucontext_t,uc_mcontext.fpc_csr)); +#elif defined(__x86_64__) + + COMPILE_ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.gregs), + mcontext_gregs_offset); +#define CHECK_REG(x) \ + COMPILE_ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_##x), \ + offsetof(ucontext_t,uc_mcontext.gregs[REG_##x]), reg_##x) + CHECK_REG(R8); + CHECK_REG(R9); + CHECK_REG(R10); + CHECK_REG(R11); + CHECK_REG(R12); + CHECK_REG(R13); + CHECK_REG(R14); + CHECK_REG(R15); + CHECK_REG(RDI); + CHECK_REG(RSI); + CHECK_REG(RBP); + CHECK_REG(RBX); + CHECK_REG(RDX); + CHECK_REG(RAX); + CHECK_REG(RCX); + CHECK_REG(RSP); + CHECK_REG(RIP); + + // sigcontext is an analog to mcontext_t. The layout should be the same. + COMPILE_ASSERT_EQ(offsetof(mcontext_t,fpregs), + offsetof(sigcontext,fpstate), sigcontext_fpstate); + +#if defined(__ANDROID__) + // Check that _fpstate from asm/sigcontext.h is essentially the same + // as _libc_fpstate. + COMPILE_ASSERT_EQ(sizeof(_libc_fpstate), sizeof(_fpstate), + sigcontext_fpstate_size); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,cwd),offsetof(_fpstate,cwd), + sigcontext_fpstate_cwd); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,swd),offsetof(_fpstate,swd), + sigcontext_fpstate_swd); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,ftw),offsetof(_fpstate,twd), + sigcontext_fpstate_twd); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,fop),offsetof(_fpstate,fop), + sigcontext_fpstate_fop); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,rip),offsetof(_fpstate,rip), + sigcontext_fpstate_rip); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,rdp),offsetof(_fpstate,rdp), + sigcontext_fpstate_rdp); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,mxcsr),offsetof(_fpstate,mxcsr), + sigcontext_fpstate_mxcsr); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,mxcr_mask), + offsetof(_fpstate,mxcsr_mask), + sigcontext_fpstate_mxcsr_mask); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,_st), offsetof(_fpstate,st_space), + sigcontext_fpstate_stspace); + COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,_xmm), offsetof(_fpstate,xmm_space), + sigcontext_fpstate_xmm_space); +#endif + + COMPILE_ASSERT_EQ(MCONTEXT_FPREGS_PTR, + offsetof(ucontext_t,uc_mcontext.fpregs), + mcontext_fpregs_ptr); + COMPILE_ASSERT_EQ(MCONTEXT_FPREGS_MEM, offsetof(ucontext_t,__fpregs_mem), + mcontext_fpregs_mem); + COMPILE_ASSERT_EQ(FPREGS_OFFSET_MXCSR, + offsetof(std::remove_pointer<fpregset_t>::type,mxcsr), + fpregs_offset_mxcsr); + COMPILE_ASSERT_EQ(UCONTEXT_SIGMASK_OFFSET, offsetof(ucontext_t, uc_sigmask), + ucontext_sigmask); +#else + ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.gregs)); +#endif +} + +TEST(AndroidUContext, SigmakOffset) { + ASSERT_EQ(static_cast<size_t>(UCONTEXT_SIGMASK_OFFSET), + offsetof(ucontext_t,uc_sigmask)); +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/crc32.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/crc32.cc new file mode 100644 index 0000000000..8df636ce4d --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/crc32.cc @@ -0,0 +1,70 @@ +// Copyright 2014 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include "common/linux/crc32.h" + +namespace google_breakpad { + +// This implementation is based on the sample implementation in RFC 1952. + +// CRC32 polynomial, in reversed form. +// See RFC 1952, or http://en.wikipedia.org/wiki/Cyclic_redundancy_check +static const uint32_t kCrc32Polynomial = 0xEDB88320; +static uint32_t kCrc32Table[256] = { 0 }; + +#define arraysize(f) (sizeof(f) / sizeof(*f)) + +static void EnsureCrc32TableInited() { + if (kCrc32Table[arraysize(kCrc32Table) - 1]) + return; // already inited + for (uint32_t i = 0; i < arraysize(kCrc32Table); ++i) { + uint32_t c = i; + for (size_t j = 0; j < 8; ++j) { + if (c & 1) { + c = kCrc32Polynomial ^ (c >> 1); + } else { + c >>= 1; + } + } + kCrc32Table[i] = c; + } +} + +uint32_t UpdateCrc32(uint32_t start, const void* buf, size_t len) { + EnsureCrc32TableInited(); + + uint32_t c = start ^ 0xFFFFFFFF; + const uint8_t* u = static_cast<const uint8_t*>(buf); + for (size_t i = 0; i < len; ++i) { + c = kCrc32Table[(c ^ u[i]) & 0xFF] ^ (c >> 8); + } + return c ^ 0xFFFFFFFF; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/crc32.h b/toolkit/crashreporter/google-breakpad/src/common/linux/crc32.h new file mode 100644 index 0000000000..e3d9db92be --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/crc32.h @@ -0,0 +1,53 @@ +// Copyright 2014 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef COMMON_LINUX_CRC32_H_ +#define COMMON_LINUX_CRC32_H_ + +#include <stdint.h> + +#include <string> + +namespace google_breakpad { + +// Updates a CRC32 checksum with |len| bytes from |buf|. |initial| holds the +// checksum result from the previous update; for the first call, it should be 0. +uint32_t UpdateCrc32(uint32_t initial, const void* buf, size_t len); + +// Computes a CRC32 checksum using |len| bytes from |buf|. +inline uint32_t ComputeCrc32(const void* buf, size_t len) { + return UpdateCrc32(0, buf, len); +} +inline uint32_t ComputeCrc32(const std::string& str) { + return ComputeCrc32(str.c_str(), str.size()); +} + +} // namespace google_breakpad + +#endif // COMMON_LINUX_CRC32_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc new file mode 100644 index 0000000000..e398f7b392 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc @@ -0,0 +1,1218 @@ +// Copyright (c) 2011 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Restructured in 2009 by: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// dump_symbols.cc: implement google_breakpad::WriteSymbolFile: +// Find all the debugging info in a file and dump it as a Breakpad symbol file. + +#include "common/linux/dump_symbols.h" + +#include <assert.h> +#include <elf.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <link.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <iostream> +#include <set> +#include <string> +#include <utility> +#include <vector> + +#include "common/arm_ex_reader.h" +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/dwarf2diehandler.h" +#include "common/dwarf_cfi_to_module.h" +#include "common/dwarf_cu_to_module.h" +#include "common/dwarf_line_to_module.h" +#include "common/dwarf_range_list_handler.h" +#include "common/linux/crc32.h" +#include "common/linux/eintr_wrapper.h" +#include "common/linux/elfutils.h" +#include "common/linux/elfutils-inl.h" +#include "common/linux/elf_symbols_to_module.h" +#include "common/linux/file_id.h" +#include "common/memory_allocator.h" +#include "common/module.h" +#include "common/path_helper.h" +#include "common/scoped_ptr.h" +#ifndef NO_STABS_SUPPORT +#include "common/stabs_reader.h" +#include "common/stabs_to_module.h" +#endif +#include "common/using_std_string.h" + +#ifndef SHT_ARM_EXIDX +// bionic and older glibc don't define this +# define SHT_ARM_EXIDX (SHT_LOPROC + 1) +#endif + +// This namespace contains helper functions. +namespace { + +using google_breakpad::DumpOptions; +using google_breakpad::DwarfCFIToModule; +using google_breakpad::DwarfCUToModule; +using google_breakpad::DwarfLineToModule; +using google_breakpad::DwarfRangeListHandler; +using google_breakpad::ElfClass; +using google_breakpad::ElfClass32; +using google_breakpad::ElfClass64; +using google_breakpad::FileID; +using google_breakpad::FindElfSectionByName; +using google_breakpad::GetOffset; +using google_breakpad::IsValidElf; +using google_breakpad::kDefaultBuildIdSize; +using google_breakpad::Module; +using google_breakpad::PageAllocator; +#ifndef NO_STABS_SUPPORT +using google_breakpad::StabsToModule; +#endif +using google_breakpad::scoped_ptr; +using google_breakpad::wasteful_vector; + +// Define AARCH64 ELF architecture if host machine does not include this define. +#ifndef EM_AARCH64 +#define EM_AARCH64 183 +#endif + +// +// FDWrapper +// +// Wrapper class to make sure opened file is closed. +// +class FDWrapper { + public: + explicit FDWrapper(int fd) : + fd_(fd) {} + ~FDWrapper() { + if (fd_ != -1) + close(fd_); + } + int get() { + return fd_; + } + int release() { + int fd = fd_; + fd_ = -1; + return fd; + } + private: + int fd_; +}; + +// +// MmapWrapper +// +// Wrapper class to make sure mapped regions are unmapped. +// +class MmapWrapper { + public: + MmapWrapper() : is_set_(false) {} + ~MmapWrapper() { + if (is_set_ && base_ != NULL) { + assert(size_ > 0); + munmap(base_, size_); + } + } + void set(void *mapped_address, size_t mapped_size) { + is_set_ = true; + base_ = mapped_address; + size_ = mapped_size; + } + void release() { + assert(is_set_); + is_set_ = false; + base_ = NULL; + size_ = 0; + } + + private: + bool is_set_; + void* base_; + size_t size_; +}; + +// Find the preferred loading address of the binary. +template<typename ElfClass> +typename ElfClass::Addr GetLoadingAddress( + const typename ElfClass::Phdr* program_headers, + int nheader) { + typedef typename ElfClass::Phdr Phdr; + + // For non-PIC executables (e_type == ET_EXEC), the load address is + // the start address of the first PT_LOAD segment. (ELF requires + // the segments to be sorted by load address.) For PIC executables + // and dynamic libraries (e_type == ET_DYN), this address will + // normally be zero. + for (int i = 0; i < nheader; ++i) { + const Phdr& header = program_headers[i]; + if (header.p_type == PT_LOAD) + return header.p_vaddr; + } + return 0; +} + +// Find the set of address ranges for all PT_LOAD segments. +template <typename ElfClass> +vector<Module::Range> GetPtLoadSegmentRanges( + const typename ElfClass::Phdr* program_headers, + int nheader) { + typedef typename ElfClass::Phdr Phdr; + vector<Module::Range> ranges; + + for (int i = 0; i < nheader; ++i) { + const Phdr& header = program_headers[i]; + if (header.p_type == PT_LOAD) { + ranges.push_back(Module::Range(header.p_vaddr, header.p_memsz)); + } + } + return ranges; +} + +#ifndef NO_STABS_SUPPORT +template<typename ElfClass> +bool LoadStabs(const typename ElfClass::Ehdr* elf_header, + const typename ElfClass::Shdr* stab_section, + const typename ElfClass::Shdr* stabstr_section, + const bool big_endian, + Module* module) { + // A callback object to handle data from the STABS reader. + StabsToModule handler(module); + // Find the addresses of the STABS data, and create a STABS reader object. + // On Linux, STABS entries always have 32-bit values, regardless of the + // address size of the architecture whose code they're describing, and + // the strings are always "unitized". + const uint8_t* stabs = + GetOffset<ElfClass, uint8_t>(elf_header, stab_section->sh_offset); + const uint8_t* stabstr = + GetOffset<ElfClass, uint8_t>(elf_header, stabstr_section->sh_offset); + google_breakpad::StabsReader reader(stabs, stab_section->sh_size, + stabstr, stabstr_section->sh_size, + big_endian, 4, true, &handler); + // Read the STABS data, and do post-processing. + if (!reader.Process()) + return false; + handler.Finalize(); + return true; +} +#endif // NO_STABS_SUPPORT + +// A range handler that accepts rangelist data parsed by +// dwarf2reader::RangeListReader and populates a range vector (typically +// owned by a function) with the results. +class DumperRangesHandler : public DwarfCUToModule::RangesHandler { + public: + DumperRangesHandler(const uint8_t *buffer, uint64 size, + dwarf2reader::ByteReader* reader) + : buffer_(buffer), size_(size), reader_(reader) { } + + bool ReadRanges(uint64 offset, Module::Address base_address, + vector<Module::Range>* ranges) { + DwarfRangeListHandler handler(base_address, ranges); + dwarf2reader::RangeListReader rangelist_reader(buffer_, size_, reader_, + &handler); + + return rangelist_reader.ReadRangeList(offset); + } + + private: + const uint8_t *buffer_; + uint64 size_; + dwarf2reader::ByteReader* reader_; +}; + +// A line-to-module loader that accepts line number info parsed by +// dwarf2reader::LineInfo and populates a Module and a line vector +// with the results. +class DumperLineToModule: public DwarfCUToModule::LineToModuleHandler { + public: + // Create a line-to-module converter using BYTE_READER. + explicit DumperLineToModule(dwarf2reader::ByteReader *byte_reader) + : byte_reader_(byte_reader) { } + void StartCompilationUnit(const string& compilation_dir) { + compilation_dir_ = compilation_dir; + } + void ReadProgram(const uint8_t *program, uint64 length, + Module* module, std::vector<Module::Line>* lines) { + DwarfLineToModule handler(module, compilation_dir_, lines); + dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler); + parser.Start(); + } + private: + string compilation_dir_; + dwarf2reader::ByteReader *byte_reader_; +}; + +template<typename ElfClass> +bool LoadDwarf(const string& dwarf_filename, + const typename ElfClass::Ehdr* elf_header, + const bool big_endian, + bool handle_inter_cu_refs, + Module* module) { + typedef typename ElfClass::Shdr Shdr; + + const dwarf2reader::Endianness endianness = big_endian ? + dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE; + dwarf2reader::ByteReader byte_reader(endianness); + + // Construct a context for this file. + DwarfCUToModule::FileContext file_context(dwarf_filename, + module, + handle_inter_cu_refs); + + // Build a map of the ELF file's sections. + const Shdr* sections = + GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff); + int num_sections = elf_header->e_shnum; + const Shdr* section_names = sections + elf_header->e_shstrndx; + for (int i = 0; i < num_sections; i++) { + const Shdr* section = §ions[i]; + string name = GetOffset<ElfClass, char>(elf_header, + section_names->sh_offset) + + section->sh_name; + const uint8_t *contents = GetOffset<ElfClass, uint8_t>(elf_header, + section->sh_offset); + file_context.AddSectionToSectionMap(name, contents, section->sh_size); + } + + // Optional .debug_ranges reader + scoped_ptr<DumperRangesHandler> ranges_handler; + dwarf2reader::SectionMap::const_iterator ranges_entry = + file_context.section_map().find(".debug_ranges"); + if (ranges_entry != file_context.section_map().end()) { + const std::pair<const uint8_t *, uint64>& ranges_section = + ranges_entry->second; + ranges_handler.reset( + new DumperRangesHandler(ranges_section.first, ranges_section.second, + &byte_reader)); + } + + // Parse all the compilation units in the .debug_info section. + DumperLineToModule line_to_module(&byte_reader); + dwarf2reader::SectionMap::const_iterator debug_info_entry = + file_context.section_map().find(".debug_info"); + assert(debug_info_entry != file_context.section_map().end()); + const std::pair<const uint8_t *, uint64>& debug_info_section = + debug_info_entry->second; + // This should never have been called if the file doesn't have a + // .debug_info section. + assert(debug_info_section.first); + uint64 debug_info_length = debug_info_section.second; + for (uint64 offset = 0; offset < debug_info_length;) { + // Make a handler for the root DIE that populates MODULE with the + // data that was found. + DwarfCUToModule::WarningReporter reporter(dwarf_filename, offset); + DwarfCUToModule root_handler(&file_context, &line_to_module, + ranges_handler.get(), &reporter); + // Make a Dwarf2Handler that drives the DIEHandler. + dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); + // Make a DWARF parser for the compilation unit at OFFSET. + dwarf2reader::CompilationUnit reader(dwarf_filename, + file_context.section_map(), + offset, + &byte_reader, + &die_dispatcher); + // Process the entire compilation unit; get the offset of the next. + offset += reader.Start(); + } + return true; +} + +// Fill REGISTER_NAMES with the register names appropriate to the +// machine architecture given in HEADER, indexed by the register +// numbers used in DWARF call frame information. Return true on +// success, or false if HEADER's machine architecture is not +// supported. +template<typename ElfClass> +bool DwarfCFIRegisterNames(const typename ElfClass::Ehdr* elf_header, + std::vector<string>* register_names) { + switch (elf_header->e_machine) { + case EM_386: + *register_names = DwarfCFIToModule::RegisterNames::I386(); + return true; + case EM_ARM: + *register_names = DwarfCFIToModule::RegisterNames::ARM(); + return true; + case EM_AARCH64: + *register_names = DwarfCFIToModule::RegisterNames::ARM64(); + return true; + case EM_MIPS: + *register_names = DwarfCFIToModule::RegisterNames::MIPS(); + return true; + case EM_X86_64: + *register_names = DwarfCFIToModule::RegisterNames::X86_64(); + return true; + default: + return false; + } +} + +template<typename ElfClass> +bool LoadDwarfCFI(const string& dwarf_filename, + const typename ElfClass::Ehdr* elf_header, + const char* section_name, + const typename ElfClass::Shdr* section, + const bool eh_frame, + const typename ElfClass::Shdr* got_section, + const typename ElfClass::Shdr* text_section, + const bool big_endian, + Module* module) { + // Find the appropriate set of register names for this file's + // architecture. + std::vector<string> register_names; + if (!DwarfCFIRegisterNames<ElfClass>(elf_header, ®ister_names)) { + fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';" + " cannot convert DWARF call frame information\n", + dwarf_filename.c_str(), elf_header->e_machine); + return false; + } + + const dwarf2reader::Endianness endianness = big_endian ? + dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE; + + // Find the call frame information and its size. + const uint8_t *cfi = + GetOffset<ElfClass, uint8_t>(elf_header, section->sh_offset); + size_t cfi_size = section->sh_size; + + // Plug together the parser, handler, and their entourages. + DwarfCFIToModule::Reporter module_reporter(dwarf_filename, section_name); + DwarfCFIToModule handler(module, register_names, &module_reporter); + dwarf2reader::ByteReader byte_reader(endianness); + + byte_reader.SetAddressSize(ElfClass::kAddrSize); + + // Provide the base addresses for .eh_frame encoded pointers, if + // possible. + byte_reader.SetCFIDataBase(section->sh_addr, cfi); + if (got_section) + byte_reader.SetDataBase(got_section->sh_addr); + if (text_section) + byte_reader.SetTextBase(text_section->sh_addr); + + dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename, + section_name); + dwarf2reader::CallFrameInfo parser(cfi, cfi_size, + &byte_reader, &handler, &dwarf_reporter, + eh_frame); + parser.Start(); + return true; +} + +template<typename ElfClass> +bool LoadARMexidx(const typename ElfClass::Ehdr* elf_header, + const typename ElfClass::Shdr* exidx_section, + const typename ElfClass::Shdr* extab_section, + uint32_t loading_addr, + Module* module) { + // To do this properly we need to know: + // * the bounds of the .ARM.exidx section in the mapped image + // * the bounds of the .ARM.extab section in the mapped image + // * the vma of the last byte in the text section associated with the .exidx + // The first two are easy. The third is a bit tricky. If we can't + // figure out what it is, just pass in zero. + const char *exidx_img + = GetOffset<ElfClass, char>(elf_header, exidx_section->sh_offset); + size_t exidx_size = exidx_section->sh_size; + const char *extab_img + = GetOffset<ElfClass, char>(elf_header, extab_section->sh_offset); + size_t extab_size = extab_section->sh_size; + + // The sh_link field of the exidx section gives the section number + // for the associated text section. + uint32_t exidx_text_last_svma = 0; + int exidx_text_sno = exidx_section->sh_link; + typedef typename ElfClass::Shdr Shdr; + // |sections| points to the section header table + const Shdr* sections + = GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff); + const int num_sections = elf_header->e_shnum; + if (exidx_text_sno >= 0 && exidx_text_sno < num_sections) { + const Shdr* exidx_text_shdr = §ions[exidx_text_sno]; + if (exidx_text_shdr->sh_size > 0) { + exidx_text_last_svma + = exidx_text_shdr->sh_addr + exidx_text_shdr->sh_size - 1; + } + } + + arm_ex_to_module::ARMExToModule handler(module); + arm_ex_reader::ExceptionTableInfo + parser(exidx_img, exidx_size, extab_img, extab_size, exidx_text_last_svma, + &handler, + reinterpret_cast<const char*>(elf_header), + loading_addr); + parser.Start(); + return true; +} + +bool LoadELF(const string& obj_file, MmapWrapper* map_wrapper, + void** elf_header) { + int obj_fd = open(obj_file.c_str(), O_RDONLY); + if (obj_fd < 0) { + fprintf(stderr, "Failed to open ELF file '%s': %s\n", + obj_file.c_str(), strerror(errno)); + return false; + } + FDWrapper obj_fd_wrapper(obj_fd); + struct stat st; + if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) { + fprintf(stderr, "Unable to fstat ELF file '%s': %s\n", + obj_file.c_str(), strerror(errno)); + return false; + } + void* obj_base = mmap(NULL, st.st_size, + PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0); + if (obj_base == MAP_FAILED) { + fprintf(stderr, "Failed to mmap ELF file '%s': %s\n", + obj_file.c_str(), strerror(errno)); + return false; + } + map_wrapper->set(obj_base, st.st_size); + *elf_header = obj_base; + if (!IsValidElf(*elf_header)) { + fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str()); + return false; + } + return true; +} + +// Get the endianness of ELF_HEADER. If it's invalid, return false. +template<typename ElfClass> +bool ElfEndianness(const typename ElfClass::Ehdr* elf_header, + bool* big_endian) { + if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) { + *big_endian = false; + return true; + } + if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB) { + *big_endian = true; + return true; + } + + fprintf(stderr, "bad data encoding in ELF header: %d\n", + elf_header->e_ident[EI_DATA]); + return false; +} + +// Given |left_abspath|, find the absolute path for |right_path| and see if the +// two absolute paths are the same. +bool IsSameFile(const char* left_abspath, const string& right_path) { + char right_abspath[PATH_MAX]; + if (!realpath(right_path.c_str(), right_abspath)) + return false; + return strcmp(left_abspath, right_abspath) == 0; +} + +// Read the .gnu_debuglink and get the debug file name. If anything goes +// wrong, return an empty string. +string ReadDebugLink(const uint8_t *debuglink, + const size_t debuglink_size, + const bool big_endian, + const string& obj_file, + const std::vector<string>& debug_dirs) { + // Include '\0' + CRC32 (4 bytes). + size_t debuglink_len = strlen(reinterpret_cast<const char *>(debuglink)) + 5; + debuglink_len = 4 * ((debuglink_len + 3) / 4); // Round up to 4 bytes. + + // Sanity check. + if (debuglink_len != debuglink_size) { + fprintf(stderr, "Mismatched .gnu_debuglink string / section size: " + "%zx %zx\n", debuglink_len, debuglink_size); + return string(); + } + + char obj_file_abspath[PATH_MAX]; + if (!realpath(obj_file.c_str(), obj_file_abspath)) { + fprintf(stderr, "Cannot resolve absolute path for %s\n", obj_file.c_str()); + return string(); + } + + std::vector<string> searched_paths; + string debuglink_path; + std::vector<string>::const_iterator it; + for (it = debug_dirs.begin(); it < debug_dirs.end(); ++it) { + const string& debug_dir = *it; + debuglink_path = debug_dir + "/" + + reinterpret_cast<const char *>(debuglink); + + // There is the annoying case of /path/to/foo.so having foo.so as the + // debug link file name. Thus this may end up opening /path/to/foo.so again, + // and there is a small chance of the two files having the same CRC. + if (IsSameFile(obj_file_abspath, debuglink_path)) + continue; + + searched_paths.push_back(debug_dir); + int debuglink_fd = open(debuglink_path.c_str(), O_RDONLY); + if (debuglink_fd < 0) + continue; + + FDWrapper debuglink_fd_wrapper(debuglink_fd); + + // The CRC is the last 4 bytes in |debuglink|. + const dwarf2reader::Endianness endianness = big_endian ? + dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE; + dwarf2reader::ByteReader byte_reader(endianness); + uint32_t expected_crc = + byte_reader.ReadFourBytes(&debuglink[debuglink_size - 4]); + + uint32_t actual_crc = 0; + while (true) { + const size_t kReadSize = 4096; + char buf[kReadSize]; + ssize_t bytes_read = HANDLE_EINTR(read(debuglink_fd, &buf, kReadSize)); + if (bytes_read < 0) { + fprintf(stderr, "Error reading debug ELF file %s.\n", + debuglink_path.c_str()); + return string(); + } + if (bytes_read == 0) + break; + actual_crc = google_breakpad::UpdateCrc32(actual_crc, buf, bytes_read); + } + if (actual_crc != expected_crc) { + fprintf(stderr, "Error reading debug ELF file - CRC32 mismatch: %s\n", + debuglink_path.c_str()); + continue; + } + + // Found debug file. + return debuglink_path; + } + + // Not found case. + fprintf(stderr, "Failed to find debug ELF file for '%s' after trying:\n", + obj_file.c_str()); + for (it = searched_paths.begin(); it < searched_paths.end(); ++it) { + const string& debug_dir = *it; + fprintf(stderr, " %s/%s\n", debug_dir.c_str(), debuglink); + } + return string(); +} + +// +// LoadSymbolsInfo +// +// Holds the state between the two calls to LoadSymbols() in case it's necessary +// to follow the .gnu_debuglink section and load debug information from a +// different file. +// +template<typename ElfClass> +class LoadSymbolsInfo { + public: + typedef typename ElfClass::Addr Addr; + + explicit LoadSymbolsInfo(const std::vector<string>& dbg_dirs) : + debug_dirs_(dbg_dirs), + has_loading_addr_(false) {} + + // Keeps track of which sections have been loaded so sections don't + // accidentally get loaded twice from two different files. + void LoadedSection(const string §ion) { + if (loaded_sections_.count(section) == 0) { + loaded_sections_.insert(section); + } else { + fprintf(stderr, "Section %s has already been loaded.\n", + section.c_str()); + } + } + + // The ELF file and linked debug file are expected to have the same preferred + // loading address. + void set_loading_addr(Addr addr, const string &filename) { + if (!has_loading_addr_) { + loading_addr_ = addr; + loaded_file_ = filename; + return; + } + + if (addr != loading_addr_) { + fprintf(stderr, + "ELF file '%s' and debug ELF file '%s' " + "have different load addresses.\n", + loaded_file_.c_str(), filename.c_str()); + assert(false); + } + } + + // Setters and getters + const std::vector<string>& debug_dirs() const { + return debug_dirs_; + } + + string debuglink_file() const { + return debuglink_file_; + } + void set_debuglink_file(string file) { + debuglink_file_ = file; + } + + private: + const std::vector<string>& debug_dirs_; // Directories in which to + // search for the debug ELF file. + + string debuglink_file_; // Full path to the debug ELF file. + + bool has_loading_addr_; // Indicate if LOADING_ADDR_ is valid. + + Addr loading_addr_; // Saves the preferred loading address from the + // first call to LoadSymbols(). + + string loaded_file_; // Name of the file loaded from the first call to + // LoadSymbols(). + + std::set<string> loaded_sections_; // Tracks the Loaded ELF sections + // between calls to LoadSymbols(). +}; + +template<typename ElfClass> +bool LoadSymbols(const string& obj_file, + const bool big_endian, + const typename ElfClass::Ehdr* elf_header, + const bool read_gnu_debug_link, + LoadSymbolsInfo<ElfClass>* info, + const DumpOptions& options, + Module* module) { + typedef typename ElfClass::Addr Addr; + typedef typename ElfClass::Phdr Phdr; + typedef typename ElfClass::Shdr Shdr; + + Addr loading_addr = GetLoadingAddress<ElfClass>( + GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff), + elf_header->e_phnum); + module->SetLoadAddress(loading_addr); + info->set_loading_addr(loading_addr, obj_file); + + // Allow filtering of extraneous debug information in partitioned libraries. + // Such libraries contain debug information for all libraries extracted from + // the same combined library, implying extensive duplication. + vector<Module::Range> address_ranges = GetPtLoadSegmentRanges<ElfClass>( + GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff), + elf_header->e_phnum); + module->SetAddressRanges(address_ranges); + + const Shdr* sections = + GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff); + const Shdr* section_names = sections + elf_header->e_shstrndx; + const char* names = + GetOffset<ElfClass, char>(elf_header, section_names->sh_offset); + const char *names_end = names + section_names->sh_size; + bool found_debug_info_section = false; + bool found_usable_info = false; + + if (options.symbol_data != ONLY_CFI) { +#ifndef NO_STABS_SUPPORT + // Look for STABS debugging information, and load it if present. + const Shdr* stab_section = + FindElfSectionByName<ElfClass>(".stab", SHT_PROGBITS, + sections, names, names_end, + elf_header->e_shnum); + if (stab_section) { + const Shdr* stabstr_section = stab_section->sh_link + sections; + if (stabstr_section) { + found_debug_info_section = true; + found_usable_info = true; + info->LoadedSection(".stab"); + if (!LoadStabs<ElfClass>(elf_header, stab_section, stabstr_section, + big_endian, module)) { + fprintf(stderr, "%s: \".stab\" section found, but failed to load" + " STABS debugging information\n", obj_file.c_str()); + } + } + } +#endif // NO_STABS_SUPPORT + + // Look for DWARF debugging information, and load it if present. + const Shdr* dwarf_section = + FindElfSectionByName<ElfClass>(".debug_info", SHT_PROGBITS, + sections, names, names_end, + elf_header->e_shnum); + + // .debug_info section type is SHT_PROGBITS for mips on pnacl toolchains, + // but MIPS_DWARF for regular gnu toolchains, so both need to be checked + if (elf_header->e_machine == EM_MIPS && !dwarf_section) { + dwarf_section = + FindElfSectionByName<ElfClass>(".debug_info", SHT_MIPS_DWARF, + sections, names, names_end, + elf_header->e_shnum); + } + + if (dwarf_section) { + found_debug_info_section = true; + found_usable_info = true; + info->LoadedSection(".debug_info"); + if (!LoadDwarf<ElfClass>(obj_file, elf_header, big_endian, + options.handle_inter_cu_refs, module)) { + fprintf(stderr, "%s: \".debug_info\" section found, but failed to load " + "DWARF debugging information\n", obj_file.c_str()); + } + } + + // See if there are export symbols available. + const Shdr* symtab_section = + FindElfSectionByName<ElfClass>(".symtab", SHT_SYMTAB, + sections, names, names_end, + elf_header->e_shnum); + const Shdr* strtab_section = + FindElfSectionByName<ElfClass>(".strtab", SHT_STRTAB, + sections, names, names_end, + elf_header->e_shnum); + if (symtab_section && strtab_section) { + info->LoadedSection(".symtab"); + + const uint8_t* symtab = + GetOffset<ElfClass, uint8_t>(elf_header, + symtab_section->sh_offset); + const uint8_t* strtab = + GetOffset<ElfClass, uint8_t>(elf_header, + strtab_section->sh_offset); + bool result = + ELFSymbolsToModule(symtab, + symtab_section->sh_size, + strtab, + strtab_section->sh_size, + big_endian, + ElfClass::kAddrSize, + module); + found_usable_info = found_usable_info || result; + } else { + // Look in dynsym only if full symbol table was not available. + const Shdr* dynsym_section = + FindElfSectionByName<ElfClass>(".dynsym", SHT_DYNSYM, + sections, names, names_end, + elf_header->e_shnum); + const Shdr* dynstr_section = + FindElfSectionByName<ElfClass>(".dynstr", SHT_STRTAB, + sections, names, names_end, + elf_header->e_shnum); + if (dynsym_section && dynstr_section) { + info->LoadedSection(".dynsym"); + + const uint8_t* dynsyms = + GetOffset<ElfClass, uint8_t>(elf_header, + dynsym_section->sh_offset); + const uint8_t* dynstrs = + GetOffset<ElfClass, uint8_t>(elf_header, + dynstr_section->sh_offset); + bool result = + ELFSymbolsToModule(dynsyms, + dynsym_section->sh_size, + dynstrs, + dynstr_section->sh_size, + big_endian, + ElfClass::kAddrSize, + module); + found_usable_info = found_usable_info || result; + } + } + } + + if (options.symbol_data != NO_CFI) { + // Dwarf Call Frame Information (CFI) is actually independent from + // the other DWARF debugging information, and can be used alone. + const Shdr* dwarf_cfi_section = + FindElfSectionByName<ElfClass>(".debug_frame", SHT_PROGBITS, + sections, names, names_end, + elf_header->e_shnum); + + // .debug_frame section type is SHT_PROGBITS for mips on pnacl toolchains, + // but MIPS_DWARF for regular gnu toolchains, so both need to be checked + if (elf_header->e_machine == EM_MIPS && !dwarf_cfi_section) { + dwarf_cfi_section = + FindElfSectionByName<ElfClass>(".debug_frame", SHT_MIPS_DWARF, + sections, names, names_end, + elf_header->e_shnum); + } + + if (dwarf_cfi_section) { + // Ignore the return value of this function; even without call frame + // information, the other debugging information could be perfectly + // useful. + info->LoadedSection(".debug_frame"); + bool result = + LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".debug_frame", + dwarf_cfi_section, false, 0, 0, big_endian, + module); + found_usable_info = found_usable_info || result; + } + + // Linux C++ exception handling information can also provide + // unwinding data. + const Shdr* eh_frame_section = + FindElfSectionByName<ElfClass>(".eh_frame", SHT_PROGBITS, + sections, names, names_end, + elf_header->e_shnum); + if (eh_frame_section) { + // Pointers in .eh_frame data may be relative to the base addresses of + // certain sections. Provide those sections if present. + const Shdr* got_section = + FindElfSectionByName<ElfClass>(".got", SHT_PROGBITS, + sections, names, names_end, + elf_header->e_shnum); + const Shdr* text_section = + FindElfSectionByName<ElfClass>(".text", SHT_PROGBITS, + sections, names, names_end, + elf_header->e_shnum); + info->LoadedSection(".eh_frame"); + // As above, ignore the return value of this function. + bool result = + LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".eh_frame", + eh_frame_section, true, + got_section, text_section, big_endian, module); + found_usable_info = found_usable_info || result; + } + } + + // ARM has special unwind tables that can be used. + const Shdr* arm_exidx_section = + FindElfSectionByName<ElfClass>(".ARM.exidx", SHT_ARM_EXIDX, + sections, names, names_end, + elf_header->e_shnum); + const Shdr* arm_extab_section = + FindElfSectionByName<ElfClass>(".ARM.extab", SHT_PROGBITS, + sections, names, names_end, + elf_header->e_shnum); + // Load information from these sections even if there is + // .debug_info, because some functions (e.g., hand-written or + // script-generated assembly) could have exidx entries but no DWARF. + // (For functions with both, the DWARF info that has already been + // parsed will take precedence.) + if (arm_exidx_section && arm_extab_section && options.symbol_data != NO_CFI) { + info->LoadedSection(".ARM.exidx"); + info->LoadedSection(".ARM.extab"); + bool result = LoadARMexidx<ElfClass>(elf_header, + arm_exidx_section, arm_extab_section, + loading_addr, module); + found_usable_info = found_usable_info || result; + } + + if (!found_debug_info_section) { + fprintf(stderr, "%s: file contains no debugging information" + " (no \".stab\" or \".debug_info\" sections)\n", + obj_file.c_str()); + + // Failed, but maybe there's a .gnu_debuglink section? + if (read_gnu_debug_link) { + const Shdr* gnu_debuglink_section + = FindElfSectionByName<ElfClass>(".gnu_debuglink", SHT_PROGBITS, + sections, names, + names_end, elf_header->e_shnum); + if (gnu_debuglink_section) { + if (!info->debug_dirs().empty()) { + const uint8_t *debuglink_contents = + GetOffset<ElfClass, uint8_t>(elf_header, + gnu_debuglink_section->sh_offset); + string debuglink_file = + ReadDebugLink(debuglink_contents, + gnu_debuglink_section->sh_size, + big_endian, + obj_file, + info->debug_dirs()); + info->set_debuglink_file(debuglink_file); + } else { + fprintf(stderr, ".gnu_debuglink section found in '%s', " + "but no debug path specified.\n", obj_file.c_str()); + } + } else { + fprintf(stderr, "%s does not contain a .gnu_debuglink section.\n", + obj_file.c_str()); + } + } else { + // Return true if some usable information was found, since the caller + // doesn't want to use .gnu_debuglink. + return found_usable_info; + } + + // No debug info was found, let the user try again with .gnu_debuglink + // if present. + return false; + } + + return true; +} + +// Return the breakpad symbol file identifier for the architecture of +// ELF_HEADER. +template<typename ElfClass> +const char* ElfArchitecture(const typename ElfClass::Ehdr* elf_header) { + typedef typename ElfClass::Half Half; + Half arch = elf_header->e_machine; + switch (arch) { + case EM_386: return "x86"; + case EM_ARM: return "arm"; + case EM_AARCH64: return "arm64"; + case EM_MIPS: return "mips"; + case EM_PPC64: return "ppc64"; + case EM_PPC: return "ppc"; + case EM_S390: return "s390"; + case EM_SPARC: return "sparc"; + case EM_SPARCV9: return "sparcv9"; + case EM_X86_64: return "x86_64"; + default: return NULL; + } +} + +template<typename ElfClass> +bool SanitizeDebugFile(const typename ElfClass::Ehdr* debug_elf_header, + const string& debuglink_file, + const string& obj_filename, + const char* obj_file_architecture, + const bool obj_file_is_big_endian) { + const char* debug_architecture = + ElfArchitecture<ElfClass>(debug_elf_header); + if (!debug_architecture) { + fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n", + debuglink_file.c_str(), debug_elf_header->e_machine); + return false; + } + if (strcmp(obj_file_architecture, debug_architecture)) { + fprintf(stderr, "%s with ELF machine architecture %s does not match " + "%s with ELF architecture %s\n", + debuglink_file.c_str(), debug_architecture, + obj_filename.c_str(), obj_file_architecture); + return false; + } + bool debug_big_endian; + if (!ElfEndianness<ElfClass>(debug_elf_header, &debug_big_endian)) + return false; + if (debug_big_endian != obj_file_is_big_endian) { + fprintf(stderr, "%s and %s does not match in endianness\n", + obj_filename.c_str(), debuglink_file.c_str()); + return false; + } + return true; +} + +template<typename ElfClass> +bool InitModuleForElfClass(const typename ElfClass::Ehdr* elf_header, + const string& obj_filename, + const string& obj_os, + scoped_ptr<Module>& module) { + PageAllocator allocator; + wasteful_vector<uint8_t> identifier(&allocator, kDefaultBuildIdSize); + if (!FileID::ElfFileIdentifierFromMappedFile(elf_header, identifier)) { + fprintf(stderr, "%s: unable to generate file identifier\n", + obj_filename.c_str()); + return false; + } + + const char *architecture = ElfArchitecture<ElfClass>(elf_header); + if (!architecture) { + fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n", + obj_filename.c_str(), elf_header->e_machine); + return false; + } + + char name_buf[NAME_MAX] = {}; + std::string name = google_breakpad::ElfFileSoNameFromMappedFile( + elf_header, name_buf, sizeof(name_buf)) + ? name_buf + : google_breakpad::BaseName(obj_filename); + + // Add an extra "0" at 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 be consistent. + string id = FileID::ConvertIdentifierToUUIDString(identifier) + "0"; + // This is just the raw Build ID in hex. + string code_id = FileID::ConvertIdentifierToString(identifier); + + module.reset(new Module(name, obj_os, architecture, id, code_id)); + + return true; +} + +template<typename ElfClass> +bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header, + const string& obj_filename, + const string& obj_os, + const std::vector<string>& debug_dirs, + const DumpOptions& options, + Module** out_module) { + typedef typename ElfClass::Ehdr Ehdr; + + *out_module = NULL; + + scoped_ptr<Module> module; + if (!InitModuleForElfClass<ElfClass>(elf_header, obj_filename, obj_os, + module)) { + return false; + } + + // Figure out what endianness this file is. + bool big_endian; + if (!ElfEndianness<ElfClass>(elf_header, &big_endian)) + return false; + + LoadSymbolsInfo<ElfClass> info(debug_dirs); + if (!LoadSymbols<ElfClass>(obj_filename, big_endian, elf_header, + !debug_dirs.empty(), &info, + options, module.get())) { + const string debuglink_file = info.debuglink_file(); + if (debuglink_file.empty()) + return false; + + // Load debuglink ELF file. + fprintf(stderr, "Found debugging info in %s\n", debuglink_file.c_str()); + MmapWrapper debug_map_wrapper; + Ehdr* debug_elf_header = NULL; + if (!LoadELF(debuglink_file, &debug_map_wrapper, + reinterpret_cast<void**>(&debug_elf_header)) || + !SanitizeDebugFile<ElfClass>(debug_elf_header, debuglink_file, + obj_filename, + module->architecture().c_str(), + big_endian)) { + return false; + } + + if (!LoadSymbols<ElfClass>(debuglink_file, big_endian, + debug_elf_header, false, &info, + options, module.get())) { + return false; + } + } + + *out_module = module.release(); + return true; +} + +} // namespace + +namespace google_breakpad { + +// Not explicitly exported, but not static so it can be used in unit tests. +bool ReadSymbolDataInternal(const uint8_t* obj_file, + const string& obj_filename, + const string& obj_os, + const std::vector<string>& debug_dirs, + const DumpOptions& options, + Module** module) { + if (!IsValidElf(obj_file)) { + fprintf(stderr, "Not a valid ELF file: %s\n", obj_filename.c_str()); + return false; + } + + int elfclass = ElfClass(obj_file); + if (elfclass == ELFCLASS32) { + return ReadSymbolDataElfClass<ElfClass32>( + reinterpret_cast<const Elf32_Ehdr*>(obj_file), obj_filename, obj_os, + debug_dirs, options, module); + } + if (elfclass == ELFCLASS64) { + return ReadSymbolDataElfClass<ElfClass64>( + reinterpret_cast<const Elf64_Ehdr*>(obj_file), obj_filename, obj_os, + debug_dirs, options, module); + } + + return false; +} + +bool WriteSymbolFile(const string &load_path, + const string &obj_file, + const string &obj_os, + const std::vector<string>& debug_dirs, + const DumpOptions& options, + std::ostream &sym_stream) { + Module* module; + if (!ReadSymbolData(load_path, obj_file, obj_os, debug_dirs, options, + &module)) + return false; + + bool result = module->Write(sym_stream, options.symbol_data); + delete module; + return result; +} + +// Read the selected object file's debugging information, and write out the +// header only to |stream|. Return true on success; if an error occurs, report +// it and return false. +bool WriteSymbolFileHeader(const string& load_path, + const string& obj_file, + const string& obj_os, + std::ostream &sym_stream) { + MmapWrapper map_wrapper; + void* elf_header = NULL; + if (!LoadELF(load_path, &map_wrapper, &elf_header)) { + fprintf(stderr, "Could not load ELF file: %s\n", obj_file.c_str()); + return false; + } + + if (!IsValidElf(elf_header)) { + fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str()); + return false; + } + + int elfclass = ElfClass(elf_header); + scoped_ptr<Module> module; + if (elfclass == ELFCLASS32) { + if (!InitModuleForElfClass<ElfClass32>( + reinterpret_cast<const Elf32_Ehdr*>(elf_header), obj_file, obj_os, + module)) { + fprintf(stderr, "Failed to load ELF module: %s\n", obj_file.c_str()); + return false; + } + } else if (elfclass == ELFCLASS64) { + if (!InitModuleForElfClass<ElfClass64>( + reinterpret_cast<const Elf64_Ehdr*>(elf_header), obj_file, obj_os, + module)) { + fprintf(stderr, "Failed to load ELF module: %s\n", obj_file.c_str()); + return false; + } + } else { + fprintf(stderr, "Unsupported module file: %s\n", obj_file.c_str()); + return false; + } + + return module->Write(sym_stream, ALL_SYMBOL_DATA); +} + +bool ReadSymbolData(const string& load_path, + const string& obj_file, + const string& obj_os, + const std::vector<string>& debug_dirs, + const DumpOptions& options, + Module** module) { + MmapWrapper map_wrapper; + void* elf_header = NULL; + if (!LoadELF(load_path, &map_wrapper, &elf_header)) + return false; + + return ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(elf_header), + obj_file, obj_os, debug_dirs, options, module); +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.h b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.h new file mode 100644 index 0000000000..eaddd8b2ec --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.h @@ -0,0 +1,93 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// dump_symbols.h: Read debugging information from an ELF file, and write +// it out as a Breakpad symbol file. + +#ifndef COMMON_LINUX_DUMP_SYMBOLS_H__ +#define COMMON_LINUX_DUMP_SYMBOLS_H__ + +#include <iostream> +#include <string> +#include <vector> + +#include "common/symbol_data.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +class Module; + +struct DumpOptions { + DumpOptions(SymbolData symbol_data, bool handle_inter_cu_refs) + : symbol_data(symbol_data), + handle_inter_cu_refs(handle_inter_cu_refs) { + } + + SymbolData symbol_data; + bool handle_inter_cu_refs; +}; + +// Find all the debugging information in OBJ_FILE, an ELF executable +// or shared library, and write it to SYM_STREAM in the Breakpad symbol +// file format. +// If OBJ_FILE has been stripped but contains a .gnu_debuglink section, +// then look for the debug file in DEBUG_DIRS. +// SYMBOL_DATA allows limiting the type of symbol data written. +bool WriteSymbolFile(const string &load_path, + const string &obj_file, + const string &obj_os, + const std::vector<string>& debug_dirs, + const DumpOptions& options, + std::ostream &sym_stream); + +// Read the selected object file's debugging information, and write out the +// header only to |stream|. Return true on success; if an error occurs, report +// it and return false. |obj_file| becomes the MODULE file name and |obj_os| +// becomes the MODULE operating system. +bool WriteSymbolFileHeader(const string& load_path, + const string& obj_file, + const string& obj_os, + std::ostream &sym_stream); + +// As above, but simply return the debugging information in MODULE +// instead of writing it to a stream. The caller owns the resulting +// Module object and must delete it when finished. +bool ReadSymbolData(const string& load_path, + const string& obj_file, + const string& obj_os, + const std::vector<string>& debug_dirs, + const DumpOptions& options, + Module** module); + +} // namespace google_breakpad + +#endif // COMMON_LINUX_DUMP_SYMBOLS_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols_unittest.cc new file mode 100644 index 0000000000..54c2109625 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols_unittest.cc @@ -0,0 +1,208 @@ +// Copyright (c) 2011 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Ted Mielczarek <ted.mielczarek@gmail.com> + +// dump_symbols_unittest.cc: +// Unittests for google_breakpad::DumpSymbols + +#include <elf.h> +#include <link.h> +#include <stdio.h> + +#include <sstream> +#include <vector> + +#include "breakpad_googletest_includes.h" +#include "common/linux/elf_gnu_compat.h" +#include "common/linux/elfutils.h" +#include "common/linux/dump_symbols.h" +#include "common/linux/synth_elf.h" +#include "common/module.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +bool ReadSymbolDataInternal(const uint8_t* obj_file, + const string& obj_filename, + const string& obj_os, + const std::vector<string>& debug_dir, + const DumpOptions& options, + Module** module); + +using google_breakpad::synth_elf::ELF; +using google_breakpad::synth_elf::Notes; +using google_breakpad::synth_elf::StringTable; +using google_breakpad::synth_elf::SymbolTable; +using google_breakpad::test_assembler::kLittleEndian; +using google_breakpad::test_assembler::Section; +using std::stringstream; +using std::vector; +using ::testing::Test; +using ::testing::Types; + +template<typename ElfClass> +class DumpSymbols : public Test { + public: + void GetElfContents(ELF& elf) { + string contents; + ASSERT_TRUE(elf.GetContents(&contents)); + ASSERT_LT(0U, contents.size()); + + elfdata_v.clear(); + elfdata_v.insert(elfdata_v.begin(), contents.begin(), contents.end()); + elfdata = &elfdata_v[0]; + } + + vector<uint8_t> elfdata_v; + uint8_t* elfdata; +}; + +typedef Types<ElfClass32, ElfClass64> ElfClasses; + +TYPED_TEST_SUITE(DumpSymbols, ElfClasses); + +TYPED_TEST(DumpSymbols, Invalid) { + Elf32_Ehdr header; + memset(&header, 0, sizeof(header)); + Module* module; + DumpOptions options(ALL_SYMBOL_DATA, true); + EXPECT_FALSE(ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(&header), + "foo", + "Linux", + vector<string>(), + options, + &module)); +} + +TYPED_TEST(DumpSymbols, SimplePublic) { + ELF elf(TypeParam::kMachine, TypeParam::kClass, kLittleEndian); + // Zero out text section for simplicity. + Section text(kLittleEndian); + text.Append(4096, 0); + elf.AddSection(".text", text, SHT_PROGBITS); + + // Add a public symbol. + StringTable table(kLittleEndian); + SymbolTable syms(kLittleEndian, TypeParam::kAddrSize, table); + syms.AddSymbol("superfunc", + (typename TypeParam::Addr)0x1000, + (typename TypeParam::Addr)0x10, + // ELF32_ST_INFO works for 32-or 64-bit. + ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), + SHN_UNDEF + 1); + int index = elf.AddSection(".dynstr", table, SHT_STRTAB); + elf.AddSection(".dynsym", syms, + SHT_DYNSYM, // type + SHF_ALLOC, // flags + 0, // addr + index, // link + sizeof(typename TypeParam::Sym)); // entsize + + elf.Finish(); + this->GetElfContents(elf); + + Module* module; + DumpOptions options(ALL_SYMBOL_DATA, true); + EXPECT_TRUE(ReadSymbolDataInternal(this->elfdata, + "foo", + "Linux", + vector<string>(), + options, + &module)); + + stringstream s; + module->Write(s, ALL_SYMBOL_DATA); + const string expected = + string("MODULE Linux ") + TypeParam::kMachineName + + " 000000000000000000000000000000000 foo\n" + "INFO CODE_ID 00000000000000000000000000000000\n" + "PUBLIC 1000 0 superfunc\n"; + EXPECT_EQ(expected, s.str()); + delete module; +} + +TYPED_TEST(DumpSymbols, SimpleBuildID) { + ELF elf(TypeParam::kMachine, TypeParam::kClass, kLittleEndian); + // Zero out text section for simplicity. + Section text(kLittleEndian); + text.Append(4096, 0); + elf.AddSection(".text", text, SHT_PROGBITS); + + // Add a Build ID + const uint8_t kExpectedIdentifierBytes[] = + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13}; + Notes notes(kLittleEndian); + notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes, + sizeof(kExpectedIdentifierBytes)); + elf.AddSection(".note.gnu.build-id", notes, SHT_NOTE); + + // Add a public symbol. + StringTable table(kLittleEndian); + SymbolTable syms(kLittleEndian, TypeParam::kAddrSize, table); + syms.AddSymbol("superfunc", + (typename TypeParam::Addr)0x1000, + (typename TypeParam::Addr)0x10, + // ELF32_ST_INFO works for 32-or 64-bit. + ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), + SHN_UNDEF + 1); + int index = elf.AddSection(".dynstr", table, SHT_STRTAB); + elf.AddSection(".dynsym", syms, + SHT_DYNSYM, // type + SHF_ALLOC, // flags + 0, // addr + index, // link + sizeof(typename TypeParam::Sym)); // entsize + + elf.Finish(); + this->GetElfContents(elf); + + Module* module; + DumpOptions options(ALL_SYMBOL_DATA, true); + EXPECT_TRUE(ReadSymbolDataInternal(this->elfdata, + "foo", + "Linux", + vector<string>(), + options, + &module)); + + stringstream s; + module->Write(s, ALL_SYMBOL_DATA); + const string expected = + string("MODULE Linux ") + TypeParam::kMachineName + + " 030201000504070608090A0B0C0D0E0F0 foo\n" + "INFO CODE_ID 000102030405060708090A0B0C0D0E0F10111213\n" + "PUBLIC 1000 0 superfunc\n"; + EXPECT_EQ(expected, s.str()); + delete module; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/eintr_wrapper.h b/toolkit/crashreporter/google-breakpad/src/common/linux/eintr_wrapper.h new file mode 100644 index 0000000000..3f1d184817 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/eintr_wrapper.h @@ -0,0 +1,58 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef COMMON_LINUX_EINTR_WRAPPER_H_ +#define COMMON_LINUX_EINTR_WRAPPER_H_ + +#include <errno.h> + +// This provides a wrapper around system calls which may be interrupted by a +// signal and return EINTR. See man 7 signal. +// + +#define HANDLE_EINTR(x) ({ \ + __typeof__(x) eintr_wrapper_result; \ + do { \ + eintr_wrapper_result = (x); \ + } while (eintr_wrapper_result == -1 && errno == EINTR); \ + eintr_wrapper_result; \ +}) + +#define IGNORE_EINTR(x) ({ \ + __typeof__(x) eintr_wrapper_result; \ + do { \ + eintr_wrapper_result = (x); \ + if (eintr_wrapper_result == -1 && errno == EINTR) { \ + eintr_wrapper_result = 0; \ + } \ + } while (0); \ + eintr_wrapper_result; \ +}) + +#endif // COMMON_LINUX_EINTR_WRAPPER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump.cc new file mode 100644 index 0000000000..0e7db7b1fa --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump.cc @@ -0,0 +1,179 @@ +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// elf_core_dump.cc: Implement google_breakpad::ElfCoreDump. +// See elf_core_dump.h for details. + +#include "common/linux/elf_core_dump.h" + +#include <stddef.h> +#include <string.h> + +namespace google_breakpad { + +// Implementation of ElfCoreDump::Note. + +ElfCoreDump::Note::Note() {} + +ElfCoreDump::Note::Note(const MemoryRange& content) : content_(content) {} + +bool ElfCoreDump::Note::IsValid() const { + return GetHeader() != NULL; +} + +const ElfCoreDump::Nhdr* ElfCoreDump::Note::GetHeader() const { + return content_.GetData<Nhdr>(0); +} + +ElfCoreDump::Word ElfCoreDump::Note::GetType() const { + const Nhdr* header = GetHeader(); + // 0 is not being used as a NOTE type. + return header ? header->n_type : 0; +} + +MemoryRange ElfCoreDump::Note::GetName() const { + const Nhdr* header = GetHeader(); + if (header) { + return content_.Subrange(sizeof(Nhdr), header->n_namesz); + } + return MemoryRange(); +} + +MemoryRange ElfCoreDump::Note::GetDescription() const { + const Nhdr* header = GetHeader(); + if (header) { + return content_.Subrange(AlignedSize(sizeof(Nhdr) + header->n_namesz), + header->n_descsz); + } + return MemoryRange(); +} + +ElfCoreDump::Note ElfCoreDump::Note::GetNextNote() const { + MemoryRange next_content; + const Nhdr* header = GetHeader(); + if (header) { + size_t next_offset = AlignedSize(sizeof(Nhdr) + header->n_namesz); + next_offset = AlignedSize(next_offset + header->n_descsz); + next_content = + content_.Subrange(next_offset, content_.length() - next_offset); + } + return Note(next_content); +} + +// static +size_t ElfCoreDump::Note::AlignedSize(size_t size) { + size_t mask = sizeof(Word) - 1; + return (size + mask) & ~mask; +} + + +// Implementation of ElfCoreDump. + +ElfCoreDump::ElfCoreDump() {} + +ElfCoreDump::ElfCoreDump(const MemoryRange& content) + : content_(content) { +} + +void ElfCoreDump::SetContent(const MemoryRange& content) { + content_ = content; +} + +bool ElfCoreDump::IsValid() const { + const Ehdr* header = GetHeader(); + return (header && + header->e_ident[0] == ELFMAG0 && + header->e_ident[1] == ELFMAG1 && + header->e_ident[2] == ELFMAG2 && + header->e_ident[3] == ELFMAG3 && + header->e_ident[4] == kClass && + header->e_version == EV_CURRENT && + header->e_type == ET_CORE); +} + +const ElfCoreDump::Ehdr* ElfCoreDump::GetHeader() const { + return content_.GetData<Ehdr>(0); +} + +const ElfCoreDump::Phdr* ElfCoreDump::GetProgramHeader(unsigned index) const { + const Ehdr* header = GetHeader(); + if (header) { + return reinterpret_cast<const Phdr*>(content_.GetArrayElement( + header->e_phoff, header->e_phentsize, index)); + } + return NULL; +} + +const ElfCoreDump::Phdr* ElfCoreDump::GetFirstProgramHeaderOfType( + Word type) const { + for (unsigned i = 0, n = GetProgramHeaderCount(); i < n; ++i) { + const Phdr* program = GetProgramHeader(i); + if (program->p_type == type) { + return program; + } + } + return NULL; +} + +unsigned ElfCoreDump::GetProgramHeaderCount() const { + const Ehdr* header = GetHeader(); + return header ? header->e_phnum : 0; +} + +bool ElfCoreDump::CopyData(void* buffer, Addr virtual_address, size_t length) { + for (unsigned i = 0, n = GetProgramHeaderCount(); i < n; ++i) { + const Phdr* program = GetProgramHeader(i); + if (program->p_type != PT_LOAD) + continue; + + size_t offset_in_segment = virtual_address - program->p_vaddr; + if (virtual_address >= program->p_vaddr && + offset_in_segment < program->p_filesz) { + const void* data = + content_.GetData(program->p_offset + offset_in_segment, length); + if (data) { + memcpy(buffer, data, length); + return true; + } + } + } + return false; +} + +ElfCoreDump::Note ElfCoreDump::GetFirstNote() const { + MemoryRange note_content; + const Phdr* program_header = GetFirstProgramHeaderOfType(PT_NOTE); + if (program_header) { + note_content = content_.Subrange(program_header->p_offset, + program_header->p_filesz); + } + return Note(note_content); +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump.h b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump.h new file mode 100644 index 0000000000..6e153745db --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump.h @@ -0,0 +1,149 @@ +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// elf_core_dump.h: Define the google_breakpad::ElfCoreDump class, which +// encapsulates an ELF core dump file mapped into memory. + +#ifndef COMMON_LINUX_ELF_CORE_DUMP_H_ +#define COMMON_LINUX_ELF_CORE_DUMP_H_ + +#include <elf.h> +#include <limits.h> +#include <link.h> +#include <stddef.h> + +#include "common/memory_range.h" + +namespace google_breakpad { + +// A class encapsulating an ELF core dump file mapped into memory, which +// provides methods for accessing program headers and the note section. +class ElfCoreDump { + public: + // ELF types based on the native word size. + typedef ElfW(Ehdr) Ehdr; + typedef ElfW(Nhdr) Nhdr; + typedef ElfW(Phdr) Phdr; + typedef ElfW(Word) Word; + typedef ElfW(Addr) Addr; +#if ULONG_MAX == 0xffffffff + static const int kClass = ELFCLASS32; +#elif ULONG_MAX == 0xffffffffffffffff + static const int kClass = ELFCLASS64; +#else +#error "Unsupported word size for ElfCoreDump." +#endif + + // A class encapsulating the note content in a core dump, which provides + // methods for accessing the name and description of a note. + class Note { + public: + Note(); + + // Constructor that takes the note content from |content|. + explicit Note(const MemoryRange& content); + + // Returns true if this note is valid, i,e. a note header is found in + // |content_|, or false otherwise. + bool IsValid() const; + + // Returns the note header, or NULL if no note header is found in + // |content_|. + const Nhdr* GetHeader() const; + + // Returns the note type, or 0 if no note header is found in |content_|. + Word GetType() const; + + // Returns a memory range covering the note name, or an empty range + // if no valid note name is found in |content_|. + MemoryRange GetName() const; + + // Returns a memory range covering the note description, or an empty + // range if no valid note description is found in |content_|. + MemoryRange GetDescription() const; + + // Returns the note following this note, or an empty note if no valid + // note is found after this note. + Note GetNextNote() const; + + private: + // Returns the size in bytes round up to the word alignment, specified + // for the note section, of a given size in bytes. + static size_t AlignedSize(size_t size); + + // Note content. + MemoryRange content_; + }; + + ElfCoreDump(); + + // Constructor that takes the core dump content from |content|. + explicit ElfCoreDump(const MemoryRange& content); + + // Sets the core dump content to |content|. + void SetContent(const MemoryRange& content); + + // Returns true if a valid ELF header in the core dump, or false otherwise. + bool IsValid() const; + + // Returns the ELF header in the core dump, or NULL if no ELF header + // is found in |content_|. + const Ehdr* GetHeader() const; + + // Returns the |index|-th program header in the core dump, or NULL if no + // ELF header is found in |content_| or |index| is out of bounds. + const Phdr* GetProgramHeader(unsigned index) const; + + // Returns the first program header of |type| in the core dump, or NULL if + // no ELF header is found in |content_| or no program header of |type| is + // found. + const Phdr* GetFirstProgramHeaderOfType(Word type) const; + + // Returns the number of program headers in the core dump, or 0 if no + // ELF header is found in |content_|. + unsigned GetProgramHeaderCount() const; + + // Copies |length| bytes of data starting at |virtual_address| in the core + // dump to |buffer|. |buffer| should be a valid pointer to a buffer of at + // least |length| bytes. Returns true if the data to be copied is found in + // the core dump, or false otherwise. + bool CopyData(void* buffer, Addr virtual_address, size_t length); + + // Returns the first note found in the note section of the core dump, or + // an empty note if no note is found. + Note GetFirstNote() const; + + private: + // Core dump content. + MemoryRange content_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_ELF_CORE_DUMP_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump_unittest.cc new file mode 100644 index 0000000000..2399c12fda --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump_unittest.cc @@ -0,0 +1,265 @@ +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// elf_core_dump_unittest.cc: Unit tests for google_breakpad::ElfCoreDump. + +#include <sys/procfs.h> + +#include <set> +#include <string> + +#include "breakpad_googletest_includes.h" +#include "common/linux/elf_core_dump.h" +#include "common/linux/memory_mapped_file.h" +#include "common/tests/file_utils.h" +#include "common/linux/tests/crash_generator.h" +#include "common/using_std_string.h" + +using google_breakpad::AutoTempDir; +using google_breakpad::CrashGenerator; +using google_breakpad::ElfCoreDump; +using google_breakpad::MemoryMappedFile; +using google_breakpad::MemoryRange; +using google_breakpad::WriteFile; +using std::set; + +TEST(ElfCoreDumpTest, DefaultConstructor) { + ElfCoreDump core; + EXPECT_FALSE(core.IsValid()); + EXPECT_EQ(NULL, core.GetHeader()); + EXPECT_EQ(0U, core.GetProgramHeaderCount()); + EXPECT_EQ(NULL, core.GetProgramHeader(0)); + EXPECT_EQ(NULL, core.GetFirstProgramHeaderOfType(PT_LOAD)); + EXPECT_FALSE(core.GetFirstNote().IsValid()); +} + +TEST(ElfCoreDumpTest, TestElfHeader) { + ElfCoreDump::Ehdr header; + memset(&header, 0, sizeof(header)); + + AutoTempDir temp_dir; + string core_path = temp_dir.path() + "/core"; + const char* core_file = core_path.c_str(); + MemoryMappedFile mapped_core_file; + ElfCoreDump core; + + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header) - 1)); + ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + EXPECT_EQ(NULL, core.GetHeader()); + EXPECT_EQ(0U, core.GetProgramHeaderCount()); + EXPECT_EQ(NULL, core.GetProgramHeader(0)); + EXPECT_EQ(NULL, core.GetFirstProgramHeaderOfType(PT_LOAD)); + EXPECT_FALSE(core.GetFirstNote().IsValid()); + + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_ident[0] = ELFMAG0; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_ident[1] = ELFMAG1; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_ident[2] = ELFMAG2; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_ident[3] = ELFMAG3; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_ident[4] = ElfCoreDump::kClass; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_version = EV_CURRENT; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_type = ET_CORE; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); + core.SetContent(mapped_core_file.content()); + EXPECT_TRUE(core.IsValid()); +} + +TEST(ElfCoreDumpTest, ValidCoreFile) { + CrashGenerator crash_generator; + if (!crash_generator.HasDefaultCorePattern()) { + fprintf(stderr, "ElfCoreDumpTest.ValidCoreFile test is skipped " + "due to non-default core pattern"); + return; + } + + const unsigned kNumOfThreads = 3; + const unsigned kCrashThread = 1; + const int kCrashSignal = SIGABRT; + ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread, + kCrashSignal, NULL)); + pid_t expected_crash_thread_id = crash_generator.GetThreadId(kCrashThread); + set<pid_t> expected_thread_ids; + for (unsigned i = 0; i < kNumOfThreads; ++i) { + expected_thread_ids.insert(crash_generator.GetThreadId(i)); + } + +#if defined(__ANDROID__) + struct stat st; + if (stat(crash_generator.GetCoreFilePath().c_str(), &st) != 0) { + fprintf(stderr, "ElfCoreDumpTest.ValidCoreFile test is skipped " + "due to no core file being generated"); + return; + } +#endif + + MemoryMappedFile mapped_core_file; + ASSERT_TRUE( + mapped_core_file.Map(crash_generator.GetCoreFilePath().c_str(), 0)); + + ElfCoreDump core; + core.SetContent(mapped_core_file.content()); + EXPECT_TRUE(core.IsValid()); + + // Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are + // ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific): + // Thread Name Type + // ------------------------------------------------------------------- + // 1st thread CORE NT_PRSTATUS + // process-wide CORE NT_PRPSINFO + // process-wide CORE NT_AUXV + // 1st thread CORE NT_FPREGSET + // 1st thread LINUX NT_PRXFPREG + // 1st thread LINUX NT_386_TLS + // + // 2nd thread CORE NT_PRSTATUS + // 2nd thread CORE NT_FPREGSET + // 2nd thread LINUX NT_PRXFPREG + // 2nd thread LINUX NT_386_TLS + // + // 3rd thread CORE NT_PRSTATUS + // 3rd thread CORE NT_FPREGSET + // 3rd thread LINUX NT_PRXFPREG + // 3rd thread LINUX NT_386_TLS + + size_t num_nt_prpsinfo = 0; + size_t num_nt_prstatus = 0; + size_t num_pr_fpvalid = 0; +#if defined(__i386__) || defined(__x86_64__) + size_t num_nt_fpregset = 0; +#endif +#if defined(__i386__) + size_t num_nt_prxfpreg = 0; +#endif + set<pid_t> actual_thread_ids; + ElfCoreDump::Note note = core.GetFirstNote(); + while (note.IsValid()) { + MemoryRange name = note.GetName(); + MemoryRange description = note.GetDescription(); + EXPECT_FALSE(name.IsEmpty()); + EXPECT_FALSE(description.IsEmpty()); + + switch (note.GetType()) { + case NT_PRPSINFO: { + EXPECT_TRUE(description.data() != NULL); + EXPECT_EQ(sizeof(elf_prpsinfo), description.length()); + ++num_nt_prpsinfo; + break; + } + case NT_PRSTATUS: { + EXPECT_TRUE(description.data() != NULL); + EXPECT_EQ(sizeof(elf_prstatus), description.length()); + const elf_prstatus* status = description.GetData<elf_prstatus>(0); + actual_thread_ids.insert(status->pr_pid); + if (num_nt_prstatus == 0) { + EXPECT_EQ(expected_crash_thread_id, status->pr_pid); + EXPECT_EQ(kCrashSignal, status->pr_info.si_signo); + } + ++num_nt_prstatus; + if (status->pr_fpvalid) + ++num_pr_fpvalid; + break; + } +#if defined(__i386__) || defined(__x86_64__) + case NT_FPREGSET: { + EXPECT_TRUE(description.data() != NULL); + EXPECT_EQ(sizeof(user_fpregs_struct), description.length()); + ++num_nt_fpregset; + break; + } +#endif +#if defined(__i386__) + case NT_PRXFPREG: { + EXPECT_TRUE(description.data() != NULL); + EXPECT_EQ(sizeof(user_fpxregs_struct), description.length()); + ++num_nt_prxfpreg; + break; + } +#endif + default: + break; + } + note = note.GetNextNote(); + } + +#if defined(THREAD_SANITIZER) + for (std::set<pid_t>::const_iterator expected = expected_thread_ids.begin(); + expected != expected_thread_ids.end(); + ++expected) { + EXPECT_NE(actual_thread_ids.find(*expected), actual_thread_ids.end()); + } + EXPECT_GE(num_nt_prstatus, kNumOfThreads); +#else + EXPECT_EQ(actual_thread_ids, expected_thread_ids); + EXPECT_EQ(num_nt_prstatus, kNumOfThreads); +#endif + EXPECT_EQ(1U, num_nt_prpsinfo); +#if defined(__i386__) || defined(__x86_64__) + EXPECT_EQ(num_pr_fpvalid, num_nt_fpregset); +#endif +#if defined(__i386__) + EXPECT_EQ(num_pr_fpvalid, num_nt_prxfpreg); +#endif +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elf_gnu_compat.h b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_gnu_compat.h new file mode 100644 index 0000000000..0a3dfedb57 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_gnu_compat.h @@ -0,0 +1,51 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2013, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Lei Zhang <thestig@google.com> + +// elf_gnu_compat.h: #defines unique to glibc's elf.h. + +#ifndef COMMON_LINUX_ELF_GNU_COMPAT_H_ +#define COMMON_LINUX_ELF_GNU_COMPAT_H_ + +#include <elf.h> + +// A note type on GNU systems corresponding to the .note.gnu.build-id section. +#ifndef NT_GNU_BUILD_ID +#define NT_GNU_BUILD_ID 3 +#endif + +// Newer Linux systems offer this. +#ifndef NT_SIGINFO +#define NT_SIGINFO 0x53494749 +#endif + +#endif // COMMON_LINUX_ELF_GNU_COMPAT_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module.cc new file mode 100644 index 0000000000..562875e11b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module.cc @@ -0,0 +1,178 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2011 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Ted Mielczarek <ted.mielczarek@gmail.com> + +#include "common/linux/elf_symbols_to_module.h" + +#include <cxxabi.h> +#include <elf.h> +#include <string.h> + +#include "common/byte_cursor.h" +#include "common/module.h" + +namespace google_breakpad { + +class ELFSymbolIterator { +public: + // The contents of an ELF symbol, adjusted for the host's endianness, + // word size, and so on. Corresponds to the data in Elf32_Sym / Elf64_Sym. + struct Symbol { + // True if this iterator has reached the end of the symbol array. When + // this is set, the other members of this structure are not valid. + bool at_end; + + // The number of this symbol within the list. + size_t index; + + // The current symbol's name offset. This is the offset within the + // string table. + size_t name_offset; + + // The current symbol's value, size, info and shndx fields. + uint64_t value; + uint64_t size; + unsigned char info; + uint16_t shndx; + }; + + // Create an ELFSymbolIterator walking the symbols in BUFFER. Treat the + // symbols as big-endian if BIG_ENDIAN is true, as little-endian + // otherwise. Assume each symbol has a 'value' field whose size is + // VALUE_SIZE. + // + ELFSymbolIterator(const ByteBuffer *buffer, bool big_endian, + size_t value_size) + : value_size_(value_size), cursor_(buffer, big_endian) { + // Actually, weird sizes could be handled just fine, but they're + // probably mistakes --- expressed in bits, say. + assert(value_size == 4 || value_size == 8); + symbol_.index = 0; + Fetch(); + } + + // Move to the next symbol. This function's behavior is undefined if + // at_end() is true when it is called. + ELFSymbolIterator &operator++() { Fetch(); symbol_.index++; return *this; } + + // Dereferencing this iterator produces a reference to an Symbol structure + // that holds the current symbol's values. The symbol is owned by this + // SymbolIterator, and will be invalidated at the next call to operator++. + const Symbol &operator*() const { return symbol_; } + const Symbol *operator->() const { return &symbol_; } + +private: + // Read the symbol at cursor_, and set symbol_ appropriately. + void Fetch() { + // Elf32_Sym and Elf64_Sym have different layouts. + unsigned char other; + if (value_size_ == 4) { + // Elf32_Sym + cursor_ + .Read(4, false, &symbol_.name_offset) + .Read(4, false, &symbol_.value) + .Read(4, false, &symbol_.size) + .Read(1, false, &symbol_.info) + .Read(1, false, &other) + .Read(2, false, &symbol_.shndx); + } else { + // Elf64_Sym + cursor_ + .Read(4, false, &symbol_.name_offset) + .Read(1, false, &symbol_.info) + .Read(1, false, &other) + .Read(2, false, &symbol_.shndx) + .Read(8, false, &symbol_.value) + .Read(8, false, &symbol_.size); + } + symbol_.at_end = !cursor_; + } + + // The size of symbols' value field, in bytes. + size_t value_size_; + + // A byte cursor traversing buffer_. + ByteCursor cursor_; + + // Values for the symbol this iterator refers to. + Symbol symbol_; +}; + +const char *SymbolString(ptrdiff_t offset, ByteBuffer& strings) { + if (offset < 0 || (size_t) offset >= strings.Size()) { + // Return the null string. + offset = 0; + } + return reinterpret_cast<const char *>(strings.start + offset); +} + +bool ELFSymbolsToModule(const uint8_t *symtab_section, + size_t symtab_size, + const uint8_t *string_section, + size_t string_size, + const bool big_endian, + size_t value_size, + Module *module) { + ByteBuffer symbols(symtab_section, symtab_size); + // Ensure that the string section is null-terminated. + if (string_section[string_size - 1] != '\0') { + const void* null_terminator = memrchr(string_section, '\0', string_size); + string_size = reinterpret_cast<const uint8_t*>(null_terminator) + - string_section; + } + ByteBuffer strings(string_section, string_size); + + // The iterator walking the symbol table. + ELFSymbolIterator iterator(&symbols, big_endian, value_size); + + while(!iterator->at_end) { + if (ELF32_ST_TYPE(iterator->info) == STT_FUNC && + iterator->shndx != SHN_UNDEF) { + Module::Extern *ext = new Module::Extern(iterator->value); + ext->name = SymbolString(iterator->name_offset, strings); +#if !defined(__ANDROID__) // Android NDK doesn't provide abi::__cxa_demangle. + int status = 0; + char* demangled = + abi::__cxa_demangle(ext->name.c_str(), NULL, NULL, &status); + if (demangled) { + if (status == 0) + ext->name = demangled; + free(demangled); + } +#endif + module->AddExtern(ext); + } + ++iterator; + } + return true; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module.h b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module.h new file mode 100644 index 0000000000..2e7c097151 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module.h @@ -0,0 +1,58 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2011 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Ted Mielczarek <ted.mielczarek@gmail.com> + +// elf_symbols_to_module.h: Exposes ELFSymbolsToModule, a function +// for reading ELF symbol tables and inserting exported symbol names +// into a google_breakpad::Module as Extern definitions. + +#ifndef BREAKPAD_COMMON_LINUX_ELF_SYMBOLS_TO_MODULE_H_ +#define BREAKPAD_COMMON_LINUX_ELF_SYMBOLS_TO_MODULE_H_ + +#include <stddef.h> +#include <stdint.h> + +namespace google_breakpad { + +class Module; + +bool ELFSymbolsToModule(const uint8_t *symtab_section, + size_t symtab_size, + const uint8_t *string_section, + size_t string_size, + const bool big_endian, + size_t value_size, + Module *module); + +} // namespace google_breakpad + + +#endif // BREAKPAD_COMMON_LINUX_ELF_SYMBOLS_TO_MODULE_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module_unittest.cc new file mode 100644 index 0000000000..8984449ab6 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module_unittest.cc @@ -0,0 +1,370 @@ +// Copyright (c) 2011 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Ted Mielczarek <ted.mielczarek@gmail.com> + +// elf_symbols_to_module_unittest.cc: +// Unittests for google_breakpad::ELFSymbolsToModule + +#include <elf.h> + +#include <string> +#include <vector> + +#include "breakpad_googletest_includes.h" +#include "common/linux/elf_symbols_to_module.h" +#include "common/linux/synth_elf.h" +#include "common/module.h" +#include "common/test_assembler.h" +#include "common/using_std_string.h" + +using google_breakpad::Module; +using google_breakpad::synth_elf::StringTable; +using google_breakpad::test_assembler::Endianness; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::Section; +using ::testing::Test; +using ::testing::TestWithParam; +using std::vector; + +class ELFSymbolsToModuleTestFixture { +public: + ELFSymbolsToModuleTestFixture(Endianness endianness, + size_t value_size) : module("a", "b", "c", "d"), + section(endianness), + table(endianness), + value_size(value_size) {} + + bool ProcessSection() { + string section_contents, table_contents; + section.GetContents(§ion_contents); + table.GetContents(&table_contents); + + bool ret = ELFSymbolsToModule(reinterpret_cast<const uint8_t*>(section_contents.data()), + section_contents.size(), + reinterpret_cast<const uint8_t*>(table_contents.data()), + table_contents.size(), + section.endianness() == kBigEndian, + value_size, + &module); + module.GetExterns(&externs, externs.end()); + return ret; + } + + Module module; + Section section; + StringTable table; + string section_contents; + // 4 or 8 (bytes) + size_t value_size; + + vector<Module::Extern *> externs; +}; + +class ELFSymbolsToModuleTest32 : public ELFSymbolsToModuleTestFixture, + public TestWithParam<Endianness> { +public: + ELFSymbolsToModuleTest32() : ELFSymbolsToModuleTestFixture(GetParam(), 4) {} + + void AddElf32Sym(const string& name, uint32_t value, + uint32_t size, unsigned info, uint16_t shndx) { + section + .D32(table.Add(name)) + .D32(value) + .D32(size) + .D8(info) + .D8(0) // other + .D16(shndx); + } +}; + +TEST_P(ELFSymbolsToModuleTest32, NoFuncs) { + ProcessSection(); + + ASSERT_EQ((size_t)0, externs.size()); +} + +TEST_P(ELFSymbolsToModuleTest32, OneFunc) { + const string kFuncName = "superfunc"; + const uint32_t kFuncAddr = 0x1000; + const uint32_t kFuncSize = 0x10; + + AddElf32Sym(kFuncName, kFuncAddr, kFuncSize, + ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 1); + + ProcessSection(); + + ASSERT_EQ((size_t)1, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_EQ(kFuncName, extern1->name); + EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); +} + +TEST_P(ELFSymbolsToModuleTest32, NameOutOfBounds) { + const string kFuncName = ""; + const uint32_t kFuncAddr = 0x1000; + const uint32_t kFuncSize = 0x10; + + table.Add("Foo"); + table.Add("Bar"); + // Can't use AddElf32Sym because it puts in a valid string offset. + section + .D32((uint32_t)table.Here().Value() + 1) + .D32(kFuncAddr) + .D32(kFuncSize) + .D8(ELF32_ST_INFO(STB_GLOBAL, STT_FUNC)) + .D8(0) // other + .D16(SHN_UNDEF + 1); + + ProcessSection(); + + ASSERT_EQ((size_t)1, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_EQ(kFuncName, extern1->name); + EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); +} + +TEST_P(ELFSymbolsToModuleTest32, NonTerminatedStringTable) { + const string kFuncName = ""; + const uint32_t kFuncAddr = 0x1000; + const uint32_t kFuncSize = 0x10; + + table.Add("Foo"); + table.Add("Bar"); + // Add a non-null-terminated string to the end of the string table + Label l; + table + .Mark(&l) + .Append("Unterminated"); + // Can't use AddElf32Sym because it puts in a valid string offset. + section + .D32((uint32_t)l.Value()) + .D32(kFuncAddr) + .D32(kFuncSize) + .D8(ELF32_ST_INFO(STB_GLOBAL, STT_FUNC)) + .D8(0) // other + .D16(SHN_UNDEF + 1); + + ProcessSection(); + + ASSERT_EQ((size_t)1, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_EQ(kFuncName, extern1->name); + EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); +} + +TEST_P(ELFSymbolsToModuleTest32, MultipleFuncs) { + const string kFuncName1 = "superfunc"; + const uint32_t kFuncAddr1 = 0x10001000; + const uint32_t kFuncSize1 = 0x10; + const string kFuncName2 = "awesomefunc"; + const uint32_t kFuncAddr2 = 0x20002000; + const uint32_t kFuncSize2 = 0x2f; + const string kFuncName3 = "megafunc"; + const uint32_t kFuncAddr3 = 0x30003000; + const uint32_t kFuncSize3 = 0x3c; + + AddElf32Sym(kFuncName1, kFuncAddr1, kFuncSize1, + ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 1); + AddElf32Sym(kFuncName2, kFuncAddr2, kFuncSize2, + ELF32_ST_INFO(STB_LOCAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 2); + AddElf32Sym(kFuncName3, kFuncAddr3, kFuncSize3, + ELF32_ST_INFO(STB_LOCAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 3); + + ProcessSection(); + + ASSERT_EQ((size_t)3, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_EQ(kFuncName1, extern1->name); + EXPECT_EQ((Module::Address)kFuncAddr1, extern1->address); + Module::Extern *extern2 = externs[1]; + EXPECT_EQ(kFuncName2, extern2->name); + EXPECT_EQ((Module::Address)kFuncAddr2, extern2->address); + Module::Extern *extern3 = externs[2]; + EXPECT_EQ(kFuncName3, extern3->name); + EXPECT_EQ((Module::Address)kFuncAddr3, extern3->address); +} + +TEST_P(ELFSymbolsToModuleTest32, SkipStuff) { + const string kFuncName = "superfunc"; + const uint32_t kFuncAddr = 0x1000; + const uint32_t kFuncSize = 0x10; + + // Should skip functions in SHN_UNDEF + AddElf32Sym("skipme", 0xFFFF, 0x10, + ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), + SHN_UNDEF); + AddElf32Sym(kFuncName, kFuncAddr, kFuncSize, + ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 1); + // Should skip non-STT_FUNC entries. + AddElf32Sym("skipmetoo", 0xAAAA, 0x10, + ELF32_ST_INFO(STB_GLOBAL, STT_FILE), + SHN_UNDEF + 1); + + ProcessSection(); + + ASSERT_EQ((size_t)1, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_EQ(kFuncName, extern1->name); + EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); +} + +// Run all the 32-bit tests with both endianness +INSTANTIATE_TEST_CASE_P(Endian, + ELFSymbolsToModuleTest32, + ::testing::Values(kLittleEndian, kBigEndian)); + +// Similar tests, but with 64-bit values. Ostensibly this could be +// shoehorned into the parameterization by using ::testing::Combine, +// but that would make it difficult to get the types right since these +// actual test cases aren't parameterized. This could also be written +// as a type-parameterized test, but combining that with a value-parameterized +// test seemed really ugly, and also makes it harder to test 64-bit +// values. +class ELFSymbolsToModuleTest64 : public ELFSymbolsToModuleTestFixture, + public TestWithParam<Endianness> { +public: + ELFSymbolsToModuleTest64() : ELFSymbolsToModuleTestFixture(GetParam(), 8) {} + + void AddElf64Sym(const string& name, uint64_t value, + uint64_t size, unsigned info, uint16_t shndx) { + section + .D32(table.Add(name)) + .D8(info) + .D8(0) // other + .D16(shndx) + .D64(value) + .D64(size); + } +}; + +TEST_P(ELFSymbolsToModuleTest64, NoFuncs) { + ProcessSection(); + + ASSERT_EQ((size_t)0, externs.size()); +} + +TEST_P(ELFSymbolsToModuleTest64, OneFunc) { + const string kFuncName = "superfunc"; + const uint64_t kFuncAddr = 0x1000200030004000ULL; + const uint64_t kFuncSize = 0x1000; + + AddElf64Sym(kFuncName, kFuncAddr, kFuncSize, + ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 1); + + ProcessSection(); + + ASSERT_EQ((size_t)1, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_EQ(kFuncName, extern1->name); + EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); +} + +TEST_P(ELFSymbolsToModuleTest64, MultipleFuncs) { + const string kFuncName1 = "superfunc"; + const uint64_t kFuncAddr1 = 0x1000100010001000ULL; + const uint64_t kFuncSize1 = 0x1000; + const string kFuncName2 = "awesomefunc"; + const uint64_t kFuncAddr2 = 0x2000200020002000ULL; + const uint64_t kFuncSize2 = 0x2f00; + const string kFuncName3 = "megafunc"; + const uint64_t kFuncAddr3 = 0x3000300030003000ULL; + const uint64_t kFuncSize3 = 0x3c00; + + AddElf64Sym(kFuncName1, kFuncAddr1, kFuncSize1, + ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 1); + AddElf64Sym(kFuncName2, kFuncAddr2, kFuncSize2, + ELF64_ST_INFO(STB_LOCAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 2); + AddElf64Sym(kFuncName3, kFuncAddr3, kFuncSize3, + ELF64_ST_INFO(STB_LOCAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 3); + + ProcessSection(); + + ASSERT_EQ((size_t)3, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_EQ(kFuncName1, extern1->name); + EXPECT_EQ((Module::Address)kFuncAddr1, extern1->address); + Module::Extern *extern2 = externs[1]; + EXPECT_EQ(kFuncName2, extern2->name); + EXPECT_EQ((Module::Address)kFuncAddr2, extern2->address); + Module::Extern *extern3 = externs[2]; + EXPECT_EQ(kFuncName3, extern3->name); + EXPECT_EQ((Module::Address)kFuncAddr3, extern3->address); +} + +TEST_P(ELFSymbolsToModuleTest64, SkipStuff) { + const string kFuncName = "superfunc"; + const uint64_t kFuncAddr = 0x1000100010001000ULL; + const uint64_t kFuncSize = 0x1000; + + // Should skip functions in SHN_UNDEF + AddElf64Sym("skipme", 0xFFFF, 0x10, + ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), + SHN_UNDEF); + AddElf64Sym(kFuncName, kFuncAddr, kFuncSize, + ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 1); + // Should skip non-STT_FUNC entries. + AddElf64Sym("skipmetoo", 0xAAAA, 0x10, + ELF64_ST_INFO(STB_GLOBAL, STT_FILE), + SHN_UNDEF + 1); + + ProcessSection(); + + ASSERT_EQ((size_t)1, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_EQ(kFuncName, extern1->name); + EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); +} + +// Run all the 64-bit tests with both endianness +INSTANTIATE_TEST_CASE_P(Endian, + ELFSymbolsToModuleTest64, + ::testing::Values(kLittleEndian, kBigEndian)); diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils-inl.h b/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils-inl.h new file mode 100644 index 0000000000..e56b37a9f5 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils-inl.h @@ -0,0 +1,74 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef COMMON_LINUX_ELFUTILS_INL_H__ +#define COMMON_LINUX_ELFUTILS_INL_H__ + +#include "common/linux/linux_libc_support.h" +#include "elfutils.h" + +namespace google_breakpad { + +template<typename ElfClass, typename T> +const T* GetOffset(const typename ElfClass::Ehdr* elf_header, + typename ElfClass::Off offset) { + return reinterpret_cast<const T*>(reinterpret_cast<uintptr_t>(elf_header) + + offset); +} + +template<typename ElfClass> +const typename ElfClass::Shdr* FindElfSectionByName( + const char* name, + typename ElfClass::Word section_type, + const typename ElfClass::Shdr* sections, + const char* section_names, + const char* names_end, + int nsection) { + assert(name != NULL); + assert(sections != NULL); + assert(nsection > 0); + + int name_len = my_strlen(name); + if (name_len == 0) + return NULL; + + for (int i = 0; i < nsection; ++i) { + const char* section_name = section_names + sections[i].sh_name; + if (sections[i].sh_type == section_type && + names_end - section_name >= name_len + 1 && + my_strcmp(name, section_name) == 0) { + return sections + i; + } + } + return NULL; +} + +} // namespace google_breakpad + +#endif // COMMON_LINUX_ELFUTILS_INL_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils.cc new file mode 100644 index 0000000000..e2e16e6dfd --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils.cc @@ -0,0 +1,241 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include "common/linux/elfutils.h" + +#include <assert.h> +#include <string.h> + +#include "common/linux/linux_libc_support.h" +#include "common/linux/elfutils-inl.h" + +#if defined(__FreeBSD__) +# define ElfW(type) Elf_##type +#endif + +namespace google_breakpad { + +namespace { + +template<typename ElfClass> +void FindElfClassSection(const char *elf_base, + const char *section_name, + typename ElfClass::Word section_type, + const void **section_start, + size_t *section_size) { + typedef typename ElfClass::Ehdr Ehdr; + typedef typename ElfClass::Shdr Shdr; + + assert(elf_base); + assert(section_start); + assert(section_size); + + assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0); + + const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base); + assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass); + + const Shdr* sections = + GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff); + const Shdr* section_names = sections + elf_header->e_shstrndx; + const char* names = + GetOffset<ElfClass, char>(elf_header, section_names->sh_offset); + const char *names_end = names + section_names->sh_size; + + const Shdr* section = + FindElfSectionByName<ElfClass>(section_name, section_type, + sections, names, names_end, + elf_header->e_shnum); + + if (section != NULL && section->sh_size > 0) { + *section_start = elf_base + section->sh_offset; + *section_size = section->sh_size; + } +} + +template<typename ElfClass> +void FindElfClassSegment(const char *elf_base, + typename ElfClass::Word segment_type, + wasteful_vector<ElfSegment> *segments) { + typedef typename ElfClass::Ehdr Ehdr; + typedef typename ElfClass::Phdr Phdr; + + assert(elf_base); + assert(segments); + + assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0); + + const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base); + assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass); + + const Phdr* phdrs = + GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff); + + for (int i = 0; i < elf_header->e_phnum; ++i) { + if (phdrs[i].p_type == segment_type) { + ElfSegment seg = {}; + seg.start = elf_base + phdrs[i].p_offset; + seg.size = phdrs[i].p_filesz; + segments->push_back(seg); + } + } +} + +} // namespace + +bool IsValidElf(const void* elf_base) { + return my_strncmp(reinterpret_cast<const char*>(elf_base), + ELFMAG, SELFMAG) == 0; +} + +int ElfClass(const void* elf_base) { + const ElfW(Ehdr)* elf_header = + reinterpret_cast<const ElfW(Ehdr)*>(elf_base); + + return elf_header->e_ident[EI_CLASS]; +} + +bool FindElfSection(const void *elf_mapped_base, + const char *section_name, + uint32_t section_type, + const void **section_start, + size_t *section_size) { + assert(elf_mapped_base); + assert(section_start); + assert(section_size); + + *section_start = NULL; + *section_size = 0; + + if (!IsValidElf(elf_mapped_base)) + return false; + + int cls = ElfClass(elf_mapped_base); + const char* elf_base = + static_cast<const char*>(elf_mapped_base); + + if (cls == ELFCLASS32) { + FindElfClassSection<ElfClass32>(elf_base, section_name, section_type, + section_start, section_size); + return *section_start != NULL; + } else if (cls == ELFCLASS64) { + FindElfClassSection<ElfClass64>(elf_base, section_name, section_type, + section_start, section_size); + return *section_start != NULL; + } + + return false; +} + +bool FindElfSegments(const void* elf_mapped_base, + uint32_t segment_type, + wasteful_vector<ElfSegment>* segments) { + assert(elf_mapped_base); + assert(segments); + + if (!IsValidElf(elf_mapped_base)) + return false; + + int cls = ElfClass(elf_mapped_base); + const char* elf_base = + static_cast<const char*>(elf_mapped_base); + + if (cls == ELFCLASS32) { + FindElfClassSegment<ElfClass32>(elf_base, segment_type, segments); + return true; + } else if (cls == ELFCLASS64) { + FindElfClassSegment<ElfClass64>(elf_base, segment_type, segments); + return true; + } + + return false; +} + +template <typename ElfClass> +bool FindElfSoNameFromDynamicSection(const void* section_start, + size_t section_size, + const void* dynstr_start, + size_t dynstr_size, + char* soname, + size_t soname_size) { + typedef typename ElfClass::Dyn Dyn; + + auto* dynamic = static_cast<const Dyn*>(section_start); + size_t dcount = section_size / sizeof(Dyn); + for (const Dyn* dyn = dynamic; dyn < dynamic + dcount; ++dyn) { + if (dyn->d_tag == DT_SONAME) { + const char* dynstr = static_cast<const char*>(dynstr_start); + if (dyn->d_un.d_val >= dynstr_size) { + // Beyond the end of the dynstr section + return false; + } + const char* str = dynstr + dyn->d_un.d_val; + const size_t maxsize = dynstr_size - dyn->d_un.d_val; + my_strlcpy(soname, str, maxsize < soname_size ? maxsize : soname_size); + return true; + } + } + + return false; +} + +bool ElfFileSoNameFromMappedFile(const void* elf_base, + char* soname, + size_t soname_size) { + if (!IsValidElf(elf_base)) { + // Not ELF + return false; + } + + const void* segment_start; + size_t segment_size; + if (!FindElfSection(elf_base, ".dynamic", SHT_DYNAMIC, &segment_start, + &segment_size)) { + // No dynamic section + return false; + } + + const void* dynstr_start; + size_t dynstr_size; + if (!FindElfSection(elf_base, ".dynstr", SHT_STRTAB, &dynstr_start, + &dynstr_size)) { + // No dynstr section + return false; + } + + int cls = ElfClass(elf_base); + return cls == ELFCLASS32 ? FindElfSoNameFromDynamicSection<ElfClass32>( + segment_start, segment_size, dynstr_start, + dynstr_size, soname, soname_size) + : FindElfSoNameFromDynamicSection<ElfClass64>( + segment_start, segment_size, dynstr_start, + dynstr_size, soname, soname_size); +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils.h b/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils.h new file mode 100644 index 0000000000..aefb6cf5f8 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils.h @@ -0,0 +1,135 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. +// +// elfutils.h: Utilities for dealing with ELF files. +// + +#ifndef COMMON_LINUX_ELFUTILS_H_ +#define COMMON_LINUX_ELFUTILS_H_ + +#include <elf.h> +#include <link.h> +#include <stdint.h> + +#include "common/memory_allocator.h" + +namespace google_breakpad { + +// Traits classes so consumers can write templatized code to deal +// with specific ELF bits. +struct ElfClass32 { + typedef Elf32_Addr Addr; + typedef Elf32_Dyn Dyn; + typedef Elf32_Ehdr Ehdr; + typedef Elf32_Nhdr Nhdr; + typedef Elf32_Phdr Phdr; + typedef Elf32_Shdr Shdr; + typedef Elf32_Half Half; + typedef Elf32_Off Off; + typedef Elf32_Sym Sym; + typedef Elf32_Word Word; + + static const int kClass = ELFCLASS32; + static const uint16_t kMachine = EM_386; + static const size_t kAddrSize = sizeof(Elf32_Addr); + static constexpr const char* kMachineName = "x86"; +}; + +struct ElfClass64 { + typedef Elf64_Addr Addr; + typedef Elf64_Dyn Dyn; + typedef Elf64_Ehdr Ehdr; + typedef Elf64_Nhdr Nhdr; + typedef Elf64_Phdr Phdr; + typedef Elf64_Shdr Shdr; + typedef Elf64_Half Half; + typedef Elf64_Off Off; + typedef Elf64_Sym Sym; + typedef Elf64_Word Word; + + static const int kClass = ELFCLASS64; + static const uint16_t kMachine = EM_X86_64; + static const size_t kAddrSize = sizeof(Elf64_Addr); + static constexpr const char* kMachineName = "x86_64"; +}; + +bool IsValidElf(const void* elf_header); +int ElfClass(const void* elf_base); + +// Attempt to find a section named |section_name| of type |section_type| +// in the ELF binary data at |elf_mapped_base|. On success, returns true +// and sets |*section_start| to point to the start of the section data, +// and |*section_size| to the size of the section's data. +bool FindElfSection(const void *elf_mapped_base, + const char *section_name, + uint32_t section_type, + const void **section_start, + size_t *section_size); + +// Internal helper method, exposed for convenience for callers +// that already have more info. +template<typename ElfClass> +const typename ElfClass::Shdr* +FindElfSectionByName(const char* name, + typename ElfClass::Word section_type, + const typename ElfClass::Shdr* sections, + const char* section_names, + const char* names_end, + int nsection); + +struct ElfSegment { + const void* start; + size_t size; +}; + +// Attempt to find all segments of type |segment_type| in the ELF +// binary data at |elf_mapped_base|. On success, returns true and fills +// |*segments| with a list of segments of the given type. +bool FindElfSegments(const void* elf_mapped_base, + uint32_t segment_type, + wasteful_vector<ElfSegment>* segments); + +// Convert an offset from an Elf header into a pointer to the mapped +// address in the current process. Takes an extra template parameter +// to specify the return type to avoid having to dynamic_cast the +// result. +template<typename ElfClass, typename T> +const T* +GetOffset(const typename ElfClass::Ehdr* elf_header, + typename ElfClass::Off offset); + +// Read the value of DT_SONAME from the elf file mapped at |elf_base|. Returns +// true and fills |soname| with the result if found. +bool ElfFileSoNameFromMappedFile(const void* elf_base, + char* soname, + size_t soname_size); + +} // namespace google_breakpad + +#endif // COMMON_LINUX_ELFUTILS_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc new file mode 100644 index 0000000000..701a4f070f --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc @@ -0,0 +1,200 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. +// +// file_id.cc: Return a unique identifier for a file +// +// See file_id.h for documentation +// + +#include "common/linux/file_id.h" + +#include <arpa/inet.h> +#include <assert.h> +#include <string.h> + +#include <algorithm> +#include <string> + +#include "common/linux/elf_gnu_compat.h" +#include "common/linux/elfutils.h" +#include "common/linux/linux_libc_support.h" +#include "common/linux/memory_mapped_file.h" +#include "common/using_std_string.h" +#include "third_party/lss/linux_syscall_support.h" + +namespace google_breakpad { + +// Used in a few places for backwards-compatibility. +const size_t kMDGUIDSize = sizeof(MDGUID); + +FileID::FileID(const char* path) : path_(path) {} + +// ELF note name and desc are 32-bits word padded. +#define NOTE_PADDING(a) ((a + 3) & ~3) + +// These 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 ElfClassBuildIDNoteIdentifier(const void *section, size_t length, + wasteful_vector<uint8_t>& identifier) { + static_assert(sizeof(ElfClass32::Nhdr) == sizeof(ElfClass64::Nhdr), + "Elf32_Nhdr and Elf64_Nhdr should be the same"); + typedef typename ElfClass32::Nhdr Nhdr; + + const void* section_end = reinterpret_cast<const char*>(section) + length; + const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section); + while (reinterpret_cast<const void *>(note_header) < section_end) { + if (note_header->n_type == NT_GNU_BUILD_ID) + break; + note_header = reinterpret_cast<const Nhdr*>( + reinterpret_cast<const char*>(note_header) + sizeof(Nhdr) + + NOTE_PADDING(note_header->n_namesz) + + NOTE_PADDING(note_header->n_descsz)); + } + if (reinterpret_cast<const void *>(note_header) >= section_end || + note_header->n_descsz == 0) { + return false; + } + + const uint8_t* build_id = reinterpret_cast<const uint8_t*>(note_header) + + sizeof(Nhdr) + NOTE_PADDING(note_header->n_namesz); + identifier.insert(identifier.end(), + build_id, + build_id + note_header->n_descsz); + + return true; +} + +// Attempt to locate a .note.gnu.build-id section in an ELF binary +// and copy it into |identifier|. +static bool FindElfBuildIDNote(const void* elf_mapped_base, + wasteful_vector<uint8_t>& identifier) { + PageAllocator allocator; + // lld normally creates 2 PT_NOTEs, gold normally creates 1. + auto_wasteful_vector<ElfSegment, 2> segs(&allocator); + if (FindElfSegments(elf_mapped_base, PT_NOTE, &segs)) { + for (ElfSegment& seg : segs) { + if (ElfClassBuildIDNoteIdentifier(seg.start, seg.size, identifier)) { + return true; + } + } + } + + void* note_section; + size_t note_size; + if (FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE, + (const void**)¬e_section, ¬e_size)) { + return ElfClassBuildIDNoteIdentifier(note_section, note_size, identifier); + } + + return false; +} + +// Attempt to locate the .text section of an ELF binary and generate +// a simple hash by XORing the first page worth of bytes into |identifier|. +static bool HashElfTextSection(const void* elf_mapped_base, + wasteful_vector<uint8_t>& identifier) { + void* text_section; + size_t text_size; + if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS, + (const void**)&text_section, &text_size) || + text_size == 0) { + return false; + } + + // Only provide |kMDGUIDSize| bytes to keep identifiers produced by this + // function backwards-compatible. + identifier.resize(kMDGUIDSize); + my_memset(&identifier[0], 0, kMDGUIDSize); + const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section); + const uint8_t* ptr_end = ptr + std::min(text_size, static_cast<size_t>(4096)); + while (ptr < ptr_end) { + for (unsigned i = 0; i < kMDGUIDSize; i++) + identifier[i] ^= ptr[i]; + ptr += kMDGUIDSize; + } + return true; +} + +// static +bool FileID::ElfFileIdentifierFromMappedFile(const void* base, + wasteful_vector<uint8_t>& identifier) { + // Look for a build id note first. + if (FindElfBuildIDNote(base, identifier)) + return true; + + // Fall back on hashing the first page of the text section. + return HashElfTextSection(base, identifier); +} + +bool FileID::ElfFileIdentifier(wasteful_vector<uint8_t>& identifier) { + MemoryMappedFile mapped_file(path_.c_str(), 0); + if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)? + return false; + + return ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); +} + +// These three functions are not ever called in an unsafe context, so it's OK +// to allocate memory and use libc. +static string bytes_to_hex_string(const uint8_t* bytes, size_t count) { + string result; + for (unsigned int idx = 0; idx < count; ++idx) { + char buf[3]; + snprintf(buf, sizeof(buf), "%02X", bytes[idx]); + result.append(buf); + } + return result; +} + +// static +string FileID::ConvertIdentifierToUUIDString( + const wasteful_vector<uint8_t>& identifier) { + uint8_t identifier_swapped[kMDGUIDSize] = { 0 }; + + // Endian-ness swap to match dump processor expectation. + memcpy(identifier_swapped, &identifier[0], + std::min(kMDGUIDSize, identifier.size())); + uint32_t* data1 = reinterpret_cast<uint32_t*>(identifier_swapped); + *data1 = htonl(*data1); + uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4); + *data2 = htons(*data2); + uint16_t* data3 = reinterpret_cast<uint16_t*>(identifier_swapped + 6); + *data3 = htons(*data3); + + return bytes_to_hex_string(identifier_swapped, kMDGUIDSize); +} + +// static +string FileID::ConvertIdentifierToString( + const wasteful_vector<uint8_t>& identifier) { + return bytes_to_hex_string(&identifier[0], identifier.size()); +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h new file mode 100644 index 0000000000..4aff021ded --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h @@ -0,0 +1,88 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. +// +// file_id.h: Return a unique identifier for a file +// + +#ifndef COMMON_LINUX_FILE_ID_H__ +#define COMMON_LINUX_FILE_ID_H__ + +#include <limits.h> +#include <string> + +#include "common/linux/guid_creator.h" +#include "common/memory_allocator.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +// GNU binutils' ld defaults to 'sha1', which is 160 bits == 20 bytes, +// so this is enough to fit that, which most binaries will use. +// This is just a sensible default for auto_wasteful_vector so most +// callers can get away with stack allocation. +static const size_t kDefaultBuildIdSize = 20; + +class FileID { + public: + explicit FileID(const char* path); + ~FileID() {} + + // Load the identifier for the elf file path specified in the constructor into + // |identifier|. + // + // The current implementation will look for a .note.gnu.build-id + // section and use that as the file id, otherwise it falls back to + // XORing the first 4096 bytes of the .text section to generate an identifier. + bool ElfFileIdentifier(wasteful_vector<uint8_t>& identifier); + + // Load the identifier for the elf file mapped into memory at |base| into + // |identifier|. Return false if the identifier could not be created for this + // file. + static bool ElfFileIdentifierFromMappedFile( + const void* base, + wasteful_vector<uint8_t>& identifier); + + // Convert the |identifier| data to a string. The string will + // be formatted as a UUID in all uppercase without dashes. + // (e.g., 22F065BBFC9C49F780FE26A7CEBD7BCE). + static string ConvertIdentifierToUUIDString( + const wasteful_vector<uint8_t>& identifier); + + // Convert the entire |identifier| data to a hex string. + static string ConvertIdentifierToString( + const wasteful_vector<uint8_t>& identifier); + + private: + // Storage for the path specified + string path_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_FILE_ID_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/file_id_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id_unittest.cc new file mode 100644 index 0000000000..f4f9ac45ce --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id_unittest.cc @@ -0,0 +1,372 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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 <string> +#include <vector> + +#include "common/linux/elf_gnu_compat.h" +#include "common/linux/elfutils.h" +#include "common/linux/file_id.h" +#include "common/linux/safe_readlink.h" +#include "common/linux/synth_elf.h" +#include "common/test_assembler.h" +#include "common/tests/auto_tempdir.h" +#include "common/using_std_string.h" +#include "breakpad_googletest_includes.h" + +using namespace google_breakpad; +using google_breakpad::synth_elf::ELF; +using google_breakpad::synth_elf::Notes; +using google_breakpad::test_assembler::kLittleEndian; +using google_breakpad::test_assembler::Section; +using std::vector; +using ::testing::Types; + +namespace { + +// Simply calling Section::Append(size, byte) produces a uninteresting pattern +// that tends to get hashed to 0000...0000. This populates the section with +// data to produce better hashes. +void PopulateSection(Section* section, int size, int prime_number) { + for (int i = 0; i < size; i++) + section->Append(1, (i % prime_number) % 256); +} + +typedef wasteful_vector<uint8_t> id_vector; + +} // namespace + +#ifndef __ANDROID__ +// This test is disabled on Android: It will always fail, since there is no +// 'strip' binary installed on test devices. +TEST(FileIDStripTest, StripSelf) { + // Calculate the File ID of this binary using + // FileID::ElfFileIdentifier, then make a copy of this binary, + // strip it, and ensure that the result is the same. + char exe_name[PATH_MAX]; + ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name)); + + // copy our binary to a temp file, and strip it + AutoTempDir temp_dir; + string templ = temp_dir.path() + "/file-id-unittest"; + char cmdline[4096]; + sprintf(cmdline, "cp \"%s\" \"%s\"", exe_name, templ.c_str()); + ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline; + sprintf(cmdline, "chmod u+w \"%s\"", templ.c_str()); + ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline; + sprintf(cmdline, "strip \"%s\"", templ.c_str()); + ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline; + + PageAllocator allocator; + id_vector identifier1(&allocator, kDefaultBuildIdSize); + id_vector identifier2(&allocator, kDefaultBuildIdSize); + + FileID fileid1(exe_name); + EXPECT_TRUE(fileid1.ElfFileIdentifier(identifier1)); + FileID fileid2(templ.c_str()); + EXPECT_TRUE(fileid2.ElfFileIdentifier(identifier2)); + + string identifier_string1 = + FileID::ConvertIdentifierToUUIDString(identifier1); + string identifier_string2 = + FileID::ConvertIdentifierToUUIDString(identifier2); + EXPECT_EQ(identifier_string1, identifier_string2); +} +#endif // !__ANDROID__ + +template<typename ElfClass> +class FileIDTest : public testing::Test { +public: + void GetElfContents(ELF& elf) { + string contents; + ASSERT_TRUE(elf.GetContents(&contents)); + ASSERT_LT(0U, contents.size()); + + elfdata_v.clear(); + elfdata_v.insert(elfdata_v.begin(), contents.begin(), contents.end()); + elfdata = &elfdata_v[0]; + } + + id_vector make_vector() { + return id_vector(&allocator, kDefaultBuildIdSize); + } + + template<size_t N> + string get_file_id(const uint8_t (&data)[N]) { + id_vector expected_identifier(make_vector()); + expected_identifier.insert(expected_identifier.end(), + &data[0], + data + N); + return FileID::ConvertIdentifierToUUIDString(expected_identifier); + } + + vector<uint8_t> elfdata_v; + uint8_t* elfdata; + PageAllocator allocator; +}; + +typedef Types<ElfClass32, ElfClass64> ElfClasses; + +TYPED_TEST_SUITE(FileIDTest, ElfClasses); + +TYPED_TEST(FileIDTest, ElfClass) { + const char expected_identifier_string[] = + "80808080808000000000008080808080"; + const size_t kTextSectionSize = 128; + + ELF elf(EM_386, TypeParam::kClass, kLittleEndian); + Section text(kLittleEndian); + for (size_t i = 0; i < kTextSectionSize; ++i) { + text.D8(i * 3); + } + elf.AddSection(".text", text, SHT_PROGBITS); + elf.Finish(); + this->GetElfContents(elf); + + id_vector identifier(this->make_vector()); + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, + identifier)); + + string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier); + EXPECT_EQ(expected_identifier_string, identifier_string); +} + +TYPED_TEST(FileIDTest, BuildID) { + const uint8_t kExpectedIdentifierBytes[] = + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13}; + const string expected_identifier_string = + this->get_file_id(kExpectedIdentifierBytes); + + ELF elf(EM_386, TypeParam::kClass, kLittleEndian); + Section text(kLittleEndian); + text.Append(4096, 0); + elf.AddSection(".text", text, SHT_PROGBITS); + Notes notes(kLittleEndian); + notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes, + sizeof(kExpectedIdentifierBytes)); + elf.AddSection(".note.gnu.build-id", notes, SHT_NOTE); + elf.Finish(); + this->GetElfContents(elf); + + id_vector identifier(this->make_vector()); + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, + identifier)); + EXPECT_EQ(sizeof(kExpectedIdentifierBytes), identifier.size()); + + string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier); + EXPECT_EQ(expected_identifier_string, identifier_string); +} + +// Test that a build id note with fewer bytes than usual is handled. +TYPED_TEST(FileIDTest, BuildIDShort) { + const uint8_t kExpectedIdentifierBytes[] = + {0x00, 0x01, 0x02, 0x03}; + const string expected_identifier_string = + this->get_file_id(kExpectedIdentifierBytes); + + ELF elf(EM_386, TypeParam::kClass, kLittleEndian); + Section text(kLittleEndian); + text.Append(4096, 0); + elf.AddSection(".text", text, SHT_PROGBITS); + Notes notes(kLittleEndian); + notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes, + sizeof(kExpectedIdentifierBytes)); + elf.AddSection(".note.gnu.build-id", notes, SHT_NOTE); + elf.Finish(); + this->GetElfContents(elf); + + id_vector identifier(this->make_vector()); + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, + identifier)); + EXPECT_EQ(sizeof(kExpectedIdentifierBytes), identifier.size()); + + string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier); + EXPECT_EQ(expected_identifier_string, identifier_string); +} + +// Test that a build id note with more bytes than usual is handled. +TYPED_TEST(FileIDTest, BuildIDLong) { + const uint8_t kExpectedIdentifierBytes[] = + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}; + const string expected_identifier_string = + this->get_file_id(kExpectedIdentifierBytes); + + ELF elf(EM_386, TypeParam::kClass, kLittleEndian); + Section text(kLittleEndian); + text.Append(4096, 0); + elf.AddSection(".text", text, SHT_PROGBITS); + Notes notes(kLittleEndian); + notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes, + sizeof(kExpectedIdentifierBytes)); + elf.AddSection(".note.gnu.build-id", notes, SHT_NOTE); + elf.Finish(); + this->GetElfContents(elf); + + id_vector identifier(this->make_vector()); + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, + identifier)); + EXPECT_EQ(sizeof(kExpectedIdentifierBytes), identifier.size()); + + string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier); + EXPECT_EQ(expected_identifier_string, identifier_string); +} + +TYPED_TEST(FileIDTest, BuildIDPH) { + const uint8_t kExpectedIdentifierBytes[] = + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13}; + const string expected_identifier_string = + this->get_file_id(kExpectedIdentifierBytes); + + ELF elf(EM_386, TypeParam::kClass, kLittleEndian); + Section text(kLittleEndian); + text.Append(4096, 0); + elf.AddSection(".text", text, SHT_PROGBITS); + Notes notes(kLittleEndian); + notes.AddNote(0, "Linux", + reinterpret_cast<const uint8_t *>("\0x42\0x02\0\0"), 4); + notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes, + sizeof(kExpectedIdentifierBytes)); + int note_idx = elf.AddSection(".note", notes, SHT_NOTE); + elf.AddSegment(note_idx, note_idx, PT_NOTE); + elf.Finish(); + this->GetElfContents(elf); + + id_vector identifier(this->make_vector()); + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, + identifier)); + EXPECT_EQ(sizeof(kExpectedIdentifierBytes), identifier.size()); + + string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier); + EXPECT_EQ(expected_identifier_string, identifier_string); +} + +TYPED_TEST(FileIDTest, BuildIDMultiplePH) { + const uint8_t kExpectedIdentifierBytes[] = + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13}; + const string expected_identifier_string = + this->get_file_id(kExpectedIdentifierBytes); + + ELF elf(EM_386, TypeParam::kClass, kLittleEndian); + Section text(kLittleEndian); + text.Append(4096, 0); + elf.AddSection(".text", text, SHT_PROGBITS); + Notes notes1(kLittleEndian); + notes1.AddNote(0, "Linux", + reinterpret_cast<const uint8_t *>("\0x42\0x02\0\0"), 4); + Notes notes2(kLittleEndian); + notes2.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes, + sizeof(kExpectedIdentifierBytes)); + int note1_idx = elf.AddSection(".note1", notes1, SHT_NOTE); + int note2_idx = elf.AddSection(".note2", notes2, SHT_NOTE); + elf.AddSegment(note1_idx, note1_idx, PT_NOTE); + elf.AddSegment(note2_idx, note2_idx, PT_NOTE); + elf.Finish(); + this->GetElfContents(elf); + + id_vector identifier(this->make_vector()); + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, + identifier)); + EXPECT_EQ(sizeof(kExpectedIdentifierBytes), identifier.size()); + + string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier); + EXPECT_EQ(expected_identifier_string, identifier_string); +} + +// Test to make sure two files with different text sections produce +// different hashes when not using a build id. +TYPED_TEST(FileIDTest, UniqueHashes) { + { + ELF elf1(EM_386, TypeParam::kClass, kLittleEndian); + Section foo_1(kLittleEndian); + PopulateSection(&foo_1, 32, 5); + elf1.AddSection(".foo", foo_1, SHT_PROGBITS); + Section text_1(kLittleEndian); + PopulateSection(&text_1, 4096, 17); + elf1.AddSection(".text", text_1, SHT_PROGBITS); + elf1.Finish(); + this->GetElfContents(elf1); + } + + id_vector identifier_1(this->make_vector()); + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, + identifier_1)); + string identifier_string_1 = + FileID::ConvertIdentifierToUUIDString(identifier_1); + + { + ELF elf2(EM_386, TypeParam::kClass, kLittleEndian); + Section text_2(kLittleEndian); + Section foo_2(kLittleEndian); + PopulateSection(&foo_2, 32, 5); + elf2.AddSection(".foo", foo_2, SHT_PROGBITS); + PopulateSection(&text_2, 4096, 31); + elf2.AddSection(".text", text_2, SHT_PROGBITS); + elf2.Finish(); + this->GetElfContents(elf2); + } + + id_vector identifier_2(this->make_vector()); + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, + identifier_2)); + string identifier_string_2 = + FileID::ConvertIdentifierToUUIDString(identifier_2); + + EXPECT_NE(identifier_string_1, identifier_string_2); +} + +TYPED_TEST(FileIDTest, ConvertIdentifierToString) { + const uint8_t kIdentifierBytes[] = + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}; + const char* kExpected = + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"; + + id_vector identifier(this->make_vector()); + identifier.insert(identifier.end(), + kIdentifierBytes, + kIdentifierBytes + sizeof(kIdentifierBytes)); + ASSERT_EQ(kExpected, + FileID::ConvertIdentifierToString(identifier)); +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc new file mode 100644 index 0000000000..a0d940b614 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc @@ -0,0 +1,207 @@ +// Copyright (c) 2009, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + + +#include "common/linux/google_crashdump_uploader.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <iostream> + +#include "common/using_std_string.h" + +namespace google_breakpad { + +GoogleCrashdumpUploader::GoogleCrashdumpUploader(const string& product, + const string& version, + const string& guid, + const string& ptime, + const string& ctime, + const string& email, + const string& comments, + const string& minidump_pathname, + const string& crash_server, + const string& proxy_host, + const string& proxy_userpassword) { + LibcurlWrapper* http_layer = new LibcurlWrapper(); + Init(product, + version, + guid, + ptime, + ctime, + email, + comments, + minidump_pathname, + crash_server, + proxy_host, + proxy_userpassword, + http_layer); +} + +GoogleCrashdumpUploader::GoogleCrashdumpUploader(const string& product, + const string& version, + const string& guid, + const string& ptime, + const string& ctime, + const string& email, + const string& comments, + const string& minidump_pathname, + const string& crash_server, + const string& proxy_host, + const string& proxy_userpassword, + LibcurlWrapper* http_layer) { + Init(product, + version, + guid, + ptime, + ctime, + email, + comments, + minidump_pathname, + crash_server, + proxy_host, + proxy_userpassword, + http_layer); +} + +void GoogleCrashdumpUploader::Init(const string& product, + const string& version, + const string& guid, + const string& ptime, + const string& ctime, + const string& email, + const string& comments, + const string& minidump_pathname, + const string& crash_server, + const string& proxy_host, + const string& proxy_userpassword, + LibcurlWrapper* http_layer) { + product_ = product; + version_ = version; + guid_ = guid; + ptime_ = ptime; + ctime_ = ctime; + email_ = email; + comments_ = comments; + http_layer_.reset(http_layer); + + crash_server_ = crash_server; + proxy_host_ = proxy_host; + proxy_userpassword_ = proxy_userpassword; + minidump_pathname_ = minidump_pathname; + std::cout << "Uploader initializing"; + std::cout << "\tProduct: " << product_; + std::cout << "\tVersion: " << version_; + std::cout << "\tGUID: " << guid_; + if (!ptime_.empty()) { + std::cout << "\tProcess uptime: " << ptime_; + } + if (!ctime_.empty()) { + std::cout << "\tCumulative Process uptime: " << ctime_; + } + if (!email_.empty()) { + std::cout << "\tEmail: " << email_; + } + if (!comments_.empty()) { + std::cout << "\tComments: " << comments_; + } +} + +bool GoogleCrashdumpUploader::CheckRequiredParametersArePresent() { + string error_text; + if (product_.empty()) { + error_text.append("\nProduct name must be specified."); + } + + if (version_.empty()) { + error_text.append("\nProduct version must be specified."); + } + + if (guid_.empty()) { + error_text.append("\nClient ID must be specified."); + } + + if (minidump_pathname_.empty()) { + error_text.append("\nMinidump pathname must be specified."); + } + + if (!error_text.empty()) { + std::cout << error_text; + return false; + } + return true; + +} + +bool GoogleCrashdumpUploader::Upload(int* http_status_code, + string* http_response_header, + string* http_response_body) { + bool ok = http_layer_->Init(); + if (!ok) { + std::cout << "http layer init failed"; + return ok; + } + + if (!CheckRequiredParametersArePresent()) { + return false; + } + + struct stat st; + int err = stat(minidump_pathname_.c_str(), &st); + if (err) { + std::cout << minidump_pathname_ << " could not be found"; + return false; + } + + parameters_["prod"] = product_; + parameters_["ver"] = version_; + parameters_["guid"] = guid_; + parameters_["ptime"] = ptime_; + parameters_["ctime"] = ctime_; + parameters_["email"] = email_; + parameters_["comments_"] = comments_; + if (!http_layer_->AddFile(minidump_pathname_, + "upload_file_minidump")) { + return false; + } + std::cout << "Sending request to " << crash_server_; + long status_code; + bool success = http_layer_->SendRequest(crash_server_, + parameters_, + &status_code, + http_response_header, + http_response_body); + if (http_status_code) { + *http_status_code = status_code; + } + return success; +} +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h new file mode 100644 index 0000000000..a2d0575b5e --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h @@ -0,0 +1,107 @@ +// Copyright (c) 2009, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + + +#ifndef COMMON_LINUX_GOOGLE_CRASHDUMP_UPLOADER_H_ +#define COMMON_LINUX_GOOGLE_CRASHDUMP_UPLOADER_H_ + +#include <string> +#include <map> + +#include "common/linux/libcurl_wrapper.h" +#include "common/scoped_ptr.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +class GoogleCrashdumpUploader { + public: + GoogleCrashdumpUploader(const string& product, + const string& version, + const string& guid, + const string& ptime, + const string& ctime, + const string& email, + const string& comments, + const string& minidump_pathname, + const string& crash_server, + const string& proxy_host, + const string& proxy_userpassword); + + GoogleCrashdumpUploader(const string& product, + const string& version, + const string& guid, + const string& ptime, + const string& ctime, + const string& email, + const string& comments, + const string& minidump_pathname, + const string& crash_server, + const string& proxy_host, + const string& proxy_userpassword, + LibcurlWrapper* http_layer); + + void Init(const string& product, + const string& version, + const string& guid, + const string& ptime, + const string& ctime, + const string& email, + const string& comments, + const string& minidump_pathname, + const string& crash_server, + const string& proxy_host, + const string& proxy_userpassword, + LibcurlWrapper* http_layer); + bool Upload(int* http_status_code, + string* http_response_header, + string* http_response_body); + + private: + bool CheckRequiredParametersArePresent(); + + scoped_ptr<LibcurlWrapper> http_layer_; + string product_; + string version_; + string guid_; + string ptime_; + string ctime_; + string email_; + string comments_; + string minidump_pathname_; + + string crash_server_; + string proxy_host_; + string proxy_userpassword_; + + std::map<string, string> parameters_; +}; +} + +#endif // COMMON_LINUX_GOOGLE_CRASHDUMP_UPLOADER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc new file mode 100644 index 0000000000..3d6612e821 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc @@ -0,0 +1,170 @@ +// Copyright (c) 2009, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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 test for crash dump uploader. + +#include <string> + +#include "common/linux/google_crashdump_uploader.h" +#include "breakpad_googletest_includes.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +using ::testing::Return; +using ::testing::_; + +class MockLibcurlWrapper : public LibcurlWrapper { + public: + MOCK_METHOD0(Init, bool()); + MOCK_METHOD2(SetProxy, bool(const string& proxy_host, + const string& proxy_userpwd)); + MOCK_METHOD2(AddFile, bool(const string& upload_file_path, + const string& basename)); + MOCK_METHOD5(SendRequest, + bool(const string& url, + const std::map<string, string>& parameters, + long* http_status_code, + string* http_header_data, + string* http_response_data)); +}; + +class GoogleCrashdumpUploaderTest : public ::testing::Test { +}; + +TEST_F(GoogleCrashdumpUploaderTest, InitFailsCausesUploadFailure) { + MockLibcurlWrapper m; + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(false)); + GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", + "1.0", + "AAA-BBB", + "", + "", + "test@test.com", + "none", + "/tmp/foo.dmp", + "http://foo.com", + "", + "", + &m); + ASSERT_FALSE(uploader->Upload(NULL, NULL, NULL)); +} + +TEST_F(GoogleCrashdumpUploaderTest, TestSendRequestHappensWithValidParameters) { + // Create a temp file + char tempfn[80] = "/tmp/googletest-upload-XXXXXX"; + int fd = mkstemp(tempfn); + ASSERT_NE(fd, -1); + close(fd); + + MockLibcurlWrapper m; + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); + EXPECT_CALL(m, AddFile(tempfn, _)).WillOnce(Return(true)); + EXPECT_CALL(m, + SendRequest("http://foo.com",_,_,_,_)).Times(1).WillOnce(Return(true)); + GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", + "1.0", + "AAA-BBB", + "", + "", + "test@test.com", + "none", + tempfn, + "http://foo.com", + "", + "", + &m); + ASSERT_TRUE(uploader->Upload(NULL, NULL, NULL)); +} + + +TEST_F(GoogleCrashdumpUploaderTest, InvalidPathname) { + MockLibcurlWrapper m; + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); + EXPECT_CALL(m, SendRequest(_,_,_,_,_)).Times(0); + GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", + "1.0", + "AAA-BBB", + "", + "", + "test@test.com", + "none", + "/tmp/foo.dmp", + "http://foo.com", + "", + "", + &m); + ASSERT_FALSE(uploader->Upload(NULL, NULL, NULL)); +} + +TEST_F(GoogleCrashdumpUploaderTest, TestRequiredParametersMustBePresent) { + // Test with empty product name. + GoogleCrashdumpUploader uploader("", + "1.0", + "AAA-BBB", + "", + "", + "test@test.com", + "none", + "/tmp/foo.dmp", + "http://foo.com", + "", + ""); + ASSERT_FALSE(uploader.Upload(NULL, NULL, NULL)); + + // Test with empty product version. + GoogleCrashdumpUploader uploader1("product", + "", + "AAA-BBB", + "", + "", + "", + "", + "/tmp/foo.dmp", + "", + "", + ""); + + ASSERT_FALSE(uploader1.Upload(NULL, NULL, NULL)); + + // Test with empty client GUID. + GoogleCrashdumpUploader uploader2("product", + "1.0", + "", + "", + "", + "", + "", + "/tmp/foo.dmp", + "", + "", + ""); + ASSERT_FALSE(uploader2.Upload(NULL, NULL, NULL)); +} +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.cc new file mode 100644 index 0000000000..03e3d781fb --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.cc @@ -0,0 +1,189 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "common/linux/eintr_wrapper.h" +#include "common/linux/guid_creator.h" + +#include <assert.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> + +#if defined(HAVE_SYS_RANDOM_H) +#include <sys/random.h> +#endif + +// +// GUIDGenerator +// +// This class is used to generate random GUID. +// Currently use random number to generate a GUID since Linux has +// no native GUID generator. This should be OK since we don't expect +// crash to happen very offen. +// +class GUIDGenerator { + public: + static uint32_t BytesToUInt32(const uint8_t bytes[]) { + return ((uint32_t) bytes[0] + | ((uint32_t) bytes[1] << 8) + | ((uint32_t) bytes[2] << 16) + | ((uint32_t) bytes[3] << 24)); + } + + static void UInt32ToBytes(uint8_t bytes[], uint32_t n) { + bytes[0] = n & 0xff; + bytes[1] = (n >> 8) & 0xff; + bytes[2] = (n >> 16) & 0xff; + bytes[3] = (n >> 24) & 0xff; + } + + static bool CreateGUID(GUID *guid) { +#if defined(HAVE_ARC4RANDOM) // Android, BSD, ... + CreateGuidFromArc4Random(guid); +#else // Linux + bool success = false; + +#if defined(HAVE_SYS_RANDOM_H) && defined(HAVE_GETRANDOM) + success = CreateGUIDFromGetrandom(guid); +#endif // HAVE_SYS_RANDOM_H && HAVE_GETRANDOM + if (!success) { + success = CreateGUIDFromDevUrandom(guid); + } + + if (!success) { + CreateGUIDFromRand(guid); + success = true; + } +#endif + + // Put in the version according to RFC 4122. + guid->data3 &= 0x0fff; + guid->data3 |= 0x4000; + + // Put in the variant according to RFC 4122. + guid->data4[0] &= 0x3f; + guid->data4[0] |= 0x80; + + return true; + } + + private: +#ifdef HAVE_ARC4RANDOM + static void CreateGuidFromArc4Random(GUID *guid) { + char *buf = reinterpret_cast<char *>(guid); + + for (size_t i = 0; i < sizeof(GUID); i += sizeof(uint32_t)) { + uint32_t random_data = arc4random(); + + memcpy(buf + i, &random_data, sizeof(uint32_t)); + } + } +#else + static void InitOnce() { + pthread_once(&once_control, &InitOnceImpl); + } + + static void InitOnceImpl() { + // time(NULL) is a very poor seed, so lacking anything better mix an + // address into it. We drop the four rightmost bits as they're likely to + // be 0 on almost all architectures. + srand(time(NULL) | ((uintptr_t)&once_control >> 4)); + } + + static pthread_once_t once_control; + +#if defined(HAVE_SYS_RANDOM_H) && defined(HAVE_GETRANDOM) + static bool CreateGUIDFromGetrandom(GUID *guid) { + char *buf = reinterpret_cast<char *>(guid); + int read_bytes = getrandom(buf, sizeof(GUID), GRND_NONBLOCK); + + return (read_bytes == static_cast<int>(sizeof(GUID))); + } +#endif // HAVE_SYS_RANDOM_H && HAVE_GETRANDOM + + // Populate the GUID using random bytes read from /dev/urandom, returns false + // if the GUID wasn't fully populated with random data. + static bool CreateGUIDFromDevUrandom(GUID *guid) { + char *buf = reinterpret_cast<char *>(guid); + int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + + if (fd == -1) { + return false; + } + + ssize_t read_bytes = HANDLE_EINTR(read(fd, buf, sizeof(GUID))); + close(fd); + + return (read_bytes == static_cast<ssize_t>(sizeof(GUID))); + } + + // Populate the GUID using a stream of random bytes obtained from rand(). + static void CreateGUIDFromRand(GUID *guid) { + char *buf = reinterpret_cast<char *>(guid); + + InitOnce(); + + for (size_t i = 0; i < sizeof(GUID); i++) { + buf[i] = rand(); + } + } +#endif +}; + +#ifndef HAVE_ARC4RANDOM +pthread_once_t GUIDGenerator::once_control = PTHREAD_ONCE_INIT; +#endif + +bool CreateGUID(GUID *guid) { + return GUIDGenerator::CreateGUID(guid); +} + +// Parse guid to string. +bool GUIDToString(const GUID *guid, char *buf, int buf_len) { + // Should allow more space the the max length of GUID. + assert(buf_len > kGUIDStringLength); + int num = snprintf(buf, buf_len, kGUIDFormatString, + guid->data1, guid->data2, guid->data3, + GUIDGenerator::BytesToUInt32(&(guid->data4[0])), + GUIDGenerator::BytesToUInt32(&(guid->data4[4]))); + if (num != kGUIDStringLength) + return false; + + buf[num] = '\0'; + return true; +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.h b/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.h new file mode 100644 index 0000000000..c86d856c4d --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.h @@ -0,0 +1,48 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef COMMON_LINUX_GUID_CREATOR_H__ +#define COMMON_LINUX_GUID_CREATOR_H__ + +#include "google_breakpad/common/minidump_format.h" + +typedef MDGUID GUID; + +// Format string for parsing GUID. +#define kGUIDFormatString "%08x-%04x-%04x-%08x-%08x" +// Length of GUID string. Don't count the ending '\0'. +#define kGUIDStringLength 36 + +// Create a guid. +bool CreateGUID(GUID *guid); + +// Get the string from guid. +bool GUIDToString(const GUID *guid, char *buf, int buf_len); + +#endif diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.cc new file mode 100644 index 0000000000..0a1019dd0d --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.cc @@ -0,0 +1,210 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include "common/linux/http_upload.h" + +#include <assert.h> +#include <dlfcn.h> +#include "third_party/curl/curl.h" + +namespace { + +// Callback to get the response data from server. +static size_t WriteCallback(void *ptr, size_t size, + size_t nmemb, void *userp) { + if (!userp) + return 0; + + string *response = reinterpret_cast<string *>(userp); + size_t real_size = size * nmemb; + response->append(reinterpret_cast<char *>(ptr), real_size); + return real_size; +} + +} // namespace + +namespace google_breakpad { + +static const char kUserAgent[] = "Breakpad/1.0 (Linux)"; + +// static +bool HTTPUpload::SendRequest(const string &url, + const string ¶meters, + const map<string, string> &files, + const string &proxy, + const string &proxy_user_pwd, + const string &ca_certificate_file, + string *response_body, + long *response_code, + string *error_description) { + if (response_code != NULL) + *response_code = 0; + + // We may have been linked statically; if curl_easy_init is in the + // current binary, no need to search for a dynamic version. + void* curl_lib = dlopen(NULL, RTLD_NOW); + if (!CheckCurlLib(curl_lib)) { + fprintf(stderr, + "Failed to open curl lib from binary, use libcurl.so instead\n"); + dlerror(); // Clear dlerror before attempting to open libraries. + dlclose(curl_lib); + curl_lib = NULL; + } + if (!curl_lib) { + curl_lib = dlopen("libcurl.so", RTLD_NOW); + } + if (!curl_lib) { + if (error_description != NULL) + *error_description = dlerror(); + curl_lib = dlopen("libcurl.so.4", RTLD_NOW); + } + if (!curl_lib) { + // Debian gives libcurl a different name when it is built against GnuTLS + // instead of OpenSSL. + curl_lib = dlopen("libcurl-gnutls.so.4", RTLD_NOW); + } + if (!curl_lib) { + curl_lib = dlopen("libcurl.so.3", RTLD_NOW); + } + if (!curl_lib) { + return false; + } + + CURL* (*curl_easy_init)(void); + *(void**) (&curl_easy_init) = dlsym(curl_lib, "curl_easy_init"); + CURL *curl = (*curl_easy_init)(); + if (error_description != NULL) + *error_description = "No Error"; + + if (!curl) { + dlclose(curl_lib); + return false; + } + + CURLcode err_code = CURLE_OK; + CURLcode (*curl_easy_setopt)(CURL *, CURLoption, ...); + *(void**) (&curl_easy_setopt) = dlsym(curl_lib, "curl_easy_setopt"); + (*curl_easy_setopt)(curl, CURLOPT_URL, url.c_str()); + (*curl_easy_setopt)(curl, CURLOPT_USERAGENT, kUserAgent); + // Support multithread by disabling timeout handling, would get SIGSEGV with + // Curl_resolv_timeout in stack trace otherwise. + // See https://curl.haxx.se/libcurl/c/threadsafe.html + (*curl_easy_setopt)(curl, CURLOPT_NOSIGNAL, 1); + // Set proxy information if necessary. + if (!proxy.empty()) + (*curl_easy_setopt)(curl, CURLOPT_PROXY, proxy.c_str()); + if (!proxy_user_pwd.empty()) + (*curl_easy_setopt)(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str()); + + if (!ca_certificate_file.empty()) + (*curl_easy_setopt)(curl, CURLOPT_CAINFO, ca_certificate_file.c_str()); + + struct curl_httppost *formpost = NULL; + struct curl_httppost *lastptr = NULL; + // Add form data. + CURLFORMcode (*curl_formadd)(struct curl_httppost **, struct curl_httppost **, ...); + *(void**) (&curl_formadd) = dlsym(curl_lib, "curl_formadd"); + (*curl_formadd)(&formpost, &lastptr, CURLFORM_COPYNAME, "extra", + CURLFORM_BUFFER, "extra.json", CURLFORM_BUFFERPTR, + parameters.c_str(), CURLFORM_BUFFERLENGTH, + parameters.length(), CURLFORM_CONTENTTYPE, "application/json", + CURLFORM_END); + + // Add form files. + map<string, string>::const_iterator iter = files.begin(); + for (iter = files.begin(); iter != files.end(); ++iter) { + (*curl_formadd)(&formpost, &lastptr, + CURLFORM_COPYNAME, iter->first.c_str(), + CURLFORM_FILE, iter->second.c_str(), + CURLFORM_END); + } + + (*curl_easy_setopt)(curl, CURLOPT_HTTPPOST, formpost); + + // Disable 100-continue header. + struct curl_slist *headerlist = NULL; + char buf[] = "Expect:"; + struct curl_slist* (*curl_slist_append)(struct curl_slist *, const char *); + *(void**) (&curl_slist_append) = dlsym(curl_lib, "curl_slist_append"); + headerlist = (*curl_slist_append)(headerlist, buf); + (*curl_easy_setopt)(curl, CURLOPT_HTTPHEADER, headerlist); + + if (response_body != NULL) { + (*curl_easy_setopt)(curl, CURLOPT_WRITEFUNCTION, WriteCallback); + (*curl_easy_setopt)(curl, CURLOPT_WRITEDATA, + reinterpret_cast<void *>(response_body)); + } + + // Fail if 400+ is returned from the web server. + (*curl_easy_setopt)(curl, CURLOPT_FAILONERROR, 1); + + CURLcode (*curl_easy_perform)(CURL *); + *(void**) (&curl_easy_perform) = dlsym(curl_lib, "curl_easy_perform"); + err_code = (*curl_easy_perform)(curl); + if (response_code != NULL) { + CURLcode (*curl_easy_getinfo)(CURL *, CURLINFO, ...); + *(void**) (&curl_easy_getinfo) = dlsym(curl_lib, "curl_easy_getinfo"); + (*curl_easy_getinfo)(curl, CURLINFO_RESPONSE_CODE, response_code); + } + const char* (*curl_easy_strerror)(CURLcode); + *(void**) (&curl_easy_strerror) = dlsym(curl_lib, "curl_easy_strerror"); +#ifndef NDEBUG + if (err_code != CURLE_OK) + fprintf(stderr, "Failed to send http request to %s, error: %s\n", + url.c_str(), + (*curl_easy_strerror)(err_code)); +#endif + if (error_description != NULL) + *error_description = (*curl_easy_strerror)(err_code); + + void (*curl_easy_cleanup)(CURL *); + *(void**) (&curl_easy_cleanup) = dlsym(curl_lib, "curl_easy_cleanup"); + (*curl_easy_cleanup)(curl); + if (formpost != NULL) { + void (*curl_formfree)(struct curl_httppost *); + *(void**) (&curl_formfree) = dlsym(curl_lib, "curl_formfree"); + (*curl_formfree)(formpost); + } + if (headerlist != NULL) { + void (*curl_slist_free_all)(struct curl_slist *); + *(void**) (&curl_slist_free_all) = dlsym(curl_lib, "curl_slist_free_all"); + (*curl_slist_free_all)(headerlist); + } + dlclose(curl_lib); + return err_code == CURLE_OK; +} + +// static +bool HTTPUpload::CheckCurlLib(void* curl_lib) { + return curl_lib && + dlsym(curl_lib, "curl_easy_init") && + dlsym(curl_lib, "curl_easy_setopt"); +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.h b/toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.h new file mode 100644 index 0000000000..95dedebc3b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.h @@ -0,0 +1,84 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// HTTPUpload provides a "nice" API to send a multipart HTTP(S) POST +// request using libcurl. It currently supports requests that contain +// parameters encoded in a JSON string, and a file to upload. + +#ifndef COMMON_LINUX_HTTP_UPLOAD_H__ +#define COMMON_LINUX_HTTP_UPLOAD_H__ + +#include <map> +#include <string> + +#include "common/using_std_string.h" + +namespace google_breakpad { + +using std::map; + +class HTTPUpload { + public: + // Sends the given sets of parameters and files as a multipart POST + // request to the given URL. + // Each key in |files| is the name of the file part of the request + // (i.e. it corresponds to the name= attribute on an <input type="file">. + // Parameters are specified as a JSON-encoded string in |parameters|. + // Only HTTP(S) URLs are currently supported. Returns true on success. + // If the request is successful and response_body is non-NULL, + // the response body will be returned in response_body. + // If response_code is non-NULL, it will be set to the HTTP response code + // received (or 0 if the request failed before getting an HTTP response). + // If the send fails, a description of the error will be + // returned in error_description. + static bool SendRequest(const string &url, + const string ¶meters, + const map<string, string> &files, + const string &proxy, + const string &proxy_user_pwd, + const string &ca_certificate_file, + string *response_body, + long *response_code, + string *error_description); + + private: + // Checks the curl_lib parameter points to a valid curl lib. + static bool CheckCurlLib(void* curl_lib); + + // No instances of this class should be created. + // Disallow all constructors, destructors, and operator=. + HTTPUpload(); + explicit HTTPUpload(const HTTPUpload &); + void operator=(const HTTPUpload &); + ~HTTPUpload(); +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_HTTP_UPLOAD_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/ignore_ret.h b/toolkit/crashreporter/google-breakpad/src/common/linux/ignore_ret.h new file mode 100644 index 0000000000..efd274c20c --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/ignore_ret.h @@ -0,0 +1,40 @@ +// Copyright (c) 2012 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef COMMON_LINUX_IGNORE_RET_H_ +#define COMMON_LINUX_IGNORE_RET_H_ + +// Some compilers are prone to warn about unused return values. In cases where +// either a) the call cannot fail, or b) there is nothing that can be done when +// the call fails, IGNORE_RET() can be used to mark the return code as ignored. +// This avoids spurious compiler warnings. + +#define IGNORE_RET(x) do { if (x) {} } while (0) + +#endif // COMMON_LINUX_IGNORE_RET_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc new file mode 100644 index 0000000000..e96c203852 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc @@ -0,0 +1,338 @@ +// Copyright (c) 2009, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include <dlfcn.h> + +#include <iostream> +#include <string> + +#include "common/linux/libcurl_wrapper.h" +#include "common/using_std_string.h" + +namespace google_breakpad { +LibcurlWrapper::LibcurlWrapper() + : init_ok_(false), + curl_lib_(nullptr), + last_curl_error_(""), + curl_(nullptr), + formpost_(nullptr), + lastptr_(nullptr), + headerlist_(nullptr) {} + +LibcurlWrapper::~LibcurlWrapper() { + if (init_ok_) { + (*easy_cleanup_)(curl_); + dlclose(curl_lib_); + } +} + +bool LibcurlWrapper::SetProxy(const string& proxy_host, + const string& proxy_userpwd) { + if (!CheckInit()) return false; + + // Set proxy information if necessary. + if (!proxy_host.empty()) { + (*easy_setopt_)(curl_, CURLOPT_PROXY, proxy_host.c_str()); + } else { + std::cout << "SetProxy called with empty proxy host."; + return false; + } + if (!proxy_userpwd.empty()) { + (*easy_setopt_)(curl_, CURLOPT_PROXYUSERPWD, proxy_userpwd.c_str()); + } else { + std::cout << "SetProxy called with empty proxy username/password."; + return false; + } + std::cout << "Set proxy host to " << proxy_host; + return true; +} + +bool LibcurlWrapper::AddFile(const string& upload_file_path, + const string& basename) { + if (!CheckInit()) return false; + + std::cout << "Adding " << upload_file_path << " to form upload."; + // Add form file. + (*formadd_)(&formpost_, &lastptr_, + CURLFORM_COPYNAME, basename.c_str(), + CURLFORM_FILE, upload_file_path.c_str(), + CURLFORM_END); + + return true; +} + +// Callback to get the response data from server. +static size_t WriteCallback(void *ptr, size_t size, + size_t nmemb, void *userp) { + if (!userp) + return 0; + + string *response = reinterpret_cast<string *>(userp); + size_t real_size = size * nmemb; + response->append(reinterpret_cast<char *>(ptr), real_size); + return real_size; +} + +bool LibcurlWrapper::SendRequest(const string& url, + const std::map<string, string>& parameters, + long* http_status_code, + string* http_header_data, + string* http_response_data) { + if (!CheckInit()) return false; + + std::map<string, string>::const_iterator iter = parameters.begin(); + for (; iter != parameters.end(); ++iter) + (*formadd_)(&formpost_, &lastptr_, + CURLFORM_COPYNAME, iter->first.c_str(), + CURLFORM_COPYCONTENTS, iter->second.c_str(), + CURLFORM_END); + + (*easy_setopt_)(curl_, CURLOPT_HTTPPOST, formpost_); + + return SendRequestInner(url, http_status_code, http_header_data, + http_response_data); +} + +bool LibcurlWrapper::SendGetRequest(const string& url, + long* http_status_code, + string* http_header_data, + string* http_response_data) { + if (!CheckInit()) return false; + + (*easy_setopt_)(curl_, CURLOPT_HTTPGET, 1L); + + return SendRequestInner(url, http_status_code, http_header_data, + http_response_data); +} + +bool LibcurlWrapper::SendPutRequest(const string& url, + const string& path, + long* http_status_code, + string* http_header_data, + string* http_response_data) { + if (!CheckInit()) return false; + + FILE* file = fopen(path.c_str(), "rb"); + (*easy_setopt_)(curl_, CURLOPT_UPLOAD, 1L); + (*easy_setopt_)(curl_, CURLOPT_PUT, 1L); + (*easy_setopt_)(curl_, CURLOPT_READDATA, file); + + bool success = SendRequestInner(url, http_status_code, http_header_data, + http_response_data); + + fclose(file); + return success; +} + +bool LibcurlWrapper::SendSimplePostRequest(const string& url, + const string& body, + const string& content_type, + long* http_status_code, + string* http_header_data, + string* http_response_data) { + if (!CheckInit()) return false; + + (*easy_setopt_)(curl_, CURLOPT_POSTFIELDSIZE, body.size()); + (*easy_setopt_)(curl_, CURLOPT_COPYPOSTFIELDS, body.c_str()); + + if (!content_type.empty()) { + string content_type_header = "Content-Type: " + content_type; + headerlist_ = (*slist_append_)( + headerlist_, + content_type_header.c_str()); + } + + return SendRequestInner(url, http_status_code, http_header_data, + http_response_data); +} + +bool LibcurlWrapper::Init() { + // First check to see if libcurl was statically linked: + curl_lib_ = dlopen(nullptr, RTLD_NOW); + if (curl_lib_ && + (!dlsym(curl_lib_, "curl_easy_init") || + !dlsym(curl_lib_, "curl_easy_setopt"))) { + // Not statically linked, try again below. + dlerror(); // Clear dlerror before attempting to open libraries. + dlclose(curl_lib_); + curl_lib_ = nullptr; + } + if (!curl_lib_) { + curl_lib_ = dlopen("libcurl.so", RTLD_NOW); + } + if (!curl_lib_) { + curl_lib_ = dlopen("libcurl.so.4", RTLD_NOW); + } + if (!curl_lib_) { + curl_lib_ = dlopen("libcurl.so.3", RTLD_NOW); + } + if (!curl_lib_) { + std::cout << "Could not find libcurl via dlopen"; + return false; + } + + if (!SetFunctionPointers()) { + std::cout << "Could not find function pointers"; + return false; + } + + curl_ = (*easy_init_)(); + + last_curl_error_ = "No Error"; + + if (!curl_) { + dlclose(curl_lib_); + std::cout << "Curl initialization failed"; + return false; + } + + init_ok_ = true; + return true; +} + +#define SET_AND_CHECK_FUNCTION_POINTER(var, function_name, type) \ + var = reinterpret_cast<type>(dlsym(curl_lib_, function_name)); \ + if (!var) { \ + std::cout << "Could not find libcurl function " << function_name; \ + init_ok_ = false; \ + return false; \ + } + +bool LibcurlWrapper::SetFunctionPointers() { + + SET_AND_CHECK_FUNCTION_POINTER(easy_init_, + "curl_easy_init", + CURL*(*)()); + + SET_AND_CHECK_FUNCTION_POINTER(easy_setopt_, + "curl_easy_setopt", + CURLcode(*)(CURL*, CURLoption, ...)); + + SET_AND_CHECK_FUNCTION_POINTER(formadd_, "curl_formadd", + CURLFORMcode(*)(curl_httppost**, curl_httppost**, ...)); + + SET_AND_CHECK_FUNCTION_POINTER(slist_append_, "curl_slist_append", + curl_slist*(*)(curl_slist*, const char*)); + + SET_AND_CHECK_FUNCTION_POINTER(easy_perform_, + "curl_easy_perform", + CURLcode(*)(CURL*)); + + SET_AND_CHECK_FUNCTION_POINTER(easy_cleanup_, + "curl_easy_cleanup", + void(*)(CURL*)); + + SET_AND_CHECK_FUNCTION_POINTER(easy_getinfo_, + "curl_easy_getinfo", + CURLcode(*)(CURL *, CURLINFO info, ...)); + + SET_AND_CHECK_FUNCTION_POINTER(easy_reset_, + "curl_easy_reset", + void(*)(CURL*)); + + SET_AND_CHECK_FUNCTION_POINTER(slist_free_all_, + "curl_slist_free_all", + void(*)(curl_slist*)); + + SET_AND_CHECK_FUNCTION_POINTER(formfree_, + "curl_formfree", + void(*)(curl_httppost*)); + return true; +} + +bool LibcurlWrapper::SendRequestInner(const string& url, + long* http_status_code, + string* http_header_data, + string* http_response_data) { + string url_copy(url); + (*easy_setopt_)(curl_, CURLOPT_URL, url_copy.c_str()); + + // Disable 100-continue header. + char buf[] = "Expect:"; + headerlist_ = (*slist_append_)(headerlist_, buf); + (*easy_setopt_)(curl_, CURLOPT_HTTPHEADER, headerlist_); + + if (http_response_data != nullptr) { + http_response_data->clear(); + (*easy_setopt_)(curl_, CURLOPT_WRITEFUNCTION, WriteCallback); + (*easy_setopt_)(curl_, CURLOPT_WRITEDATA, + reinterpret_cast<void*>(http_response_data)); + } + if (http_header_data != nullptr) { + http_header_data->clear(); + (*easy_setopt_)(curl_, CURLOPT_HEADERFUNCTION, WriteCallback); + (*easy_setopt_)(curl_, CURLOPT_HEADERDATA, + reinterpret_cast<void*>(http_header_data)); + } + CURLcode err_code = CURLE_OK; + err_code = (*easy_perform_)(curl_); + easy_strerror_ = reinterpret_cast<const char* (*)(CURLcode)> + (dlsym(curl_lib_, "curl_easy_strerror")); + + if (http_status_code != nullptr) { + (*easy_getinfo_)(curl_, CURLINFO_RESPONSE_CODE, http_status_code); + } + +#ifndef NDEBUG + if (err_code != CURLE_OK) + fprintf(stderr, "Failed to send http request to %s, error: %s\n", + url.c_str(), + (*easy_strerror_)(err_code)); +#endif + + Reset(); + + return err_code == CURLE_OK; +} + +void LibcurlWrapper::Reset() { + if (headerlist_ != nullptr) { + (*slist_free_all_)(headerlist_); + headerlist_ = nullptr; + } + + if (formpost_ != nullptr) { + (*formfree_)(formpost_); + formpost_ = nullptr; + } + + (*easy_reset_)(curl_); +} + +bool LibcurlWrapper::CheckInit() { + if (!init_ok_) { + std::cout << "LibcurlWrapper: You must call Init(), and have it return " + "'true' before invoking any other methods.\n"; + return false; + } + + return true; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h b/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h new file mode 100644 index 0000000000..77aa6cbb72 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h @@ -0,0 +1,119 @@ +// Copyright (c) 2009, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// A wrapper for libcurl to do HTTP Uploads, to support easy mocking +// and unit testing of the HTTPUpload class. + +#ifndef COMMON_LINUX_LIBCURL_WRAPPER_H_ +#define COMMON_LINUX_LIBCURL_WRAPPER_H_ + +#include <string> +#include <map> + +#include "common/using_std_string.h" +#include "third_party/curl/curl.h" + +namespace google_breakpad { +class LibcurlWrapper { + public: + LibcurlWrapper(); + virtual ~LibcurlWrapper(); + virtual bool Init(); + virtual bool SetProxy(const string& proxy_host, + const string& proxy_userpwd); + virtual bool AddFile(const string& upload_file_path, + const string& basename); + virtual bool SendRequest(const string& url, + const std::map<string, string>& parameters, + long* http_status_code, + string* http_header_data, + string* http_response_data); + bool SendGetRequest(const string& url, + long* http_status_code, + string* http_header_data, + string* http_response_data); + bool SendPutRequest(const string& url, + const string& path, + long* http_status_code, + string* http_header_data, + string* http_response_data); + bool SendSimplePostRequest(const string& url, + const string& body, + const string& content_type, + long* http_status_code, + string* http_header_data, + string* http_response_data); + + private: + // This function initializes class state corresponding to function + // pointers into the CURL library. + bool SetFunctionPointers(); + + bool SendRequestInner(const string& url, + long* http_status_code, + string* http_header_data, + string* http_response_data); + + void Reset(); + + bool CheckInit(); + + bool init_ok_; // Whether init succeeded + void* curl_lib_; // Pointer to result of dlopen() on + // curl library + string last_curl_error_; // The text of the last error when + // dealing + // with CURL. + + CURL *curl_; // Pointer for handle for CURL calls. + + CURL* (*easy_init_)(void); + + // Stateful pointers for calling into curl_formadd() + struct curl_httppost *formpost_; + struct curl_httppost *lastptr_; + struct curl_slist *headerlist_; + + // Function pointers into CURL library + CURLcode (*easy_setopt_)(CURL *, CURLoption, ...); + CURLFORMcode (*formadd_)(struct curl_httppost **, + struct curl_httppost **, ...); + struct curl_slist* (*slist_append_)(struct curl_slist *, const char *); + void (*slist_free_all_)(struct curl_slist *); + CURLcode (*easy_perform_)(CURL *); + const char* (*easy_strerror_)(CURLcode); + void (*easy_cleanup_)(CURL *); + CURLcode (*easy_getinfo_)(CURL *, CURLINFO info, ...); + void (*easy_reset_)(CURL*); + void (*formfree_)(struct curl_httppost *); + +}; +} + +#endif // COMMON_LINUX_LIBCURL_WRAPPER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.cc new file mode 100644 index 0000000000..79281a9043 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.cc @@ -0,0 +1,248 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// This source file provides replacements for libc functions that we need. If +// we call the libc functions directly we risk crashing in the dynamic linker +// as it tries to resolve uncached PLT entries. + +#include "common/linux/linux_libc_support.h" + +#include <stddef.h> + +extern "C" { + +size_t my_strlen(const char* s) { + size_t len = 0; + while (*s++) len++; + return len; +} + +int my_strcmp(const char* a, const char* b) { + for (;;) { + if (*a < *b) + return -1; + else if (*a > *b) + return 1; + else if (*a == 0) + return 0; + a++; + b++; + } +} + +int my_strncmp(const char* a, const char* b, size_t len) { + for (size_t i = 0; i < len; ++i) { + if (*a < *b) + return -1; + else if (*a > *b) + return 1; + else if (*a == 0) + return 0; + a++; + b++; + } + + return 0; +} + +// Parse a non-negative integer. +// result: (output) the resulting non-negative integer +// s: a NUL terminated string +// Return true iff successful. +bool my_strtoui(int* result, const char* s) { + if (*s == 0) + return false; + int r = 0; + for (;; s++) { + if (*s == 0) + break; + const int old_r = r; + r *= 10; + if (*s < '0' || *s > '9') + return false; + r += *s - '0'; + if (r < old_r) + return false; + } + + *result = r; + return true; +} + +// Return the length of the given unsigned integer when expressed in base 10. +unsigned my_uint_len(uintmax_t i) { + if (!i) + return 1; + + int len = 0; + while (i) { + len++; + i /= 10; + } + + return len; +} + +// Convert an unsigned integer to a string +// output: (output) the resulting string is written here. This buffer must be +// large enough to hold the resulting string. Call |my_uint_len| to get the +// required length. +// i: the unsigned integer to serialise. +// i_len: the length of the integer in base 10 (see |my_uint_len|). +void my_uitos(char* output, uintmax_t i, unsigned i_len) { + for (unsigned index = i_len; index; --index, i /= 10) + output[index - 1] = '0' + (i % 10); +} + +const char* my_strchr(const char* haystack, char needle) { + while (*haystack && *haystack != needle) + haystack++; + if (*haystack == needle) + return haystack; + return (const char*) 0; +} + +const char* my_strrchr(const char* haystack, char needle) { + const char* ret = NULL; + while (*haystack) { + if (*haystack == needle) + ret = haystack; + haystack++; + } + return ret; +} + +const char* my_strstr(const char* haystack, const char* needle) { + while (*haystack != 0) { + if((*haystack == *needle) && + (my_strncmp(haystack, needle, my_strlen(needle)) == 0)) { + return haystack; + } + haystack++; + } + return nullptr; +} + +void* my_memchr(const void* src, int needle, size_t src_len) { + const unsigned char* p = (const unsigned char*)src; + const unsigned char* p_end = p + src_len; + for (; p < p_end; ++p) { + if (*p == needle) + return (void*)p; + } + return NULL; +} + +// Read a hex value +// result: (output) the resulting value +// s: a string +// Returns a pointer to the first invalid charactor. +const char* my_read_hex_ptr(uintptr_t* result, const char* s) { + uintptr_t r = 0; + + for (;; ++s) { + if (*s >= '0' && *s <= '9') { + r <<= 4; + r += *s - '0'; + } else if (*s >= 'a' && *s <= 'f') { + r <<= 4; + r += (*s - 'a') + 10; + } else if (*s >= 'A' && *s <= 'F') { + r <<= 4; + r += (*s - 'A') + 10; + } else { + break; + } + } + + *result = r; + return s; +} + +const char* my_read_decimal_ptr(uintptr_t* result, const char* s) { + uintptr_t r = 0; + + for (;; ++s) { + if (*s >= '0' && *s <= '9') { + r *= 10; + r += *s - '0'; + } else { + break; + } + } + *result = r; + return s; +} + +void my_memset(void* ip, char c, size_t len) { + char* p = (char *) ip; + while (len--) + *p++ = c; +} + +size_t my_strlcpy(char* s1, const char* s2, size_t len) { + size_t pos1 = 0; + size_t pos2 = 0; + + while (s2[pos2] != '\0') { + if (pos1 + 1 < len) { + s1[pos1] = s2[pos2]; + pos1++; + } + pos2++; + } + if (len > 0) + s1[pos1] = '\0'; + + return pos2; +} + +size_t my_strlcat(char* s1, const char* s2, size_t len) { + size_t pos1 = 0; + + while (pos1 < len && s1[pos1] != '\0') + pos1++; + + if (pos1 == len) + return pos1; + + return pos1 + my_strlcpy(s1 + pos1, s2, len - pos1); +} + +int my_isspace(int ch) { + // Matches the C locale. + const char spaces[] = " \t\f\n\r\t\v"; + for (size_t i = 0; i < sizeof(spaces); i++) { + if (ch == spaces[i]) + return 1; + } + return 0; +} + +} // extern "C" diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h new file mode 100644 index 0000000000..03e3aa0657 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h @@ -0,0 +1,98 @@ +// Copyright (c) 2009, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// This header provides replacements for libc functions that we need. We if +// call the libc functions directly we risk crashing in the dynamic linker as +// it tries to resolve uncached PLT entries. + +#ifndef CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_ +#define CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_ + +#include <stdint.h> +#include <limits.h> +#include <sys/types.h> + +extern "C" { + +extern size_t my_strlen(const char* s); + +extern int my_strcmp(const char* a, const char* b); + +extern int my_strncmp(const char* a, const char* b, size_t len); + +// Parse a non-negative integer. +// result: (output) the resulting non-negative integer +// s: a NUL terminated string +// Return true iff successful. +extern bool my_strtoui(int* result, const char* s); + +// Return the length of the given unsigned integer when expressed in base 10. +extern unsigned my_uint_len(uintmax_t i); + +// Convert an unsigned integer to a string +// output: (output) the resulting string is written here. This buffer must be +// large enough to hold the resulting string. Call |my_uint_len| to get the +// required length. +// i: the unsigned integer to serialise. +// i_len: the length of the integer in base 10 (see |my_uint_len|). +extern void my_uitos(char* output, uintmax_t i, unsigned i_len); + +extern const char* my_strchr(const char* haystack, char needle); + +extern const char* my_strrchr(const char* haystack, char needle); + +extern const char *my_strstr(const char *haystack, const char *needle); + +// Read a hex value +// result: (output) the resulting value +// s: a string +// Returns a pointer to the first invalid charactor. +extern const char* my_read_hex_ptr(uintptr_t* result, const char* s); + +extern const char* my_read_decimal_ptr(uintptr_t* result, const char* s); + +extern void my_memset(void* ip, char c, size_t len); + +extern void* my_memchr(const void* src, int c, size_t len); + +// The following are considered safe to use in a compromised environment. +// Besides, this gives the compiler an opportunity to optimize their calls. +#define my_memcpy memcpy +#define my_memmove memmove +#define my_memcmp memcmp + +extern size_t my_strlcpy(char* s1, const char* s2, size_t len); + +extern size_t my_strlcat(char* s1, const char* s2, size_t len); + +extern int my_isspace(int ch); + +} // extern "C" + +#endif // CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc new file mode 100644 index 0000000000..adadfed44d --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc @@ -0,0 +1,213 @@ +// Copyright (c) 2009, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include "breakpad_googletest_includes.h" +#include "common/linux/linux_libc_support.h" + +namespace { +typedef testing::Test LinuxLibcSupportTest; +} + +TEST(LinuxLibcSupportTest, strlen) { + static const char* test_data[] = { "", "a", "aa", "aaa", "aabc", NULL }; + for (unsigned i = 0; ; ++i) { + if (!test_data[i]) + break; + ASSERT_EQ(strlen(test_data[i]), my_strlen(test_data[i])); + } +} + +TEST(LinuxLibcSupportTest, strcmp) { + static const char* test_data[] = { + "", "", + "a", "", + "", "a", + "a", "b", + "a", "a", + "ab", "aa", + "abc", "ab", + "abc", "abc", + NULL, + }; + + for (unsigned i = 0; ; ++i) { + if (!test_data[i*2]) + break; + int libc_result = strcmp(test_data[i*2], test_data[i*2 + 1]); + if (libc_result > 1) + libc_result = 1; + else if (libc_result < -1) + libc_result = -1; + ASSERT_EQ(my_strcmp(test_data[i*2], test_data[i*2 + 1]), libc_result); + } +} + +TEST(LinuxLibcSupportTest, strtoui) { + int result; + + ASSERT_FALSE(my_strtoui(&result, "")); + ASSERT_FALSE(my_strtoui(&result, "-1")); + ASSERT_FALSE(my_strtoui(&result, "-")); + ASSERT_FALSE(my_strtoui(&result, "a")); + ASSERT_FALSE(my_strtoui(&result, "23472893472938472987987398472398")); + + ASSERT_TRUE(my_strtoui(&result, "0")); + ASSERT_EQ(result, 0); + ASSERT_TRUE(my_strtoui(&result, "1")); + ASSERT_EQ(result, 1); + ASSERT_TRUE(my_strtoui(&result, "12")); + ASSERT_EQ(result, 12); + ASSERT_TRUE(my_strtoui(&result, "123")); + ASSERT_EQ(result, 123); + ASSERT_TRUE(my_strtoui(&result, "0123")); + ASSERT_EQ(result, 123); +} + +TEST(LinuxLibcSupportTest, uint_len) { + ASSERT_EQ(my_uint_len(0), 1U); + ASSERT_EQ(my_uint_len(2), 1U); + ASSERT_EQ(my_uint_len(5), 1U); + ASSERT_EQ(my_uint_len(9), 1U); + ASSERT_EQ(my_uint_len(10), 2U); + ASSERT_EQ(my_uint_len(99), 2U); + ASSERT_EQ(my_uint_len(100), 3U); + ASSERT_EQ(my_uint_len(101), 3U); + ASSERT_EQ(my_uint_len(1000), 4U); + // 0xFFFFFFFFFFFFFFFF + ASSERT_EQ(my_uint_len(18446744073709551615LLU), 20U); +} + +TEST(LinuxLibcSupportTest, uitos) { + char buf[32]; + + my_uitos(buf, 0, 1); + ASSERT_EQ(0, memcmp(buf, "0", 1)); + + my_uitos(buf, 1, 1); + ASSERT_EQ(0, memcmp(buf, "1", 1)); + + my_uitos(buf, 10, 2); + ASSERT_EQ(0, memcmp(buf, "10", 2)); + + my_uitos(buf, 63, 2); + ASSERT_EQ(0, memcmp(buf, "63", 2)); + + my_uitos(buf, 101, 3); + ASSERT_EQ(0, memcmp(buf, "101", 2)); + + // 0xFFFFFFFFFFFFFFFF + my_uitos(buf, 18446744073709551615LLU, 20); + ASSERT_EQ(0, memcmp(buf, "18446744073709551615", 20)); +} + +TEST(LinuxLibcSupportTest, strchr) { + ASSERT_EQ(NULL, my_strchr("abc", 'd')); + ASSERT_EQ(NULL, my_strchr("", 'd')); + ASSERT_EQ(NULL, my_strchr("efghi", 'd')); + + ASSERT_TRUE(my_strchr("a", 'a')); + ASSERT_TRUE(my_strchr("abc", 'a')); + ASSERT_TRUE(my_strchr("bcda", 'a')); + ASSERT_TRUE(my_strchr("sdfasdf", 'a')); + + static const char abc3[] = "abcabcabc"; + ASSERT_EQ(abc3, my_strchr(abc3, 'a')); +} + +TEST(LinuxLibcSupportTest, strrchr) { + ASSERT_EQ(NULL, my_strrchr("abc", 'd')); + ASSERT_EQ(NULL, my_strrchr("", 'd')); + ASSERT_EQ(NULL, my_strrchr("efghi", 'd')); + + ASSERT_TRUE(my_strrchr("a", 'a')); + ASSERT_TRUE(my_strrchr("abc", 'a')); + ASSERT_TRUE(my_strrchr("bcda", 'a')); + ASSERT_TRUE(my_strrchr("sdfasdf", 'a')); + + static const char abc3[] = "abcabcabc"; + ASSERT_EQ(abc3 + 6, my_strrchr(abc3, 'a')); +} + +TEST(LinuxLibcSupportTest, memchr) { + ASSERT_EQ(NULL, my_memchr("abc", 'd', 3)); + ASSERT_EQ(NULL, my_memchr("abcd", 'd', 3)); + ASSERT_EQ(NULL, my_memchr("a", 'a', 0)); + + static const char abc3[] = "abcabcabc"; + ASSERT_EQ(abc3, my_memchr(abc3, 'a', 3)); + ASSERT_EQ(abc3, my_memchr(abc3, 'a', 9)); + ASSERT_EQ(abc3+1, my_memchr(abc3, 'b', 9)); + ASSERT_EQ(abc3+2, my_memchr(abc3, 'c', 9)); +} + +TEST(LinuxLibcSupportTest, read_hex_ptr) { + uintptr_t result; + const char* last; + + last = my_read_hex_ptr(&result, ""); + ASSERT_EQ(result, 0U); + ASSERT_EQ(*last, 0); + + last = my_read_hex_ptr(&result, "0"); + ASSERT_EQ(result, 0U); + ASSERT_EQ(*last, 0); + + last = my_read_hex_ptr(&result, "0123"); + ASSERT_EQ(result, 0x123U); + ASSERT_EQ(*last, 0); + + last = my_read_hex_ptr(&result, "0123a"); + ASSERT_EQ(result, 0x123aU); + ASSERT_EQ(*last, 0); + + last = my_read_hex_ptr(&result, "0123a-"); + ASSERT_EQ(result, 0x123aU); + ASSERT_EQ(*last, '-'); +} + +TEST(LinuxLibcSupportTest, read_decimal_ptr) { + uintptr_t result; + const char* last; + + last = my_read_decimal_ptr(&result, "0"); + ASSERT_EQ(result, 0U); + ASSERT_EQ(*last, 0); + + last = my_read_decimal_ptr(&result, "0123"); + ASSERT_EQ(result, 123U); + ASSERT_EQ(*last, 0); + + last = my_read_decimal_ptr(&result, "1234"); + ASSERT_EQ(result, 1234U); + ASSERT_EQ(*last, 0); + + last = my_read_decimal_ptr(&result, "01234-"); + ASSERT_EQ(result, 1234U); + ASSERT_EQ(*last, '-'); +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.cc new file mode 100644 index 0000000000..4e938269f2 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.cc @@ -0,0 +1,107 @@ +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// memory_mapped_file.cc: Implement google_breakpad::MemoryMappedFile. +// See memory_mapped_file.h for details. + +#include "common/linux/memory_mapped_file.h" + +#include <fcntl.h> +#include <sys/mman.h> +#if defined(__ANDROID__) +#include <sys/stat.h> +#endif +#include <unistd.h> + +#include "common/memory_range.h" +#include "third_party/lss/linux_syscall_support.h" + +namespace google_breakpad { + +MemoryMappedFile::MemoryMappedFile() {} + +MemoryMappedFile::MemoryMappedFile(const char* path, size_t offset) { + Map(path, offset); +} + +MemoryMappedFile::~MemoryMappedFile() { + Unmap(); +} + +#include <unistd.h> + +bool MemoryMappedFile::Map(const char* path, size_t offset) { + Unmap(); + + int fd = sys_open(path, O_RDONLY, 0); + if (fd == -1) { + return false; + } + +#if defined(__x86_64__) || defined(__aarch64__) || \ + (defined(__mips__) && _MIPS_SIM == _ABI64) + + struct kernel_stat st; + if (sys_fstat(fd, &st) == -1 || st.st_size < 0) { +#else + struct kernel_stat64 st; + if (sys_fstat64(fd, &st) == -1 || st.st_size < 0) { +#endif + sys_close(fd); + return false; + } + + // Strangely file size can be negative, but we check above that it is not. + size_t file_len = static_cast<size_t>(st.st_size); + // If the file does not extend beyond the offset, simply use an empty + // MemoryRange and return true. Don't bother to call mmap() + // even though mmap() can handle an empty file on some platforms. + if (offset >= file_len) { + sys_close(fd); + return true; + } + + void* data = sys_mmap(NULL, file_len, PROT_READ, MAP_PRIVATE, fd, offset); + sys_close(fd); + if (data == MAP_FAILED) { + return false; + } + + content_.Set(data, file_len - offset); + return true; +} + +void MemoryMappedFile::Unmap() { + if (content_.data()) { + sys_munmap(const_cast<uint8_t*>(content_.data()), content_.length()); + content_.Set(NULL, 0); + } +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.h b/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.h new file mode 100644 index 0000000000..fa660cc91a --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.h @@ -0,0 +1,87 @@ +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// memory_mapped_file.h: Define the google_breakpad::MemoryMappedFile +// class, which maps a file into memory for read-only access. + +#ifndef COMMON_LINUX_MEMORY_MAPPED_FILE_H_ +#define COMMON_LINUX_MEMORY_MAPPED_FILE_H_ + +#include <stddef.h> +#include "common/basictypes.h" +#include "common/memory_range.h" + +namespace google_breakpad { + +// A utility class for mapping a file into memory for read-only access of +// the file content. Its implementation avoids calling into libc functions +// by directly making system calls for open, close, mmap, and munmap. +class MemoryMappedFile { + public: + MemoryMappedFile(); + + // Constructor that calls Map() to map a file at |path| into memory. + // If Map() fails, the object behaves as if it is default constructed. + MemoryMappedFile(const char* path, size_t offset); + + ~MemoryMappedFile(); + + // Maps a file at |path| into memory, which can then be accessed via + // content() as a MemoryRange object or via data(), and returns true on + // success. Mapping an empty file will succeed but with data() and size() + // returning NULL and 0, respectively. An existing mapping is unmapped + // before a new mapping is created. + bool Map(const char* path, size_t offset); + + // Unmaps the memory for the mapped file. It's a no-op if no file is + // mapped. + void Unmap(); + + // Returns a MemoryRange object that covers the memory for the mapped + // file. The MemoryRange object is empty if no file is mapped. + const MemoryRange& content() const { return content_; } + + // Returns a pointer to the beginning of the memory for the mapped file. + // or NULL if no file is mapped or the mapped file is empty. + const void* data() const { return content_.data(); } + + // Returns the size in bytes of the mapped file, or zero if no file + // is mapped. + size_t size() const { return content_.length(); } + + private: + // Mapped file content as a MemoryRange object. + MemoryRange content_; + + DISALLOW_COPY_AND_ASSIGN(MemoryMappedFile); +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_MEMORY_MAPPED_FILE_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file_unittest.cc new file mode 100644 index 0000000000..fad59f40cd --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file_unittest.cc @@ -0,0 +1,208 @@ +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// memory_mapped_file_unittest.cc: +// Unit tests for google_breakpad::MemoryMappedFile. + +#include <fcntl.h> +#include <string.h> +#include <unistd.h> + +#include <string> + +#include "breakpad_googletest_includes.h" +#include "common/linux/memory_mapped_file.h" +#include "common/tests/auto_tempdir.h" +#include "common/tests/file_utils.h" +#include "common/using_std_string.h" + +using google_breakpad::AutoTempDir; +using google_breakpad::MemoryMappedFile; +using google_breakpad::WriteFile; + +namespace { + +class MemoryMappedFileTest : public testing::Test { + protected: + void ExpectNoMappedData(const MemoryMappedFile& mapped_file) { + EXPECT_TRUE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() == NULL); + EXPECT_EQ(0U, mapped_file.size()); + } +}; + +} // namespace + +TEST_F(MemoryMappedFileTest, DefaultConstructor) { + MemoryMappedFile mapped_file; + ExpectNoMappedData(mapped_file); +} + +TEST_F(MemoryMappedFileTest, UnmapWithoutMap) { + MemoryMappedFile mapped_file; + mapped_file.Unmap(); +} + +TEST_F(MemoryMappedFileTest, MapNonexistentFile) { + { + MemoryMappedFile mapped_file("nonexistent-file", 0); + ExpectNoMappedData(mapped_file); + } + { + MemoryMappedFile mapped_file; + EXPECT_FALSE(mapped_file.Map("nonexistent-file", 0)); + ExpectNoMappedData(mapped_file); + } +} + +TEST_F(MemoryMappedFileTest, MapEmptyFile) { + AutoTempDir temp_dir; + string test_file = temp_dir.path() + "/empty_file"; + ASSERT_TRUE(WriteFile(test_file.c_str(), NULL, 0)); + + { + MemoryMappedFile mapped_file(test_file.c_str(), 0); + ExpectNoMappedData(mapped_file); + } + { + MemoryMappedFile mapped_file; + EXPECT_TRUE(mapped_file.Map(test_file.c_str(), 0)); + ExpectNoMappedData(mapped_file); + } +} + +TEST_F(MemoryMappedFileTest, MapNonEmptyFile) { + char data[256]; + size_t data_size = sizeof(data); + for (size_t i = 0; i < data_size; ++i) { + data[i] = i; + } + + AutoTempDir temp_dir; + string test_file = temp_dir.path() + "/test_file"; + ASSERT_TRUE(WriteFile(test_file.c_str(), data, data_size)); + + { + MemoryMappedFile mapped_file(test_file.c_str(), 0); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data, mapped_file.data(), data_size)); + } + { + MemoryMappedFile mapped_file; + EXPECT_TRUE(mapped_file.Map(test_file.c_str(), 0)); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data, mapped_file.data(), data_size)); + } +} + +TEST_F(MemoryMappedFileTest, RemapAfterMap) { + char data1[256]; + size_t data1_size = sizeof(data1); + for (size_t i = 0; i < data1_size; ++i) { + data1[i] = i; + } + + char data2[50]; + size_t data2_size = sizeof(data2); + for (size_t i = 0; i < data2_size; ++i) { + data2[i] = 255 - i; + } + + AutoTempDir temp_dir; + string test_file1 = temp_dir.path() + "/test_file1"; + string test_file2 = temp_dir.path() + "/test_file2"; + ASSERT_TRUE(WriteFile(test_file1.c_str(), data1, data1_size)); + ASSERT_TRUE(WriteFile(test_file2.c_str(), data2, data2_size)); + + { + MemoryMappedFile mapped_file(test_file1.c_str(), 0); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data1_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data1, mapped_file.data(), data1_size)); + + mapped_file.Map(test_file2.c_str(), 0); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data2_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data2, mapped_file.data(), data2_size)); + } + { + MemoryMappedFile mapped_file; + EXPECT_TRUE(mapped_file.Map(test_file1.c_str(), 0)); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data1_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data1, mapped_file.data(), data1_size)); + + mapped_file.Map(test_file2.c_str(), 0); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data2_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data2, mapped_file.data(), data2_size)); + } +} + +TEST_F(MemoryMappedFileTest, MapWithOffset) { + // Put more data in the test file this time. Offsets can only be + // done on page boundaries, so we need a two page file to test this. + const int page_size = 4096; + char data1[2 * page_size]; + size_t data1_size = sizeof(data1); + for (size_t i = 0; i < data1_size; ++i) { + data1[i] = i & 0x7f; + } + + AutoTempDir temp_dir; + string test_file1 = temp_dir.path() + "/test_file1"; + ASSERT_TRUE(WriteFile(test_file1.c_str(), data1, data1_size)); + { + MemoryMappedFile mapped_file(test_file1.c_str(), page_size); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data1_size - page_size, mapped_file.size()); + EXPECT_EQ( + 0, + memcmp(data1 + page_size, mapped_file.data(), data1_size - page_size)); + } + { + MemoryMappedFile mapped_file; + mapped_file.Map(test_file1.c_str(), page_size); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data1_size - page_size, mapped_file.size()); + EXPECT_EQ( + 0, + memcmp(data1 + page_size, mapped_file.data(), data1_size - page_size)); + } +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/moz.build b/toolkit/crashreporter/google-breakpad/src/common/linux/moz.build new file mode 100644 index 0000000000..ecd1e5ffe7 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/moz.build @@ -0,0 +1,42 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +UNIFIED_SOURCES += [ + 'elfutils.cc', + 'linux_libc_support.cc', + 'memory_mapped_file.cc', + 'safe_readlink.cc', +] + +# file_id.cc cannot be built in unified mode because it uses a custom DISABLE_STL_WRAPPING +SOURCES += [ + 'file_id.cc', +] + +if CONFIG['OS_TARGET'] != 'Android': + UNIFIED_SOURCES += [ + 'http_upload.cc', + ] + +if CONFIG['OS_TARGET'] == 'Android': + DEFINES['ANDROID_NDK_MAJOR_VERSION'] = CONFIG['ANDROID_NDK_MAJOR_VERSION'] + DEFINES['ANDROID_NDK_MINOR_VERSION'] = CONFIG['ANDROID_NDK_MINOR_VERSION'] + COMPILE_FLAGS['OS_INCLUDES'] += [ + '-I%s/toolkit/crashreporter/google-breakpad/src/common/android/include' % TOPSRCDIR, + ] + +if not CONFIG['HAVE_GETCONTEXT']: + # We don't support unifying assembly files. + SOURCES += [ + 'breakpad_getcontext.S', + ] + +Library('breakpad_linux_common_s') + +include('/toolkit/crashreporter/crashreporter.mozbuild') + +if CONFIG['CC_TYPE'] in ('clang', 'gcc'): + CXXFLAGS += ['-Wno-error=stack-protector'] diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink.cc new file mode 100644 index 0000000000..870c28af3b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink.cc @@ -0,0 +1,53 @@ +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// safe_readlink.cc: Implement google_breakpad::SafeReadLink. +// See safe_readlink.h for details. + +#include <stddef.h> + +#include "third_party/lss/linux_syscall_support.h" + +namespace google_breakpad { + +bool SafeReadLink(const char* path, char* buffer, size_t buffer_size) { + // sys_readlink() does not add a NULL byte to |buffer|. In order to return + // a NULL-terminated string in |buffer|, |buffer_size| should be at least + // one byte longer than the expected path length. Also, sys_readlink() + // returns the actual path length on success, which does not count the + // NULL byte, so |result_size| should be less than |buffer_size|. + ssize_t result_size = sys_readlink(path, buffer, buffer_size); + if (result_size >= 0 && static_cast<size_t>(result_size) < buffer_size) { + buffer[result_size] = '\0'; + return true; + } + return false; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink.h b/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink.h new file mode 100644 index 0000000000..4ae131b580 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink.h @@ -0,0 +1,65 @@ +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// safe_readlink.h: Define the google_breakpad::SafeReadLink function, +// which wraps sys_readlink and gurantees the result is NULL-terminated. + +#ifndef COMMON_LINUX_SAFE_READLINK_H_ +#define COMMON_LINUX_SAFE_READLINK_H_ + +#include <stddef.h> + +namespace google_breakpad { + +// This function wraps sys_readlink() and performs the same functionalty, +// but guarantees |buffer| is NULL-terminated if sys_readlink() returns +// no error. It takes the same arguments as sys_readlink(), but unlike +// sys_readlink(), it returns true on success. +// +// |buffer_size| specifies the size of |buffer| in bytes. As this function +// always NULL-terminates |buffer| on success, |buffer_size| should be +// at least one byte longer than the expected path length (e.g. PATH_MAX, +// which is typically defined as the maximum length of a path name +// including the NULL byte). +// +// The implementation of this function calls sys_readlink() instead of +// readlink(), it can thus be used in the context where calling to libc +// functions is discouraged. +bool SafeReadLink(const char* path, char* buffer, size_t buffer_size); + +// Same as the three-argument version of SafeReadLink() but deduces the +// size of |buffer| if it is a char array of known size. +template <size_t N> +bool SafeReadLink(const char* path, char (&buffer)[N]) { + return SafeReadLink(path, buffer, sizeof(buffer)); +} + +} // namespace google_breakpad + +#endif // COMMON_LINUX_SAFE_READLINK_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink_unittest.cc new file mode 100644 index 0000000000..d346b2a807 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink_unittest.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// safe_readlink_unittest.cc: Unit tests for google_breakpad::SafeReadLink. + +#include "breakpad_googletest_includes.h" +#include "common/linux/safe_readlink.h" + +using google_breakpad::SafeReadLink; + +TEST(SafeReadLinkTest, ZeroBufferSize) { + char buffer[1]; + EXPECT_FALSE(SafeReadLink("/proc/self/exe", buffer, 0)); +} + +TEST(SafeReadLinkTest, BufferSizeTooSmall) { + char buffer[1]; + EXPECT_FALSE(SafeReadLink("/proc/self/exe", buffer, 1)); +} + +TEST(SafeReadLinkTest, BoundaryBufferSize) { + char buffer[PATH_MAX]; + EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer, sizeof(buffer))); + size_t path_length = strlen(buffer); + EXPECT_LT(0U, path_length); + EXPECT_GT(sizeof(buffer), path_length); + + // Buffer size equals to the expected path length plus 1 for the NULL byte. + char buffer2[PATH_MAX]; + EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer2, path_length + 1)); + EXPECT_EQ(path_length, strlen(buffer2)); + EXPECT_EQ(0, strncmp(buffer, buffer2, PATH_MAX)); + + // Buffer size equals to the expected path length. + EXPECT_FALSE(SafeReadLink("/proc/self/exe", buffer, path_length)); +} + +TEST(SafeReadLinkTest, NonexistentPath) { + char buffer[PATH_MAX]; + EXPECT_FALSE(SafeReadLink("nonexistent_path", buffer, sizeof(buffer))); +} + +TEST(SafeReadLinkTest, NonSymbolicLinkPath) { + char actual_path[PATH_MAX]; + EXPECT_TRUE(SafeReadLink("/proc/self/exe", actual_path, sizeof(actual_path))); + + char buffer[PATH_MAX]; + EXPECT_FALSE(SafeReadLink(actual_path, buffer, sizeof(buffer))); +} + +TEST(SafeReadLinkTest, DeduceBufferSizeFromCharArray) { + char buffer[PATH_MAX]; + char* buffer_pointer = buffer; + EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer_pointer, sizeof(buffer))); + size_t path_length = strlen(buffer); + + // Use the template version of SafeReadLink to deduce the buffer size + // from the char array. + char buffer2[PATH_MAX]; + EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer2)); + EXPECT_EQ(path_length, strlen(buffer2)); + EXPECT_EQ(0, strncmp(buffer, buffer2, PATH_MAX)); +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_collector_client.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_collector_client.cc new file mode 100644 index 0000000000..92b25ddba9 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_collector_client.cc @@ -0,0 +1,195 @@ +// Copyright (c) 2019 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include "common/linux/symbol_collector_client.h" + +#include <stdio.h> + +#include <iostream> +#include <regex> + +#include "common/linux/libcurl_wrapper.h" + +namespace google_breakpad { +namespace sym_upload { + +// static +bool SymbolCollectorClient::CreateUploadUrl( + LibcurlWrapper* libcurl_wrapper, + const string& api_url, + const string& api_key, + UploadUrlResponse* uploadUrlResponse) { + string header, response; + long response_code; + + string url = api_url + "/v1/uploads:create"; + if (!api_key.empty()) { + url += "?key=" + api_key; + } + + if (!libcurl_wrapper->SendSimplePostRequest(url, + /*body=*/"", + /*content_type=*/"", + &response_code, + &header, + &response)) { + printf("Failed to create upload url.\n"); + printf("Response code: %ld\n", response_code); + printf("Response:\n"); + printf("%s\n", response.c_str()); + return false; + } + + // Note camel-case rather than underscores. + std::regex upload_url_regex("\"uploadUrl\": \"([^\"]+)\""); + std::regex upload_key_regex("\"uploadKey\": \"([^\"]+)\""); + + std::smatch upload_url_match; + if (!std::regex_search(response, upload_url_match, upload_url_regex) || + upload_url_match.size() != 2) { + printf("Failed to parse create url response."); + printf("Response:\n"); + printf("%s\n", response.c_str()); + return false; + } + string upload_url = upload_url_match[1].str(); + + std::smatch upload_key_match; + if (!std::regex_search(response, upload_key_match, upload_key_regex) || + upload_key_match.size() != 2) { + printf("Failed to parse create url response."); + printf("Response:\n"); + printf("%s\n", response.c_str()); + return false; + } + string upload_key = upload_key_match[1].str(); + + uploadUrlResponse->upload_url = upload_url; + uploadUrlResponse->upload_key = upload_key; + return true; +} + +// static +CompleteUploadResult SymbolCollectorClient::CompleteUpload( + LibcurlWrapper* libcurl_wrapper, + const string& api_url, + const string& api_key, + const string& upload_key, + const string& debug_file, + const string& debug_id, + const string& type) { + string header, response; + long response_code; + + string url = api_url + "/v1/uploads/" + upload_key + ":complete"; + if (!api_key.empty()) { + url += "?key=" + api_key; + } + string body = + "{ symbol_id: {" + "debug_file: \"" + debug_file + "\", " + "debug_id: \"" + debug_id + "\" }, " + "symbol_upload_type: \"" + type + "\" }"; + + if (!libcurl_wrapper->SendSimplePostRequest(url, + body, + "application/son", + &response_code, + &header, + &response)) { + printf("Failed to complete upload.\n"); + printf("Response code: %ld\n", response_code); + printf("Response:\n"); + printf("%s\n", response.c_str()); + return CompleteUploadResult::Error; + } + + std::regex result_regex("\"result\": \"([^\"]+)\""); + std::smatch result_match; + if (!std::regex_search(response, result_match, result_regex) || + result_match.size() != 2) { + printf("Failed to parse complete upload response."); + printf("Response:\n"); + printf("%s\n", response.c_str()); + return CompleteUploadResult::Error; + } + string result = result_match[1].str(); + + if (result.compare("DUPLICATE_DATA") == 0) { + return CompleteUploadResult::DuplicateData; + } + + return CompleteUploadResult::Ok; +} + +// static +SymbolStatus SymbolCollectorClient::CheckSymbolStatus( + LibcurlWrapper* libcurl_wrapper, + const string& api_url, + const string& api_key, + const string& debug_file, + const string& debug_id) { + string header, response; + long response_code; + string url = api_url + + "/v1/symbols/" + debug_file + "/" + debug_id + ":checkStatus"; + if (!api_key.empty()) { + url += "?key=" + api_key; + } + + if (!libcurl_wrapper->SendGetRequest( + url, + &response_code, + &header, + &response)) { + printf("Failed to check symbol status, error message.\n"); + printf("Response code: %ld\n", response_code); + printf("Response:\n"); + printf("%s\n", response.c_str()); + return SymbolStatus::Unknown; + } + + std::regex status_regex("\"status\": \"([^\"]+)\""); + std::smatch status_match; + if (!std::regex_search(response, status_match, status_regex) || + status_match.size() != 2) { + printf("Failed to parse check symbol status response."); + printf("Response:\n"); + printf("%s\n", response.c_str()); + return SymbolStatus::Unknown; + } + string status = status_match[1].str(); + + return (status.compare("FOUND") == 0) ? + SymbolStatus::Found : + SymbolStatus::Missing; +} + +} // namespace sym_upload +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_collector_client.h b/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_collector_client.h new file mode 100644 index 0000000000..0e23242a2b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_collector_client.h @@ -0,0 +1,88 @@ +// Copyright (c) 2019, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef COMMON_LINUX_SYMBOL_COLLECTOR_CLIENT_H_ +#define COMMON_LINUX_SYMBOL_COLLECTOR_CLIENT_H_ + +#include <string> + +#include "common/linux/libcurl_wrapper.h" +#include "common/using_std_string.h" + +namespace google_breakpad { +namespace sym_upload { + +struct UploadUrlResponse { + string upload_url; + string upload_key; +}; + +enum SymbolStatus { + Found, + Missing, + Unknown +}; + +enum CompleteUploadResult { + Ok, + DuplicateData, + Error +}; + +// Helper class to communicate with a sym-upload-v2 service over HTTP/REST, +// via libcurl. +class SymbolCollectorClient { + public: + static bool CreateUploadUrl( + LibcurlWrapper* libcurl_wrapper, + const string& api_url, + const string& api_key, + UploadUrlResponse* uploadUrlResponse); + + static CompleteUploadResult CompleteUpload( + LibcurlWrapper* libcurl_wrapper, + const string& api_url, + const string& api_key, + const string& upload_key, + const string& debug_file, + const string& debug_id, + const string& type); + + static SymbolStatus CheckSymbolStatus( + LibcurlWrapper* libcurl_wrapper, + const string& api_url, + const string& api_key, + const string& debug_file, + const string& debug_id); +}; + +} // namespace sym_upload +} // namespace google_breakpad + +#endif // COMMON_LINUX_SYMBOL_COLLECTOR_CLIENT_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_upload.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_upload.cc new file mode 100644 index 0000000000..87741a0a21 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_upload.cc @@ -0,0 +1,284 @@ +// Copyright (c) 2011 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// symbol_upload.cc: implemented google_breakpad::sym_upload::Start, a helper +// function for linux symbol upload tool. + +#include "common/linux/symbol_upload.h" + +#include <assert.h> +#include <stdio.h> + +#include <functional> +#include <iostream> +#include <vector> + +#include "common/linux/http_upload.h" +#include "common/linux/libcurl_wrapper.h" +#include "common/linux/symbol_collector_client.h" + +namespace google_breakpad { +namespace sym_upload { + +void TokenizeByChar(const string &source_string, int c, + std::vector<string> *results) { + assert(results); + string::size_type cur_pos = 0, next_pos = 0; + while ((next_pos = source_string.find(c, cur_pos)) != string::npos) { + if (next_pos != cur_pos) + results->push_back(source_string.substr(cur_pos, next_pos - cur_pos)); + cur_pos = next_pos + 1; + } + if (cur_pos < source_string.size() && next_pos != cur_pos) + results->push_back(source_string.substr(cur_pos)); +} + +//============================================================================= +// Parse out the module line which have 5 parts. +// MODULE <os> <cpu> <uuid> <module-name> +bool ModuleDataForSymbolFile(const string &file, + std::vector<string> *module_parts) { + assert(module_parts); + const size_t kModulePartNumber = 5; + FILE* fp = fopen(file.c_str(), "r"); + if (fp) { + char buffer[1024]; + if (fgets(buffer, sizeof(buffer), fp)) { + string line(buffer); + string::size_type line_break_pos = line.find_first_of('\n'); + if (line_break_pos == string::npos) { + assert(0 && "The file is invalid!"); + fclose(fp); + return false; + } + line.resize(line_break_pos); + const char kDelimiter = ' '; + TokenizeByChar(line, kDelimiter, module_parts); + if (module_parts->size() != kModulePartNumber) + module_parts->clear(); + } + fclose(fp); + } + + return module_parts->size() == kModulePartNumber; +} + +//============================================================================= +string CompactIdentifier(const string &uuid) { + std::vector<string> components; + TokenizeByChar(uuid, '-', &components); + string result; + for (size_t i = 0; i < components.size(); ++i) + result += components[i]; + return result; +} + +// |options| describes the current sym_upload options. +// |module_parts| contains the strings parsed from the MODULE entry of the +// Breakpad symbol file being uploaded. +// |compacted_id| is the debug_id from the MODULE entry of the Breakpad symbol +// file being uploaded, with all hyphens removed. +bool SymUploadV1Start( + const Options& options, + std::vector<string> module_parts, + const string& compacted_id) { + std::map<string, string> parameters; + // Add parameters + if (!options.version.empty()) + parameters["version"] = options.version; + + // MODULE <os> <cpu> <uuid> <module-name> + // 0 1 2 3 4 + parameters["os"] = module_parts[1]; + parameters["cpu"] = module_parts[2]; + parameters["debug_file"] = module_parts[4]; + parameters["code_file"] = module_parts[4]; + parameters["debug_identifier"] = compacted_id; + + std::map<string, string> files; + files["symbol_file"] = options.symbolsPath; + + string response, error; + long response_code; + bool success = HTTPUpload::SendRequest(options.uploadURLStr, + parameters, + files, + options.proxy, + options.proxy_user_pwd, + /*ca_certificate_file=*/"", + &response, + &response_code, + &error); + + if (!success) { + printf("Failed to send symbol file: %s\n", error.c_str()); + printf("Response code: %ld\n", response_code); + printf("Response:\n"); + printf("%s\n", response.c_str()); + } else if (response_code == 0) { + printf("Failed to send symbol file: No response code\n"); + } else if (response_code != 200) { + printf("Failed to send symbol file: Response code %ld\n", response_code); + printf("Response:\n"); + printf("%s\n", response.c_str()); + } else { + printf("Successfully sent the symbol file.\n"); + } + + return success; +} + +// |options| describes the current sym_upload options. +// |code_id| is the basename of the module for which symbols are being +// uploaded. +// |debug_id| is the debug_id of the module for which symbols are being +// uploaded. +bool SymUploadV2Start( + const Options& options, + const string& code_file, + const string& debug_id, + const string& type) { + google_breakpad::LibcurlWrapper libcurl_wrapper; + if (!libcurl_wrapper.Init()) { + printf("Failed to init google_breakpad::LibcurlWrapper.\n"); + return false; + } + + if (!options.force) { + SymbolStatus symbolStatus = SymbolCollectorClient::CheckSymbolStatus( + &libcurl_wrapper, + options.uploadURLStr, + options.api_key, + code_file, + debug_id); + if (symbolStatus == SymbolStatus::Found) { + printf("Symbol file already exists, upload aborted." + " Use \"-f\" to overwrite.\n"); + return true; + } else if (symbolStatus == SymbolStatus::Unknown) { + printf("Failed to check for existing symbol.\n"); + return false; + } + } + + UploadUrlResponse uploadUrlResponse; + if (!SymbolCollectorClient::CreateUploadUrl( + &libcurl_wrapper, + options.uploadURLStr, + options.api_key, + &uploadUrlResponse)) { + printf("Failed to create upload URL.\n"); + return false; + } + + string signed_url = uploadUrlResponse.upload_url; + string upload_key = uploadUrlResponse.upload_key; + string header; + string response; + long response_code; + + if (!libcurl_wrapper.SendPutRequest(signed_url, + options.symbolsPath, + &response_code, + &header, + &response)) { + printf("Failed to send symbol file.\n"); + printf("Response code: %ld\n", response_code); + printf("Response:\n"); + printf("%s\n", response.c_str()); + return false; + } else if (response_code == 0) { + printf("Failed to send symbol file: No response code\n"); + return false; + } else if (response_code != 200) { + printf("Failed to send symbol file: Response code %ld\n", response_code); + printf("Response:\n"); + printf("%s\n", response.c_str()); + return false; + } + + CompleteUploadResult completeUploadResult = + SymbolCollectorClient::CompleteUpload(&libcurl_wrapper, + options.uploadURLStr, + options.api_key, + upload_key, + code_file, + debug_id, + type); + if (completeUploadResult == CompleteUploadResult::Error) { + printf("Failed to complete upload.\n"); + return false; + } else if (completeUploadResult == CompleteUploadResult::DuplicateData) { + printf("Uploaded file checksum matched existing file checksum," + " no change necessary.\n"); + } else { + printf("Successfully sent the symbol file.\n"); + } + + return true; +} + +//============================================================================= +void Start(Options* options) { + if (options->upload_protocol == UploadProtocol::SYM_UPLOAD_V2) { + string code_file; + string debug_id; + string type; + + if (options->type.empty() || options->type == kBreakpadSymbolType) { + // Breakpad upload so read these from input file. + std::vector<string> module_parts; + if (!ModuleDataForSymbolFile(options->symbolsPath, &module_parts)) { + fprintf(stderr, "Failed to parse symbol file!\n"); + return; + } + code_file = module_parts[4]; + debug_id = CompactIdentifier(module_parts[3]); + type = kBreakpadSymbolType; + } else { + // Native upload so these must be explicitly set. + code_file = options->code_file; + debug_id = options->debug_id; + type = options->type; + } + + options->success = SymUploadV2Start(*options, code_file, debug_id, type); + } else { + std::vector<string> module_parts; + if (!ModuleDataForSymbolFile(options->symbolsPath, &module_parts)) { + fprintf(stderr, "Failed to parse symbol file!\n"); + return; + } + const string compacted_id = CompactIdentifier(module_parts[3]); + options->success = SymUploadV1Start(*options, module_parts, compacted_id); + } +} + +} // namespace sym_upload +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_upload.h b/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_upload.h new file mode 100644 index 0000000000..9033152bf2 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_upload.h @@ -0,0 +1,76 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2011 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// symbol_upload.h: helper functions for linux symbol upload tool. + +#ifndef COMMON_LINUX_SYMBOL_UPLOAD_H_ +#define COMMON_LINUX_SYMBOL_UPLOAD_H_ + +#include <string> + +#include "common/using_std_string.h" + +namespace google_breakpad { +namespace sym_upload { + +enum class UploadProtocol { + SYM_UPLOAD_V1, + SYM_UPLOAD_V2, +}; + +constexpr char kBreakpadSymbolType[] = "BREAKPAD"; + +struct Options { + Options() : upload_protocol(UploadProtocol::SYM_UPLOAD_V1), force(false) {} + + string symbolsPath; + string uploadURLStr; + string proxy; + string proxy_user_pwd; + string version; + bool success; + UploadProtocol upload_protocol; + bool force; + string api_key; + + // These only need to be set for native symbol uploads. + string code_file; + string debug_id; + string type; +}; + +// Starts upload to symbol server with options. +void Start(Options* options); + +} // namespace sym_upload +} // namespace google_breakpad + +#endif // COMMON_LINUX_SYMBOL_UPLOAD_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf.cc new file mode 100644 index 0000000000..98e81dab75 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf.cc @@ -0,0 +1,263 @@ +#include "common/linux/synth_elf.h" + +#include <assert.h> +#include <elf.h> +#include <stdio.h> +#include <string.h> + +#include "common/linux/elf_gnu_compat.h" +#include "common/using_std_string.h" + +namespace google_breakpad { +namespace synth_elf { + +ELF::ELF(uint16_t machine, + uint8_t file_class, + Endianness endianness) + : Section(endianness), + addr_size_(file_class == ELFCLASS64 ? 8 : 4), + program_count_(0), + program_header_table_(endianness), + section_count_(0), + section_header_table_(endianness), + section_header_strings_(endianness) { + // Could add support for more machine types here if needed. + assert(machine == EM_386 || + machine == EM_X86_64 || + machine == EM_ARM); + assert(file_class == ELFCLASS32 || file_class == ELFCLASS64); + + start() = 0; + // Add ELF header + // e_ident + // EI_MAG0...EI_MAG3 + D8(ELFMAG0); + D8(ELFMAG1); + D8(ELFMAG2); + D8(ELFMAG3); + // EI_CLASS + D8(file_class); + // EI_DATA + D8(endianness == kLittleEndian ? ELFDATA2LSB : ELFDATA2MSB); + // EI_VERSION + D8(EV_CURRENT); + // EI_OSABI + D8(ELFOSABI_SYSV); + // EI_ABIVERSION + D8(0); + // EI_PAD + Append(7, 0); + assert(Size() == EI_NIDENT); + + // e_type + D16(ET_EXEC); //TODO: allow passing ET_DYN? + // e_machine + D16(machine); + // e_version + D32(EV_CURRENT); + // e_entry + Append(endianness, addr_size_, 0); + // e_phoff + Append(endianness, addr_size_, program_header_label_); + // e_shoff + Append(endianness, addr_size_, section_header_label_); + // e_flags + D32(0); + // e_ehsize + D16(addr_size_ == 8 ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr)); + // e_phentsize + D16(addr_size_ == 8 ? sizeof(Elf64_Phdr) : sizeof(Elf32_Phdr)); + // e_phnum + D16(program_count_label_); + // e_shentsize + D16(addr_size_ == 8 ? sizeof(Elf64_Shdr) : sizeof(Elf32_Shdr)); + // e_shnum + D16(section_count_label_); + // e_shstrndx + D16(section_header_string_index_); + + // Add an empty section for SHN_UNDEF. + Section shn_undef; + AddSection("", shn_undef, SHT_NULL); +} + +int ELF::AddSection(const string& name, const Section& section, + uint32_t type, uint32_t flags, uint64_t addr, + uint32_t link, uint64_t entsize, uint64_t offset) { + Label offset_label; + Label string_label(section_header_strings_.Add(name)); + size_t size = section.Size(); + + int index = section_count_; + ++section_count_; + + section_header_table_ + // sh_name + .D32(string_label) + // sh_type + .D32(type) + // sh_flags + .Append(endianness(), addr_size_, flags) + // sh_addr + .Append(endianness(), addr_size_, addr) + // sh_offset + .Append(endianness(), addr_size_, offset_label) + // sh_size + .Append(endianness(), addr_size_, size) + // sh_link + .D32(link) + // sh_info + .D32(0) + // sh_addralign + .Append(endianness(), addr_size_, 0) + // sh_entsize + .Append(endianness(), addr_size_, entsize); + + sections_.push_back(ElfSection(section, type, addr, offset, offset_label, + size)); + return index; +} + +void ELF::AppendSection(ElfSection §ion) { + // NULL and NOBITS sections have no content, so they + // don't need to be written to the file. + if (section.type_ == SHT_NULL) { + section.offset_label_ = 0; + } else if (section.type_ == SHT_NOBITS) { + section.offset_label_ = section.offset_; + } else { + Mark(§ion.offset_label_); + Append(section); + Align(4); + } +} + +void ELF::AddSegment(int start, int end, uint32_t type, uint32_t flags) { + assert(start > 0); + assert(size_t(start) < sections_.size()); + assert(end > 0); + assert(size_t(end) < sections_.size()); + ++program_count_; + + // p_type + program_header_table_.D32(type); + + if (addr_size_ == 8) { + // p_flags + program_header_table_.D32(flags); + } + + size_t filesz = 0; + size_t memsz = 0; + bool prev_was_nobits = false; + for (int i = start; i <= end; ++i) { + size_t size = sections_[i].size_; + if (sections_[i].type_ != SHT_NOBITS) { + assert(!prev_was_nobits); + // non SHT_NOBITS sections are 4-byte aligned (see AddSection) + size = (size + 3) & ~3; + filesz += size; + } else { + prev_was_nobits = true; + } + memsz += size; + } + + program_header_table_ + // p_offset + .Append(endianness(), addr_size_, sections_[start].offset_label_) + // p_vaddr + .Append(endianness(), addr_size_, sections_[start].addr_) + // p_paddr + .Append(endianness(), addr_size_, sections_[start].addr_) + // p_filesz + .Append(endianness(), addr_size_, filesz) + // p_memsz + .Append(endianness(), addr_size_, memsz); + + if (addr_size_ == 4) { + // p_flags + program_header_table_.D32(flags); + } + + // p_align + program_header_table_.Append(endianness(), addr_size_, 0); +} + +void ELF::Finish() { + // Add the section header string table at the end. + section_header_string_index_ = section_count_; + //printf(".shstrtab size: %ld\n", section_header_strings_.Size()); + AddSection(".shstrtab", section_header_strings_, SHT_STRTAB); + //printf("section_count_: %ld, sections_.size(): %ld\n", + // section_count_, sections_.size()); + if (program_count_) { + Mark(&program_header_label_); + Append(program_header_table_); + } else { + program_header_label_ = 0; + } + + for (vector<ElfSection>::iterator it = sections_.begin(); + it < sections_.end(); ++it) { + AppendSection(*it); + } + section_count_label_ = section_count_; + program_count_label_ = program_count_; + + // Section header table starts here. + Mark(§ion_header_label_); + Append(section_header_table_); +} + +SymbolTable::SymbolTable(Endianness endianness, + size_t addr_size, + StringTable& table) : Section(endianness), + table_(table) { +#ifndef NDEBUG + addr_size_ = addr_size; +#endif + assert(addr_size_ == 4 || addr_size_ == 8); +} + +void SymbolTable::AddSymbol(const string& name, uint32_t value, + uint32_t size, unsigned info, uint16_t shndx) { + assert(addr_size_ == 4); + D32(table_.Add(name)); + D32(value); + D32(size); + D8(info); + D8(0); // other + D16(shndx); +} + +void SymbolTable::AddSymbol(const string& name, uint64_t value, + uint64_t size, unsigned info, uint16_t shndx) { + assert(addr_size_ == 8); + D32(table_.Add(name)); + D8(info); + D8(0); // other + D16(shndx); + D64(value); + D64(size); +} + +void Notes::AddNote(int type, const string &name, const uint8_t* desc_bytes, + size_t desc_size) { + // Elf32_Nhdr and Elf64_Nhdr are exactly the same. + Elf32_Nhdr note_header; + memset(¬e_header, 0, sizeof(note_header)); + note_header.n_namesz = name.length() + 1; + note_header.n_descsz = desc_size; + note_header.n_type = type; + + Append(reinterpret_cast<const uint8_t*>(¬e_header), + sizeof(note_header)); + AppendCString(name); + Align(4); + Append(desc_bytes, desc_size); + Align(4); +} + +} // namespace synth_elf +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf.h b/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf.h new file mode 100644 index 0000000000..1d2a20ca22 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf.h @@ -0,0 +1,197 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Ted Mielczarek <ted.mielczarek@gmail.com> + +// synth_elf.h: Interface to synth_elf::ELF: fake ELF generator. + +#ifndef COMMON_LINUX_SYNTH_ELF_H_ +#define COMMON_LINUX_SYNTH_ELF_H_ + +#include "common/test_assembler.h" + +#include <list> +#include <vector> +#include <map> +#include <string> +#include <utility> + +#include "common/using_std_string.h" + +namespace google_breakpad { +namespace synth_elf { + +using std::list; +using std::vector; +using std::map; +using std::pair; +using test_assembler::Endianness; +using test_assembler::kLittleEndian; +using test_assembler::kUnsetEndian; +using test_assembler::Label; +using test_assembler::Section; + +// String tables are common in ELF headers, so subclass Section +// to make them easy to generate. +class StringTable : public Section { +public: + StringTable(Endianness endianness = kUnsetEndian) + : Section(endianness) { + start() = 0; + empty_string = Add(""); + } + + // Add the string s to the string table, and return + // a label containing the offset into the string table + // at which it was added. + Label Add(const string& s) { + if (strings_.find(s) != strings_.end()) + return strings_[s]; + + Label string_label(Here()); + AppendCString(s); + strings_[s] = string_label; + return string_label; + } + + // All StringTables contain an empty string as their first + // entry. + Label empty_string; + + // Avoid inserting duplicate strings. + map<string,Label> strings_; +}; + +// A Section representing an entire ELF file. +class ELF : public Section { + public: + ELF(uint16_t machine, // EM_386, etc + uint8_t file_class, // ELFCLASS{32,64} + Endianness endianness = kLittleEndian); + + // Add the Section section to the section header table and append it + // to the file. Returns the index of the section in the section + // header table. + int AddSection(const string& name, const Section& section, + uint32_t type, uint32_t flags = 0, uint64_t addr = 0, + uint32_t link = 0, uint64_t entsize = 0, uint64_t offset = 0); + + // Add a segment containing from section index start to section index end. + // The indexes must have been gotten from AddSection. + void AddSegment(int start, int end, uint32_t type, uint32_t flags = 0); + + // Write out all data. GetContents may be used after this. + void Finish(); + + private: + // Size of an address, in bytes. + const size_t addr_size_; + + // Offset to the program header table. + Label program_header_label_; + // Number of entries in the program header table. + int program_count_; + Label program_count_label_; + // The program header table itself. + Section program_header_table_; + + // Offset to the section header table. + Label section_header_label_; + // Number of entries in the section header table. + int section_count_; + Label section_count_label_; + // The section header table itself. + Section section_header_table_; + + // Index of the section header string table in the section + // header table. + Label section_header_string_index_; + // Section containing the names of section header table entries. + StringTable section_header_strings_; + + // Record of an added section + struct ElfSection : public Section { + ElfSection(const Section& section, uint32_t type, uint32_t addr, + uint32_t offset, Label offset_label, uint32_t size) + : Section(section), type_(type), addr_(addr), offset_(offset) + , offset_label_(offset_label), size_(size) { + } + + uint32_t type_; + uint32_t addr_; + uint32_t offset_; + Label offset_label_; + uint32_t size_; + }; + + vector<ElfSection> sections_; + + void AppendSection(ElfSection §ion); +}; + +// A class to build .symtab or .dynsym sections. +class SymbolTable : public Section { + public: + // table is the StringTable that contains symbol names. The caller + // must ensure that it remains alive for the life of the + // SymbolTable. + SymbolTable(Endianness endianness, size_t addr_size, StringTable& table); + + // Add an Elf32_Sym. + void AddSymbol(const string& name, uint32_t value, + uint32_t size, unsigned info, uint16_t shndx); + // Add an Elf64_Sym. + void AddSymbol(const string& name, uint64_t value, + uint64_t size, unsigned info, uint16_t shndx); + + private: +#ifndef NDEBUG + size_t addr_size_; +#endif + StringTable& table_; +}; + +// A class for note sections +class Notes : public Section { +public: + Notes(Endianness endianness) + : Section(endianness) { + } + + // Add a note. + void AddNote(int type, const string &name, const uint8_t* desc_bytes, + size_t desc_size); +}; + +} // namespace synth_elf +} // namespace google_breakpad + +#endif // COMMON_LINUX_SYNTH_ELF_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf_unittest.cc new file mode 100644 index 0000000000..cd74c28624 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf_unittest.cc @@ -0,0 +1,413 @@ +// Copyright (c) 2011 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Ted Mielczarek <ted.mielczarek@gmail.com> + +// synth_elf_unittest.cc: +// Unittests for google_breakpad::synth_elf::ELF + +#include <elf.h> + +#include "breakpad_googletest_includes.h" +#include "common/linux/elfutils.h" +#include "common/linux/synth_elf.h" +#include "common/using_std_string.h" + +using google_breakpad::ElfClass32; +using google_breakpad::ElfClass64; +using google_breakpad::synth_elf::ELF; +using google_breakpad::synth_elf::Notes; +using google_breakpad::synth_elf::Section; +using google_breakpad::synth_elf::StringTable; +using google_breakpad::synth_elf::SymbolTable; +using google_breakpad::test_assembler::Endianness; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; +using google_breakpad::test_assembler::Label; +using ::testing::Test; +using ::testing::Types; + +class StringTableTest : public Test { +public: + StringTableTest() : table(kLittleEndian) {} + + StringTable table; +}; + +TEST_F(StringTableTest, Empty) { + EXPECT_EQ(1U, table.Size()); + string contents; + ASSERT_TRUE(table.GetContents(&contents)); + const char* kExpectedContents = "\0"; + EXPECT_EQ(0, memcmp(kExpectedContents, + contents.c_str(), + contents.size())); + ASSERT_TRUE(table.empty_string.IsKnownConstant()); + EXPECT_EQ(0U, table.empty_string.Value()); +} + +TEST_F(StringTableTest, Basic) { + const string s1("table fills with strings"); + const string s2("offsets preserved as labels"); + const string s3("verified with tests"); + const char* kExpectedContents = + "\0table fills with strings\0" + "offsets preserved as labels\0" + "verified with tests\0"; + Label l1(table.Add(s1)); + Label l2(table.Add(s2)); + Label l3(table.Add(s3)); + string contents; + ASSERT_TRUE(table.GetContents(&contents)); + EXPECT_EQ(0, memcmp(kExpectedContents, + contents.c_str(), + contents.size())); + // empty_string is at zero, other strings start at 1. + ASSERT_TRUE(l1.IsKnownConstant()); + EXPECT_EQ(1U, l1.Value()); + // Each string has an extra byte for a trailing null. + EXPECT_EQ(1 + s1.length() + 1, l2.Value()); + EXPECT_EQ(1 + s1.length() + 1 + s2.length() + 1, l3.Value()); +} + +TEST_F(StringTableTest, Duplicates) { + const string s1("string 1"); + const string s2("string 2"); + const string s3(""); + const char* kExpectedContents = "\0string 1\0string 2\0"; + Label l1(table.Add(s1)); + Label l2(table.Add(s2)); + // Adding strings twice should return the same Label. + Label l3(table.Add(s3)); + Label l4(table.Add(s2)); + string contents; + ASSERT_TRUE(table.GetContents(&contents)); + EXPECT_EQ(0, memcmp(kExpectedContents, + contents.c_str(), + contents.size())); + EXPECT_EQ(0U, table.empty_string.Value()); + EXPECT_EQ(table.empty_string.Value(), l3.Value()); + EXPECT_EQ(l2.Value(), l4.Value()); +} + +class SymbolTableTest : public Test {}; + +TEST_F(SymbolTableTest, Simple32) { + StringTable table(kLittleEndian); + SymbolTable syms(kLittleEndian, 4, table); + + const string kFuncName1 = "superfunc"; + const uint32_t kFuncAddr1 = 0x10001000; + const uint32_t kFuncSize1 = 0x10; + const string kFuncName2 = "awesomefunc"; + const uint32_t kFuncAddr2 = 0x20002000; + const uint32_t kFuncSize2 = 0x2f; + const string kFuncName3 = "megafunc"; + const uint32_t kFuncAddr3 = 0x30003000; + const uint32_t kFuncSize3 = 0x3c; + + syms.AddSymbol(kFuncName1, kFuncAddr1, kFuncSize1, + ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), + SHN_UNDEF + 1); + syms.AddSymbol(kFuncName2, kFuncAddr2, kFuncSize2, + ELF32_ST_INFO(STB_LOCAL, STT_FUNC), + SHN_UNDEF + 2); + syms.AddSymbol(kFuncName3, kFuncAddr3, kFuncSize3, + ELF32_ST_INFO(STB_LOCAL, STT_FUNC), + SHN_UNDEF + 3); + + const char kExpectedStringTable[] = "\0superfunc\0awesomefunc\0megafunc"; + const size_t kExpectedStringTableSize = sizeof(kExpectedStringTable); + EXPECT_EQ(kExpectedStringTableSize, table.Size()); + string table_contents; + table.GetContents(&table_contents); + EXPECT_EQ(0, memcmp(kExpectedStringTable, + table_contents.c_str(), + table_contents.size())); + + const uint8_t kExpectedSymbolContents[] = { + // Symbol 1 + 0x01, 0x00, 0x00, 0x00, // name + 0x00, 0x10, 0x00, 0x10, // value + 0x10, 0x00, 0x00, 0x00, // size + ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), // info + 0x00, // other + 0x01, 0x00, // shndx + // Symbol 2 + 0x0B, 0x00, 0x00, 0x00, // name + 0x00, 0x20, 0x00, 0x20, // value + 0x2f, 0x00, 0x00, 0x00, // size + ELF32_ST_INFO(STB_LOCAL, STT_FUNC), // info + 0x00, // other + 0x02, 0x00, // shndx + // Symbol 3 + 0x17, 0x00, 0x00, 0x00, // name + 0x00, 0x30, 0x00, 0x30, // value + 0x3c, 0x00, 0x00, 0x00, // size + ELF32_ST_INFO(STB_LOCAL, STT_FUNC), // info + 0x00, // other + 0x03, 0x00, // shndx + }; + const size_t kExpectedSymbolSize = sizeof(kExpectedSymbolContents); + EXPECT_EQ(kExpectedSymbolSize, syms.Size()); + + string symbol_contents; + syms.GetContents(&symbol_contents); + EXPECT_EQ(0, memcmp(kExpectedSymbolContents, + symbol_contents.c_str(), + symbol_contents.size())); +} + +template<typename ElfClass> +class BasicElf : public Test {}; + +// Doesn't seem worthwhile writing the tests to be endian-independent +// when they're unlikely to ever be run on big-endian systems. +#if defined(__i386__) || defined(__x86_64__) + +typedef Types<ElfClass32, ElfClass64> ElfClasses; + +TYPED_TEST_SUITE(BasicElf, ElfClasses); + +TYPED_TEST(BasicElf, EmptyLE) { + typedef typename TypeParam::Ehdr Ehdr; + typedef typename TypeParam::Phdr Phdr; + typedef typename TypeParam::Shdr Shdr; + const size_t kStringTableSize = sizeof("\0.shstrtab"); + const size_t kStringTableAlign = 4 - kStringTableSize % 4; + const size_t kExpectedSize = sizeof(Ehdr) + + // Two sections, SHT_NULL + the section header string table. + 2 * sizeof(Shdr) + + kStringTableSize + kStringTableAlign; + + // It doesn't really matter that the machine type is right for the class. + ELF elf(EM_386, TypeParam::kClass, kLittleEndian); + elf.Finish(); + EXPECT_EQ(kExpectedSize, elf.Size()); + + string contents; + ASSERT_TRUE(elf.GetContents(&contents)); + ASSERT_EQ(kExpectedSize, contents.size()); + const Ehdr* header = + reinterpret_cast<const Ehdr*>(contents.data()); + const uint8_t kIdent[] = { + ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, + TypeParam::kClass, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + EXPECT_EQ(0, memcmp(kIdent, header->e_ident, sizeof(kIdent))); + EXPECT_EQ(ET_EXEC, header->e_type); + EXPECT_EQ(EM_386, header->e_machine); + EXPECT_EQ(static_cast<unsigned int>(EV_CURRENT), header->e_version); + EXPECT_EQ(0U, header->e_entry); + EXPECT_EQ(0U, header->e_phoff); + EXPECT_EQ(sizeof(Ehdr) + kStringTableSize + kStringTableAlign, + header->e_shoff); + EXPECT_EQ(0U, header->e_flags); + EXPECT_EQ(sizeof(Ehdr), header->e_ehsize); + EXPECT_EQ(sizeof(Phdr), header->e_phentsize); + EXPECT_EQ(0, header->e_phnum); + EXPECT_EQ(sizeof(Shdr), header->e_shentsize); + EXPECT_EQ(2, header->e_shnum); + EXPECT_EQ(1, header->e_shstrndx); + + const Shdr* shdr = + reinterpret_cast<const Shdr*>(contents.data() + header->e_shoff); + EXPECT_EQ(0U, shdr[0].sh_name); + EXPECT_EQ(static_cast<unsigned int>(SHT_NULL), shdr[0].sh_type); + EXPECT_EQ(0U, shdr[0].sh_flags); + EXPECT_EQ(0U, shdr[0].sh_addr); + EXPECT_EQ(0U, shdr[0].sh_offset); + EXPECT_EQ(0U, shdr[0].sh_size); + EXPECT_EQ(0U, shdr[0].sh_link); + EXPECT_EQ(0U, shdr[0].sh_info); + EXPECT_EQ(0U, shdr[0].sh_addralign); + EXPECT_EQ(0U, shdr[0].sh_entsize); + + EXPECT_EQ(1U, shdr[1].sh_name); + EXPECT_EQ(static_cast<unsigned int>(SHT_STRTAB), shdr[1].sh_type); + EXPECT_EQ(0U, shdr[1].sh_flags); + EXPECT_EQ(0U, shdr[1].sh_addr); + EXPECT_EQ(sizeof(Ehdr), shdr[1].sh_offset); + EXPECT_EQ(kStringTableSize, shdr[1].sh_size); + EXPECT_EQ(0U, shdr[1].sh_link); + EXPECT_EQ(0U, shdr[1].sh_info); + EXPECT_EQ(0U, shdr[1].sh_addralign); + EXPECT_EQ(0U, shdr[1].sh_entsize); +} + +TYPED_TEST(BasicElf, BasicLE) { + typedef typename TypeParam::Ehdr Ehdr; + typedef typename TypeParam::Phdr Phdr; + typedef typename TypeParam::Shdr Shdr; + const size_t kStringTableSize = sizeof("\0.text\0.bss\0.shstrtab"); + const size_t kStringTableAlign = 4 - kStringTableSize % 4; + const size_t kExpectedSize = sizeof(Ehdr) + + // Four sections, SHT_NULL + the section header string table + + // 4096 bytes of the size-aligned .text section + one program header. + sizeof(Phdr) + 4 * sizeof(Shdr) + 4096 + + kStringTableSize + kStringTableAlign; + + // It doesn't really matter that the machine type is right for the class. + ELF elf(EM_386, TypeParam::kClass, kLittleEndian); + Section text(kLittleEndian); + text.Append(4094, 0); + int text_idx = elf.AddSection(".text", text, SHT_PROGBITS); + Section bss(kLittleEndian); + bss.Append(16, 0); + int bss_idx = elf.AddSection(".bss", bss, SHT_NOBITS); + elf.AddSegment(text_idx, bss_idx, PT_LOAD); + elf.Finish(); + EXPECT_EQ(kExpectedSize, elf.Size()); + + string contents; + ASSERT_TRUE(elf.GetContents(&contents)); + ASSERT_EQ(kExpectedSize, contents.size()); + const Ehdr* header = + reinterpret_cast<const Ehdr*>(contents.data()); + const uint8_t kIdent[] = { + ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, + TypeParam::kClass, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + EXPECT_EQ(0, memcmp(kIdent, header->e_ident, sizeof(kIdent))); + EXPECT_EQ(ET_EXEC, header->e_type); + EXPECT_EQ(EM_386, header->e_machine); + EXPECT_EQ(static_cast<unsigned int>(EV_CURRENT), header->e_version); + EXPECT_EQ(0U, header->e_entry); + EXPECT_EQ(sizeof(Ehdr), header->e_phoff); + EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr) + 4096 + kStringTableSize + + kStringTableAlign, header->e_shoff); + EXPECT_EQ(0U, header->e_flags); + EXPECT_EQ(sizeof(Ehdr), header->e_ehsize); + EXPECT_EQ(sizeof(Phdr), header->e_phentsize); + EXPECT_EQ(1, header->e_phnum); + EXPECT_EQ(sizeof(Shdr), header->e_shentsize); + EXPECT_EQ(4, header->e_shnum); + EXPECT_EQ(3, header->e_shstrndx); + + const Shdr* shdr = + reinterpret_cast<const Shdr*>(contents.data() + header->e_shoff); + EXPECT_EQ(0U, shdr[0].sh_name); + EXPECT_EQ(static_cast<unsigned int>(SHT_NULL), shdr[0].sh_type); + EXPECT_EQ(0U, shdr[0].sh_flags); + EXPECT_EQ(0U, shdr[0].sh_addr); + EXPECT_EQ(0U, shdr[0].sh_offset); + EXPECT_EQ(0U, shdr[0].sh_size); + EXPECT_EQ(0U, shdr[0].sh_link); + EXPECT_EQ(0U, shdr[0].sh_info); + EXPECT_EQ(0U, shdr[0].sh_addralign); + EXPECT_EQ(0U, shdr[0].sh_entsize); + + EXPECT_EQ(1U, shdr[1].sh_name); + EXPECT_EQ(static_cast<unsigned int>(SHT_PROGBITS), shdr[1].sh_type); + EXPECT_EQ(0U, shdr[1].sh_flags); + EXPECT_EQ(0U, shdr[1].sh_addr); + EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr), shdr[1].sh_offset); + EXPECT_EQ(4094U, shdr[1].sh_size); + EXPECT_EQ(0U, shdr[1].sh_link); + EXPECT_EQ(0U, shdr[1].sh_info); + EXPECT_EQ(0U, shdr[1].sh_addralign); + EXPECT_EQ(0U, shdr[1].sh_entsize); + + EXPECT_EQ(sizeof("\0.text"), shdr[2].sh_name); + EXPECT_EQ(static_cast<unsigned int>(SHT_NOBITS), shdr[2].sh_type); + EXPECT_EQ(0U, shdr[2].sh_flags); + EXPECT_EQ(0U, shdr[2].sh_addr); + EXPECT_EQ(0U, shdr[2].sh_offset); + EXPECT_EQ(16U, shdr[2].sh_size); + EXPECT_EQ(0U, shdr[2].sh_link); + EXPECT_EQ(0U, shdr[2].sh_info); + EXPECT_EQ(0U, shdr[2].sh_addralign); + EXPECT_EQ(0U, shdr[2].sh_entsize); + + EXPECT_EQ(sizeof("\0.text\0.bss"), shdr[3].sh_name); + EXPECT_EQ(static_cast<unsigned int>(SHT_STRTAB), shdr[3].sh_type); + EXPECT_EQ(0U, shdr[3].sh_flags); + EXPECT_EQ(0U, shdr[3].sh_addr); + EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr) + 4096, shdr[3].sh_offset); + EXPECT_EQ(kStringTableSize, shdr[3].sh_size); + EXPECT_EQ(0U, shdr[3].sh_link); + EXPECT_EQ(0U, shdr[3].sh_info); + EXPECT_EQ(0U, shdr[3].sh_addralign); + EXPECT_EQ(0U, shdr[3].sh_entsize); + + const Phdr* phdr = + reinterpret_cast<const Phdr*>(contents.data() + header->e_phoff); + EXPECT_EQ(static_cast<unsigned int>(PT_LOAD), phdr->p_type); + EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr), phdr->p_offset); + EXPECT_EQ(0U, phdr->p_vaddr); + EXPECT_EQ(0U, phdr->p_paddr); + EXPECT_EQ(4096U, phdr->p_filesz); + EXPECT_EQ(4096U + 16U, phdr->p_memsz); + EXPECT_EQ(0U, phdr->p_flags); + EXPECT_EQ(0U, phdr->p_align); +} + +class ElfNotesTest : public Test {}; + +TEST_F(ElfNotesTest, Empty) { + Notes notes(kLittleEndian); + string contents; + ASSERT_TRUE(notes.GetContents(&contents)); + EXPECT_EQ(0U, contents.size()); +} + +TEST_F(ElfNotesTest, Notes) { + Notes notes(kLittleEndian); + notes.AddNote(1, "Linux", reinterpret_cast<const uint8_t *>("\x42\x02\0\0"), + 4); + notes.AddNote(2, "a", reinterpret_cast<const uint8_t *>("foobar"), + sizeof("foobar") - 1); + + const uint8_t kExpectedNotesContents[] = { + // Note 1 + 0x06, 0x00, 0x00, 0x00, // name size, including terminating zero + 0x04, 0x00, 0x00, 0x00, // desc size + 0x01, 0x00, 0x00, 0x00, // type + 'L', 'i', 'n', 'u', 'x', 0x00, 0x00, 0x00, // padded "Linux" + 0x42, 0x02, 0x00, 0x00, // desc + // Note 2 + 0x02, 0x00, 0x00, 0x00, // name size + 0x06, 0x00, 0x00, 0x00, // desc size + 0x02, 0x00, 0x00, 0x00, // type + 'a', 0x00, 0x00, 0x00, // padded "a" + 'f', 'o', 'o', 'b', 'a', 'r', 0x00, 0x00, // padded "foobar" + }; + const size_t kExpectedNotesSize = sizeof(kExpectedNotesContents); + EXPECT_EQ(kExpectedNotesSize, notes.Size()); + + string notes_contents; + ASSERT_TRUE(notes.GetContents(¬es_contents)); + EXPECT_EQ(0, memcmp(kExpectedNotesContents, + notes_contents.data(), + notes_contents.size())); +} + +#endif // defined(__i386__) || defined(__x86_64__) diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/tests/auto_testfile.h b/toolkit/crashreporter/google-breakpad/src/common/linux/tests/auto_testfile.h new file mode 100644 index 0000000000..92fe017b92 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/tests/auto_testfile.h @@ -0,0 +1,124 @@ +// Copyright (c) 2013, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Utility class for creating a temporary file for unit tests +// that is deleted in the destructor. + +#ifndef GOOGLE_BREAKPAD_COMMON_LINUX_TESTS_AUTO_TESTFILE +#define GOOGLE_BREAKPAD_COMMON_LINUX_TESTS_AUTO_TESTFILE + +#include <unistd.h> +#include <sys/types.h> + +#include <string> + +#include "breakpad_googletest_includes.h" +#include "common/linux/eintr_wrapper.h" +#include "common/tests/auto_tempdir.h" + +namespace google_breakpad { + +class AutoTestFile { + public: + // Create a new empty test file. + // test_prefix: (input) test-specific prefix, can't be NULL. + explicit AutoTestFile(const char* test_prefix) { + Init(test_prefix); + } + + // Create a new test file, and fill it with initial data from a C string. + // The terminating zero is not written. + // test_prefix: (input) test-specific prefix, can't be NULL. + // text: (input) initial content. + AutoTestFile(const char* test_prefix, const char* text) { + Init(test_prefix); + if (fd_ >= 0) + WriteText(text, static_cast<size_t>(strlen(text))); + } + + AutoTestFile(const char* test_prefix, const char* text, size_t text_len) { + Init(test_prefix); + if (fd_ >= 0) + WriteText(text, text_len); + } + + // Destroy test file on scope exit. + ~AutoTestFile() { + if (fd_ >= 0) { + close(fd_); + fd_ = -1; + } + } + + // Returns true iff the test file could be created properly. + // Useful in tests inside EXPECT_TRUE(file.IsOk()); + bool IsOk() { + return fd_ >= 0; + } + + // Returns the Posix file descriptor for the test file, or -1 + // If IsOk() returns false. Note: on Windows, this always returns -1. + int GetFd() { + return fd_; + } + + private: + void Init(const char* test_prefix) { + fd_ = -1; + char path_templ[PATH_MAX]; + int ret = snprintf(path_templ, sizeof(path_templ), + TEMPDIR "/%s-unittest.XXXXXX", + test_prefix); + if (ret >= static_cast<int>(sizeof(path_templ))) + return; + + fd_ = mkstemp(path_templ); + if (fd_ < 0) + return; + + unlink(path_templ); + } + + void WriteText(const char* text, size_t text_len) { + ssize_t r = HANDLE_EINTR(write(fd_, text, text_len)); + if (r != static_cast<ssize_t>(text_len)) { + close(fd_); + fd_ = -1; + return; + } + + lseek(fd_, 0, SEEK_SET); + } + + int fd_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_COMMON_LINUX_TESTS_AUTO_TESTFILE diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/tests/crash_generator.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/tests/crash_generator.cc new file mode 100644 index 0000000000..6896a688d9 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/tests/crash_generator.cc @@ -0,0 +1,329 @@ +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// crash_generator.cc: Implement google_breakpad::CrashGenerator. +// See crash_generator.h for details. + +#include "common/linux/tests/crash_generator.h" + +#include <pthread.h> +#include <sched.h> +#include <signal.h> +#include <stdio.h> +#include <sys/mman.h> +#include <sys/resource.h> +#include <sys/syscall.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <string> + +#if defined(__ANDROID__) +#include "common/android/testing/pthread_fixes.h" +#endif +#include "common/linux/eintr_wrapper.h" +#include "common/tests/auto_tempdir.h" +#include "common/tests/file_utils.h" +#include "common/using_std_string.h" + +namespace { + +struct ThreadData { + pthread_t thread; + pthread_barrier_t* barrier; + pid_t* thread_id_ptr; +}; + +const char* const kProcFilesToCopy[] = { + "auxv", "cmdline", "environ", "maps", "status" +}; +const size_t kNumProcFilesToCopy = + sizeof(kProcFilesToCopy) / sizeof(kProcFilesToCopy[0]); + +int gettid() { + // Glibc does not provide a wrapper for this. + return syscall(__NR_gettid); +} + +int tkill(pid_t tid, int sig) { + // Glibc does not provide a wrapper for this. + return syscall(__NR_tkill, tid, sig); +} + +// Core file size limit set to 1 MB, which is big enough for test purposes. +const rlim_t kCoreSizeLimit = 1024 * 1024; + +void *thread_function(void *data) { + ThreadData* thread_data = reinterpret_cast<ThreadData*>(data); + volatile pid_t thread_id = gettid(); + *(thread_data->thread_id_ptr) = thread_id; + int result = pthread_barrier_wait(thread_data->barrier); + if (result != 0 && result != PTHREAD_BARRIER_SERIAL_THREAD) { + perror("Failed to wait for sync barrier"); + exit(1); + } + while (true) { + sched_yield(); + } +} + +} // namespace + +namespace google_breakpad { + +CrashGenerator::CrashGenerator() + : shared_memory_(NULL), + shared_memory_size_(0) { +} + +CrashGenerator::~CrashGenerator() { + UnmapSharedMemory(); +} + +bool CrashGenerator::HasDefaultCorePattern() const { + char buffer[8]; + ssize_t buffer_size = sizeof(buffer); + return ReadFile("/proc/sys/kernel/core_pattern", buffer, &buffer_size) && + buffer_size == 5 && memcmp(buffer, "core", 4) == 0; +} + +string CrashGenerator::GetCoreFilePath() const { + return temp_dir_.path() + "/core"; +} + +string CrashGenerator::GetDirectoryOfProcFilesCopy() const { + return temp_dir_.path() + "/proc"; +} + +pid_t CrashGenerator::GetThreadId(unsigned index) const { + return reinterpret_cast<pid_t*>(shared_memory_)[index]; +} + +pid_t* CrashGenerator::GetThreadIdPointer(unsigned index) { + return reinterpret_cast<pid_t*>(shared_memory_) + index; +} + +bool CrashGenerator::MapSharedMemory(size_t memory_size) { + if (!UnmapSharedMemory()) + return false; + + void* mapped_memory = mmap(0, memory_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (mapped_memory == MAP_FAILED) { + perror("CrashGenerator: Failed to map shared memory"); + return false; + } + + memset(mapped_memory, 0, memory_size); + shared_memory_ = mapped_memory; + shared_memory_size_ = memory_size; + return true; +} + +bool CrashGenerator::UnmapSharedMemory() { + if (!shared_memory_) + return true; + + if (munmap(shared_memory_, shared_memory_size_) == 0) { + shared_memory_ = NULL; + shared_memory_size_ = 0; + return true; + } + + perror("CrashGenerator: Failed to unmap shared memory"); + return false; +} + +bool CrashGenerator::SetCoreFileSizeLimit(rlim_t limit) const { + struct rlimit limits = { limit, limit }; + if (setrlimit(RLIMIT_CORE, &limits) == -1) { + perror("CrashGenerator: Failed to set core file size limit"); + return false; + } + return true; +} + +bool CrashGenerator::CreateChildCrash( + unsigned num_threads, unsigned crash_thread, int crash_signal, + pid_t* child_pid) { + if (num_threads == 0 || crash_thread >= num_threads) { + fprintf(stderr, "CrashGenerator: Invalid thread counts; num_threads=%u" + " crash_thread=%u\n", num_threads, crash_thread); + return false; + } + + if (!MapSharedMemory(num_threads * sizeof(pid_t))) { + perror("CrashGenerator: Unable to map shared memory"); + return false; + } + + pid_t pid = fork(); + if (pid == 0) { + // Custom signal handlers, which may have been installed by a test launcher, + // are undesirable in this child. + if (signal(crash_signal, SIG_DFL) == SIG_ERR) { + perror("CrashGenerator: signal"); + exit(1); + } + if (chdir(temp_dir_.path().c_str()) == -1) { + perror("CrashGenerator: Failed to change directory"); + exit(1); + } + if (SetCoreFileSizeLimit(kCoreSizeLimit)) { + CreateThreadsInChildProcess(num_threads); + string proc_dir = GetDirectoryOfProcFilesCopy(); + if (mkdir(proc_dir.c_str(), 0755) == -1) { + perror("CrashGenerator: Failed to create proc directory"); + exit(1); + } + if (!CopyProcFiles(getpid(), proc_dir.c_str())) { + fprintf(stderr, "CrashGenerator: Failed to copy proc files\n"); + exit(1); + } + // On Android the signal sometimes doesn't seem to get sent even though + // tkill returns '0'. Retry a couple of times if the signal doesn't get + // through on the first go: + // https://bugs.chromium.org/p/google-breakpad/issues/detail?id=579 +#if defined(__ANDROID__) + const int kRetries = 60; + const unsigned int kSleepTimeInSeconds = 1; +#else + const int kRetries = 1; + const unsigned int kSleepTimeInSeconds = 600; +#endif + for (int i = 0; i < kRetries; i++) { + if (tkill(*GetThreadIdPointer(crash_thread), crash_signal) == -1) { + perror("CrashGenerator: Failed to kill thread by signal"); + } else { + // At this point, we've queued the signal for delivery, but there's no + // guarantee when it'll be delivered. We don't want the main thread to + // race and exit before the thread we signaled is processed. So sleep + // long enough that we won't flake even under fairly high load. + // TODO: See if we can't be a bit more deterministic. There doesn't + // seem to be an API to check on signal delivery status, so we can't + // really poll and wait for the kernel to declare the signal has been + // delivered. If it has, and things worked, we'd be killed, so the + // sleep length doesn't really matter. + sleep(kSleepTimeInSeconds); + } + } + } else { + perror("CrashGenerator: Failed to set core limit"); + } + exit(1); + } else if (pid == -1) { + perror("CrashGenerator: Failed to create child process"); + return false; + } + + int status; + if (HANDLE_EINTR(waitpid(pid, &status, 0)) == -1) { + perror("CrashGenerator: Failed to wait for child process"); + return false; + } + if (!WIFSIGNALED(status) || WTERMSIG(status) != crash_signal) { + fprintf(stderr, "CrashGenerator: Child process not killed by the expected signal\n" + " exit status=0x%x pid=%u signaled=%s sig=%d expected=%d\n", + status, pid, WIFSIGNALED(status) ? "true" : "false", + WTERMSIG(status), crash_signal); + return false; + } + + if (child_pid) + *child_pid = pid; + return true; +} + +bool CrashGenerator::CopyProcFiles(pid_t pid, const char* path) const { + char from_path[PATH_MAX], to_path[PATH_MAX]; + for (size_t i = 0; i < kNumProcFilesToCopy; ++i) { + int num_chars = snprintf(from_path, PATH_MAX, "/proc/%d/%s", + pid, kProcFilesToCopy[i]); + if (num_chars < 0 || num_chars >= PATH_MAX) + return false; + + num_chars = snprintf(to_path, PATH_MAX, "%s/%s", + path, kProcFilesToCopy[i]); + if (num_chars < 0 || num_chars >= PATH_MAX) + return false; + + if (!CopyFile(from_path, to_path)) + return false; + } + return true; +} + +void CrashGenerator::CreateThreadsInChildProcess(unsigned num_threads) { + *GetThreadIdPointer(0) = getpid(); + + if (num_threads <= 1) + return; + + // This method does not clean up any pthread resource, as the process + // is expected to be killed anyway. + ThreadData* thread_data = new ThreadData[num_threads]; + + // Create detached threads so that we do not worry about pthread_join() + // later being called or not. + pthread_attr_t thread_attributes; + if (pthread_attr_init(&thread_attributes) != 0 || + pthread_attr_setdetachstate(&thread_attributes, + PTHREAD_CREATE_DETACHED) != 0) { + fprintf(stderr, "CrashGenerator: Failed to initialize thread attribute\n"); + exit(1); + } + + pthread_barrier_t thread_barrier; + if (pthread_barrier_init(&thread_barrier, NULL, num_threads) != 0) { + fprintf(stderr, "CrashGenerator: Failed to initialize thread barrier\n"); + exit(1); + } + + for (unsigned i = 1; i < num_threads; ++i) { + thread_data[i].barrier = &thread_barrier; + thread_data[i].thread_id_ptr = GetThreadIdPointer(i); + if (pthread_create(&thread_data[i].thread, &thread_attributes, + thread_function, &thread_data[i]) != 0) { + fprintf(stderr, "CrashGenerator: Failed to create thread %d\n", i); + exit(1); + } + } + + int result = pthread_barrier_wait(&thread_barrier); + if (result != 0 && result != PTHREAD_BARRIER_SERIAL_THREAD) { + fprintf(stderr, "CrashGenerator: Failed to wait for thread barrier\n"); + exit(1); + } + + pthread_barrier_destroy(&thread_barrier); + pthread_attr_destroy(&thread_attributes); + delete[] thread_data; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/tests/crash_generator.h b/toolkit/crashreporter/google-breakpad/src/common/linux/tests/crash_generator.h new file mode 100644 index 0000000000..7e2fcbf98a --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/tests/crash_generator.h @@ -0,0 +1,117 @@ +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// crash_generator.h: Define the google_breakpad::CrashGenerator class, +// which is used to generate a crash (and a core dump file) for testing. + +#ifndef COMMON_LINUX_TESTS_CRASH_GENERATOR_H_ +#define COMMON_LINUX_TESTS_CRASH_GENERATOR_H_ + +#include <sys/resource.h> + +#include <string> + +#include "common/tests/auto_tempdir.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +// A utility class for generating a crash (and a core dump file) for +// testing. It creates a child process with the specified number of +// threads, which is then termainated by the specified signal. A core +// dump file is expected to be created upon the termination of the child +// process, which can then be used for testing code that processes core +// dump files. +class CrashGenerator { + public: + CrashGenerator(); + + ~CrashGenerator(); + + // Returns true if a core dump file named 'core' will be generated in + // the current directory for a test that produces a crash by checking + // if /proc/sys/kernel/core_pattern has the default value 'core'. + bool HasDefaultCorePattern() const; + + // Returns the expected path of the core dump file. + string GetCoreFilePath() const; + + // Returns the directory of a copy of proc files of the child process. + string GetDirectoryOfProcFilesCopy() const; + + // Creates a crash (and a core dump file) by creating a child process with + // |num_threads| threads, and the terminating the child process by sending + // a signal with number |crash_signal| to the |crash_thread|-th thread. + // Returns true on success. + bool CreateChildCrash(unsigned num_threads, unsigned crash_thread, + int crash_signal, pid_t* child_pid); + + // Returns the thread ID of the |index|-th thread in the child process. + // This method does not validate |index|. + pid_t GetThreadId(unsigned index) const; + + private: + // Copies the following proc files of the process with |pid| to the directory + // at |path|: auxv, cmdline, environ, maps, status + // The directory must have been created. Returns true on success. + bool CopyProcFiles(pid_t pid, const char* path) const; + + // Creates |num_threads| threads in the child process. + void CreateThreadsInChildProcess(unsigned num_threads); + + // Sets the maximum size of core dump file (both the soft and hard limit) + // to |limit| bytes. Returns true on success. + bool SetCoreFileSizeLimit(rlim_t limit) const; + + // Creates a shared memory of |memory_size| bytes for communicating thread + // IDs between the parent and child process. Returns true on success. + bool MapSharedMemory(size_t memory_size); + + // Releases any shared memory created by MapSharedMemory(). Returns true on + // success. + bool UnmapSharedMemory(); + + // Returns the pointer to the thread ID of the |index|-th thread in the child + // process. This method does not validate |index|. + pid_t* GetThreadIdPointer(unsigned index); + + // Temporary directory in which a core file is generated. + AutoTempDir temp_dir_; + + // Shared memory for communicating thread IDs between the parent and + // child process. + void* shared_memory_; + + // Number of bytes mapped for |shared_memory_|. + size_t shared_memory_size_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_TESTS_CRASH_GENERATOR_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/ucontext_constants.h b/toolkit/crashreporter/google-breakpad/src/common/linux/ucontext_constants.h new file mode 100644 index 0000000000..c390508a1a --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/ucontext_constants.h @@ -0,0 +1,153 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// This header can be included either from a C, C++ or Assembly file. +// Its purpose is to contain constants that must match the offsets of +// various fields in ucontext_t. +// +// They should match the definitions from signal.h. +// +// Used by src/common/linux/breakpad_getcontext.S +// Tested by src/common/linux/breakpad_getcontext_unittest.cc +// +// This header should not be used by anything else. + +#ifndef GOOGLEBREAKPAD_COMMON_LINUX_UCONTEXT_CONSTANTS_H +#define GOOGLEBREAKPAD_COMMON_LINUX_UCONTEXT_CONSTANTS_H + +#if defined(__arm__) + +#define MCONTEXT_GREGS_OFFSET 32 +#define UCONTEXT_SIGMASK_OFFSET 104 + +#elif defined(__aarch64__) + +#define UCONTEXT_SIGMASK_OFFSET 40 + +#define MCONTEXT_GREGS_OFFSET 184 +#define MCONTEXT_SP_OFFSET 432 +#define MCONTEXT_PC_OFFSET 440 +#define MCONTEXT_PSTATE_OFFSET 448 +#define MCONTEXT_EXTENSION_OFFSET 464 + +#define FPSIMD_MAGIC 0x46508001 + +#define FPSIMD_CONTEXT_MAGIC_OFFSET 0 +#define FPSIMD_CONTEXT_SIZE_OFFSET 4 +#define FPSIMD_CONTEXT_FPSR_OFFSET 8 +#define FPSIMD_CONTEXT_FPCR_OFFSET 12 +#define FPSIMD_CONTEXT_VREGS_OFFSET 16 +#define FPSIMD_CONTEXT_SIZE 528 + +#define REGISTER_SIZE 8 +#define SIMD_REGISTER_SIZE 16 + +#elif defined(__i386__) + +#define MCONTEXT_GREGS_OFFSET 20 +#define MCONTEXT_GS_OFFSET (MCONTEXT_GREGS_OFFSET + 0*4) +#define MCONTEXT_FS_OFFSET (MCONTEXT_GREGS_OFFSET + 1*4) +#define MCONTEXT_ES_OFFSET (MCONTEXT_GREGS_OFFSET + 2*4) +#define MCONTEXT_DS_OFFSET (MCONTEXT_GREGS_OFFSET + 3*4) +#define MCONTEXT_EDI_OFFSET (MCONTEXT_GREGS_OFFSET + 4*4) +#define MCONTEXT_ESI_OFFSET (MCONTEXT_GREGS_OFFSET + 5*4) +#define MCONTEXT_EBP_OFFSET (MCONTEXT_GREGS_OFFSET + 6*4) +#define MCONTEXT_ESP_OFFSET (MCONTEXT_GREGS_OFFSET + 7*4) +#define MCONTEXT_EBX_OFFSET (MCONTEXT_GREGS_OFFSET + 8*4) +#define MCONTEXT_EDX_OFFSET (MCONTEXT_GREGS_OFFSET + 9*4) +#define MCONTEXT_ECX_OFFSET (MCONTEXT_GREGS_OFFSET + 10*4) +#define MCONTEXT_EAX_OFFSET (MCONTEXT_GREGS_OFFSET + 11*4) +#define MCONTEXT_TRAPNO_OFFSET (MCONTEXT_GREGS_OFFSET + 12*4) +#define MCONTEXT_ERR_OFFSET (MCONTEXT_GREGS_OFFSET + 13*4) +#define MCONTEXT_EIP_OFFSET (MCONTEXT_GREGS_OFFSET + 14*4) +#define MCONTEXT_CS_OFFSET (MCONTEXT_GREGS_OFFSET + 15*4) +#define MCONTEXT_EFL_OFFSET (MCONTEXT_GREGS_OFFSET + 16*4) +#define MCONTEXT_UESP_OFFSET (MCONTEXT_GREGS_OFFSET + 17*4) +#define MCONTEXT_SS_OFFSET (MCONTEXT_GREGS_OFFSET + 18*4) + +#define UCONTEXT_SIGMASK_OFFSET 108 + +#define UCONTEXT_FPREGS_OFFSET 96 +#if defined(__BIONIC__) +#define UCONTEXT_FPREGS_MEM_OFFSET 116 +#else +#define UCONTEXT_FPREGS_MEM_OFFSET 236 +#endif + +#elif defined(__mips__) + +#if _MIPS_SIM == _ABIO32 +#define MCONTEXT_PC_OFFSET 32 +#define MCONTEXT_GREGS_OFFSET 40 +#define MCONTEXT_FPREGS_OFFSET 296 +#define MCONTEXT_FPC_CSR 556 +#define UCONTEXT_SIGMASK_OFFSET 616 +#else +#define MCONTEXT_GREGS_OFFSET 40 +#define MCONTEXT_FPREGS_OFFSET 296 +#define MCONTEXT_PC_OFFSET 616 +#define MCONTEXT_FPC_CSR 624 +#define UCONTEXT_SIGMASK_OFFSET 640 +#endif + +#elif defined(__x86_64__) + +#define MCONTEXT_GREGS_OFFSET 40 +#define UCONTEXT_SIGMASK_OFFSET 296 + +#define MCONTEXT_GREGS_R8 40 +#define MCONTEXT_GREGS_R9 48 +#define MCONTEXT_GREGS_R10 56 +#define MCONTEXT_GREGS_R11 64 +#define MCONTEXT_GREGS_R12 72 +#define MCONTEXT_GREGS_R13 80 +#define MCONTEXT_GREGS_R14 88 +#define MCONTEXT_GREGS_R15 96 +#define MCONTEXT_GREGS_RDI 104 +#define MCONTEXT_GREGS_RSI 112 +#define MCONTEXT_GREGS_RBP 120 +#define MCONTEXT_GREGS_RBX 128 +#define MCONTEXT_GREGS_RDX 136 +#define MCONTEXT_GREGS_RAX 144 +#define MCONTEXT_GREGS_RCX 152 +#define MCONTEXT_GREGS_RSP 160 +#define MCONTEXT_GREGS_RIP 168 +#define MCONTEXT_FPREGS_PTR 224 +#if defined(__BIONIC__) +#define MCONTEXT_FPREGS_MEM 304 +#else +#define MCONTEXT_FPREGS_MEM 424 +#endif +#define FPREGS_OFFSET_MXCSR 24 + +#else +#error "This header has not been ported for your CPU" +#endif + +#endif // GOOGLEBREAKPAD_COMMON_ANDROID_UCONTEXT_CONSTANTS_H diff --git a/toolkit/crashreporter/google-breakpad/src/common/long_string_dictionary.cc b/toolkit/crashreporter/google-breakpad/src/common/long_string_dictionary.cc new file mode 100644 index 0000000000..46bbf61370 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/long_string_dictionary.cc @@ -0,0 +1,178 @@ +// Copyright (c) 2017, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include "common/long_string_dictionary.h" + +#include <assert.h> +#include <string.h> + +#include <algorithm> +#include <string> + +#include "common/simple_string_dictionary.h" + +#define arraysize(f) (sizeof(f) / sizeof(*f)) + +namespace { +// Suffixes for segment keys. +const char* const kSuffixes[] = {"__1", "__2", "__3", "__4", "__5", "__6", + "__7", "__8", "__9", "__10"}; +#if !defined(NDEBUG) +// The maximum suffix string length. +const size_t kMaxSuffixLength = 4; +#endif +} // namespace + +namespace google_breakpad { + +using std::string; + +void LongStringDictionary::SetKeyValue(const char* key, const char* value) { + assert(key); + if (!key) + return; + + RemoveKey(key); + + if (!value) { + return; + } + + // Key must not be an empty string. + assert(key[0] != '\0'); + if (key[0] == '\0') + return; + + // If the value is not valid for segmentation, forwards the key and the value + // to SetKeyValue of SimpleStringDictionary and returns. + size_t value_length = strlen(value); + if (value_length <= (value_size - 1)) { + SimpleStringDictionary::SetKeyValue(key, value); + return; + } + + size_t key_length = strlen(key); + assert(key_length + kMaxSuffixLength <= (key_size - 1)); + + char segment_key[key_size]; + char segment_value[value_size]; + + strcpy(segment_key, key); + + const char* remain_value = value; + size_t remain_value_length = strlen(value); + + for (unsigned long i = 0; i < arraysize(kSuffixes); i++) { + if (remain_value_length == 0) { + return; + } + + strcpy(segment_key + key_length, kSuffixes[i]); + + size_t segment_value_length = + std::min(remain_value_length, value_size - 1); + + strncpy(segment_value, remain_value, segment_value_length); + segment_value[segment_value_length] = '\0'; + + remain_value += segment_value_length; + remain_value_length -= segment_value_length; + + SimpleStringDictionary::SetKeyValue(segment_key, segment_value); + } +} + +bool LongStringDictionary::RemoveKey(const char* key) { + assert(key); + if (!key) + return false; + + if (SimpleStringDictionary::RemoveKey(key)) { + return true; + } + + size_t key_length = strlen(key); + assert(key_length + kMaxSuffixLength <= (key_size - 1)); + + char segment_key[key_size]; + strcpy(segment_key, key); + + unsigned long i = 0; + for (; i < arraysize(kSuffixes); i++) { + strcpy(segment_key + key_length, kSuffixes[i]); + if (!SimpleStringDictionary::RemoveKey(segment_key)) { + break; + } + } + return i != 0; +} + +const string LongStringDictionary::GetValueForKey(const char* key) const { + assert(key); + if (!key) + return ""; + + // Key must not be an empty string. + assert(key[0] != '\0'); + if (key[0] == '\0') + return ""; + + const char* value = SimpleStringDictionary::GetValueForKey(key); + if (value) + return string(value); + + size_t key_length = strlen(key); + assert(key_length + kMaxSuffixLength <= (key_size - 1)); + + bool found_segment = false; + char segment_key[key_size]; + string return_value; + + strcpy(segment_key, key); + for (unsigned long i = 0; i < arraysize(kSuffixes); i++) { + strcpy(segment_key + key_length, kSuffixes[i]); + + const char* segment_value = + SimpleStringDictionary::GetValueForKey(segment_key); + + if (segment_value != NULL) { + found_segment = true; + return_value.append(segment_value); + } else { + break; + } + } + + if (found_segment) { + return return_value; + } + return ""; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/long_string_dictionary.h b/toolkit/crashreporter/google-breakpad/src/common/long_string_dictionary.h new file mode 100644 index 0000000000..68bf03de2f --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/long_string_dictionary.h @@ -0,0 +1,87 @@ +// Copyright (c) 2017, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef COMMON_LONG_STRING_DICTIONARY_H_ +#define COMMON_LONG_STRING_DICTIONARY_H_ + +#include <string> + +#include "common/simple_string_dictionary.h" + +namespace google_breakpad { +// key_size is the maxium size that |key| can take in +// SimpleStringDictionary which is defined in simple_string_dictionary.h. +// +// value_size is the maxium size that |value| can take in +// SimpleStringDictionary which is defined in simple_string_dictionary.h. +// +// LongStringDictionary is a subclass of SimpleStringDictionary which supports +// longer values to be stored in the dictionary. The maximum length supported is +// (value_size - 1) * 10. +// +// For example, LongStringDictionary will store long value with key 'abc' into +// segment values with segment keys 'abc__1', 'abc__2', 'abc__3', ... +// +// Clients must avoid using the same suffixes as their key's suffix when +// LongStringDictionary is used. +class LongStringDictionary : public SimpleStringDictionary { + public: + // Stores |value| into |key|, or segment values into segment keys. The maxium + // number of segments is 10. If |value| can not be stored in 10 segments, it + // will be truncated. Replacing the existing value if |key| is already present + // and replacing the existing segment values if segment keys are already + // present. + // + // |key| must not be NULL. If the |value| need to be divided into segments, + // the lengh of |key| must be smaller enough so that lengths of segment keys + // which are key with suffixes are all samller than (key_size - 1). Currently, + // the max length of suffixes are 4. + // + // If |value| is NULL, the key and its corresponding segment keys are removed + // from the map. If there is no more space in the map, then the operation + // silently fails. + void SetKeyValue(const char* key, const char* value); + + // Given |key|, removes any associated value or associated segment values. + // |key| must not be NULL. If the key is not found, searchs its segment keys + // and removes corresponding segment values if found. + bool RemoveKey(const char* key); + + // Given |key|, returns its corresponding |value|. |key| must not be NULL. If + // the key is found, its corresponding |value| is returned. + // + // If no corresponding |value| is found, segment keys of the given |key| will + // be used to search for corresponding segment values. If segment values + // exist, assembled value from them is returned. If no segment value exists, + // NULL is returned. + const std::string GetValueForKey(const char* key) const; +}; +} // namespace google_breakpad + +#endif // COMMON_LONG_STRING_DICTIONARY_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/long_string_dictionary_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/long_string_dictionary_unittest.cc new file mode 100644 index 0000000000..f9b645ba7b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/long_string_dictionary_unittest.cc @@ -0,0 +1,301 @@ +// Copyright (c) 2017, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include <algorithm> +#include <string> + +#include "breakpad_googletest_includes.h" +#include "common/long_string_dictionary.h" + +namespace google_breakpad { + +using std::string; + +TEST(LongStringDictionary, LongStringDictionary) { + // Make a new dictionary + LongStringDictionary dict; + + // Set three distinct values on three keys + dict.SetKeyValue("key1", "value1"); + dict.SetKeyValue("key2", "value2"); + dict.SetKeyValue("key3", "value3"); + + EXPECT_EQ("value1", dict.GetValueForKey("key1")); + EXPECT_EQ("value2", dict.GetValueForKey("key2")); + EXPECT_EQ("value3", dict.GetValueForKey("key3")); + EXPECT_EQ(3u, dict.GetCount()); + // try an unknown key + EXPECT_EQ("", dict.GetValueForKey("key4")); + + // Remove a key + dict.RemoveKey("key3"); + + // Now make sure it's not there anymore + EXPECT_EQ("", dict.GetValueForKey("key3")); + + // Remove by setting value to NULL + dict.SetKeyValue("key2", NULL); + + // Now make sure it's not there anymore + EXPECT_EQ("", dict.GetValueForKey("key2")); +} + +// Add a bunch of values to the dictionary, remove some entries in the middle, +// and then add more. +TEST(LongStringDictionary, Iterator) { + LongStringDictionary* dict = new LongStringDictionary(); + ASSERT_TRUE(dict); + + char key[LongStringDictionary::key_size]; + char value[LongStringDictionary::value_size]; + + const int kDictionaryCapacity = LongStringDictionary::num_entries; + const int kPartitionIndex = kDictionaryCapacity - 5; + + // We assume at least this size in the tests below + ASSERT_GE(kDictionaryCapacity, 64); + + // We'll keep track of the number of key/value pairs we think should + // be in the dictionary + int expectedDictionarySize = 0; + + // Set a bunch of key/value pairs like key0/value0, key1/value1, ... + for (int i = 0; i < kPartitionIndex; ++i) { + sprintf(key, "key%d", i); + sprintf(value, "value%d", i); + dict->SetKeyValue(key, value); + } + expectedDictionarySize = kPartitionIndex; + + // set a couple of the keys twice (with the same value) - should be nop + dict->SetKeyValue("key2", "value2"); + dict->SetKeyValue("key4", "value4"); + dict->SetKeyValue("key15", "value15"); + + // Remove some random elements in the middle + dict->RemoveKey("key7"); + dict->RemoveKey("key18"); + dict->RemoveKey("key23"); + dict->RemoveKey("key31"); + expectedDictionarySize -= 4; // we just removed four key/value pairs + + // Set some more key/value pairs like key59/value59, key60/value60, ... + for (int i = kPartitionIndex; i < kDictionaryCapacity; ++i) { + sprintf(key, "key%d", i); + sprintf(value, "value%d", i); + dict->SetKeyValue(key, value); + } + expectedDictionarySize += kDictionaryCapacity - kPartitionIndex; + + // Now create an iterator on the dictionary + SimpleStringDictionary::Iterator iter(*dict); + + // We then verify that it iterates through exactly the number of + // key/value pairs we expect, and that they match one-for-one with what we + // would expect. The ordering of the iteration does not matter... + + // used to keep track of number of occurrences found for key/value pairs + int count[kDictionaryCapacity]; + memset(count, 0, sizeof(count)); + + int totalCount = 0; + + const SimpleStringDictionary::Entry* entry; + while ((entry = iter.Next())) { + totalCount++; + + // Extract keyNumber from a string of the form key<keyNumber> + int keyNumber; + sscanf(entry->key, "key%d", &keyNumber); + + // Extract valueNumber from a string of the form value<valueNumber> + int valueNumber; + sscanf(entry->value, "value%d", &valueNumber); + + // The value number should equal the key number since that's how we set them + EXPECT_EQ(keyNumber, valueNumber); + + // Key and value numbers should be in proper range: + // 0 <= keyNumber < kDictionaryCapacity + bool isKeyInGoodRange = (keyNumber >= 0 && keyNumber < kDictionaryCapacity); + bool isValueInGoodRange = + (valueNumber >= 0 && valueNumber < kDictionaryCapacity); + EXPECT_TRUE(isKeyInGoodRange); + EXPECT_TRUE(isValueInGoodRange); + + if (isKeyInGoodRange && isValueInGoodRange) { + ++count[keyNumber]; + } + } + + // Make sure each of the key/value pairs showed up exactly one time, except + // for the ones which we removed. + for (size_t i = 0; i < kDictionaryCapacity; ++i) { + // Skip over key7, key18, key23, and key31, since we removed them + if (!(i == 7 || i == 18 || i == 23 || i == 31)) { + EXPECT_EQ(count[i], 1); + } + } + + // Make sure the number of iterations matches the expected dictionary size. + EXPECT_EQ(totalCount, expectedDictionarySize); +} + +TEST(LongStringDictionary, AddRemove) { + LongStringDictionary dict; + dict.SetKeyValue("rob", "ert"); + dict.SetKeyValue("mike", "pink"); + dict.SetKeyValue("mark", "allays"); + + EXPECT_EQ(3u, dict.GetCount()); + EXPECT_EQ("ert", dict.GetValueForKey("rob")); + EXPECT_EQ("pink", dict.GetValueForKey("mike")); + EXPECT_EQ("allays", dict.GetValueForKey("mark")); + + dict.RemoveKey("mike"); + + EXPECT_EQ(2u, dict.GetCount()); + EXPECT_EQ("", dict.GetValueForKey("mike")); + + dict.SetKeyValue("mark", "mal"); + EXPECT_EQ(2u, dict.GetCount()); + EXPECT_EQ("mal", dict.GetValueForKey("mark")); + + dict.RemoveKey("mark"); + EXPECT_EQ(1u, dict.GetCount()); + EXPECT_EQ("", dict.GetValueForKey("mark")); +} + +TEST(LongStringDictionary, AddRemoveLongValue) { + LongStringDictionary dict; + + string long_value = string(256, 'x'); + dict.SetKeyValue("rob", long_value.c_str()); + + EXPECT_EQ(2u, dict.GetCount()); + + string long_value_part_1 = string(255, 'x'); + + EXPECT_EQ(long_value_part_1, dict.GetValueForKey("rob__1")); + EXPECT_EQ("x", dict.GetValueForKey("rob__2")); + + EXPECT_EQ(long_value, dict.GetValueForKey("rob")); + + dict.RemoveKey("rob"); + EXPECT_EQ(0u, dict.GetCount()); +} + +TEST(LongStringDictionary, AddRemoveSuperLongValue) { + LongStringDictionary dict; + + string long_value = string(255 * 10, 'x'); + dict.SetKeyValue("rob", long_value.c_str()); + + EXPECT_EQ(10u, dict.GetCount()); + + string long_value_part = string(255, 'x'); + + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__1")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__2")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__3")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__4")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__5")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__6")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__7")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__8")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__9")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__10")); + EXPECT_EQ(10u, dict.GetCount()); + + EXPECT_EQ(long_value, dict.GetValueForKey("rob")); + + dict.RemoveKey("rob"); + EXPECT_EQ(0u, dict.GetCount()); +} + +TEST(LongStringDictionary, TruncateSuperLongValue) { + LongStringDictionary dict; + + string long_value = string(255 * 11, 'x'); + dict.SetKeyValue("rob", long_value.c_str()); + + EXPECT_EQ(10u, dict.GetCount()); + + string long_value_part = string(255, 'x'); + + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__1")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__2")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__3")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__4")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__5")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__6")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__7")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__8")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__9")); + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__10")); + EXPECT_EQ(10u, dict.GetCount()); + + string expected_long_value = string(255 * 10, 'x'); + EXPECT_EQ(expected_long_value, dict.GetValueForKey("rob")); + + dict.RemoveKey("rob"); + EXPECT_EQ(0u, dict.GetCount()); +} + +TEST(LongStringDictionary, OverrideLongValue) { + LongStringDictionary dict; + + string long_value = string(255 * 10, 'x'); + dict.SetKeyValue("rob", long_value.c_str()); + + EXPECT_EQ(10u, dict.GetCount()); + EXPECT_EQ(long_value, dict.GetValueForKey("rob")); + + dict.SetKeyValue("rob", "short_value"); + + EXPECT_EQ(1u, dict.GetCount()); + EXPECT_EQ("short_value", dict.GetValueForKey("rob")); +} + +TEST(LongStringDictionary, OverrideShortValue) { + LongStringDictionary dict; + + dict.SetKeyValue("rob", "short_value"); + + EXPECT_EQ(1u, dict.GetCount()); + EXPECT_EQ("short_value", dict.GetValueForKey("rob")); + + string long_value = string(255 * 10, 'x'); + dict.SetKeyValue("rob", long_value.c_str()); + + EXPECT_EQ(10u, dict.GetCount()); + EXPECT_EQ(long_value, dict.GetValueForKey("rob")); +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/Breakpad.xcconfig b/toolkit/crashreporter/google-breakpad/src/common/mac/Breakpad.xcconfig new file mode 100644 index 0000000000..f091369081 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/Breakpad.xcconfig @@ -0,0 +1,52 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +GCC_C_LANGUAGE_STANDARD = c99 + +GCC_WARN_CHECK_SWITCH_STATEMENTS = YES +// TODO(nealsid): Get the code so we can turn on the 64_TO_32 warning. +GCC_WARN_64_TO_32_BIT_CONVERSION = NO +GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES +GCC_WARN_ABOUT_RETURN_TYPE = YES +GCC_WARN_MISSING_PARENTHESES = YES + +// Once https://bugs.chromium.org/p/google-breakpad/issues/detail?id=697 +// is fixed this should be reenabled. +//GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES +GCC_WARN_ABOUT_MISSING_NEWLINE = YES +GCC_WARN_SIGN_COMPARE = YES +GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES +GCC_WARN_UNDECLARED_SELECTOR = YES +GCC_WARN_UNKNOWN_PRAGMAS = YES +GCC_WARN_UNUSED_VARIABLE = YES +GCC_TREAT_WARNINGS_AS_ERRORS = YES + +DEBUG_INFORMATION_FORMAT = dwarf-with-dsym + +ALWAYS_SEARCH_USER_PATHS = NO diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/BreakpadDebug.xcconfig b/toolkit/crashreporter/google-breakpad/src/common/mac/BreakpadDebug.xcconfig new file mode 100644 index 0000000000..94cdd8cfca --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/BreakpadDebug.xcconfig @@ -0,0 +1,32 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include "Breakpad.xcconfig" + +GCC_OPTIMIZATION_LEVEL = 0 diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/BreakpadRelease.xcconfig b/toolkit/crashreporter/google-breakpad/src/common/mac/BreakpadRelease.xcconfig new file mode 100644 index 0000000000..920f277db8 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/BreakpadRelease.xcconfig @@ -0,0 +1,34 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include "Breakpad.xcconfig" + +GCC_OPTIMIZATION_LEVEL = s +GCC_WARN_UNINITIALIZED_AUTOS = YES +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) NDEBUG diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/GTMDefines.h b/toolkit/crashreporter/google-breakpad/src/common/mac/GTMDefines.h new file mode 100644 index 0000000000..fce2990a3e --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/GTMDefines.h @@ -0,0 +1,396 @@ +// +// GTMDefines.h +// +// Copyright 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +// ============================================================================ + +#include <AvailabilityMacros.h> +#include <TargetConditionals.h> + +#ifdef __OBJC__ +#include <Foundation/NSObjCRuntime.h> +#endif // __OBJC__ + +#if TARGET_OS_IPHONE +#include <Availability.h> +#endif // TARGET_OS_IPHONE + +// Not all __IPHONE_X macros defined in past SDKs +#ifndef __IPHONE_3_0 + #define __IPHONE_3_0 30000 +#endif +#ifndef __IPHONE_3_1 + #define __IPHONE_3_1 30100 +#endif +#ifndef __IPHONE_3_2 + #define __IPHONE_3_2 30200 +#endif +#ifndef __IPHONE_4_0 + #define __IPHONE_4_0 40000 +#endif +#ifndef __IPHONE_4_3 + #define __IPHONE_4_3 40300 +#endif +#ifndef __IPHONE_5_0 + #define __IPHONE_5_0 50000 +#endif + +// ---------------------------------------------------------------------------- +// CPP symbols that can be overridden in a prefix to control how the toolbox +// is compiled. +// ---------------------------------------------------------------------------- + + +// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and +// GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens +// when a validation fails. If you implement your own validators, you may want +// to control their internals using the same macros for consistency. +#ifndef GTM_CONTAINERS_VALIDATION_FAILED_ASSERT + #define GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 0 +#endif + +// Give ourselves a consistent way to do inlines. Apple's macros even use +// a few different actual definitions, so we're based off of the foundation +// one. +#if !defined(GTM_INLINE) + #if (defined (__GNUC__) && (__GNUC__ == 4)) || defined (__clang__) + #define GTM_INLINE static __inline__ __attribute__((always_inline)) + #else + #define GTM_INLINE static __inline__ + #endif +#endif + +// Give ourselves a consistent way of doing externs that links up nicely +// when mixing objc and objc++ +#if !defined (GTM_EXTERN) + #if defined __cplusplus + #define GTM_EXTERN extern "C" + #define GTM_EXTERN_C_BEGIN extern "C" { + #define GTM_EXTERN_C_END } + #else + #define GTM_EXTERN extern + #define GTM_EXTERN_C_BEGIN + #define GTM_EXTERN_C_END + #endif +#endif + +// Give ourselves a consistent way of exporting things if we have visibility +// set to hidden. +#if !defined (GTM_EXPORT) + #define GTM_EXPORT __attribute__((visibility("default"))) +#endif + +// Give ourselves a consistent way of declaring something as unused. This +// doesn't use __unused because that is only supported in gcc 4.2 and greater. +#if !defined (GTM_UNUSED) +#define GTM_UNUSED(x) ((void)(x)) +#endif + +// _GTMDevLog & _GTMDevAssert +// +// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for +// developer level errors. This implementation simply macros to NSLog/NSAssert. +// It is not intended to be a general logging/reporting system. +// +// Please see https://github.com/google/google-toolbox-for-mac/wiki/DevLogNAssert +// for a little more background on the usage of these macros. +// +// _GTMDevLog log some error/problem in debug builds +// _GTMDevAssert assert if conditon isn't met w/in a method/function +// in all builds. +// +// To replace this system, just provide different macro definitions in your +// prefix header. Remember, any implementation you provide *must* be thread +// safe since this could be called by anything in what ever situtation it has +// been placed in. +// + +// We only define the simple macros if nothing else has defined this. +#ifndef _GTMDevLog + +#ifdef DEBUG + #define _GTMDevLog(...) NSLog(__VA_ARGS__) +#else + #define _GTMDevLog(...) do { } while (0) +#endif + +#endif // _GTMDevLog + +#ifndef _GTMDevAssert +// we directly invoke the NSAssert handler so we can pass on the varargs +// (NSAssert doesn't have a macro we can use that takes varargs) +#if !defined(NS_BLOCK_ASSERTIONS) + #define _GTMDevAssert(condition, ...) \ + do { \ + if (!(condition)) { \ + [[NSAssertionHandler currentHandler] \ + handleFailureInFunction:[NSString stringWithUTF8String:__PRETTY_FUNCTION__] \ + file:[NSString stringWithUTF8String:__FILE__] \ + lineNumber:__LINE__ \ + description:__VA_ARGS__]; \ + } \ + } while(0) +#else // !defined(NS_BLOCK_ASSERTIONS) + #define _GTMDevAssert(condition, ...) do { } while (0) +#endif // !defined(NS_BLOCK_ASSERTIONS) + +#endif // _GTMDevAssert + +// _GTMCompileAssert +// _GTMCompileAssert is an assert that is meant to fire at compile time if you +// want to check things at compile instead of runtime. For example if you +// want to check that a wchar is 4 bytes instead of 2 you would use +// _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X) +// Note that the second "arg" is not in quotes, and must be a valid processor +// symbol in it's own right (no spaces, punctuation etc). + +// Wrapping this in an #ifndef allows external groups to define their own +// compile time assert scheme. +#ifndef _GTMCompileAssert + // We got this technique from here: + // http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html + + #define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg + #define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg) + #define _GTMCompileAssert(test, msg) \ + typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ] +#endif // _GTMCompileAssert + +// ---------------------------------------------------------------------------- +// CPP symbols defined based on the project settings so the GTM code has +// simple things to test against w/o scattering the knowledge of project +// setting through all the code. +// ---------------------------------------------------------------------------- + +// Provide a single constant CPP symbol that all of GTM uses for ifdefing +// iPhone code. +#if TARGET_OS_IPHONE // iPhone SDK + // For iPhone specific stuff + #define GTM_IPHONE_SDK 1 + #if TARGET_IPHONE_SIMULATOR + #define GTM_IPHONE_DEVICE 0 + #define GTM_IPHONE_SIMULATOR 1 + #else + #define GTM_IPHONE_DEVICE 1 + #define GTM_IPHONE_SIMULATOR 0 + #endif // TARGET_IPHONE_SIMULATOR + // By default, GTM has provided it's own unittesting support, define this + // to use the support provided by Xcode, especially for the Xcode4 support + // for unittesting. + #ifndef GTM_IPHONE_USE_SENTEST + #define GTM_IPHONE_USE_SENTEST 0 + #endif + #define GTM_MACOS_SDK 0 +#else + // For MacOS specific stuff + #define GTM_MACOS_SDK 1 + #define GTM_IPHONE_SDK 0 + #define GTM_IPHONE_SIMULATOR 0 + #define GTM_IPHONE_DEVICE 0 + #define GTM_IPHONE_USE_SENTEST 0 +#endif + +// Some of our own availability macros +#if GTM_MACOS_SDK +#define GTM_AVAILABLE_ONLY_ON_IPHONE UNAVAILABLE_ATTRIBUTE +#define GTM_AVAILABLE_ONLY_ON_MACOS +#else +#define GTM_AVAILABLE_ONLY_ON_IPHONE +#define GTM_AVAILABLE_ONLY_ON_MACOS UNAVAILABLE_ATTRIBUTE +#endif + +// GC was dropped by Apple, define the old constant incase anyone still keys +// off of it. +#ifndef GTM_SUPPORT_GC + #define GTM_SUPPORT_GC 0 +#endif + +// Some support for advanced clang static analysis functionality +// See http://clang-analyzer.llvm.org/annotations.html +#ifndef __has_feature // Optional. + #define __has_feature(x) 0 // Compatibility with non-clang compilers. +#endif + +#ifndef NS_RETURNS_RETAINED + #if __has_feature(attribute_ns_returns_retained) + #define NS_RETURNS_RETAINED __attribute__((ns_returns_retained)) + #else + #define NS_RETURNS_RETAINED + #endif +#endif + +#ifndef NS_RETURNS_NOT_RETAINED + #if __has_feature(attribute_ns_returns_not_retained) + #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) + #else + #define NS_RETURNS_NOT_RETAINED + #endif +#endif + +#ifndef CF_RETURNS_RETAINED + #if __has_feature(attribute_cf_returns_retained) + #define CF_RETURNS_RETAINED __attribute__((cf_returns_retained)) + #else + #define CF_RETURNS_RETAINED + #endif +#endif + +#ifndef CF_RETURNS_NOT_RETAINED + #if __has_feature(attribute_cf_returns_not_retained) + #define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained)) + #else + #define CF_RETURNS_NOT_RETAINED + #endif +#endif + +#ifndef NS_CONSUMED + #if __has_feature(attribute_ns_consumed) + #define NS_CONSUMED __attribute__((ns_consumed)) + #else + #define NS_CONSUMED + #endif +#endif + +#ifndef CF_CONSUMED + #if __has_feature(attribute_cf_consumed) + #define CF_CONSUMED __attribute__((cf_consumed)) + #else + #define CF_CONSUMED + #endif +#endif + +#ifndef NS_CONSUMES_SELF + #if __has_feature(attribute_ns_consumes_self) + #define NS_CONSUMES_SELF __attribute__((ns_consumes_self)) + #else + #define NS_CONSUMES_SELF + #endif +#endif + +// Defined on 10.6 and above. +#ifndef NS_FORMAT_ARGUMENT + #define NS_FORMAT_ARGUMENT(A) +#endif + +// Defined on 10.6 and above. +#ifndef NS_FORMAT_FUNCTION + #define NS_FORMAT_FUNCTION(F,A) +#endif + +// Defined on 10.6 and above. +#ifndef CF_FORMAT_ARGUMENT + #define CF_FORMAT_ARGUMENT(A) +#endif + +// Defined on 10.6 and above. +#ifndef CF_FORMAT_FUNCTION + #define CF_FORMAT_FUNCTION(F,A) +#endif + +#ifndef GTM_NONNULL + #if defined(__has_attribute) + #if __has_attribute(nonnull) + #define GTM_NONNULL(x) __attribute__((nonnull x)) + #else + #define GTM_NONNULL(x) + #endif + #else + #define GTM_NONNULL(x) + #endif +#endif + +// Invalidates the initializer from which it's called. +#ifndef GTMInvalidateInitializer + #if __has_feature(objc_arc) + #define GTMInvalidateInitializer() \ + do { \ + [self class]; /* Avoid warning of dead store to |self|. */ \ + _GTMDevAssert(NO, @"Invalid initializer."); \ + return nil; \ + } while (0) + #else + #define GTMInvalidateInitializer() \ + do { \ + [self release]; \ + _GTMDevAssert(NO, @"Invalid initializer."); \ + return nil; \ + } while (0) + #endif +#endif + +#ifndef GTMCFAutorelease + #if __has_feature(objc_arc) + #define GTMCFAutorelease(x) CFBridgingRelease(x) + #else + #define GTMCFAutorelease(x) ([(id)x autorelease]) + #endif +#endif + +#ifdef __OBJC__ + +// Declared here so that it can easily be used for logging tracking if +// necessary. See GTMUnitTestDevLog.h for details. +@class NSString; +GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...) NS_FORMAT_FUNCTION(1, 2); + +// Macro to allow you to create NSStrings out of other macros. +// #define FOO foo +// NSString *fooString = GTM_NSSTRINGIFY(FOO); +#if !defined (GTM_NSSTRINGIFY) + #define GTM_NSSTRINGIFY_INNER(x) @#x + #define GTM_NSSTRINGIFY(x) GTM_NSSTRINGIFY_INNER(x) +#endif + +// Macro to allow fast enumeration when building for 10.5 or later, and +// reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration +// does keys, so pick the right thing, nothing is done on the FastEnumeration +// side to be sure you're getting what you wanted. +#ifndef GTM_FOREACH_OBJECT + #if TARGET_OS_IPHONE + #define GTM_FOREACH_ENUMEREE(element, enumeration) \ + for (element in enumeration) + #define GTM_FOREACH_OBJECT(element, collection) \ + for (element in collection) + #define GTM_FOREACH_KEY(element, collection) \ + for (element in collection) + #else + #define GTM_FOREACH_ENUMEREE(element, enumeration) \ + for (NSEnumerator *_ ## element ## _enum = enumeration; \ + (element = [_ ## element ## _enum nextObject]) != nil; ) + #define GTM_FOREACH_OBJECT(element, collection) \ + GTM_FOREACH_ENUMEREE(element, [collection objectEnumerator]) + #define GTM_FOREACH_KEY(element, collection) \ + GTM_FOREACH_ENUMEREE(element, [collection keyEnumerator]) + #endif +#endif + +// ============================================================================ + +// GTM_SEL_STRING is for specifying selector (usually property) names to KVC +// or KVO methods. +// In debug it will generate warnings for undeclared selectors if +// -Wunknown-selector is turned on. +// In release it will have no runtime overhead. +#ifndef GTM_SEL_STRING + #ifdef DEBUG + #define GTM_SEL_STRING(selName) NSStringFromSelector(@selector(selName)) + #else + #define GTM_SEL_STRING(selName) @#selName + #endif // DEBUG +#endif // GTM_SEL_STRING + +#endif // __OBJC__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/GTMLogger.h b/toolkit/crashreporter/google-breakpad/src/common/mac/GTMLogger.h new file mode 100644 index 0000000000..c4fd140290 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/GTMLogger.h @@ -0,0 +1,504 @@ +// +// GTMLogger.h +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +// Key Abstractions +// ---------------- +// +// This file declares multiple classes and protocols that are used by the +// GTMLogger logging system. The 4 main abstractions used in this file are the +// following: +// +// * logger (GTMLogger) - The main logging class that users interact with. It +// has methods for logging at different levels and uses a log writer, a log +// formatter, and a log filter to get the job done. +// +// * log writer (GTMLogWriter) - Writes a given string to some log file, where +// a "log file" can be a physical file on disk, a POST over HTTP to some URL, +// or even some in-memory structure (e.g., a ring buffer). +// +// * log formatter (GTMLogFormatter) - Given a format string and arguments as +// a va_list, returns a single formatted NSString. A "formatted string" could +// be a string with the date prepended, a string with values in a CSV format, +// or even a string of XML. +// +// * log filter (GTMLogFilter) - Given a formatted log message as an NSString +// and the level at which the message is to be logged, this class will decide +// whether the given message should be logged or not. This is a flexible way +// to filter out messages logged at a certain level, messages that contain +// certain text, or filter nothing out at all. This gives the caller the +// flexibility to dynamically enable debug logging in Release builds. +// +// This file also declares some classes to handle the common log writer, log +// formatter, and log filter cases. Callers can also create their own writers, +// formatters, and filters and they can even build them on top of the ones +// declared here. Keep in mind that your custom writer/formatter/filter may be +// called from multiple threads, so it must be thread-safe. + +#import <Foundation/Foundation.h> +#import "GTMDefines.h" + +// Predeclaration of used protocols that are declared later in this file. +@protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter; + +// GTMLogger +// +// GTMLogger is the primary user-facing class for an object-oriented logging +// system. It is built on the concept of log formatters (GTMLogFormatter), log +// writers (GTMLogWriter), and log filters (GTMLogFilter). When a message is +// sent to a GTMLogger to log a message, the message is formatted using the log +// formatter, then the log filter is consulted to see if the message should be +// logged, and if so, the message is sent to the log writer to be written out. +// +// GTMLogger is intended to be a flexible and thread-safe logging solution. Its +// flexibility comes from the fact that GTMLogger instances can be customized +// with user defined formatters, filters, and writers. And these writers, +// filters, and formatters can be combined, stacked, and customized in arbitrary +// ways to suit the needs at hand. For example, multiple writers can be used at +// the same time, and a GTMLogger instance can even be used as another +// GTMLogger's writer. This allows for arbitrarily deep logging trees. +// +// A standard GTMLogger uses a writer that sends messages to standard out, a +// formatter that smacks a timestamp and a few other bits of interesting +// information on the message, and a filter that filters out debug messages from +// release builds. Using the standard log settings, a log message will look like +// the following: +// +// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] foo=<Foo: 0x123> +// +// The output contains the date and time of the log message, the name of the +// process followed by its process ID/thread ID, the log level at which the +// message was logged (in the previous example the level was 1: +// kGTMLoggerLevelDebug), and finally, the user-specified log message itself (in +// this case, the log message was @"foo=%@", foo). +// +// Multiple instances of GTMLogger can be created, each configured their own +// way. Though GTMLogger is not a singleton (in the GoF sense), it does provide +// access to a shared (i.e., globally accessible) GTMLogger instance. This makes +// it convenient for all code in a process to use the same GTMLogger instance. +// The shared GTMLogger instance can also be configured in an arbitrary, and +// these configuration changes will affect all code that logs through the shared +// instance. + +// +// Log Levels +// ---------- +// GTMLogger has 3 different log levels: Debug, Info, and Error. GTMLogger +// doesn't take any special action based on the log level; it simply forwards +// this information on to formatters, filters, and writers, each of which may +// optionally take action based on the level. Since log level filtering is +// performed at runtime, log messages are typically not filtered out at compile +// time. The exception to this rule is that calls to the GTMLoggerDebug() macro +// *ARE* filtered out of non-DEBUG builds. This is to be backwards compatible +// with behavior that many developers are currently used to. Note that this +// means that GTMLoggerDebug(@"hi") will be compiled out of Release builds, but +// [[GTMLogger sharedLogger] logDebug:@"hi"] will NOT be compiled out. +// +// Standard loggers are created with the GTMLogLevelFilter log filter, which +// filters out certain log messages based on log level, and some other settings. +// +// In addition to the -logDebug:, -logInfo:, and -logError: methods defined on +// GTMLogger itself, there are also C macros that make usage of the shared +// GTMLogger instance very convenient. These macros are: +// +// GTMLoggerDebug(...) +// GTMLoggerInfo(...) +// GTMLoggerError(...) +// +// Again, a notable feature of these macros is that GTMLogDebug() calls *will be +// compiled out of non-DEBUG builds*. +// +// Standard Loggers +// ---------------- +// GTMLogger has the concept of "standard loggers". A standard logger is simply +// a logger that is pre-configured with some standard/common writer, formatter, +// and filter combination. Standard loggers are created using the creation +// methods beginning with "standard". The alternative to a standard logger is a +// regular logger, which will send messages to stdout, with no special +// formatting, and no filtering. +// +// How do I use GTMLogger? +// ---------------------- +// The typical way you will want to use GTMLogger is to simply use the +// GTMLogger*() macros for logging from code. That way we can easily make +// changes to the GTMLogger class and simply update the macros accordingly. Only +// your application startup code (perhaps, somewhere in main()) should use the +// GTMLogger class directly in order to configure the shared logger, which all +// of the code using the macros will be using. Again, this is just the typical +// situation. +// +// To be complete, there are cases where you may want to use GTMLogger directly, +// or even create separate GTMLogger instances for some reason. That's fine, +// too. +// +// Examples +// -------- +// The following show some common GTMLogger use cases. +// +// 1. You want to log something as simply as possible. Also, this call will only +// appear in debug builds. In non-DEBUG builds it will be completely removed. +// +// GTMLoggerDebug(@"foo = %@", foo); +// +// 2. The previous example is similar to the following. The major difference is +// that the previous call (example 1) will be compiled out of Release builds +// but this statement will not be compiled out. +// +// [[GTMLogger sharedLogger] logDebug:@"foo = %@", foo]; +// +// 3. Send all logging output from the shared logger to a file. We do this by +// creating an NSFileHandle for writing associated with a file, and setting +// that file handle as the logger's writer. +// +// NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" +// create:YES]; +// [[GTMLogger sharedLogger] setWriter:f]; +// GTMLoggerError(@"hi"); // This will be sent to /tmp/f.log +// +// 4. Create a new GTMLogger that will log to a file. This example differs from +// the previous one because here we create a new GTMLogger that is different +// from the shared logger. +// +// GTMLogger *logger = [GTMLogger standardLoggerWithPath:@"/tmp/temp.log"]; +// [logger logInfo:@"hi temp log file"]; +// +// 5. Create a logger that writes to stdout and does NOT do any formatting to +// the log message. This might be useful, for example, when writing a help +// screen for a command-line tool to standard output. +// +// GTMLogger *logger = [GTMLogger logger]; +// [logger logInfo:@"%@ version 0.1 usage", progName]; +// +// 6. Send log output to stdout AND to a log file. The trick here is that +// NSArrays function as composite log writers, which means when an array is +// set as the log writer, it forwards all logging messages to all of its +// contained GTMLogWriters. +// +// // Create array of GTMLogWriters +// NSArray *writers = [NSArray arrayWithObjects: +// [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" create:YES], +// [NSFileHandle fileHandleWithStandardOutput], nil]; +// +// GTMLogger *logger = [GTMLogger standardLogger]; +// [logger setWriter:writers]; +// [logger logInfo:@"hi"]; // Output goes to stdout and /tmp/f.log +// +// For futher details on log writers, formatters, and filters, see the +// documentation below. +// +// NOTE: GTMLogger is application level logging. By default it does nothing +// with _GTMDevLog/_GTMDevAssert (see GTMDefines.h). An application can choose +// to bridge _GTMDevLog/_GTMDevAssert to GTMLogger by providing macro +// definitions in its prefix header (see GTMDefines.h for how one would do +// that). +// +@interface GTMLogger : NSObject { + @private + id<GTMLogWriter> writer_; + id<GTMLogFormatter> formatter_; + id<GTMLogFilter> filter_; +} + +// +// Accessors for the shared logger instance +// + +// Returns a shared/global standard GTMLogger instance. Callers should typically +// use this method to get a GTMLogger instance, unless they explicitly want +// their own instance to configure for their own needs. This is the only method +// that returns a shared instance; all the rest return new GTMLogger instances. ++ (id)sharedLogger; + +// Sets the shared logger instance to |logger|. Future calls to +sharedLogger +// will return |logger| instead. ++ (void)setSharedLogger:(GTMLogger *)logger; + +// +// Creation methods +// + +// Returns a new autoreleased GTMLogger instance that will log to stdout, using +// the GTMLogStandardFormatter, and the GTMLogLevelFilter filter. ++ (id)standardLogger; + +// Same as +standardLogger, but logs to stderr. ++ (id)standardLoggerWithStderr; + +// Same as +standardLogger but levels >= kGTMLoggerLevelError are routed to +// stderr, everything else goes to stdout. ++ (id)standardLoggerWithStdoutAndStderr; + +// Returns a new standard GTMLogger instance with a log writer that will +// write to the file at |path|, and will use the GTMLogStandardFormatter and +// GTMLogLevelFilter classes. If |path| does not exist, it will be created. ++ (id)standardLoggerWithPath:(NSString *)path; + +// Returns an autoreleased GTMLogger instance that will use the specified +// |writer|, |formatter|, and |filter|. ++ (id)loggerWithWriter:(id<GTMLogWriter>)writer + formatter:(id<GTMLogFormatter>)formatter + filter:(id<GTMLogFilter>)filter; + +// Returns an autoreleased GTMLogger instance that logs to stdout, with the +// basic formatter, and no filter. The returned logger differs from the logger +// returned by +standardLogger because this one does not do any filtering and +// does not do any special log formatting; this is the difference between a +// "regular" logger and a "standard" logger. ++ (id)logger; + +// Designated initializer. This method returns a GTMLogger initialized with the +// specified |writer|, |formatter|, and |filter|. See the setter methods below +// for what values will be used if nil is passed for a parameter. +- (id)initWithWriter:(id<GTMLogWriter>)writer + formatter:(id<GTMLogFormatter>)formatter + filter:(id<GTMLogFilter>)filter; + +// +// Logging methods +// + +// Logs a message at the debug level (kGTMLoggerLevelDebug). +- (void)logDebug:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2); +// Logs a message at the info level (kGTMLoggerLevelInfo). +- (void)logInfo:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2); +// Logs a message at the error level (kGTMLoggerLevelError). +- (void)logError:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2); +// Logs a message at the assert level (kGTMLoggerLevelAssert). +- (void)logAssert:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2); + + +// +// Accessors +// + +// Accessor methods for the log writer. If the log writer is set to nil, +// [NSFileHandle fileHandleWithStandardOutput] is used. +- (id<GTMLogWriter>)writer; +- (void)setWriter:(id<GTMLogWriter>)writer; + +// Accessor methods for the log formatter. If the log formatter is set to nil, +// GTMLogBasicFormatter is used. This formatter will format log messages in a +// plain printf style. +- (id<GTMLogFormatter>)formatter; +- (void)setFormatter:(id<GTMLogFormatter>)formatter; + +// Accessor methods for the log filter. If the log filter is set to nil, +// GTMLogNoFilter is used, which allows all log messages through. +- (id<GTMLogFilter>)filter; +- (void)setFilter:(id<GTMLogFilter>)filter; + +@end // GTMLogger + + +// Helper functions that are used by the convenience GTMLogger*() macros that +// enable the logging of function names. +@interface GTMLogger (GTMLoggerMacroHelpers) +- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... + NS_FORMAT_FUNCTION(2, 3); +- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... + NS_FORMAT_FUNCTION(2, 3); +- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... + NS_FORMAT_FUNCTION(2, 3); +- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... + NS_FORMAT_FUNCTION(2, 3); +@end // GTMLoggerMacroHelpers + + +// The convenience macros are only defined if they haven't already been defined. +#ifndef GTMLoggerInfo + +// Convenience macros that log to the shared GTMLogger instance. These macros +// are how users should typically log to GTMLogger. Notice that GTMLoggerDebug() +// calls will be compiled out of non-Debug builds. +#define GTMLoggerDebug(...) \ + [[GTMLogger sharedLogger] logFuncDebug:__func__ msg:__VA_ARGS__] +#define GTMLoggerInfo(...) \ + [[GTMLogger sharedLogger] logFuncInfo:__func__ msg:__VA_ARGS__] +#define GTMLoggerError(...) \ + [[GTMLogger sharedLogger] logFuncError:__func__ msg:__VA_ARGS__] +#define GTMLoggerAssert(...) \ + [[GTMLogger sharedLogger] logFuncAssert:__func__ msg:__VA_ARGS__] + +// If we're not in a debug build, remove the GTMLoggerDebug statements. This +// makes calls to GTMLoggerDebug "compile out" of Release builds +#ifndef DEBUG +#undef GTMLoggerDebug +#define GTMLoggerDebug(...) do {} while(0) +#endif + +#endif // !defined(GTMLoggerInfo) + +// Log levels. +typedef enum { + kGTMLoggerLevelUnknown, + kGTMLoggerLevelDebug, + kGTMLoggerLevelInfo, + kGTMLoggerLevelError, + kGTMLoggerLevelAssert, +} GTMLoggerLevel; + + +// +// Log Writers +// + +// Protocol to be implemented by a GTMLogWriter instance. +@protocol GTMLogWriter <NSObject> +// Writes the given log message to where the log writer is configured to write. +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level; +@end // GTMLogWriter + + +// Simple category on NSFileHandle that makes NSFileHandles valid log writers. +// This is convenient because something like, say, +fileHandleWithStandardError +// now becomes a valid log writer. Log messages are written to the file handle +// with a newline appended. +@interface NSFileHandle (GTMFileHandleLogWriter) <GTMLogWriter> +// Opens the file at |path| in append mode, and creates the file with |mode| +// if it didn't previously exist. ++ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode; +@end // NSFileHandle + + +// This category makes NSArray a GTMLogWriter that can be composed of other +// GTMLogWriters. This is the classic Composite GoF design pattern. When the +// GTMLogWriter -logMessage:level: message is sent to the array, the array +// forwards the message to all of its elements that implement the GTMLogWriter +// protocol. +// +// This is useful in situations where you would like to send log output to +// multiple log writers at the same time. Simply create an NSArray of the log +// writers you wish to use, then set the array as the "writer" for your +// GTMLogger instance. +@interface NSArray (GTMArrayCompositeLogWriter) <GTMLogWriter> +@end // GTMArrayCompositeLogWriter + + +// This category adapts the GTMLogger interface so that it can be used as a log +// writer; it's an "adapter" in the GoF Adapter pattern sense. +// +// This is useful when you want to configure a logger to log to a specific +// writer with a specific formatter and/or filter. But you want to also compose +// that with a different log writer that may have its own formatter and/or +// filter. +@interface GTMLogger (GTMLoggerLogWriter) <GTMLogWriter> +@end // GTMLoggerLogWriter + + +// +// Log Formatters +// + +// Protocol to be implemented by a GTMLogFormatter instance. +@protocol GTMLogFormatter <NSObject> +// Returns a formatted string using the format specified in |fmt| and the va +// args specified in |args|. +- (NSString *)stringForFunc:(NSString *)func + withFormat:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0); +@end // GTMLogFormatter + + +// A basic log formatter that formats a string the same way that NSLog (or +// printf) would. It does not do anything fancy, nor does it add any data of its +// own. +@interface GTMLogBasicFormatter : NSObject <GTMLogFormatter> + +// Helper method for prettying C99 __func__ and GCC __PRETTY_FUNCTION__ +- (NSString *)prettyNameForFunc:(NSString *)func; + +@end // GTMLogBasicFormatter + + +// A log formatter that formats the log string like the basic formatter, but +// also prepends a timestamp and some basic process info to the message, as +// shown in the following sample output. +// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] log mesage here +@interface GTMLogStandardFormatter : GTMLogBasicFormatter { + @private + NSDateFormatter *dateFormatter_; // yyyy-MM-dd HH:mm:ss.SSS + NSString *pname_; + pid_t pid_; +} +@end // GTMLogStandardFormatter + + +// +// Log Filters +// + +// Protocol to be imlemented by a GTMLogFilter instance. +@protocol GTMLogFilter <NSObject> +// Returns YES if |msg| at |level| should be filtered out; NO otherwise. +- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level; +@end // GTMLogFilter + + +// A log filter that filters messages at the kGTMLoggerLevelDebug level out of +// non-debug builds. Messages at the kGTMLoggerLevelInfo level are also filtered +// out of non-debug builds unless GTMVerboseLogging is set in the environment or +// the processes's defaults. Messages at the kGTMLoggerLevelError level are +// never filtered. +@interface GTMLogLevelFilter : NSObject <GTMLogFilter> +@end // GTMLogLevelFilter + +// A simple log filter that does NOT filter anything out; +// -filterAllowsMessage:level will always return YES. This can be a convenient +// way to enable debug-level logging in release builds (if you so desire). +@interface GTMLogNoFilter : NSObject <GTMLogFilter> +@end // GTMLogNoFilter + + +// Base class for custom level filters. Not for direct use, use the minimum +// or maximum level subclasses below. +@interface GTMLogAllowedLevelFilter : NSObject <GTMLogFilter> { + @private + NSIndexSet *allowedLevels_; +} +@end + +// A log filter that allows you to set a minimum log level. Messages below this +// level will be filtered. +@interface GTMLogMininumLevelFilter : GTMLogAllowedLevelFilter + +// Designated initializer, logs at levels < |level| will be filtered. +- (id)initWithMinimumLevel:(GTMLoggerLevel)level; + +@end + +// A log filter that allows you to set a maximum log level. Messages whose level +// exceeds this level will be filtered. This is really only useful if you have +// a composite GTMLogger that is sending the other messages elsewhere. +@interface GTMLogMaximumLevelFilter : GTMLogAllowedLevelFilter + +// Designated initializer, logs at levels > |level| will be filtered. +- (id)initWithMaximumLevel:(GTMLoggerLevel)level; + +@end + + +// For subclasses only +@interface GTMLogger (PrivateMethods) + +- (void)logInternalFunc:(const char *)func + format:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0); + +@end + diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/GTMLogger.m b/toolkit/crashreporter/google-breakpad/src/common/mac/GTMLogger.m new file mode 100644 index 0000000000..ebc5836a25 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/GTMLogger.m @@ -0,0 +1,611 @@ +// +// GTMLogger.m +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMLogger.h" +#import <fcntl.h> +#import <unistd.h> +#import <stdlib.h> +#import <pthread.h> + + +#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42) +// Some versions of GCC (4.2 and below AFAIK) aren't great about supporting +// -Wmissing-format-attribute +// when the function is anything more complex than foo(NSString *fmt, ...). +// You see the error inside the function when you turn ... into va_args and +// attempt to call another function (like vsprintf for example). +// So we just shut off the warning for this file. We reenable it at the end. +#pragma GCC diagnostic ignored "-Wmissing-format-attribute" +#endif // !__clang__ + +// Reference to the shared GTMLogger instance. This is not a singleton, it's +// just an easy reference to one shared instance. +static GTMLogger *gSharedLogger = nil; + + +@implementation GTMLogger + +// Returns a pointer to the shared logger instance. If none exists, a standard +// logger is created and returned. ++ (id)sharedLogger { + @synchronized(self) { + if (gSharedLogger == nil) { + gSharedLogger = [[self standardLogger] retain]; + } + } + return [[gSharedLogger retain] autorelease]; +} + ++ (void)setSharedLogger:(GTMLogger *)logger { + @synchronized(self) { + [gSharedLogger autorelease]; + gSharedLogger = [logger retain]; + } +} + ++ (id)standardLogger { + // Don't trust NSFileHandle not to throw + @try { + id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput]; + id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init] + autorelease]; + id<GTMLogFilter> filter = [[[GTMLogLevelFilter alloc] init] autorelease]; + return [[[self alloc] initWithWriter:writer + formatter:fr + filter:filter] autorelease]; + } + @catch (id e) { + // Ignored + } + return nil; +} + ++ (id)standardLoggerWithStderr { + // Don't trust NSFileHandle not to throw + @try { + id me = [self standardLogger]; + [me setWriter:[NSFileHandle fileHandleWithStandardError]]; + return me; + } + @catch (id e) { + // Ignored + } + return nil; +} + ++ (id)standardLoggerWithStdoutAndStderr { + // We're going to take advantage of the GTMLogger to GTMLogWriter adaptor + // and create a composite logger that an outer "standard" logger can use + // as a writer. Our inner loggers should apply no formatting since the main + // logger does that and we want the caller to be able to change formatters + // or add writers without knowing the inner structure of our composite. + + // Don't trust NSFileHandle not to throw + @try { + GTMLogBasicFormatter *formatter = [[[GTMLogBasicFormatter alloc] init] + autorelease]; + GTMLogger *stdoutLogger = + [self loggerWithWriter:[NSFileHandle fileHandleWithStandardOutput] + formatter:formatter + filter:[[[GTMLogMaximumLevelFilter alloc] + initWithMaximumLevel:kGTMLoggerLevelInfo] + autorelease]]; + GTMLogger *stderrLogger = + [self loggerWithWriter:[NSFileHandle fileHandleWithStandardError] + formatter:formatter + filter:[[[GTMLogMininumLevelFilter alloc] + initWithMinimumLevel:kGTMLoggerLevelError] + autorelease]]; + GTMLogger *compositeWriter = + [self loggerWithWriter:[NSArray arrayWithObjects: + stdoutLogger, stderrLogger, nil] + formatter:formatter + filter:[[[GTMLogNoFilter alloc] init] autorelease]]; + GTMLogger *outerLogger = [self standardLogger]; + [outerLogger setWriter:compositeWriter]; + return outerLogger; + } + @catch (id e) { + // Ignored + } + return nil; +} + ++ (id)standardLoggerWithPath:(NSString *)path { + @try { + NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644]; + if (fh == nil) return nil; + id me = [self standardLogger]; + [me setWriter:fh]; + return me; + } + @catch (id e) { + // Ignored + } + return nil; +} + ++ (id)loggerWithWriter:(id<GTMLogWriter>)writer + formatter:(id<GTMLogFormatter>)formatter + filter:(id<GTMLogFilter>)filter { + return [[[self alloc] initWithWriter:writer + formatter:formatter + filter:filter] autorelease]; +} + ++ (id)logger { + return [[[self alloc] init] autorelease]; +} + +- (id)init { + return [self initWithWriter:nil formatter:nil filter:nil]; +} + +- (id)initWithWriter:(id<GTMLogWriter>)writer + formatter:(id<GTMLogFormatter>)formatter + filter:(id<GTMLogFilter>)filter { + if ((self = [super init])) { + [self setWriter:writer]; + [self setFormatter:formatter]; + [self setFilter:filter]; + } + return self; +} + +- (void)dealloc { + // Unlikely, but |writer_| may be an NSFileHandle, which can throw + @try { + [formatter_ release]; + [filter_ release]; + [writer_ release]; + } + @catch (id e) { + // Ignored + } + [super dealloc]; +} + +- (id<GTMLogWriter>)writer { + return [[writer_ retain] autorelease]; +} + +- (void)setWriter:(id<GTMLogWriter>)writer { + @synchronized(self) { + [writer_ autorelease]; + writer_ = nil; + if (writer == nil) { + // Try to use stdout, but don't trust NSFileHandle + @try { + writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain]; + } + @catch (id e) { + // Leave |writer_| nil + } + } else { + writer_ = [writer retain]; + } + } +} + +- (id<GTMLogFormatter>)formatter { + return [[formatter_ retain] autorelease]; +} + +- (void)setFormatter:(id<GTMLogFormatter>)formatter { + @synchronized(self) { + [formatter_ autorelease]; + formatter_ = nil; + if (formatter == nil) { + @try { + formatter_ = [[GTMLogBasicFormatter alloc] init]; + } + @catch (id e) { + // Leave |formatter_| nil + } + } else { + formatter_ = [formatter retain]; + } + } +} + +- (id<GTMLogFilter>)filter { + return [[filter_ retain] autorelease]; +} + +- (void)setFilter:(id<GTMLogFilter>)filter { + @synchronized(self) { + [filter_ autorelease]; + filter_ = nil; + if (filter == nil) { + @try { + filter_ = [[GTMLogNoFilter alloc] init]; + } + @catch (id e) { + // Leave |filter_| nil + } + } else { + filter_ = [filter retain]; + } + } +} + +- (void)logDebug:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelDebug]; + va_end(args); +} + +- (void)logInfo:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelInfo]; + va_end(args); +} + +- (void)logError:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelError]; + va_end(args); +} + +- (void)logAssert:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelAssert]; + va_end(args); +} + +@end // GTMLogger + +@implementation GTMLogger (GTMLoggerMacroHelpers) + +- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelDebug]; + va_end(args); +} + +- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelInfo]; + va_end(args); +} + +- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelError]; + va_end(args); +} + +- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelAssert]; + va_end(args); +} + +@end // GTMLoggerMacroHelpers + +@implementation GTMLogger (PrivateMethods) + +- (void)logInternalFunc:(const char *)func + format:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level { + // Primary point where logging happens, logging should never throw, catch + // everything. + @try { + NSString *fname = func ? [NSString stringWithUTF8String:func] : nil; + NSString *msg = [formatter_ stringForFunc:fname + withFormat:fmt + valist:args + level:level]; + if (msg && [filter_ filterAllowsMessage:msg level:level]) + [writer_ logMessage:msg level:level]; + } + @catch (id e) { + // Ignored + } +} + +@end // PrivateMethods + + +@implementation NSFileHandle (GTMFileHandleLogWriter) + ++ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode { + int fd = -1; + if (path) { + int flags = O_WRONLY | O_APPEND | O_CREAT; + fd = open([path fileSystemRepresentation], flags, mode); + } + if (fd == -1) return nil; + return [[[self alloc] initWithFileDescriptor:fd + closeOnDealloc:YES] autorelease]; +} + +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { + @synchronized(self) { + // Closed pipes should not generate exceptions in our caller. Catch here + // as well [GTMLogger logInternalFunc:...] so that an exception in this + // writer does not prevent other writers from having a chance. + @try { + NSString *line = [NSString stringWithFormat:@"%@\n", msg]; + [self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]]; + } + @catch (id e) { + // Ignored + } + } +} + +@end // GTMFileHandleLogWriter + + +@implementation NSArray (GTMArrayCompositeLogWriter) + +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { + @synchronized(self) { + id<GTMLogWriter> child = nil; + GTM_FOREACH_OBJECT(child, self) { + if ([child conformsToProtocol:@protocol(GTMLogWriter)]) + [child logMessage:msg level:level]; + } + } +} + +@end // GTMArrayCompositeLogWriter + + +@implementation GTMLogger (GTMLoggerLogWriter) + +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { + switch (level) { + case kGTMLoggerLevelDebug: + [self logDebug:@"%@", msg]; + break; + case kGTMLoggerLevelInfo: + [self logInfo:@"%@", msg]; + break; + case kGTMLoggerLevelError: + [self logError:@"%@", msg]; + break; + case kGTMLoggerLevelAssert: + [self logAssert:@"%@", msg]; + break; + default: + // Ignore the message. + break; + } +} + +@end // GTMLoggerLogWriter + + +@implementation GTMLogBasicFormatter + +- (NSString *)prettyNameForFunc:(NSString *)func { + NSString *name = [func stringByTrimmingCharactersInSet: + [NSCharacterSet whitespaceAndNewlineCharacterSet]]; + NSString *function = @"(unknown)"; + if ([name length]) { + if (// Objective C __func__ and __PRETTY_FUNCTION__ + [name hasPrefix:@"-["] || [name hasPrefix:@"+["] || + // C++ __PRETTY_FUNCTION__ and other preadorned formats + [name hasSuffix:@")"]) { + function = name; + } else { + // Assume C99 __func__ + function = [NSString stringWithFormat:@"%@()", name]; + } + } + return function; +} + +- (NSString *)stringForFunc:(NSString *)func + withFormat:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level { + // Performance note: We may want to do a quick check here to see if |fmt| + // contains a '%', and if not, simply return 'fmt'. + if (!(fmt && args)) return nil; + return [[[NSString alloc] initWithFormat:fmt arguments:args] autorelease]; +} + +@end // GTMLogBasicFormatter + + +@implementation GTMLogStandardFormatter + +- (id)init { + if ((self = [super init])) { + dateFormatter_ = [[NSDateFormatter alloc] init]; + [dateFormatter_ setFormatterBehavior:NSDateFormatterBehavior10_4]; + [dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"]; + pname_ = [[[NSProcessInfo processInfo] processName] copy]; + pid_ = [[NSProcessInfo processInfo] processIdentifier]; + if (!(dateFormatter_ && pname_)) { + [self release]; + return nil; + } + } + return self; +} + +- (void)dealloc { + [dateFormatter_ release]; + [pname_ release]; + [super dealloc]; +} + +- (NSString *)stringForFunc:(NSString *)func + withFormat:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level { + NSString *tstamp = nil; + @synchronized (dateFormatter_) { + tstamp = [dateFormatter_ stringFromDate:[NSDate date]]; + } + return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@", + tstamp, pname_, pid_, pthread_self(), + level, [self prettyNameForFunc:func], + // |super| has guard for nil |fmt| and |args| + [super stringForFunc:func withFormat:fmt valist:args level:level]]; +} + +@end // GTMLogStandardFormatter + + +@implementation GTMLogLevelFilter + +// Check the environment and the user preferences for the GTMVerboseLogging key +// to see if verbose logging has been enabled. The environment variable will +// override the defaults setting, so check the environment first. +// COV_NF_START +static BOOL IsVerboseLoggingEnabled(void) { + static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging"; + NSString *value = [[[NSProcessInfo processInfo] environment] + objectForKey:kVerboseLoggingKey]; + if (value) { + // Emulate [NSString boolValue] for pre-10.5 + value = [value stringByTrimmingCharactersInSet: + [NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if ([[value uppercaseString] hasPrefix:@"Y"] || + [[value uppercaseString] hasPrefix:@"T"] || + [value intValue]) { + return YES; + } else { + return NO; + } + } + return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey]; +} +// COV_NF_END + +// In DEBUG builds, log everything. If we're not in a debug build we'll assume +// that we're in a Release build. +- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level { +#if defined(DEBUG) && DEBUG + return YES; +#endif + + BOOL allow = YES; + + switch (level) { + case kGTMLoggerLevelDebug: + allow = NO; + break; + case kGTMLoggerLevelInfo: + allow = IsVerboseLoggingEnabled(); + break; + case kGTMLoggerLevelError: + allow = YES; + break; + case kGTMLoggerLevelAssert: + allow = YES; + break; + default: + allow = YES; + break; + } + + return allow; +} + +@end // GTMLogLevelFilter + + +@implementation GTMLogNoFilter + +- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level { + return YES; // Allow everything through +} + +@end // GTMLogNoFilter + + +@implementation GTMLogAllowedLevelFilter + +// Private designated initializer +- (id)initWithAllowedLevels:(NSIndexSet *)levels { + self = [super init]; + if (self != nil) { + allowedLevels_ = [levels retain]; + // Cap min/max level + if (!allowedLevels_ || + // NSIndexSet is unsigned so only check the high bound, but need to + // check both first and last index because NSIndexSet appears to allow + // wraparound. + ([allowedLevels_ firstIndex] > kGTMLoggerLevelAssert) || + ([allowedLevels_ lastIndex] > kGTMLoggerLevelAssert)) { + [self release]; + return nil; + } + } + return self; +} + +- (id)init { + // Allow all levels in default init + return [self initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange: + NSMakeRange(kGTMLoggerLevelUnknown, + (kGTMLoggerLevelAssert - kGTMLoggerLevelUnknown + 1))]]; +} + +- (void)dealloc { + [allowedLevels_ release]; + [super dealloc]; +} + +- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level { + return [allowedLevels_ containsIndex:level]; +} + +@end // GTMLogAllowedLevelFilter + + +@implementation GTMLogMininumLevelFilter + +- (id)initWithMinimumLevel:(GTMLoggerLevel)level { + return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange: + NSMakeRange(level, + (kGTMLoggerLevelAssert - level + 1))]]; +} + +@end // GTMLogMininumLevelFilter + + +@implementation GTMLogMaximumLevelFilter + +- (id)initWithMaximumLevel:(GTMLoggerLevel)level { + return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange: + NSMakeRange(kGTMLoggerLevelUnknown, level + 1)]]; +} + +@end // GTMLogMaximumLevelFilter + +#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42) +// See comment at top of file. +#pragma GCC diagnostic error "-Wmissing-format-attribute" +#endif // !__clang__ + diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/HTTPMultipartUpload.h b/toolkit/crashreporter/google-breakpad/src/common/mac/HTTPMultipartUpload.h new file mode 100644 index 0000000000..0cea733e5d --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/HTTPMultipartUpload.h @@ -0,0 +1,61 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// HTTPMultipartUpload: A multipart/form-data HTTP uploader. +// Each parameter pair is sent as a boundary +// Each file is sent with a name field in addition to the filename and data +// The data will be sent synchronously. + +#import <Foundation/Foundation.h> + +@interface HTTPMultipartUpload : NSObject { + @protected + NSURL *url_; // The destination URL (STRONG) + NSMutableString *parameters_; // The JSON payload for sending data (STRONG) + NSMutableDictionary *files_; // Dictionary of name/file-path (STRONG) + NSString *boundary_; // The boundary string (STRONG) + NSHTTPURLResponse *response_; // The response from the send (STRONG) +} + +- (id)initWithURL:(NSURL *)url; + +- (NSURL *)URL; + +- (void)setParameters:(NSMutableString *)parameters; +- (NSMutableString *)parameters; + +- (void)addFileAtPath:(NSString *)path name:(NSString *)name; +- (void)addFileContents:(NSData *)data name:(NSString *)name; +- (NSDictionary *)files; + +// Set the data and return the response +- (NSData *)send:(NSError **)error; +- (NSHTTPURLResponse *)response; + +@end diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/HTTPMultipartUpload.m b/toolkit/crashreporter/google-breakpad/src/common/mac/HTTPMultipartUpload.m new file mode 100644 index 0000000000..d2480493f6 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/HTTPMultipartUpload.m @@ -0,0 +1,262 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#import "HTTPMultipartUpload.h" +#import "GTMDefines.h" + +// As -[NSString stringByAddingPercentEscapesUsingEncoding:] has been +// deprecated with iOS 9.0 / OS X 10.11 SDKs, this function re-implements it +// using -[NSString stringByAddingPercentEncodingWithAllowedCharacters:] when +// using those SDKs. +static NSString *PercentEncodeNSString(NSString *key) { +#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_9_0) && \ + __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0) || \ + (defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \ + defined(MAC_OS_X_VERSION_10_11) && \ + MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11) + return [key stringByAddingPercentEncodingWithAllowedCharacters: + [NSCharacterSet URLQueryAllowedCharacterSet]]; +#else + return [key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; +#endif +} + +// As -[NSURLConnection sendSynchronousRequest:returningResponse:error:] has +// been deprecated with iOS 9.0 / OS X 10.11 SDKs, this function re-implements +// it using -[NSURLSession dataTaskWithRequest:completionHandler:] which is +// available on iOS 7+. +static NSData *SendSynchronousNSURLRequest(NSURLRequest *req, + NSURLResponse **out_response, + NSError **out_error) { +#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_7_0) && \ + __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0) || \ + (defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \ + defined(MAC_OS_X_VERSION_10_11) && \ + MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11) + __block NSData* result = nil; + __block NSError* error = nil; + __block NSURLResponse* response = nil; + dispatch_semaphore_t wait_semaphone = dispatch_semaphore_create(0); + [[[NSURLSession sharedSession] + dataTaskWithRequest:req + completionHandler:^(NSData *data, + NSURLResponse *resp, + NSError *err) { + if (out_error) + error = [err retain]; + if (out_response) + response = [resp retain]; + if (err == nil) + result = [data retain]; + dispatch_semaphore_signal(wait_semaphone); + }] resume]; + dispatch_semaphore_wait(wait_semaphone, DISPATCH_TIME_FOREVER); + dispatch_release(wait_semaphone); + if (out_error) + *out_error = [error autorelease]; + if (out_response) + *out_response = [response autorelease]; + return [result autorelease]; +#else + return [NSURLConnection sendSynchronousRequest:req + returningResponse:out_response + error:out_error]; +#endif +} +@interface HTTPMultipartUpload(PrivateMethods) +- (NSString *)multipartBoundary; +// Each of the following methods will append the starting multipart boundary, +// but not the ending one. +- (NSData *)formDataForJSON:(NSString *)json; +- (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name; +- (NSData *)formDataForFile:(NSString *)file name:(NSString *)name; +@end + +@implementation HTTPMultipartUpload +//============================================================================= +#pragma mark - +#pragma mark || Private || +//============================================================================= +- (NSString *)multipartBoundary { + // The boundary has 27 '-' characters followed by 16 hex digits + return [NSString stringWithFormat:@"---------------------------%08X%08X", + rand(), rand()]; +} + +//============================================================================= +- (NSData *)formDataForJSON:(NSString *)json { + NSMutableData *data = [NSMutableData data]; + NSString *fmt = @"--%@\r\nContent-Disposition: form-data; name=\"extra\"; " + "filename=\"extra.json\"\r\nContent-Type: application/json\r\n\r\n"; + NSString *form = [NSString stringWithFormat:fmt, boundary_]; + + [data appendData:[form dataUsingEncoding:NSUTF8StringEncoding]]; + [data appendData:[json dataUsingEncoding:NSUTF8StringEncoding]]; + + return data; +} + +//============================================================================= +- (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name { + NSMutableData *data = [NSMutableData data]; + NSString *escaped = PercentEncodeNSString(name); + NSString *fmt = @"--%@\r\nContent-Disposition: form-data; name=\"%@\"; " + "filename=\"minidump.dmp\"\r\nContent-Type: application/octet-stream\r\n\r\n"; + NSString *pre = [NSString stringWithFormat:fmt, boundary_, escaped]; + + [data appendData:[pre dataUsingEncoding:NSUTF8StringEncoding]]; + [data appendData:contents]; + + return data; +} + +//============================================================================= +- (NSData *)formDataForFile:(NSString *)file name:(NSString *)name { + NSData *contents = [NSData dataWithContentsOfFile:file]; + + return [self formDataForFileContents:contents name:name]; +} + +//============================================================================= +#pragma mark - +#pragma mark || Public || +//============================================================================= +- (id)initWithURL:(NSURL *)url { + if ((self = [super init])) { + url_ = [url copy]; + boundary_ = [[self multipartBoundary] retain]; + files_ = [[NSMutableDictionary alloc] init]; + } + + return self; +} + +//============================================================================= +- (void)dealloc { + [url_ release]; + [parameters_ release]; + [files_ release]; + [boundary_ release]; + [response_ release]; + + [super dealloc]; +} + +//============================================================================= +- (NSURL *)URL { + return url_; +} + +//============================================================================= +- (void)setParameters:(NSMutableString *)parameters { + if (parameters != parameters_) { + [parameters_ release]; + parameters_ = [parameters mutableCopy]; + } +} + +//============================================================================= +- (NSMutableString *)parameters { + return parameters_; +} + +//============================================================================= +- (void)addFileAtPath:(NSString *)path name:(NSString *)name { + [files_ setObject:path forKey:name]; +} + +//============================================================================= +- (void)addFileContents:(NSData *)data name:(NSString *)name { + [files_ setObject:data forKey:name]; +} + +//============================================================================= +- (NSDictionary *)files { + return files_; +} + +//============================================================================= +- (NSData *)send:(NSError **)error { + NSMutableURLRequest *req = + [[NSMutableURLRequest alloc] + initWithURL:url_ cachePolicy:NSURLRequestUseProtocolCachePolicy + timeoutInterval:60.0]; + + NSMutableData *postBody = [NSMutableData data]; + + [req setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", + boundary_] forHTTPHeaderField:@"Content-type"]; + + // Add JSON parameters to the message + [postBody appendData:[self formDataForJSON:parameters_]]; + + // Add any files to the message + NSArray *fileNames = [files_ allKeys]; + for (NSString *name in fileNames) { + id fileOrData = [files_ objectForKey:name]; + NSData *fileData; + + // The object can be either the path to a file (NSString) or the contents + // of the file (NSData). + if ([fileOrData isKindOfClass:[NSData class]]) + fileData = [self formDataForFileContents:fileOrData name:name]; + else + fileData = [self formDataForFile:fileOrData name:name]; + + [postBody appendData:fileData]; + } + + NSString *epilogue = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary_]; + [postBody appendData:[epilogue dataUsingEncoding:NSUTF8StringEncoding]]; + + [req setHTTPBody:postBody]; + [req setHTTPMethod:@"POST"]; + + [response_ release]; + response_ = nil; + + NSData *data = nil; + if ([[req URL] isFileURL]) { + [[req HTTPBody] writeToURL:[req URL] options:0 error:error]; + } else { + NSURLResponse *response = nil; + data = SendSynchronousNSURLRequest(req, &response, error); + response_ = (NSHTTPURLResponse *)[response retain]; + } + [req release]; + + return data; +} + +//============================================================================= +- (NSHTTPURLResponse *)response { + return response_; +} + +@end diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/MachIPC.h b/toolkit/crashreporter/google-breakpad/src/common/mac/MachIPC.h new file mode 100644 index 0000000000..e3d4a0fdbc --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/MachIPC.h @@ -0,0 +1,303 @@ +// Copyright (c) 2007, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. +// +// MachIPC.h +// +// Some helpful wrappers for using Mach IPC calls + +#ifndef MACH_IPC_H__ +#define MACH_IPC_H__ + +#import <mach/mach.h> +#import <mach/message.h> +#import <servers/bootstrap.h> +#import <sys/types.h> + +#import <CoreServices/CoreServices.h> + +//============================================================================== +// DISCUSSION: +// +// The three main classes of interest are +// +// MachMessage: a wrapper for a mach message of the following form +// mach_msg_header_t +// mach_msg_body_t +// optional descriptors +// optional extra message data +// +// MachReceiveMessage and MachSendMessage subclass MachMessage +// and are used instead of MachMessage which is an abstract base class +// +// ReceivePort: +// Represents a mach port for which we have receive rights +// +// MachPortSender: +// Represents a mach port for which we have send rights +// +// Here's an example to receive a message on a server port: +// +// // This creates our named server port +// ReceivePort receivePort("com.Google.MyService"); +// +// MachReceiveMessage message; +// kern_return_t result = receivePort.WaitForMessage(&message, 0); +// +// if (result == KERN_SUCCESS && message.GetMessageID() == 57) { +// mach_port_t task = message.GetTranslatedPort(0); +// mach_port_t thread = message.GetTranslatedPort(1); +// +// char *messageString = message.GetData(); +// +// printf("message string = %s\n", messageString); +// } +// +// Here is an example of using these classes to send a message to this port: +// +// // send to already named port +// MachPortSender sender("com.Google.MyService"); +// MachSendMessage message(57); // our message ID is 57 +// +// // add some ports to be translated for us +// message.AddDescriptor(mach_task_self()); // our task +// message.AddDescriptor(mach_thread_self()); // this thread +// +// char messageString[] = "Hello server!\n"; +// message.SetData(messageString, strlen(messageString)+1); +// +// kern_return_t result = sender.SendMessage(message, 1000); // timeout 1000ms +// + +namespace google_breakpad { +#ifndef PRINT_MACH_RESULT +#define PRINT_MACH_RESULT(result_, message_) \ + printf(message_" %s (%d)\n", mach_error_string(result_), result_ ); +#endif + +//============================================================================== +// A wrapper class for mach_msg_port_descriptor_t (with same memory layout) +// with convenient constructors and accessors +class MachMsgPortDescriptor : public mach_msg_port_descriptor_t { + public: + // General-purpose constructor + MachMsgPortDescriptor(mach_port_t in_name, + mach_msg_type_name_t in_disposition) { + name = in_name; + pad1 = 0; + pad2 = 0; + disposition = in_disposition; + type = MACH_MSG_PORT_DESCRIPTOR; + } + + // For passing send rights to a port + MachMsgPortDescriptor(mach_port_t in_name) { + name = in_name; + pad1 = 0; + pad2 = 0; + disposition = MACH_MSG_TYPE_COPY_SEND; + type = MACH_MSG_PORT_DESCRIPTOR; + } + + // Copy constructor + MachMsgPortDescriptor(const MachMsgPortDescriptor& desc) { + name = desc.name; + pad1 = desc.pad1; + pad2 = desc.pad2; + disposition = desc.disposition; + type = desc.type; + } + + mach_port_t GetMachPort() const { + return name; + } + + mach_msg_type_name_t GetDisposition() const { + return disposition; + } + + // For convenience + operator mach_port_t() const { + return GetMachPort(); + } +}; + +//============================================================================== +// MachMessage: a wrapper for a mach message +// (mach_msg_header_t, mach_msg_body_t, extra data) +// +// This considerably simplifies the construction of a message for sending +// and the getting at relevant data and descriptors for the receiver. +// +// Currently the combined size of the descriptors plus data must be +// less than 1024. But as a benefit no memory allocation is necessary. +// +// TODO: could consider adding malloc() support for very large messages +// +// A MachMessage object is used by ReceivePort::WaitForMessage +// and MachPortSender::SendMessage +// +class MachMessage { + public: + + // The receiver of the message can retrieve the raw data this way + uint8_t *GetData() { + return GetDataLength() > 0 ? GetDataPacket()->data : NULL; + } + + uint32_t GetDataLength() { + return EndianU32_LtoN(GetDataPacket()->data_length); + } + + // The message ID may be used as a code identifying the type of message + void SetMessageID(int32_t message_id) { + GetDataPacket()->id = EndianU32_NtoL(message_id); + } + + int32_t GetMessageID() { return EndianU32_LtoN(GetDataPacket()->id); } + + // Adds a descriptor (typically a mach port) to be translated + // returns true if successful, otherwise not enough space + bool AddDescriptor(const MachMsgPortDescriptor &desc); + + int GetDescriptorCount() const { return body.msgh_descriptor_count; } + MachMsgPortDescriptor *GetDescriptor(int n); + + // Convenience method which gets the mach port described by the descriptor + mach_port_t GetTranslatedPort(int n); + + // A simple message is one with no descriptors + bool IsSimpleMessage() const { return GetDescriptorCount() == 0; } + + // Sets raw data for the message (returns false if not enough space) + bool SetData(void *data, int32_t data_length); + + protected: + // Consider this an abstract base class - must create an actual instance + // of MachReceiveMessage or MachSendMessage + + MachMessage() { + memset(this, 0, sizeof(MachMessage)); + } + + friend class ReceivePort; + friend class MachPortSender; + + // Represents raw data in our message + struct MessageDataPacket { + int32_t id; // little-endian + int32_t data_length; // little-endian + uint8_t data[1]; // actual size limited by sizeof(MachMessage) + }; + + MessageDataPacket* GetDataPacket(); + + void SetDescriptorCount(int n); + void SetDescriptor(int n, const MachMsgPortDescriptor &desc); + + // Returns total message size setting msgh_size in the header to this value + mach_msg_size_t CalculateSize(); + + mach_msg_header_t head; + mach_msg_body_t body; + uint8_t padding[1024]; // descriptors and data may be embedded here +}; + +//============================================================================== +// MachReceiveMessage and MachSendMessage are useful to separate the idea +// of a mach message being sent and being received, and adds increased type +// safety: +// ReceivePort::WaitForMessage() only accepts a MachReceiveMessage +// MachPortSender::SendMessage() only accepts a MachSendMessage + +//============================================================================== +class MachReceiveMessage : public MachMessage { + public: + MachReceiveMessage() : MachMessage() {} +}; + +//============================================================================== +class MachSendMessage : public MachMessage { + public: + MachSendMessage(int32_t message_id); +}; + +//============================================================================== +// Represents a mach port for which we have receive rights +class ReceivePort { + public: + // Creates a new mach port for receiving messages and registers a name for it + explicit ReceivePort(const char *receive_port_name); + + // Given an already existing mach port, use it. We take ownership of the + // port and deallocate it in our destructor. + explicit ReceivePort(mach_port_t receive_port); + + // Create a new mach port for receiving messages + ReceivePort(); + + ~ReceivePort(); + + // Waits on the mach port until message received or timeout + kern_return_t WaitForMessage(MachReceiveMessage *out_message, + mach_msg_timeout_t timeout); + + // The underlying mach port that we wrap + mach_port_t GetPort() const { return port_; } + + private: + ReceivePort(const ReceivePort&); // disable copy c-tor + + mach_port_t port_; + kern_return_t init_result_; +}; + +//============================================================================== +// Represents a mach port for which we have send rights +class MachPortSender { + public: + // get a port with send rights corresponding to a named registered service + explicit MachPortSender(const char *receive_port_name); + + + // Given an already existing mach port, use it. + explicit MachPortSender(mach_port_t send_port); + + kern_return_t SendMessage(MachSendMessage &message, + mach_msg_timeout_t timeout); + + private: + MachPortSender(const MachPortSender&); // disable copy c-tor + + mach_port_t send_port_; + kern_return_t init_result_; +}; + +} // namespace google_breakpad + +#endif // MACH_IPC_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/MachIPC.mm b/toolkit/crashreporter/google-breakpad/src/common/mac/MachIPC.mm new file mode 100644 index 0000000000..dc9773f77a --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/MachIPC.mm @@ -0,0 +1,306 @@ +// Copyright (c) 2007, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. +// +// MachIPC.mm +// Wrapper for mach IPC calls + +#import <stdio.h> +#import "MachIPC.h" +#include "common/mac/bootstrap_compat.h" + +namespace google_breakpad { +//============================================================================== +MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() { + head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); + + // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage() + head.msgh_local_port = MACH_PORT_NULL; + head.msgh_reserved = 0; + head.msgh_id = 0; + + SetDescriptorCount(0); // start out with no descriptors + + SetMessageID(message_id); + SetData(NULL, 0); // client may add data later +} + +//============================================================================== +// returns true if successful +bool MachMessage::SetData(void *data, + int32_t data_length) { + // first check to make sure we have enough space + size_t size = CalculateSize(); + size_t new_size = size + data_length; + + if (new_size > sizeof(MachMessage)) { + return false; // not enough space + } + + GetDataPacket()->data_length = EndianU32_NtoL(data_length); + if (data) memcpy(GetDataPacket()->data, data, data_length); + + CalculateSize(); + + return true; +} + +//============================================================================== +// calculates and returns the total size of the message +// Currently, the entire message MUST fit inside of the MachMessage +// messsage size <= sizeof(MachMessage) +mach_msg_size_t MachMessage::CalculateSize() { + size_t size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t); + + // add space for MessageDataPacket + int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3; + size += 2*sizeof(int32_t) + alignedDataLength; + + // add space for descriptors + size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor); + + head.msgh_size = static_cast<mach_msg_size_t>(size); + + return head.msgh_size; +} + +//============================================================================== +MachMessage::MessageDataPacket *MachMessage::GetDataPacket() { + size_t desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount(); + MessageDataPacket *packet = + reinterpret_cast<MessageDataPacket*>(padding + desc_size); + + return packet; +} + +//============================================================================== +void MachMessage::SetDescriptor(int n, + const MachMsgPortDescriptor &desc) { + MachMsgPortDescriptor *desc_array = + reinterpret_cast<MachMsgPortDescriptor*>(padding); + desc_array[n] = desc; +} + +//============================================================================== +// returns true if successful otherwise there was not enough space +bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) { + // first check to make sure we have enough space + int size = CalculateSize(); + size_t new_size = size + sizeof(MachMsgPortDescriptor); + + if (new_size > sizeof(MachMessage)) { + return false; // not enough space + } + + // unfortunately, we need to move the data to allow space for the + // new descriptor + u_int8_t *p = reinterpret_cast<u_int8_t*>(GetDataPacket()); + bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t)); + + SetDescriptor(GetDescriptorCount(), desc); + SetDescriptorCount(GetDescriptorCount() + 1); + + CalculateSize(); + + return true; +} + +//============================================================================== +void MachMessage::SetDescriptorCount(int n) { + body.msgh_descriptor_count = n; + + if (n > 0) { + head.msgh_bits |= MACH_MSGH_BITS_COMPLEX; + } else { + head.msgh_bits &= ~MACH_MSGH_BITS_COMPLEX; + } +} + +//============================================================================== +MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) { + if (n < GetDescriptorCount()) { + MachMsgPortDescriptor *desc = + reinterpret_cast<MachMsgPortDescriptor*>(padding); + return desc + n; + } + + return nil; +} + +//============================================================================== +mach_port_t MachMessage::GetTranslatedPort(int n) { + if (n < GetDescriptorCount()) { + return GetDescriptor(n)->GetMachPort(); + } + return MACH_PORT_NULL; +} + +#pragma mark - + +//============================================================================== +// create a new mach port for receiving messages and register a name for it +ReceivePort::ReceivePort(const char *receive_port_name) { + mach_port_t current_task = mach_task_self(); + + init_result_ = mach_port_allocate(current_task, + MACH_PORT_RIGHT_RECEIVE, + &port_); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = mach_port_insert_right(current_task, + port_, + port_, + MACH_MSG_TYPE_MAKE_SEND); + + if (init_result_ != KERN_SUCCESS) + return; + + mach_port_t task_bootstrap_port = 0; + init_result_ = task_get_bootstrap_port(current_task, &task_bootstrap_port); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = breakpad::BootstrapRegister( + bootstrap_port, + const_cast<char*>(receive_port_name), + port_); +} + +//============================================================================== +// create a new mach port for receiving messages +ReceivePort::ReceivePort() { + mach_port_t current_task = mach_task_self(); + + init_result_ = mach_port_allocate(current_task, + MACH_PORT_RIGHT_RECEIVE, + &port_); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = mach_port_insert_right(current_task, + port_, + port_, + MACH_MSG_TYPE_MAKE_SEND); +} + +//============================================================================== +// Given an already existing mach port, use it. We take ownership of the +// port and deallocate it in our destructor. +ReceivePort::ReceivePort(mach_port_t receive_port) + : port_(receive_port), + init_result_(KERN_SUCCESS) { +} + +//============================================================================== +ReceivePort::~ReceivePort() { + if (init_result_ == KERN_SUCCESS) + mach_port_deallocate(mach_task_self(), port_); +} + +//============================================================================== +kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message, + mach_msg_timeout_t timeout) { + if (!out_message) { + return KERN_INVALID_ARGUMENT; + } + + // return any error condition encountered in constructor + if (init_result_ != KERN_SUCCESS) + return init_result_; + + out_message->head.msgh_bits = 0; + out_message->head.msgh_local_port = port_; + out_message->head.msgh_remote_port = MACH_PORT_NULL; + out_message->head.msgh_reserved = 0; + out_message->head.msgh_id = 0; + + mach_msg_option_t options = MACH_RCV_MSG; + if (timeout != MACH_MSG_TIMEOUT_NONE) + options |= MACH_RCV_TIMEOUT; + kern_return_t result = mach_msg(&out_message->head, + options, + 0, + sizeof(MachMessage), + port_, + timeout, // timeout in ms + MACH_PORT_NULL); + + return result; +} + +#pragma mark - + +//============================================================================== +// get a port with send rights corresponding to a named registered service +MachPortSender::MachPortSender(const char *receive_port_name) { + mach_port_t task_bootstrap_port = 0; + init_result_ = task_get_bootstrap_port(mach_task_self(), + &task_bootstrap_port); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = bootstrap_look_up(task_bootstrap_port, + const_cast<char*>(receive_port_name), + &send_port_); +} + +//============================================================================== +MachPortSender::MachPortSender(mach_port_t send_port) + : send_port_(send_port), + init_result_(KERN_SUCCESS) { +} + +//============================================================================== +kern_return_t MachPortSender::SendMessage(MachSendMessage &message, + mach_msg_timeout_t timeout) { + if (message.head.msgh_size == 0) { + return KERN_INVALID_VALUE; // just for safety -- never should occur + }; + + if (init_result_ != KERN_SUCCESS) + return init_result_; + + message.head.msgh_remote_port = send_port_; + + kern_return_t result = mach_msg(&message.head, + MACH_SEND_MSG | MACH_SEND_TIMEOUT, + message.head.msgh_size, + 0, + MACH_PORT_NULL, + timeout, // timeout in ms + MACH_PORT_NULL); + + return result; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/arch_utilities.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/arch_utilities.cc new file mode 100644 index 0000000000..c0e4bac54b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/arch_utilities.cc @@ -0,0 +1,264 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include "common/mac/arch_utilities.h" + +#include <mach-o/arch.h> +#include <mach-o/fat.h> +#include <stdio.h> +#include <string.h> + +#ifndef CPU_SUBTYPE_ARM_V7S +#define CPU_SUBTYPE_ARM_V7S (static_cast<cpu_subtype_t>(11)) +#endif // CPU_SUBTYPE_ARM_V7S + +#ifndef CPU_TYPE_ARM64 +#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) +#endif // CPU_TYPE_ARM64 + +#ifndef CPU_SUBTYPE_ARM64_ALL +#define CPU_SUBTYPE_ARM64_ALL (static_cast<cpu_subtype_t>(0)) +#endif // CPU_SUBTYPE_ARM64_ALL + +#ifndef CPU_SUBTYPE_ARM64_E +#define CPU_SUBTYPE_ARM64_E (static_cast<cpu_subtype_t>(2)) +#endif // CPU_SUBTYPE_ARM64_E + +namespace { + +const NXArchInfo* ArchInfo_arm64(cpu_subtype_t cpu_subtype) { + const char* name = NULL; + switch (cpu_subtype) { + case CPU_SUBTYPE_ARM64_ALL: + name = "arm64"; + break; + case CPU_SUBTYPE_ARM64_E: + name = "arm64e"; + break; + default: + return NULL; + } + + NXArchInfo* arm64 = new NXArchInfo; + *arm64 = *NXGetArchInfoFromCpuType(CPU_TYPE_ARM, + CPU_SUBTYPE_ARM_V7); + arm64->name = name; + arm64->cputype = CPU_TYPE_ARM64; + arm64->cpusubtype = cpu_subtype; + arm64->description = "arm 64"; + return arm64; +} + +const NXArchInfo* ArchInfo_armv7s() { + NXArchInfo* armv7s = new NXArchInfo; + *armv7s = *NXGetArchInfoFromCpuType(CPU_TYPE_ARM, + CPU_SUBTYPE_ARM_V7); + armv7s->name = "armv7s"; + armv7s->cpusubtype = CPU_SUBTYPE_ARM_V7S; + armv7s->description = "arm v7s"; + return armv7s; +} + +} // namespace + +namespace google_breakpad { + +const NXArchInfo* BreakpadGetArchInfoFromName(const char* arch_name) { + // TODO: Remove this when the OS knows about arm64. + if (!strcmp("arm64", arch_name)) + return BreakpadGetArchInfoFromCpuType(CPU_TYPE_ARM64, + CPU_SUBTYPE_ARM64_ALL); + + if (!strcmp("arm64e", arch_name)) + return BreakpadGetArchInfoFromCpuType(CPU_TYPE_ARM64, + CPU_SUBTYPE_ARM64_E); + + // TODO: Remove this when the OS knows about armv7s. + if (!strcmp("armv7s", arch_name)) + return BreakpadGetArchInfoFromCpuType(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S); + + return NXGetArchInfoFromName(arch_name); +} + +const NXArchInfo* BreakpadGetArchInfoFromCpuType(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype) { + // TODO: Remove this when the OS knows about arm64. + if (cpu_type == CPU_TYPE_ARM64 && cpu_subtype == CPU_SUBTYPE_ARM64_ALL) { + static const NXArchInfo* arm64 = ArchInfo_arm64(cpu_subtype); + return arm64; + } + + if (cpu_type == CPU_TYPE_ARM64 && cpu_subtype == CPU_SUBTYPE_ARM64_E) { + static const NXArchInfo* arm64e = ArchInfo_arm64(cpu_subtype); + return arm64e; + } + + // TODO: Remove this when the OS knows about armv7s. + if (cpu_type == CPU_TYPE_ARM && cpu_subtype == CPU_SUBTYPE_ARM_V7S) { + static const NXArchInfo* armv7s = ArchInfo_armv7s(); + return armv7s; + } + + return NXGetArchInfoFromCpuType(cpu_type, cpu_subtype); +} + +} // namespace google_breakpad + +#ifndef __APPLE__ +namespace { + +enum Architecture { + kArch_i386 = 0, + kArch_x86_64, + kArch_x86_64h, + kArch_arm, + kArch_arm64, + kArch_arm64e, + kArch_ppc, + // This must be last. + kNumArchitectures +}; + +// enum Architecture above and kKnownArchitectures below +// must be kept in sync. +const NXArchInfo kKnownArchitectures[] = { + { + "i386", + CPU_TYPE_I386, + CPU_SUBTYPE_I386_ALL, + NX_LittleEndian, + "Intel 80x86" + }, + { + "x86_64", + CPU_TYPE_X86_64, + CPU_SUBTYPE_X86_64_ALL, + NX_LittleEndian, + "Intel x86-64" + }, + { + "x86_64h", + CPU_TYPE_X86_64, + CPU_SUBTYPE_X86_64_H, + NX_LittleEndian, + "Intel x86-64h Haswell" + }, + { + "arm", + CPU_TYPE_ARM, + CPU_SUBTYPE_ARM_ALL, + NX_LittleEndian, + "ARM" + }, + { + "arm64", + CPU_TYPE_ARM64, + CPU_SUBTYPE_ARM64_ALL, + NX_LittleEndian, + "ARM64" + }, + { + "arm64e", + CPU_TYPE_ARM64, + CPU_SUBTYPE_ARM64_E, + NX_LittleEndian, + "ARM64e" + }, + { + "ppc", + CPU_TYPE_POWERPC, + CPU_SUBTYPE_POWERPC_ALL, + NX_BigEndian, + "PowerPC" + } +}; + +} // namespace + +const NXArchInfo *NXGetLocalArchInfo(void) { + Architecture arch; +#if defined(__i386__) + arch = kArch_i386; +#elif defined(__x86_64__) + arch = kArch_x86_64; +#elif defined(__arm64) + arch = kArch_arm64; +#elif defined(__arm__) + arch = kArch_arm; +#elif defined(__powerpc__) + arch = kArch_ppc; +#else + #error "Unsupported CPU architecture" +#endif + return &kKnownArchitectures[arch]; +} + +const NXArchInfo *NXGetArchInfoFromName(const char *name) { + for (int arch = 0; arch < kNumArchitectures; ++arch) { + if (!strcmp(name, kKnownArchitectures[arch].name)) { + return &kKnownArchitectures[arch]; + } + } + return NULL; +} + +const NXArchInfo *NXGetArchInfoFromCpuType(cpu_type_t cputype, + cpu_subtype_t cpusubtype) { + const NXArchInfo *candidate = NULL; + for (int arch = 0; arch < kNumArchitectures; ++arch) { + if (kKnownArchitectures[arch].cputype == cputype) { + if (kKnownArchitectures[arch].cpusubtype == cpusubtype) { + return &kKnownArchitectures[arch]; + } + if (!candidate) { + candidate = &kKnownArchitectures[arch]; + } + } + } + return candidate; +} + +struct fat_arch *NXFindBestFatArch(cpu_type_t cputype, + cpu_subtype_t cpusubtype, + struct fat_arch *fat_archs, + uint32_t nfat_archs) { + struct fat_arch *candidate = NULL; + for (uint32_t f = 0; f < nfat_archs; ++f) { + if (fat_archs[f].cputype == cputype) { + if (fat_archs[f].cpusubtype == cpusubtype) { + return &fat_archs[f]; + } + if (!candidate) { + candidate = &fat_archs[f]; + } + } + } + return candidate; +} +#endif // !__APPLE__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/arch_utilities.h b/toolkit/crashreporter/google-breakpad/src/common/mac/arch_utilities.h new file mode 100644 index 0000000000..397c1f5873 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/arch_utilities.h @@ -0,0 +1,47 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// arch_utilities.h: Utilities for architecture introspection for Mac platform. + +#ifndef COMMON_MAC_ARCH_UTILITIES_H__ +#define COMMON_MAC_ARCH_UTILITIES_H__ + +#include <mach-o/arch.h> + +namespace google_breakpad { + +// Custom implementation of |NXGetArchInfoFromName| and +// |NXGetArchInfoFromCpuType| that handle newer CPU on older OSes. +const NXArchInfo* BreakpadGetArchInfoFromName(const char* arch_name); +const NXArchInfo* BreakpadGetArchInfoFromCpuType(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype); + +} // namespace google_breakpad + +#endif // COMMON_MAC_ARCH_UTILITIES_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/bootstrap_compat.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/bootstrap_compat.cc new file mode 100644 index 0000000000..d875d95b5f --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/bootstrap_compat.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include "common/mac/bootstrap_compat.h" + +namespace breakpad { + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +kern_return_t BootstrapRegister(mach_port_t bp, + name_t service_name, + mach_port_t sp) { + return bootstrap_register(bp, service_name, sp); +} +#pragma GCC diagnostic warning "-Wdeprecated-declarations" + +} // namesapce breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/bootstrap_compat.h b/toolkit/crashreporter/google-breakpad/src/common/mac/bootstrap_compat.h new file mode 100644 index 0000000000..8ca7357c34 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/bootstrap_compat.h @@ -0,0 +1,54 @@ +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef COMMON_MAC_BOOTSTRAP_COMPAT_H_ +#define COMMON_MAC_BOOTSTRAP_COMPAT_H_ + +#include <servers/bootstrap.h> + +namespace breakpad { + +// Wrapper for bootstrap_register to avoid deprecation warnings. +// +// In 10.6, it's possible to call bootstrap_check_in as the one-stop-shop for +// handling what bootstrap_register is used for. In 10.5, bootstrap_check_in +// can't check in a service whose name has not yet been registered, despite +// bootstrap_register being marked as deprecated in that OS release. Breakpad +// needs to register new service names, and in 10.5, calling +// bootstrap_register is the only way to achieve that. Attempts to call +// bootstrap_check_in for a new service name on 10.5 will result in +// BOOTSTRAP_UNKNOWN_SERVICE being returned rather than registration of the +// new service name. +kern_return_t BootstrapRegister(mach_port_t bp, + name_t service_name, + mach_port_t sp); + +} // namespace breakpad + +#endif // COMMON_MAC_BOOTSTRAP_COMPAT_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/byteswap.h b/toolkit/crashreporter/google-breakpad/src/common/mac/byteswap.h new file mode 100644 index 0000000000..b7bbc0b95b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/byteswap.h @@ -0,0 +1,73 @@ +// -*- mode: c++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jim@mozilla.com> <jimb@red-bean.com> + +// byteswap.h: Overloaded functions for conveniently byteswapping values. + +#ifndef COMMON_MAC_BYTESWAP_H_ +#define COMMON_MAC_BYTESWAP_H_ + +#ifdef __APPLE__ +#include <libkern/OSByteOrder.h> + +static inline uint16_t ByteSwap(uint16_t v) { return OSSwapInt16(v); } +static inline uint32_t ByteSwap(uint32_t v) { return OSSwapInt32(v); } +static inline uint64_t ByteSwap(uint64_t v) { return OSSwapInt64(v); } +static inline int16_t ByteSwap(int16_t v) { return OSSwapInt16(v); } +static inline int32_t ByteSwap(int32_t v) { return OSSwapInt32(v); } +static inline int64_t ByteSwap(int64_t v) { return OSSwapInt64(v); } + +#elif defined(__linux__) +// For NXByteOrder +#include <architecture/byte_order.h> +#include <stdint.h> +#include <endian.h> +#include_next <byteswap.h> + +static inline uint16_t ByteSwap(uint16_t v) { return bswap_16(v); } +static inline uint32_t ByteSwap(uint32_t v) { return bswap_32(v); } +static inline uint64_t ByteSwap(uint64_t v) { return bswap_64(v); } +static inline int16_t ByteSwap(int16_t v) { return bswap_16(v); } +static inline int32_t ByteSwap(int32_t v) { return bswap_32(v); } +static inline int64_t ByteSwap(int64_t v) { return bswap_64(v); } + +static inline NXByteOrder NXHostByteOrder() { +#ifdef __LITTLE_ENDIAN + return NX_LittleEndian; +#else + return NX_BigEndian; +#endif +} + +#endif // __APPLE__ + +#endif // COMMON_MAC_BYTESWAP_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.cc new file mode 100644 index 0000000000..3c48f23528 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.cc @@ -0,0 +1,679 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// dump_syms.cc: Create a symbol file for use with minidumps + +#include "common/mac/dump_syms.h" + +#include <assert.h> +#include <dirent.h> +#include <errno.h> +#include <mach-o/arch.h> +#include <mach-o/fat.h> +#include <stdint.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <ostream> +#include <string> +#include <vector> + +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/dwarf2reader.h" +#include "common/dwarf_cfi_to_module.h" +#include "common/dwarf_cu_to_module.h" +#include "common/dwarf_line_to_module.h" +#include "common/dwarf_range_list_handler.h" +#include "common/mac/file_id.h" +#include "common/mac/arch_utilities.h" +#include "common/mac/macho_reader.h" +#include "common/module.h" +#include "common/path_helper.h" +#include "common/scoped_ptr.h" +#include "common/stabs_reader.h" +#include "common/stabs_to_module.h" +#include "common/symbol_data.h" + +#ifndef CPU_TYPE_ARM +#define CPU_TYPE_ARM (static_cast<cpu_type_t>(12)) +#endif // CPU_TYPE_ARM + +#ifndef CPU_TYPE_ARM64 +#define CPU_TYPE_ARM64 (static_cast<cpu_type_t>(16777228)) +#endif // CPU_TYPE_ARM64 + +using dwarf2reader::ByteReader; +using google_breakpad::DwarfCUToModule; +using google_breakpad::DwarfLineToModule; +using google_breakpad::DwarfRangeListHandler; +using google_breakpad::FileID; +using google_breakpad::mach_o::FatReader; +using google_breakpad::mach_o::Section; +using google_breakpad::mach_o::Segment; +using google_breakpad::Module; +using google_breakpad::StabsReader; +using google_breakpad::StabsToModule; +using google_breakpad::scoped_ptr; +using std::make_pair; +using std::pair; +using std::string; +using std::vector; + +namespace { +// Return a vector<string> with absolute paths to all the entries +// in directory (excluding . and ..). +vector<string> list_directory(const string& directory) { + vector<string> entries; + DIR* dir = opendir(directory.c_str()); + if (!dir) { + return entries; + } + + string path = directory; + if (path[path.length() - 1] != '/') { + path += '/'; + } + + struct dirent* entry = NULL; + while ((entry = readdir(dir))) { + if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) { + entries.push_back(path + entry->d_name); + } + } + + closedir(dir); + return entries; +} +} + +namespace google_breakpad { + +bool DumpSymbols::Read(const string &filename) { + struct stat st; + if (stat(filename.c_str(), &st) == -1) { + fprintf(stderr, "Could not access object file %s: %s\n", + filename.c_str(), strerror(errno)); + return false; + } + + input_pathname_ = filename; + + // Does this filename refer to a dSYM bundle? + string contents_path = input_pathname_ + "/Contents/Resources/DWARF"; + if (S_ISDIR(st.st_mode) && + access(contents_path.c_str(), F_OK) == 0) { + // If there's one file under Contents/Resources/DWARF then use that, + // otherwise bail out. + const vector<string> entries = list_directory(contents_path); + if (entries.size() == 0) { + fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n", + input_pathname_.c_str()); + return false; + } + if (entries.size() > 1) { + fprintf(stderr, "Too many DWARF files in bundle: %s\n", + input_pathname_.c_str()); + return false; + } + + object_filename_ = entries[0]; + } else { + object_filename_ = input_pathname_; + } + + // Read the file's contents into memory. + bool read_ok = true; + string error; + if (stat(object_filename_.c_str(), &st) != -1) { + FILE* f = fopen(object_filename_.c_str(), "rb"); + if (f) { + contents_.reset(new uint8_t[st.st_size]); + off_t total = 0; + while (total < st.st_size && !feof(f)) { + size_t read = fread(&contents_[0] + total, 1, st.st_size - total, f); + if (read == 0) { + if (ferror(f)) { + read_ok = false; + error = strerror(errno); + } + break; + } + total += read; + } + fclose(f); + } else { + error = strerror(errno); + } + } + + if (!read_ok) { + fprintf(stderr, "Error reading object file: %s: %s\n", + object_filename_.c_str(), + error.c_str()); + return false; + } + + // Get the list of object files present in the file. + FatReader::Reporter fat_reporter(object_filename_); + FatReader fat_reader(&fat_reporter); + if (!fat_reader.Read(&contents_[0], + st.st_size)) { + return false; + } + + // Get our own copy of fat_reader's object file list. + size_t object_files_count; + const SuperFatArch *object_files = + fat_reader.object_files(&object_files_count); + if (object_files_count == 0) { + fprintf(stderr, "Fat binary file contains *no* architectures: %s\n", + object_filename_.c_str()); + return false; + } + object_files_.resize(object_files_count); + memcpy(&object_files_[0], object_files, + sizeof(SuperFatArch) * object_files_count); + + return true; +} + +bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype) { + // Find the best match for the architecture the user requested. + const SuperFatArch *best_match = FindBestMatchForArchitecture( + cpu_type, cpu_subtype); + if (!best_match) return false; + + // Record the selected object file. + selected_object_file_ = best_match; + return true; +} + +bool DumpSymbols::SetArchitecture(const std::string &arch_name) { + bool arch_set = false; + const NXArchInfo *arch_info = + google_breakpad::BreakpadGetArchInfoFromName(arch_name.c_str()); + if (arch_info) { + arch_set = SetArchitecture(arch_info->cputype, arch_info->cpusubtype); + } + return arch_set; +} + +SuperFatArch* DumpSymbols::FindBestMatchForArchitecture( + cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) { + // Check if all the object files can be converted to struct fat_arch. + bool can_convert_to_fat_arch = true; + vector<struct fat_arch> fat_arch_vector; + for (vector<SuperFatArch>::const_iterator it = object_files_.begin(); + it != object_files_.end(); + ++it) { + struct fat_arch arch; + bool success = it->ConvertToFatArch(&arch); + if (!success) { + can_convert_to_fat_arch = false; + break; + } + fat_arch_vector.push_back(arch); + } + + // If all the object files can be converted to struct fat_arch, use + // NXFindBestFatArch. + if (can_convert_to_fat_arch) { + const struct fat_arch *best_match + = NXFindBestFatArch(cpu_type, cpu_subtype, &fat_arch_vector[0], + static_cast<uint32_t>(fat_arch_vector.size())); + + for (size_t i = 0; i < fat_arch_vector.size(); ++i) { + if (best_match == &fat_arch_vector[i]) + return &object_files_[i]; + } + assert(best_match == NULL); + return NULL; + } + + // Check for an exact match with cpu_type and cpu_subtype. + for (vector<SuperFatArch>::iterator it = object_files_.begin(); + it != object_files_.end(); + ++it) { + if (static_cast<cpu_type_t>(it->cputype) == cpu_type && + static_cast<cpu_subtype_t>(it->cpusubtype) == cpu_subtype) + return &*it; + } + + // No exact match found. + // TODO(erikchen): If it becomes necessary, we can copy the implementation of + // NXFindBestFatArch, located at + // http://web.mit.edu/darwin/src/modules/cctools/libmacho/arch.c. + fprintf(stderr, "Failed to find an exact match for an object file with cpu " + "type: %d and cpu subtype: %d. Furthermore, at least one object file is " + "larger than 2**32.\n", cpu_type, cpu_subtype); + return NULL; +} + +string DumpSymbols::Identifier() { + FileID file_id(object_filename_.c_str()); + unsigned char identifier_bytes[16]; + cpu_type_t cpu_type = selected_object_file_->cputype; + cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype & ~CPU_SUBTYPE_MASK; + if (!file_id.MachoIdentifier(cpu_type, cpu_subtype, identifier_bytes)) { + fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n", + object_filename_.c_str()); + return ""; + } + + char identifier_string[40]; + FileID::ConvertIdentifierToString(identifier_bytes, identifier_string, + sizeof(identifier_string)); + + string compacted(identifier_string); + for(size_t i = compacted.find('-'); i != string::npos; + i = compacted.find('-', i)) + compacted.erase(i, 1); + + return compacted; +} + +// A range handler that accepts rangelist data parsed by +// dwarf2reader::RangeListReader and populates a range vector (typically +// owned by a function) with the results. +class DumpSymbols::DumperRangesHandler: + public DwarfCUToModule::RangesHandler { + public: + DumperRangesHandler(const uint8_t *buffer, uint64 size, + dwarf2reader::ByteReader* reader) + : buffer_(buffer), size_(size), reader_(reader) { } + + bool ReadRanges(uint64 offset, Module::Address base_address, + vector<Module::Range>* ranges) { + DwarfRangeListHandler handler(base_address, ranges); + dwarf2reader::RangeListReader rangelist_reader(buffer_, size_, reader_, + &handler); + + return rangelist_reader.ReadRangeList(offset); + } + + private: + const uint8_t *buffer_; + uint64 size_; + dwarf2reader::ByteReader* reader_; +}; + +// A line-to-module loader that accepts line number info parsed by +// dwarf2reader::LineInfo and populates a Module and a line vector +// with the results. +class DumpSymbols::DumperLineToModule: + public DwarfCUToModule::LineToModuleHandler { + public: + // Create a line-to-module converter using BYTE_READER. + DumperLineToModule(dwarf2reader::ByteReader *byte_reader) + : byte_reader_(byte_reader) { } + + void StartCompilationUnit(const string& compilation_dir) { + compilation_dir_ = compilation_dir; + } + + void ReadProgram(const uint8_t *program, uint64 length, + Module *module, vector<Module::Line> *lines) { + DwarfLineToModule handler(module, compilation_dir_, lines); + dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler); + parser.Start(); + } + private: + string compilation_dir_; + dwarf2reader::ByteReader *byte_reader_; // WEAK +}; + +bool DumpSymbols::CreateEmptyModule(scoped_ptr<Module>& module) { + // Select an object file, if SetArchitecture hasn't been called to set one + // explicitly. + if (!selected_object_file_) { + // If there's only one architecture, that's the one. + if (object_files_.size() == 1) + selected_object_file_ = &object_files_[0]; + else { + // Look for an object file whose architecture matches our own. + const NXArchInfo *local_arch = NXGetLocalArchInfo(); + if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) { + fprintf(stderr, "%s: object file contains more than one" + " architecture, none of which match the current" + " architecture; specify an architecture explicitly" + " with '-a ARCH' to resolve the ambiguity\n", + object_filename_.c_str()); + return false; + } + } + } + + assert(selected_object_file_); + + // Find the name of the selected file's architecture, to appear in + // the MODULE record and in error messages. + const NXArchInfo *selected_arch_info = + google_breakpad::BreakpadGetArchInfoFromCpuType( + selected_object_file_->cputype, selected_object_file_->cpusubtype); + + const char *selected_arch_name = selected_arch_info->name; + if (strcmp(selected_arch_name, "i386") == 0) + selected_arch_name = "x86"; + + // Produce a name to use in error messages that includes the + // filename, and the architecture, if there is more than one. + selected_object_name_ = object_filename_; + if (object_files_.size() > 1) { + selected_object_name_ += ", architecture "; + selected_object_name_ + selected_arch_name; + } + + // Compute a module name, to appear in the MODULE record. + string module_name = google_breakpad::BaseName(object_filename_); + + // Choose an identifier string, to appear in the MODULE record. + string identifier = Identifier(); + if (identifier.empty()) + return false; + identifier += "0"; + + // Create a module to hold the debugging information. + module.reset(new Module(module_name, + "mac", + selected_arch_name, + identifier)); + return true; +} + +void DumpSymbols::ReadDwarf(google_breakpad::Module *module, + const mach_o::Reader &macho_reader, + const mach_o::SectionMap &dwarf_sections, + bool handle_inter_cu_refs) const { + // Build a byte reader of the appropriate endianness. + ByteReader byte_reader(macho_reader.big_endian() + ? dwarf2reader::ENDIANNESS_BIG + : dwarf2reader::ENDIANNESS_LITTLE); + + // Construct a context for this file. + DwarfCUToModule::FileContext file_context(selected_object_name_, + module, + handle_inter_cu_refs); + + // Build a dwarf2reader::SectionMap from our mach_o::SectionMap. + for (mach_o::SectionMap::const_iterator it = dwarf_sections.begin(); + it != dwarf_sections.end(); ++it) { + file_context.AddSectionToSectionMap( + it->first, + it->second.contents.start, + it->second.contents.Size()); + } + + // Find the __debug_info section. + dwarf2reader::SectionMap::const_iterator debug_info_entry = + file_context.section_map().find("__debug_info"); + // There had better be a __debug_info section! + if (debug_info_entry == file_context.section_map().end()) { + fprintf(stderr, "%s: __DWARF segment of file has no __debug_info section\n", + selected_object_name_.c_str()); + return; + } + const std::pair<const uint8_t*, uint64>& debug_info_section = + debug_info_entry->second; + + // Build a line-to-module loader for the root handler to use. + DumperLineToModule line_to_module(&byte_reader); + + // Optional .debug_ranges reader + scoped_ptr<DumperRangesHandler> ranges_handler; + dwarf2reader::SectionMap::const_iterator ranges_entry = + file_context.section_map().find("__debug_ranges"); + if (ranges_entry != file_context.section_map().end()) { + const std::pair<const uint8_t *, uint64>& ranges_section = + ranges_entry->second; + ranges_handler.reset( + new DumperRangesHandler(ranges_section.first, ranges_section.second, + &byte_reader)); + } + + // Walk the __debug_info section, one compilation unit at a time. + uint64 debug_info_length = debug_info_section.second; + for (uint64 offset = 0; offset < debug_info_length;) { + // Make a handler for the root DIE that populates MODULE with the + // debug info. + DwarfCUToModule::WarningReporter reporter(selected_object_name_, + offset); + DwarfCUToModule root_handler(&file_context, &line_to_module, + ranges_handler.get(), &reporter); + // Make a Dwarf2Handler that drives our DIEHandler. + dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); + // Make a DWARF parser for the compilation unit at OFFSET. + dwarf2reader::CompilationUnit dwarf_reader(selected_object_name_, + file_context.section_map(), + offset, + &byte_reader, + &die_dispatcher); + // Process the entire compilation unit; get the offset of the next. + offset += dwarf_reader.Start(); + } +} + +bool DumpSymbols::ReadCFI(google_breakpad::Module *module, + const mach_o::Reader &macho_reader, + const mach_o::Section §ion, + bool eh_frame) const { + // Find the appropriate set of register names for this file's + // architecture. + vector<string> register_names; + switch (macho_reader.cpu_type()) { + case CPU_TYPE_X86: + register_names = DwarfCFIToModule::RegisterNames::I386(); + break; + case CPU_TYPE_X86_64: + register_names = DwarfCFIToModule::RegisterNames::X86_64(); + break; + case CPU_TYPE_ARM: + register_names = DwarfCFIToModule::RegisterNames::ARM(); + break; + case CPU_TYPE_ARM64: + register_names = DwarfCFIToModule::RegisterNames::ARM64(); + break; + default: { + const NXArchInfo *arch = google_breakpad::BreakpadGetArchInfoFromCpuType( + macho_reader.cpu_type(), macho_reader.cpu_subtype()); + fprintf(stderr, "%s: cannot convert DWARF call frame information for ", + selected_object_name_.c_str()); + if (arch) + fprintf(stderr, "architecture '%s'", arch->name); + else + fprintf(stderr, "architecture %d,%d", + macho_reader.cpu_type(), macho_reader.cpu_subtype()); + fprintf(stderr, " to Breakpad symbol file: no register name table\n"); + return false; + } + } + + // Find the call frame information and its size. + const uint8_t *cfi = section.contents.start; + size_t cfi_size = section.contents.Size(); + + // Plug together the parser, handler, and their entourages. + DwarfCFIToModule::Reporter module_reporter(selected_object_name_, + section.section_name); + DwarfCFIToModule handler(module, register_names, &module_reporter); + dwarf2reader::ByteReader byte_reader(macho_reader.big_endian() ? + dwarf2reader::ENDIANNESS_BIG : + dwarf2reader::ENDIANNESS_LITTLE); + byte_reader.SetAddressSize(macho_reader.bits_64() ? 8 : 4); + // At the moment, according to folks at Apple and some cursory + // investigation, Mac OS X only uses DW_EH_PE_pcrel-based pointers, so + // this is the only base address the CFI parser will need. + byte_reader.SetCFIDataBase(section.address, cfi); + + dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(selected_object_name_, + section.section_name); + dwarf2reader::CallFrameInfo parser(cfi, cfi_size, + &byte_reader, &handler, &dwarf_reporter, + eh_frame); + parser.Start(); + return true; +} + +// A LoadCommandHandler that loads whatever debugging data it finds into a +// Module. +class DumpSymbols::LoadCommandDumper: + public mach_o::Reader::LoadCommandHandler { + public: + // Create a load command dumper handling load commands from READER's + // file, and adding data to MODULE. + LoadCommandDumper(const DumpSymbols &dumper, + google_breakpad::Module *module, + const mach_o::Reader &reader, + SymbolData symbol_data, + bool handle_inter_cu_refs) + : dumper_(dumper), + module_(module), + reader_(reader), + symbol_data_(symbol_data), + handle_inter_cu_refs_(handle_inter_cu_refs) { } + + bool SegmentCommand(const mach_o::Segment &segment); + bool SymtabCommand(const ByteBuffer &entries, const ByteBuffer &strings); + + private: + const DumpSymbols &dumper_; + google_breakpad::Module *module_; // WEAK + const mach_o::Reader &reader_; + const SymbolData symbol_data_; + const bool handle_inter_cu_refs_; +}; + +bool DumpSymbols::LoadCommandDumper::SegmentCommand(const Segment &segment) { + mach_o::SectionMap section_map; + if (!reader_.MapSegmentSections(segment, §ion_map)) + return false; + + if (segment.name == "__TEXT") { + module_->SetLoadAddress(segment.vmaddr); + if (symbol_data_ != NO_CFI) { + mach_o::SectionMap::const_iterator eh_frame = + section_map.find("__eh_frame"); + if (eh_frame != section_map.end()) { + // If there is a problem reading this, don't treat it as a fatal error. + dumper_.ReadCFI(module_, reader_, eh_frame->second, true); + } + } + return true; + } + + if (segment.name == "__DWARF") { + if (symbol_data_ != ONLY_CFI) { + dumper_.ReadDwarf(module_, reader_, section_map, handle_inter_cu_refs_); + } + if (symbol_data_ != NO_CFI) { + mach_o::SectionMap::const_iterator debug_frame + = section_map.find("__debug_frame"); + if (debug_frame != section_map.end()) { + // If there is a problem reading this, don't treat it as a fatal error. + dumper_.ReadCFI(module_, reader_, debug_frame->second, false); + } + } + } + + return true; +} + +bool DumpSymbols::LoadCommandDumper::SymtabCommand(const ByteBuffer &entries, + const ByteBuffer &strings) { + StabsToModule stabs_to_module(module_); + // Mac OS X STABS are never "unitized", and the size of the 'value' field + // matches the address size of the executable. + StabsReader stabs_reader(entries.start, entries.Size(), + strings.start, strings.Size(), + reader_.big_endian(), + reader_.bits_64() ? 8 : 4, + true, + &stabs_to_module); + if (!stabs_reader.Process()) + return false; + stabs_to_module.Finalize(); + return true; +} + +bool DumpSymbols::ReadSymbolData(Module** out_module) { + scoped_ptr<Module> module; + if (!CreateEmptyModule(module)) + return false; + + // Parse the selected object file. + mach_o::Reader::Reporter reporter(selected_object_name_); + mach_o::Reader reader(&reporter); + if (!reader.Read(&contents_[0] + + selected_object_file_->offset, + selected_object_file_->size, + selected_object_file_->cputype, + selected_object_file_->cpusubtype)) + return false; + + // Walk its load commands, and deal with whatever is there. + LoadCommandDumper load_command_dumper(*this, module.get(), reader, + symbol_data_, handle_inter_cu_refs_); + if (!reader.WalkLoadCommands(&load_command_dumper)) + return false; + + *out_module = module.release(); + + return true; +} + +bool DumpSymbols::WriteSymbolFile(std::ostream &stream) { + Module* module = NULL; + + if (ReadSymbolData(&module) && module) { + bool res = module->Write(stream, symbol_data_); + delete module; + return res; + } + + return false; +} + +// Read the selected object file's debugging information, and write out the +// header only to |stream|. Return true on success; if an error occurs, report +// it and return false. +bool DumpSymbols::WriteSymbolFileHeader(std::ostream &stream) { + scoped_ptr<Module> module; + if (!CreateEmptyModule(module)) + return false; + + return module->Write(stream, symbol_data_); +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.h b/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.h new file mode 100644 index 0000000000..1e57f86ddd --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.h @@ -0,0 +1,196 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// dump_syms.h: Declaration of google_breakpad::DumpSymbols, a class for +// reading debugging information from Mach-O files and writing it out as a +// Breakpad symbol file. + +#include <mach-o/loader.h> +#include <stdio.h> +#include <stdlib.h> + +#include <ostream> +#include <string> +#include <vector> + +#include "common/byte_cursor.h" +#include "common/mac/macho_reader.h" +#include "common/mac/super_fat_arch.h" +#include "common/module.h" +#include "common/scoped_ptr.h" +#include "common/symbol_data.h" + +namespace google_breakpad { + +class DumpSymbols { + public: + DumpSymbols(SymbolData symbol_data, bool handle_inter_cu_refs) + : symbol_data_(symbol_data), + handle_inter_cu_refs_(handle_inter_cu_refs), + input_pathname_(), + object_filename_(), + contents_(), + object_files_(), + selected_object_file_(), + selected_object_name_() { } + ~DumpSymbols() { + } + + // Prepare to read debugging information from |filename|. |filename| may be + // the name of a universal binary, a Mach-O file, or a dSYM bundle + // containing either of the above. On success, return true; if there is a + // problem reading |filename|, report it and return false. + bool Read(const std::string &filename); + + // If this dumper's file includes an object file for |cpu_type| and + // |cpu_subtype|, then select that object file for dumping, and return + // true. Otherwise, return false, and leave this dumper's selected + // architecture unchanged. + // + // By default, if this dumper's file contains only one object file, then + // the dumper will dump those symbols; and if it contains more than one + // object file, then the dumper will dump the object file whose + // architecture matches that of this dumper program. + bool SetArchitecture(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype); + + // If this dumper's file includes an object file for |arch_name|, then select + // that object file for dumping, and return true. Otherwise, return false, + // and leave this dumper's selected architecture unchanged. + // + // By default, if this dumper's file contains only one object file, then + // the dumper will dump those symbols; and if it contains more than one + // object file, then the dumper will dump the object file whose + // architecture matches that of this dumper program. + bool SetArchitecture(const std::string &arch_name); + + // Return a pointer to an array of SuperFatArch structures describing the + // object files contained in this dumper's file. Set *|count| to the number + // of elements in the array. The returned array is owned by this DumpSymbols + // instance. + // + // If there are no available architectures, this function + // may return NULL. + const SuperFatArch* AvailableArchitectures(size_t *count) { + *count = object_files_.size(); + if (object_files_.size() > 0) + return &object_files_[0]; + return NULL; + } + + // Read the selected object file's debugging information, and write it out to + // |stream|. Return true on success; if an error occurs, report it and + // return false. + bool WriteSymbolFile(std::ostream &stream); + + // Read the selected object file's debugging information, and write out the + // header only to |stream|. Return true on success; if an error occurs, report + // it and return false. + bool WriteSymbolFileHeader(std::ostream &stream); + + // As above, but simply return the debugging information in module + // instead of writing it to a stream. The caller owns the resulting + // module object and must delete it when finished. + bool ReadSymbolData(Module** module); + + private: + // Used internally. + class DumperLineToModule; + class DumperRangesHandler; + class LoadCommandDumper; + + // This method behaves similarly to NXFindBestFatArch, but it supports + // SuperFatArch. + SuperFatArch* FindBestMatchForArchitecture( + cpu_type_t cpu_type, cpu_subtype_t cpu_subtype); + + // Return an identifier string for the file this DumpSymbols is dumping. + std::string Identifier(); + + + // Creates an empty module object. + bool CreateEmptyModule(scoped_ptr<Module>& module); + + // Read debugging information from |dwarf_sections|, which was taken from + // |macho_reader|, and add it to |module|. + void ReadDwarf(google_breakpad::Module *module, + const mach_o::Reader &macho_reader, + const mach_o::SectionMap &dwarf_sections, + bool handle_inter_cu_refs) const; + + // Read DWARF CFI or .eh_frame data from |section|, belonging to + // |macho_reader|, and record it in |module|. If |eh_frame| is true, + // then the data is .eh_frame-format data; otherwise, it is standard DWARF + // .debug_frame data. On success, return true; on failure, report + // the problem and return false. + bool ReadCFI(google_breakpad::Module *module, + const mach_o::Reader &macho_reader, + const mach_o::Section §ion, + bool eh_frame) const; + + // The selection of what type of symbol data to read/write. + const SymbolData symbol_data_; + + // Whether to handle references between compilation units. + const bool handle_inter_cu_refs_; + + // The name of the file or bundle whose symbols this will dump. + // This is the path given to Read, for use in error messages. + std::string input_pathname_; + + // The name of the file this DumpSymbols will actually read debugging + // information from. Normally, this is the same as input_pathname_, but if + // filename refers to a dSYM bundle, then this is the resource file + // within that bundle. + std::string object_filename_; + + // The complete contents of object_filename_, mapped into memory. + scoped_array<uint8_t> contents_; + + // A vector of SuperFatArch structures describing the object files + // object_filename_ contains. If object_filename_ refers to a fat binary, + // this may have more than one element; if it refers to a Mach-O file, this + // has exactly one element. + vector<SuperFatArch> object_files_; + + // The object file in object_files_ selected to dump, or NULL if + // SetArchitecture hasn't been called yet. + const SuperFatArch *selected_object_file_; + + // A string that identifies the selected object file, for use in error + // messages. This is usually object_filename_, but if that refers to a + // fat binary, it includes an indication of the particular architecture + // within that binary. + string selected_object_name_; +}; + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/file_id.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/file_id.cc new file mode 100644 index 0000000000..4661d5d625 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/file_id.cc @@ -0,0 +1,106 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// file_id.cc: Return a unique identifier for a file +// +// See file_id.h for documentation +// +// Author: Dan Waylonis + +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "common/mac/file_id.h" +#include "common/mac/macho_id.h" + +using MacFileUtilities::MachoID; + +namespace google_breakpad { + +FileID::FileID(const char *path) { + snprintf(path_, sizeof(path_), "%s", path); +} + +bool FileID::FileIdentifier(unsigned char identifier[16]) { + int fd = open(path_, O_RDONLY); + if (fd == -1) + return false; + + MD5Context md5; + MD5Init(&md5); + + // Read 4k x 2 bytes at a time. This is faster than just 4k bytes, but + // doesn't seem to be an unreasonable size for the stack. + unsigned char buffer[4096 * 2]; + size_t buffer_size = sizeof(buffer); + while ((buffer_size = read(fd, buffer, buffer_size) > 0)) { + MD5Update(&md5, buffer, static_cast<unsigned>(buffer_size)); + } + + close(fd); + MD5Final(identifier, &md5); + + return true; +} + +bool FileID::MachoIdentifier(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + unsigned char identifier[16]) { + MachoID macho(path_); + + if (macho.UUIDCommand(cpu_type, cpu_subtype, identifier)) + return true; + + return macho.MD5(cpu_type, cpu_subtype, identifier); +} + +// static +void FileID::ConvertIdentifierToString(const unsigned char identifier[16], + char *buffer, int buffer_length) { + int buffer_idx = 0; + for (int idx = 0; (buffer_idx < buffer_length) && (idx < 16); ++idx) { + int hi = (identifier[idx] >> 4) & 0x0F; + int lo = (identifier[idx]) & 0x0F; + + if (idx == 4 || idx == 6 || idx == 8 || idx == 10) + buffer[buffer_idx++] = '-'; + + buffer[buffer_idx++] = + static_cast<char>((hi >= 10) ? ('A' + hi - 10) : ('0' + hi)); + buffer[buffer_idx++] = + static_cast<char>((lo >= 10) ? ('A' + lo - 10) : ('0' + lo)); + } + + // NULL terminate + buffer[(buffer_idx < buffer_length) ? buffer_idx : buffer_idx - 1] = 0; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/file_id.h b/toolkit/crashreporter/google-breakpad/src/common/mac/file_id.h new file mode 100644 index 0000000000..5d60e84c9a --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/file_id.h @@ -0,0 +1,81 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// file_id.h: Return a unique identifier for a file +// +// Author: Dan Waylonis + +#ifndef COMMON_MAC_FILE_ID_H__ +#define COMMON_MAC_FILE_ID_H__ + +#include <limits.h> +#include <mach/machine.h> + +namespace google_breakpad { + +class FileID { + public: + FileID(const char *path); + ~FileID() {} + + // Load the identifier for the file path specified in the constructor into + // |identifier|. Return false if the identifier could not be created for the + // file. + // The current implementation will return the MD5 hash of the file's bytes. + bool FileIdentifier(unsigned char identifier[16]); + + // Treat the file as a mach-o file that will contain one or more archicture. + // Accepted values for |cpu_type| and |cpu_subtype| (e.g., CPU_TYPE_X86 or + // CPU_TYPE_POWERPC) are listed in /usr/include/mach/machine.h. + // If |cpu_type| is 0, then the native cpu type is used. If |cpu_subtype| is + // CPU_SUBTYPE_MULTIPLE, the match is only done on |cpu_type|. + // Returns false if opening the file failed or if the |cpu_type|/|cpu_subtype| + // is not present in the file. + // Return the unique identifier in |identifier|. + // The current implementation will look for the (in order of priority): + // LC_UUID, LC_ID_DYLIB, or MD5 hash of the given |cpu_type|. + bool MachoIdentifier(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + unsigned char identifier[16]); + + // Convert the |identifier| data to a NULL terminated string. The string will + // be formatted as a UUID (e.g., 22F065BB-FC9C-49F7-80FE-26A7CEBD7BCE). + // The |buffer| should be at least 37 bytes long to receive all of the data + // and termination. Shorter buffers will contain truncated data. + static void ConvertIdentifierToString(const unsigned char identifier[16], + char *buffer, int buffer_length); + + private: + // Storage for the path specified + char path_[PATH_MAX]; +}; + +} // namespace google_breakpad + +#endif // COMMON_MAC_FILE_ID_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/launch_reporter.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/launch_reporter.cc new file mode 100644 index 0000000000..245be82659 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/launch_reporter.cc @@ -0,0 +1,84 @@ +// Copyright (c) 2014, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include <stdio.h> +#include <sys/wait.h> +#include <unistd.h> + +namespace google_breakpad { + +void LaunchReporter(const char *reporter_executable_path, + const char *config_file_path) { + const char* argv[] = { reporter_executable_path, config_file_path, NULL }; + + // Launch the reporter + pid_t pid = fork(); + + if (pid == -1) { + perror("fork"); + fprintf(stderr, "Failed to fork reporter process\n"); + return; + } + + // If we're in the child, load in our new executable and run. + // The parent will not wait for the child to complete. + if (pid == 0) { + execv(argv[0], (char* const*)argv); + perror("exec"); + fprintf(stderr, + "Failed to launch reporter process from path %s\n", + reporter_executable_path); + unlink(config_file_path); // launch failed - get rid of config file + _exit(1); + } + + // Wait until the Reporter child process exits. + // + + // We'll use a timeout of one minute. + int timeout_count = 60; // 60 seconds + + while (timeout_count-- > 0) { + int status; + pid_t result = waitpid(pid, &status, WNOHANG); + + if (result == 0) { + // The child has not yet finished. + sleep(1); + } else if (result == -1) { + // error occurred. + break; + } else { + // child has finished + break; + } + } +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/launch_reporter.h b/toolkit/crashreporter/google-breakpad/src/common/mac/launch_reporter.h new file mode 100644 index 0000000000..4531123c2d --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/launch_reporter.h @@ -0,0 +1,43 @@ +// Copyright (c) 2014, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef COMMON_MAC_LAUNCH_REPORTER_H__ +#define COMMON_MAC_LAUNCH_REPORTER_H__ + +namespace google_breakpad { + +// Launch the crash dump sender app. +// |reporter_executable_path| is the path to the sender executable. +// |config_file_path| is the path to the config file. +void LaunchReporter(const char *reporter_executable_path, + const char *config_file_path); + +} // namespace google_breakpad + +#endif // COMMON_MAC_LAUNCH_REPORTER_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/macho_id.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_id.cc new file mode 100644 index 0000000000..c396ad888d --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_id.cc @@ -0,0 +1,369 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// macho_id.cc: Functions to gather identifying information from a macho file +// +// See macho_id.h for documentation +// +// Author: Dan Waylonis + + +#include <fcntl.h> +#include <mach-o/loader.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include "common/mac/macho_id.h" +#include "common/mac/macho_walker.h" +#include "common/mac/macho_utilities.h" + +namespace MacFileUtilities { + +using google_breakpad::MD5Init; +using google_breakpad::MD5Update; +using google_breakpad::MD5Final; + +MachoID::MachoID(const char *path) + : memory_(0), + memory_size_(0), + crc_(0), + md5_context_(), + update_function_(NULL) { + snprintf(path_, sizeof(path_), "%s", path); +} + +MachoID::MachoID(const char *path, void *memory, size_t size) + : memory_(memory), + memory_size_(size), + crc_(0), + md5_context_(), + update_function_(NULL) { + snprintf(path_, sizeof(path_), "%s", path); +} + +MachoID::~MachoID() { +} + +// The CRC info is from http://en.wikipedia.org/wiki/Adler-32 +// With optimizations from http://www.zlib.net/ + +// The largest prime smaller than 65536 +#define MOD_ADLER 65521 +// MAX_BLOCK is the largest n such that 255n(n+1)/2 + (n+1)(MAX_BLOCK-1) <= 2^32-1 +#define MAX_BLOCK 5552 + +void MachoID::UpdateCRC(unsigned char *bytes, size_t size) { +// Unrolled loops for summing +#define DO1(buf,i) {sum1 += (buf)[i]; sum2 += sum1;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + // Split up the crc + uint32_t sum1 = crc_ & 0xFFFF; + uint32_t sum2 = (crc_ >> 16) & 0xFFFF; + + // Do large blocks + while (size >= MAX_BLOCK) { + size -= MAX_BLOCK; + int block_count = MAX_BLOCK / 16; + do { + DO16(bytes); + bytes += 16; + } while (--block_count); + sum1 %= MOD_ADLER; + sum2 %= MOD_ADLER; + } + + // Do remaining bytes + if (size) { + while (size >= 16) { + size -= 16; + DO16(bytes); + bytes += 16; + } + while (size--) { + sum1 += *bytes++; + sum2 += sum1; + } + sum1 %= MOD_ADLER; + sum2 %= MOD_ADLER; + crc_ = (sum2 << 16) | sum1; + } +} + +void MachoID::UpdateMD5(unsigned char *bytes, size_t size) { + MD5Update(&md5_context_, bytes, static_cast<unsigned>(size)); +} + +void MachoID::Update(MachoWalker *walker, off_t offset, size_t size) { + if (!update_function_ || !size) + return; + + // Read up to 4k bytes at a time + unsigned char buffer[4096]; + size_t buffer_size; + off_t file_offset = offset; + while (size > 0) { + if (size > sizeof(buffer)) { + buffer_size = sizeof(buffer); + size -= buffer_size; + } else { + buffer_size = size; + size = 0; + } + + if (!walker->ReadBytes(buffer, buffer_size, file_offset)) + return; + + (this->*update_function_)(buffer, buffer_size); + file_offset += buffer_size; + } +} + +bool MachoID::UUIDCommand(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + unsigned char bytes[16]) { + struct breakpad_uuid_command uuid_cmd; + uuid_cmd.cmd = 0; + if (!WalkHeader(cpu_type, cpu_subtype, UUIDWalkerCB, &uuid_cmd)) + return false; + + // If we found the command, we'll have initialized the uuid_command + // structure + if (uuid_cmd.cmd == LC_UUID) { + memcpy(bytes, uuid_cmd.uuid, sizeof(uuid_cmd.uuid)); + return true; + } + + return false; +} + +bool MachoID::IDCommand(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + unsigned char identifier[16]) { + struct dylib_command dylib_cmd; + dylib_cmd.cmd = 0; + if (!WalkHeader(cpu_type, cpu_subtype, IDWalkerCB, &dylib_cmd)) + return false; + + // If we found the command, we'll have initialized the dylib_command + // structure + if (dylib_cmd.cmd == LC_ID_DYLIB) { + // Take the hashed filename, version, and compatability version bytes + // to form the first 12 bytes, pad the rest with zeros + + // create a crude hash of the filename to generate the first 4 bytes + identifier[0] = 0; + identifier[1] = 0; + identifier[2] = 0; + identifier[3] = 0; + + for (int j = 0, i = (int)strlen(path_)-1; i>=0 && path_[i]!='/'; ++j, --i) { + identifier[j%4] += path_[i]; + } + + identifier[4] = (dylib_cmd.dylib.current_version >> 24) & 0xFF; + identifier[5] = (dylib_cmd.dylib.current_version >> 16) & 0xFF; + identifier[6] = (dylib_cmd.dylib.current_version >> 8) & 0xFF; + identifier[7] = dylib_cmd.dylib.current_version & 0xFF; + identifier[8] = (dylib_cmd.dylib.compatibility_version >> 24) & 0xFF; + identifier[9] = (dylib_cmd.dylib.compatibility_version >> 16) & 0xFF; + identifier[10] = (dylib_cmd.dylib.compatibility_version >> 8) & 0xFF; + identifier[11] = dylib_cmd.dylib.compatibility_version & 0xFF; + identifier[12] = (cpu_type >> 24) & 0xFF; + identifier[13] = (cpu_type >> 16) & 0xFF; + identifier[14] = (cpu_type >> 8) & 0xFF; + identifier[15] = cpu_type & 0xFF; + + return true; + } + + return false; +} + +uint32_t MachoID::Adler32(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) { + update_function_ = &MachoID::UpdateCRC; + crc_ = 0; + + if (!WalkHeader(cpu_type, cpu_subtype, WalkerCB, this)) + return 0; + + return crc_; +} + +bool MachoID::MD5(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype, unsigned char identifier[16]) { + update_function_ = &MachoID::UpdateMD5; + + MD5Init(&md5_context_); + + if (!WalkHeader(cpu_type, cpu_subtype, WalkerCB, this)) + return false; + + MD5Final(identifier, &md5_context_); + return true; +} + +bool MachoID::WalkHeader(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + MachoWalker::LoadCommandCallback callback, + void *context) { + if (memory_) { + MachoWalker walker(memory_, memory_size_, callback, context); + return walker.WalkHeader(cpu_type, cpu_subtype); + } else { + MachoWalker walker(path_, callback, context); + return walker.WalkHeader(cpu_type, cpu_subtype); + } +} + +// static +bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, + bool swap, void *context) { + MachoID *macho_id = (MachoID *)context; + + if (cmd->cmd == LC_SEGMENT) { + struct segment_command seg; + + if (!walker->ReadBytes(&seg, sizeof(seg), offset)) + return false; + + if (swap) + breakpad_swap_segment_command(&seg); + + struct mach_header_64 header; + off_t header_offset; + + if (!walker->CurrentHeader(&header, &header_offset)) + return false; + + // Process segments that have sections: + // (e.g., __TEXT, __DATA, __IMPORT, __OBJC) + offset += sizeof(struct segment_command); + struct section sec; + for (unsigned long i = 0; i < seg.nsects; ++i) { + if (!walker->ReadBytes(&sec, sizeof(sec), offset)) + return false; + + if (swap) + breakpad_swap_section(&sec, 1); + + // sections of type S_ZEROFILL are "virtual" and contain no data + // in the file itself + if ((sec.flags & SECTION_TYPE) != S_ZEROFILL && sec.offset != 0) + macho_id->Update(walker, header_offset + sec.offset, sec.size); + + offset += sizeof(struct section); + } + } else if (cmd->cmd == LC_SEGMENT_64) { + struct segment_command_64 seg64; + + if (!walker->ReadBytes(&seg64, sizeof(seg64), offset)) + return false; + + if (swap) + breakpad_swap_segment_command_64(&seg64); + + struct mach_header_64 header; + off_t header_offset; + + if (!walker->CurrentHeader(&header, &header_offset)) + return false; + + // Process segments that have sections: + // (e.g., __TEXT, __DATA, __IMPORT, __OBJC) + offset += sizeof(struct segment_command_64); + struct section_64 sec64; + for (unsigned long i = 0; i < seg64.nsects; ++i) { + if (!walker->ReadBytes(&sec64, sizeof(sec64), offset)) + return false; + + if (swap) + breakpad_swap_section_64(&sec64, 1); + + // sections of type S_ZEROFILL are "virtual" and contain no data + // in the file itself + if ((sec64.flags & SECTION_TYPE) != S_ZEROFILL && sec64.offset != 0) + macho_id->Update(walker, + header_offset + sec64.offset, + (size_t)sec64.size); + + offset += sizeof(struct section_64); + } + } + + // Continue processing + return true; +} + +// static +bool MachoID::UUIDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, + bool swap, void *context) { + if (cmd->cmd == LC_UUID) { + struct breakpad_uuid_command *uuid_cmd = + (struct breakpad_uuid_command *)context; + + if (!walker->ReadBytes(uuid_cmd, sizeof(struct breakpad_uuid_command), + offset)) + return false; + + if (swap) + breakpad_swap_uuid_command(uuid_cmd); + + return false; + } + + // Continue processing + return true; +} + +// static +bool MachoID::IDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, + bool swap, void *context) { + if (cmd->cmd == LC_ID_DYLIB) { + struct dylib_command *dylib_cmd = (struct dylib_command *)context; + + if (!walker->ReadBytes(dylib_cmd, sizeof(struct dylib_command), offset)) + return false; + + if (swap) + breakpad_swap_dylib_command(dylib_cmd); + + return false; + } + + // Continue processing + return true; +} + +} // namespace MacFileUtilities diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/macho_id.h b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_id.h new file mode 100644 index 0000000000..1037549124 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_id.h @@ -0,0 +1,131 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// macho_id.h: Functions to gather identifying information from a macho file +// +// Author: Dan Waylonis + +#ifndef COMMON_MAC_MACHO_ID_H__ +#define COMMON_MAC_MACHO_ID_H__ + +#include <limits.h> +#include <mach/machine.h> +#include <mach-o/loader.h> + +#include "common/mac/macho_walker.h" +#include "common/md5.h" + +namespace MacFileUtilities { + +class MachoID { + public: + MachoID(const char *path); + MachoID(const char *path, void *memory, size_t size); + ~MachoID(); + + // For the given |cpu_type| and |cpu_subtype|, return a UUID from the LC_UUID + // command. + // Return false if there isn't a LC_UUID command. + bool UUIDCommand(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + unsigned char identifier[16]); + + // For the given |cpu_type| and |cpu_subtype|, return a UUID from the + // LC_ID_DYLIB command. + // Return false if there isn't a LC_ID_DYLIB command. + bool IDCommand(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + unsigned char identifier[16]); + + // For the given |cpu_type| and |cpu_subtype|, return the Adler32 CRC for the + // mach-o data segment(s). + // Return 0 on error (e.g., if the file is not a mach-o file) + uint32_t Adler32(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype); + + // For the given |cpu_type|, and |cpu_subtype| return the MD5 for the mach-o + // data segment(s). + // Return true on success, false otherwise + bool MD5(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + unsigned char identifier[16]); + + private: + // Signature of class member function to be called with data read from file + typedef void (MachoID::*UpdateFunction)(unsigned char *bytes, size_t size); + + // Update the CRC value by examining |size| |bytes| and applying the algorithm + // to each byte. + void UpdateCRC(unsigned char *bytes, size_t size); + + // Update the MD5 value by examining |size| |bytes| and applying the algorithm + // to each byte. + void UpdateMD5(unsigned char *bytes, size_t size); + + // Bottleneck for update routines + void Update(MachoWalker *walker, off_t offset, size_t size); + + // Factory for the MachoWalker + bool WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype, + MachoWalker::LoadCommandCallback callback, void *context); + + // The callback from the MachoWalker for CRC and MD5 + static bool WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, + bool swap, void *context); + + // The callback from the MachoWalker for LC_UUID + static bool UUIDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, + bool swap, void *context); + + // The callback from the MachoWalker for LC_ID_DYLIB + static bool IDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, + bool swap, void *context); + + // File path + char path_[PATH_MAX]; + + // Memory region to read from + void *memory_; + + // Size of the memory region + size_t memory_size_; + + // The current crc value + uint32_t crc_; + + // The MD5 context + google_breakpad::MD5Context md5_context_; + + // The current update to call from the Update callback + UpdateFunction update_function_; +}; + +} // namespace MacFileUtilities + +#endif // COMMON_MAC_MACHO_ID_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/macho_reader.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_reader.cc new file mode 100644 index 0000000000..91e1fdd2b2 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_reader.cc @@ -0,0 +1,558 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// macho_reader.cc: Implementation of google_breakpad::Mach_O::FatReader and +// google_breakpad::Mach_O::Reader. See macho_reader.h for details. + +#include "common/mac/macho_reader.h" + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#include <limits> + +// Unfortunately, CPU_TYPE_ARM is not define for 10.4. +#if !defined(CPU_TYPE_ARM) +#define CPU_TYPE_ARM 12 +#endif + +#if !defined(CPU_TYPE_ARM_64) +#define CPU_TYPE_ARM_64 16777228 +#endif + +namespace google_breakpad { +namespace mach_o { + +// If NDEBUG is #defined, then the 'assert' macro doesn't evaluate its +// arguments, so you can't place expressions that do necessary work in +// the argument of an assert. Nor can you assign the result of the +// expression to a variable and assert that the variable's value is +// true: you'll get unused variable warnings when NDEBUG is #defined. +// +// ASSERT_ALWAYS_EVAL always evaluates its argument, and asserts that +// the result is true if NDEBUG is not #defined. +#if defined(NDEBUG) +#define ASSERT_ALWAYS_EVAL(x) (x) +#else +#define ASSERT_ALWAYS_EVAL(x) assert(x) +#endif + +void FatReader::Reporter::BadHeader() { + fprintf(stderr, "%s: file is neither a fat binary file" + " nor a Mach-O object file\n", filename_.c_str()); +} + +void FatReader::Reporter::TooShort() { + fprintf(stderr, "%s: file too short for the data it claims to contain\n", + filename_.c_str()); +} + +void FatReader::Reporter::MisplacedObjectFile() { + fprintf(stderr, "%s: file too short for the object files it claims" + " to contain\n", filename_.c_str()); +} + +bool FatReader::Read(const uint8_t *buffer, size_t size) { + buffer_.start = buffer; + buffer_.end = buffer + size; + ByteCursor cursor(&buffer_); + + // Fat binaries always use big-endian, so read the magic number in + // that endianness. To recognize Mach-O magic numbers, which can use + // either endianness, check for both the proper and reversed forms + // of the magic numbers. + cursor.set_big_endian(true); + if (cursor >> magic_) { + if (magic_ == FAT_MAGIC) { + // How many object files does this fat binary contain? + uint32_t object_files_count; + if (!(cursor >> object_files_count)) { // nfat_arch + reporter_->TooShort(); + return false; + } + + // Read the list of object files. + object_files_.resize(object_files_count); + for (size_t i = 0; i < object_files_count; i++) { + struct fat_arch objfile; + + // Read this object file entry, byte-swapping as appropriate. + cursor >> objfile.cputype + >> objfile.cpusubtype + >> objfile.offset + >> objfile.size + >> objfile.align; + + SuperFatArch super_fat_arch(objfile); + object_files_[i] = super_fat_arch; + + if (!cursor) { + reporter_->TooShort(); + return false; + } + // Does the file actually have the bytes this entry refers to? + size_t fat_size = buffer_.Size(); + if (objfile.offset > fat_size || + objfile.size > fat_size - objfile.offset) { + reporter_->MisplacedObjectFile(); + return false; + } + } + + return true; + } else if (magic_ == MH_MAGIC || magic_ == MH_MAGIC_64 || + magic_ == MH_CIGAM || magic_ == MH_CIGAM_64) { + // If this is a little-endian Mach-O file, fix the cursor's endianness. + if (magic_ == MH_CIGAM || magic_ == MH_CIGAM_64) + cursor.set_big_endian(false); + // Record the entire file as a single entry in the object file list. + object_files_.resize(1); + + // Get the cpu type and subtype from the Mach-O header. + if (!(cursor >> object_files_[0].cputype + >> object_files_[0].cpusubtype)) { + reporter_->TooShort(); + return false; + } + + object_files_[0].offset = 0; + object_files_[0].size = static_cast<uint64_t>(buffer_.Size()); + // This alignment is correct for 32 and 64-bit x86 and ppc. + // See get_align in the lipo source for other architectures: + // http://www.opensource.apple.com/source/cctools/cctools-773/misc/lipo.c + object_files_[0].align = 12; // 2^12 == 4096 + return true; + } + } + reporter_->BadHeader(); + return false; +} + +void Reader::Reporter::BadHeader() { + fprintf(stderr, "%s: file is not a Mach-O object file\n", filename_.c_str()); +} + +void Reader::Reporter::CPUTypeMismatch(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + cpu_type_t expected_cpu_type, + cpu_subtype_t expected_cpu_subtype) { + fprintf(stderr, "%s: CPU type %d, subtype %d does not match expected" + " type %d, subtype %d\n", + filename_.c_str(), cpu_type, cpu_subtype, + expected_cpu_type, expected_cpu_subtype); +} + +void Reader::Reporter::HeaderTruncated() { + fprintf(stderr, "%s: file does not contain a complete Mach-O header\n", + filename_.c_str()); +} + +void Reader::Reporter::LoadCommandRegionTruncated() { + fprintf(stderr, "%s: file too short to hold load command region" + " given in Mach-O header\n", filename_.c_str()); +} + +void Reader::Reporter::LoadCommandsOverrun(size_t claimed, size_t i, + LoadCommandType type) { + fprintf(stderr, "%s: file's header claims there are %zu" + " load commands, but load command #%zu", + filename_.c_str(), claimed, i); + if (type) fprintf(stderr, ", of type %d,", type); + fprintf(stderr, " extends beyond the end of the load command region\n"); +} + +void Reader::Reporter::LoadCommandTooShort(size_t i, LoadCommandType type) { + fprintf(stderr, "%s: the contents of load command #%zu, of type %d," + " extend beyond the size given in the load command's header\n", + filename_.c_str(), i, type); +} + +void Reader::Reporter::SectionsMissing(const string &name) { + fprintf(stderr, "%s: the load command for segment '%s'" + " is too short to hold the section headers it claims to have\n", + filename_.c_str(), name.c_str()); +} + +void Reader::Reporter::MisplacedSegmentData(const string &name) { + fprintf(stderr, "%s: the segment '%s' claims its contents lie beyond" + " the end of the file\n", filename_.c_str(), name.c_str()); +} + +void Reader::Reporter::MisplacedSectionData(const string §ion, + const string &segment) { + fprintf(stderr, "%s: the section '%s' in segment '%s'" + " claims its contents lie outside the segment's contents\n", + filename_.c_str(), section.c_str(), segment.c_str()); +} + +void Reader::Reporter::MisplacedSymbolTable() { + fprintf(stderr, "%s: the LC_SYMTAB load command claims that the symbol" + " table's contents are located beyond the end of the file\n", + filename_.c_str()); +} + +void Reader::Reporter::UnsupportedCPUType(cpu_type_t cpu_type) { + fprintf(stderr, "%s: CPU type %d is not supported\n", + filename_.c_str(), cpu_type); +} + +bool Reader::Read(const uint8_t *buffer, + size_t size, + cpu_type_t expected_cpu_type, + cpu_subtype_t expected_cpu_subtype) { + assert(!buffer_.start); + buffer_.start = buffer; + buffer_.end = buffer + size; + ByteCursor cursor(&buffer_, true); + uint32_t magic; + if (!(cursor >> magic)) { + reporter_->HeaderTruncated(); + return false; + } + + if (expected_cpu_type != CPU_TYPE_ANY) { + uint32_t expected_magic; + // validate that magic matches the expected cpu type + switch (expected_cpu_type) { + case CPU_TYPE_ARM: + case CPU_TYPE_I386: + expected_magic = MH_CIGAM; + break; + case CPU_TYPE_POWERPC: + expected_magic = MH_MAGIC; + break; + case CPU_TYPE_ARM_64: + case CPU_TYPE_X86_64: + expected_magic = MH_CIGAM_64; + break; + case CPU_TYPE_POWERPC64: + expected_magic = MH_MAGIC_64; + break; + default: + reporter_->UnsupportedCPUType(expected_cpu_type); + return false; + } + + if (expected_magic != magic) { + reporter_->BadHeader(); + return false; + } + } + + // Since the byte cursor is in big-endian mode, a reversed magic number + // always indicates a little-endian file, regardless of our own endianness. + switch (magic) { + case MH_MAGIC: big_endian_ = true; bits_64_ = false; break; + case MH_CIGAM: big_endian_ = false; bits_64_ = false; break; + case MH_MAGIC_64: big_endian_ = true; bits_64_ = true; break; + case MH_CIGAM_64: big_endian_ = false; bits_64_ = true; break; + default: + reporter_->BadHeader(); + return false; + } + cursor.set_big_endian(big_endian_); + uint32_t commands_size, reserved; + cursor >> cpu_type_ >> cpu_subtype_ >> file_type_ >> load_command_count_ + >> commands_size >> flags_; + if (bits_64_) + cursor >> reserved; + if (!cursor) { + reporter_->HeaderTruncated(); + return false; + } + + if (expected_cpu_type != CPU_TYPE_ANY && + (expected_cpu_type != cpu_type_ || + expected_cpu_subtype != cpu_subtype_)) { + reporter_->CPUTypeMismatch(cpu_type_, cpu_subtype_, + expected_cpu_type, expected_cpu_subtype); + return false; + } + + cursor + .PointTo(&load_commands_.start, commands_size) + .PointTo(&load_commands_.end, 0); + if (!cursor) { + reporter_->LoadCommandRegionTruncated(); + return false; + } + + return true; +} + +bool Reader::WalkLoadCommands(Reader::LoadCommandHandler *handler) const { + ByteCursor list_cursor(&load_commands_, big_endian_); + + for (size_t index = 0; index < load_command_count_; ++index) { + // command refers to this load command alone, so that cursor will + // refuse to read past the load command's end. But since we haven't + // read the size yet, let command initially refer to the entire + // remainder of the load command series. + ByteBuffer command(list_cursor.here(), list_cursor.Available()); + ByteCursor cursor(&command, big_endian_); + + // Read the command type and size --- fields common to all commands. + uint32_t type, size; + if (!(cursor >> type)) { + reporter_->LoadCommandsOverrun(load_command_count_, index, 0); + return false; + } + if (!(cursor >> size) || size > command.Size()) { + reporter_->LoadCommandsOverrun(load_command_count_, index, type); + return false; + } + + // Now that we've read the length, restrict command's range to this + // load command only. + command.end = command.start + size; + + switch (type) { + case LC_SEGMENT: + case LC_SEGMENT_64: { + Segment segment; + segment.bits_64 = (type == LC_SEGMENT_64); + size_t word_size = segment.bits_64 ? 8 : 4; + cursor.CString(&segment.name, 16); + cursor + .Read(word_size, false, &segment.vmaddr) + .Read(word_size, false, &segment.vmsize) + .Read(word_size, false, &segment.fileoff) + .Read(word_size, false, &segment.filesize); + cursor >> segment.maxprot + >> segment.initprot + >> segment.nsects + >> segment.flags; + if (!cursor) { + reporter_->LoadCommandTooShort(index, type); + return false; + } + if (segment.fileoff > buffer_.Size() || + segment.filesize > buffer_.Size() - segment.fileoff) { + reporter_->MisplacedSegmentData(segment.name); + return false; + } + // Mach-O files in .dSYM bundles have the contents of the loaded + // segments removed, and their file offsets and file sizes zeroed + // out. To help us handle this special case properly, give such + // segments' contents NULL starting and ending pointers. + if (segment.fileoff == 0 && segment.filesize == 0) { + segment.contents.start = segment.contents.end = NULL; + } else { + segment.contents.start = buffer_.start + segment.fileoff; + segment.contents.end = segment.contents.start + segment.filesize; + } + // The section list occupies the remainder of this load command's space. + segment.section_list.start = cursor.here(); + segment.section_list.end = command.end; + + if (!handler->SegmentCommand(segment)) + return false; + break; + } + + case LC_SYMTAB: { + uint32_t symoff, nsyms, stroff, strsize; + cursor >> symoff >> nsyms >> stroff >> strsize; + if (!cursor) { + reporter_->LoadCommandTooShort(index, type); + return false; + } + // How big are the entries in the symbol table? + // sizeof(struct nlist_64) : sizeof(struct nlist), + // but be paranoid about alignment vs. target architecture. + size_t symbol_size = bits_64_ ? 16 : 12; + // How big is the entire symbol array? + size_t symbols_size = nsyms * symbol_size; + if (symoff > buffer_.Size() || symbols_size > buffer_.Size() - symoff || + stroff > buffer_.Size() || strsize > buffer_.Size() - stroff) { + reporter_->MisplacedSymbolTable(); + return false; + } + ByteBuffer entries(buffer_.start + symoff, symbols_size); + ByteBuffer names(buffer_.start + stroff, strsize); + if (!handler->SymtabCommand(entries, names)) + return false; + break; + } + + default: { + if (!handler->UnknownCommand(type, command)) + return false; + break; + } + } + + list_cursor.set_here(command.end); + } + + return true; +} + +// A load command handler that looks for a segment of a given name. +class Reader::SegmentFinder : public LoadCommandHandler { + public: + // Create a load command handler that looks for a segment named NAME, + // and sets SEGMENT to describe it if found. + SegmentFinder(const string &name, Segment *segment) + : name_(name), segment_(segment), found_() { } + + // Return true if the traversal found the segment, false otherwise. + bool found() const { return found_; } + + bool SegmentCommand(const Segment &segment) { + if (segment.name == name_) { + *segment_ = segment; + found_ = true; + return false; + } + return true; + } + + private: + // The name of the segment our creator is looking for. + const string &name_; + + // Where we should store the segment if found. (WEAK) + Segment *segment_; + + // True if we found the segment. + bool found_; +}; + +bool Reader::FindSegment(const string &name, Segment *segment) const { + SegmentFinder finder(name, segment); + WalkLoadCommands(&finder); + return finder.found(); +} + +bool Reader::WalkSegmentSections(const Segment &segment, + SectionHandler *handler) const { + size_t word_size = segment.bits_64 ? 8 : 4; + ByteCursor cursor(&segment.section_list, big_endian_); + + for (size_t i = 0; i < segment.nsects; i++) { + Section section; + section.bits_64 = segment.bits_64; + uint64_t size, offset; + uint32_t dummy32; + cursor + .CString(§ion.section_name, 16) + .CString(§ion.segment_name, 16) + .Read(word_size, false, §ion.address) + .Read(word_size, false, &size) + .Read(sizeof(uint32_t), false, &offset) // clears high bits of |offset| + >> section.align + >> dummy32 + >> dummy32 + >> section.flags + >> dummy32 + >> dummy32; + if (section.bits_64) + cursor >> dummy32; + if (!cursor) { + reporter_->SectionsMissing(segment.name); + return false; + } + + // Even 64-bit Mach-O isn’t a true 64-bit format in that it doesn’t handle + // 64-bit file offsets gracefully. Segment load commands do contain 64-bit + // file offsets, but sections within do not. Because segments load + // contiguously, recompute each section’s file offset on the basis of its + // containing segment’s file offset and the difference between the section’s + // and segment’s load addresses. If truncation is detected, honor the + // recomputed offset. + if (segment.bits_64 && + segment.fileoff + segment.filesize > + std::numeric_limits<uint32_t>::max()) { + const uint64_t section_offset_recomputed = + segment.fileoff + section.address - segment.vmaddr; + if (offset == static_cast<uint32_t>(section_offset_recomputed)) { + offset = section_offset_recomputed; + } + } + + const uint32_t section_type = section.flags & SECTION_TYPE; + if (section_type == S_ZEROFILL || section_type == S_THREAD_LOCAL_ZEROFILL || + section_type == S_GB_ZEROFILL) { + // Zero-fill sections have a size, but no contents. + section.contents.start = section.contents.end = NULL; + } else if (segment.contents.start == NULL && + segment.contents.end == NULL) { + // Mach-O files in .dSYM bundles have the contents of the loaded + // segments removed, and their file offsets and file sizes zeroed + // out. However, the sections within those segments still have + // non-zero sizes. There's no reason to call MisplacedSectionData in + // this case; the caller may just need the section's load + // address. But do set the contents' limits to NULL, for safety. + section.contents.start = section.contents.end = NULL; + } else { + if (offset < size_t(segment.contents.start - buffer_.start) || + offset > size_t(segment.contents.end - buffer_.start) || + size > size_t(segment.contents.end - buffer_.start - offset)) { + reporter_->MisplacedSectionData(section.section_name, + section.segment_name); + return false; + } + section.contents.start = buffer_.start + offset; + section.contents.end = section.contents.start + size; + } + if (!handler->HandleSection(section)) + return false; + } + return true; +} + +// A SectionHandler that builds a SectionMap for the sections within a +// given segment. +class Reader::SectionMapper: public SectionHandler { + public: + // Create a SectionHandler that populates MAP with an entry for + // each section it is given. + SectionMapper(SectionMap *map) : map_(map) { } + bool HandleSection(const Section §ion) { + (*map_)[section.section_name] = section; + return true; + } + private: + // The map under construction. (WEAK) + SectionMap *map_; +}; + +bool Reader::MapSegmentSections(const Segment &segment, + SectionMap *section_map) const { + section_map->clear(); + SectionMapper mapper(section_map); + return WalkSegmentSections(segment, &mapper); +} + +} // namespace mach_o +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/macho_reader.h b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_reader.h new file mode 100644 index 0000000000..145d17d1f2 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_reader.h @@ -0,0 +1,464 @@ +// -*- mode: C++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// macho_reader.h: A class for parsing Mach-O files. + +#ifndef BREAKPAD_COMMON_MAC_MACHO_READER_H_ +#define BREAKPAD_COMMON_MAC_MACHO_READER_H_ + +#include <mach-o/loader.h> +#include <mach-o/fat.h> +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> + +#include <map> +#include <string> +#include <vector> + +#include "common/byte_cursor.h" +#include "common/mac/super_fat_arch.h" + +namespace google_breakpad { +namespace mach_o { + +using std::map; +using std::string; +using std::vector; + +// The Mac headers don't specify particular types for these groups of +// constants, but defining them here provides some documentation +// value. We also give them the same width as the fields in which +// they appear, which makes them a bit easier to use with ByteCursors. +typedef uint32_t Magic; +typedef uint32_t FileType; +typedef uint32_t FileFlags; +typedef uint32_t LoadCommandType; +typedef uint32_t SegmentFlags; +typedef uint32_t SectionFlags; + +// A parser for fat binary files, used to store universal binaries. +// When applied to a (non-fat) Mach-O file, this behaves as if the +// file were a fat file containing a single object file. +class FatReader { + public: + + // A class for reporting errors found while parsing fat binary files. The + // default definitions of these methods print messages to stderr. + class Reporter { + public: + // Create a reporter that attributes problems to |filename|. + explicit Reporter(const string &filename) : filename_(filename) { } + + virtual ~Reporter() { } + + // The data does not begin with a fat binary or Mach-O magic number. + // This is a fatal error. + virtual void BadHeader(); + + // The Mach-O fat binary file ends abruptly, without enough space + // to contain an object file it claims is present. + virtual void MisplacedObjectFile(); + + // The file ends abruptly: either it is not large enough to hold a + // complete header, or the header implies that contents are present + // beyond the actual end of the file. + virtual void TooShort(); + + private: + // The filename to which the reader should attribute problems. + string filename_; + }; + + // Create a fat binary file reader that uses |reporter| to report problems. + explicit FatReader(Reporter *reporter) : reporter_(reporter) { } + + // Read the |size| bytes at |buffer| as a fat binary file. On success, + // return true; on failure, report the problem to reporter_ and return + // false. + // + // If the data is a plain Mach-O file, rather than a fat binary file, + // then the reader behaves as if it had found a fat binary file whose + // single object file is the Mach-O file. + bool Read(const uint8_t *buffer, size_t size); + + // Return an array of 'SuperFatArch' structures describing the + // object files present in this fat binary file. Set |size| to the + // number of elements in the array. + // + // Assuming Read returned true, the entries are validated: it is safe to + // assume that the offsets and sizes in each SuperFatArch refer to subranges + // of the bytes passed to Read. + // + // If there are no object files in this fat binary, then this + // function can return NULL. + // + // The array is owned by this FatReader instance; it will be freed when + // this FatReader is destroyed. + // + // This function returns a C-style array instead of a vector to make it + // possible to use the result with OS X functions like NXFindBestFatArch, + // so that the symbol dumper will behave consistently with other OS X + // utilities that work with fat binaries. + const SuperFatArch* object_files(size_t *count) const { + *count = object_files_.size(); + if (object_files_.size() > 0) + return &object_files_[0]; + return NULL; + } + + private: + // We use this to report problems parsing the file's contents. (WEAK) + Reporter *reporter_; + + // The contents of the fat binary or Mach-O file we're parsing. We do not + // own the storage it refers to. + ByteBuffer buffer_; + + // The magic number of this binary, in host byte order. + Magic magic_; + + // The list of object files in this binary. + // object_files_.size() == fat_header.nfat_arch + vector<SuperFatArch> object_files_; +}; + +// A segment in a Mach-O file. All these fields have been byte-swapped as +// appropriate for use by the executing architecture. +struct Segment { + // The ByteBuffers below point into the bytes passed to the Reader that + // created this Segment. + + ByteBuffer section_list; // This segment's section list. + ByteBuffer contents; // This segment's contents. + + // This segment's name. + string name; + + // The address at which this segment should be loaded in memory. If + // bits_64 is false, only the bottom 32 bits of this value are valid. + uint64_t vmaddr; + + // The size of this segment when loaded into memory. This may be larger + // than contents.Size(), in which case the extra area will be + // initialized with zeros. If bits_64 is false, only the bottom 32 bits + // of this value are valid. + uint64_t vmsize; + + // The file offset and size of the segment in the Mach-O image. + uint64_t fileoff; + uint64_t filesize; + + // The maximum and initial VM protection of this segment's contents. + uint32_t maxprot; + uint32_t initprot; + + // The number of sections in section_list. + uint32_t nsects; + + // Flags describing this segment, from SegmentFlags. + uint32_t flags; + + // True if this is a 64-bit section; false if it is a 32-bit section. + bool bits_64; +}; + +// A section in a Mach-O file. All these fields have been byte-swapped as +// appropriate for use by the executing architecture. +struct Section { + // This section's contents. This points into the bytes passed to the + // Reader that created this Section. + ByteBuffer contents; + + // This section's name. + string section_name; // section[_64].sectname + // The name of the segment this section belongs to. + string segment_name; // section[_64].segname + + // The address at which this section's contents should be loaded in + // memory. If bits_64 is false, only the bottom 32 bits of this value + // are valid. + uint64_t address; + + // The contents of this section should be loaded into memory at an + // address which is a multiple of (two raised to this power). + uint32_t align; + + // Flags from SectionFlags describing the section's contents. + uint32_t flags; + + // We don't support reading relocations yet. + + // True if this is a 64-bit section; false if it is a 32-bit section. + bool bits_64; +}; + +// A map from section names to Sections. +typedef map<string, Section> SectionMap; + +// A reader for a Mach-O file. +// +// This does not handle fat binaries; see FatReader above. FatReader +// provides a friendly interface for parsing data that could be either a +// fat binary or a Mach-O file. +class Reader { + public: + + // A class for reporting errors found while parsing Mach-O files. The + // default definitions of these member functions print messages to + // stderr. + class Reporter { + public: + // Create a reporter that attributes problems to |filename|. + explicit Reporter(const string &filename) : filename_(filename) { } + virtual ~Reporter() { } + + // Reporter functions for fatal errors return void; the reader will + // definitely return an error to its caller after calling them + + // The data does not begin with a Mach-O magic number, or the magic + // number does not match the expected value for the cpu architecture. + // This is a fatal error. + virtual void BadHeader(); + + // The data contained in a Mach-O fat binary (|cpu_type|, |cpu_subtype|) + // does not match the expected CPU architecture + // (|expected_cpu_type|, |expected_cpu_subtype|). + virtual void CPUTypeMismatch(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + cpu_type_t expected_cpu_type, + cpu_subtype_t expected_cpu_subtype); + + // The file ends abruptly: either it is not large enough to hold a + // complete header, or the header implies that contents are present + // beyond the actual end of the file. + virtual void HeaderTruncated(); + + // The file's load command region, as given in the Mach-O header, is + // too large for the file. + virtual void LoadCommandRegionTruncated(); + + // The file's Mach-O header claims the file contains |claimed| load + // commands, but the I'th load command, of type |type|, extends beyond + // the end of the load command region, as given by the Mach-O header. + // If |type| is zero, the command's type was unreadable. + virtual void LoadCommandsOverrun(size_t claimed, size_t i, + LoadCommandType type); + + // The contents of the |i|'th load command, of type |type|, extend beyond + // the size given in the load command's header. + virtual void LoadCommandTooShort(size_t i, LoadCommandType type); + + // The LC_SEGMENT or LC_SEGMENT_64 load command for the segment named + // |name| is too short to hold the sections that its header says it does. + // (This more specific than LoadCommandTooShort.) + virtual void SectionsMissing(const string &name); + + // The segment named |name| claims that its contents lie beyond the end + // of the file. + virtual void MisplacedSegmentData(const string &name); + + // The section named |section| in the segment named |segment| claims that + // its contents do not lie entirely within the segment. + virtual void MisplacedSectionData(const string §ion, + const string &segment); + + // The LC_SYMTAB command claims that symbol table contents are located + // beyond the end of the file. + virtual void MisplacedSymbolTable(); + + // An attempt was made to read a Mach-O file of the unsupported + // CPU architecture |cpu_type|. + virtual void UnsupportedCPUType(cpu_type_t cpu_type); + + private: + string filename_; + }; + + // A handler for sections parsed from a segment. The WalkSegmentSections + // member function accepts an instance of this class, and applies it to + // each section defined in a given segment. + class SectionHandler { + public: + virtual ~SectionHandler() { } + + // Called to report that the segment's section list contains |section|. + // This should return true if the iteration should continue, or false + // if it should stop. + virtual bool HandleSection(const Section §ion) = 0; + }; + + // A handler for the load commands in a Mach-O file. + class LoadCommandHandler { + public: + LoadCommandHandler() { } + virtual ~LoadCommandHandler() { } + + // When called from WalkLoadCommands, the following handler functions + // should return true if they wish to continue iterating over the load + // command list, or false if they wish to stop iterating. + // + // When called from LoadCommandIterator::Handle or Reader::Handle, + // these functions' return values are simply passed through to Handle's + // caller. + // + // The definitions provided by this base class simply return true; the + // default is to silently ignore sections whose member functions the + // subclass doesn't override. + + // COMMAND is load command we don't recognize. We provide only the + // command type and a ByteBuffer enclosing the command's data (If we + // cannot parse the command type or its size, we call + // reporter_->IncompleteLoadCommand instead.) + virtual bool UnknownCommand(LoadCommandType type, + const ByteBuffer &contents) { + return true; + } + + // The load command is LC_SEGMENT or LC_SEGMENT_64, defining a segment + // with the properties given in |segment|. + virtual bool SegmentCommand(const Segment &segment) { + return true; + } + + // The load command is LC_SYMTAB. |entries| holds the array of nlist + // entries, and |names| holds the strings the entries refer to. + virtual bool SymtabCommand(const ByteBuffer &entries, + const ByteBuffer &names) { + return true; + } + + // Add handler functions for more load commands here as needed. + }; + + // Create a Mach-O file reader that reports problems to |reporter|. + explicit Reader(Reporter *reporter) + : reporter_(reporter) { } + + // Read the given data as a Mach-O file. The reader retains pointers + // into the data passed, so the data should live as long as the reader + // does. On success, return true; on failure, return false. + // + // At most one of these functions should be invoked once on each Reader + // instance. + bool Read(const uint8_t *buffer, + size_t size, + cpu_type_t expected_cpu_type, + cpu_subtype_t expected_cpu_subtype); + bool Read(const ByteBuffer &buffer, + cpu_type_t expected_cpu_type, + cpu_subtype_t expected_cpu_subtype) { + return Read(buffer.start, + buffer.Size(), + expected_cpu_type, + expected_cpu_subtype); + } + + // Return this file's characteristics, as found in the Mach-O header. + cpu_type_t cpu_type() const { return cpu_type_; } + cpu_subtype_t cpu_subtype() const { return cpu_subtype_; } + FileType file_type() const { return file_type_; } + FileFlags flags() const { return flags_; } + + // Return true if this is a 64-bit Mach-O file, false if it is a 32-bit + // Mach-O file. + bool bits_64() const { return bits_64_; } + + // Return true if this is a big-endian Mach-O file, false if it is + // little-endian. + bool big_endian() const { return big_endian_; } + + // Apply |handler| to each load command in this Mach-O file, stopping when + // a handler function returns false. If we encounter a malformed load + // command, report it via reporter_ and return false. Return true if all + // load commands were parseable and all handlers returned true. + bool WalkLoadCommands(LoadCommandHandler *handler) const; + + // Set |segment| to describe the segment named |name|, if present. If + // found, |segment|'s byte buffers refer to a subregion of the bytes + // passed to Read. If we find the section, return true; otherwise, + // return false. + bool FindSegment(const string &name, Segment *segment) const; + + // Apply |handler| to each section defined in |segment|. If |handler| returns + // false, stop iterating and return false. If all calls to |handler| return + // true and we reach the end of the section list, return true. + bool WalkSegmentSections(const Segment &segment, SectionHandler *handler) + const; + + // Clear |section_map| and then populate it with a map of the sections + // in |segment|, from section names to Section structures. + // Each Section's contents refer to bytes in |segment|'s contents. + // On success, return true; if a problem occurs, report it and return false. + bool MapSegmentSections(const Segment &segment, SectionMap *section_map) + const; + + private: + // Used internally. + class SegmentFinder; + class SectionMapper; + + // We use this to report problems parsing the file's contents. (WEAK) + Reporter *reporter_; + + // The contents of the Mach-O file we're parsing. We do not own the + // storage it refers to. + ByteBuffer buffer_; + + // True if this file is big-endian. + bool big_endian_; + + // True if this file is a 64-bit Mach-O file. + bool bits_64_; + + // This file's cpu type and subtype. + cpu_type_t cpu_type_; // mach_header[_64].cputype + cpu_subtype_t cpu_subtype_; // mach_header[_64].cpusubtype + + // This file's type. + FileType file_type_; // mach_header[_64].filetype + + // The region of buffer_ occupied by load commands. + ByteBuffer load_commands_; + + // The number of load commands in load_commands_. + uint32_t load_command_count_; // mach_header[_64].ncmds + + // This file's header flags. + FileFlags flags_; +}; + +} // namespace mach_o +} // namespace google_breakpad + +#endif // BREAKPAD_COMMON_MAC_MACHO_READER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/macho_reader_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_reader_unittest.cc new file mode 100644 index 0000000000..d8459d8cd4 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_reader_unittest.cc @@ -0,0 +1,1902 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// macho_reader_unittest.cc: Unit tests for google_breakpad::Mach_O::FatReader +// and google_breakpad::Mach_O::Reader. + +#include <map> +#include <string> +#include <vector> + +#include "breakpad_googletest_includes.h" +#include "common/mac/macho_reader.h" +#include "common/test_assembler.h" + +namespace mach_o = google_breakpad::mach_o; +namespace test_assembler = google_breakpad::test_assembler; + +using mach_o::FatReader; +using mach_o::FileFlags; +using mach_o::FileType; +using mach_o::LoadCommandType; +using mach_o::Reader; +using mach_o::Section; +using mach_o::SectionMap; +using mach_o::Segment; +using test_assembler::Endianness; +using test_assembler::Label; +using test_assembler::kBigEndian; +using test_assembler::kLittleEndian; +using test_assembler::kUnsetEndian; +using google_breakpad::ByteBuffer; +using std::map; +using std::string; +using std::vector; +using testing::AllOf; +using testing::DoAll; +using testing::Field; +using testing::InSequence; +using testing::Matcher; +using testing::Return; +using testing::SaveArg; +using testing::Test; +using testing::_; + + +// Mock classes for the reader's various reporters and handlers. + +class MockFatReaderReporter: public FatReader::Reporter { + public: + MockFatReaderReporter(const string &filename) + : FatReader::Reporter(filename) { } + MOCK_METHOD0(BadHeader, void()); + MOCK_METHOD0(MisplacedObjectFile, void()); + MOCK_METHOD0(TooShort, void()); +}; + +class MockReaderReporter: public Reader::Reporter { + public: + MockReaderReporter(const string &filename) : Reader::Reporter(filename) { } + MOCK_METHOD0(BadHeader, void()); + MOCK_METHOD4(CPUTypeMismatch, void(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + cpu_type_t expected_cpu_type, + cpu_subtype_t expected_cpu_subtype)); + MOCK_METHOD0(HeaderTruncated, void()); + MOCK_METHOD0(LoadCommandRegionTruncated, void()); + MOCK_METHOD3(LoadCommandsOverrun, void(size_t claimed, size_t i, + LoadCommandType type)); + MOCK_METHOD2(LoadCommandTooShort, void(size_t i, LoadCommandType type)); + MOCK_METHOD1(SectionsMissing, void(const string &name)); + MOCK_METHOD1(MisplacedSegmentData, void(const string &name)); + MOCK_METHOD2(MisplacedSectionData, void(const string §ion, + const string &segment)); + MOCK_METHOD0(MisplacedSymbolTable, void()); + MOCK_METHOD1(UnsupportedCPUType, void(cpu_type_t cpu_type)); +}; + +class MockLoadCommandHandler: public Reader::LoadCommandHandler { + public: + MOCK_METHOD2(UnknownCommand, bool(LoadCommandType, const ByteBuffer &)); + MOCK_METHOD1(SegmentCommand, bool(const Segment &)); + MOCK_METHOD2(SymtabCommand, bool(const ByteBuffer &, const ByteBuffer &)); +}; + +class MockSectionHandler: public Reader::SectionHandler { + public: + MOCK_METHOD1(HandleSection, bool(const Section §ion)); +}; + + +// Tests for mach_o::FatReader. + +// Since the effect of these functions is to write to stderr, the +// results of these tests must be inspected by hand. +TEST(FatReaderReporter, BadHeader) { + FatReader::Reporter reporter("filename"); + reporter.BadHeader(); +} + +TEST(FatReaderReporter, MisplacedObjectFile) { + FatReader::Reporter reporter("filename"); + reporter.MisplacedObjectFile(); +} + +TEST(FatReaderReporter, TooShort) { + FatReader::Reporter reporter("filename"); + reporter.TooShort(); +} + +TEST(MachOReaderReporter, BadHeader) { + Reader::Reporter reporter("filename"); + reporter.BadHeader(); +} + +TEST(MachOReaderReporter, CPUTypeMismatch) { + Reader::Reporter reporter("filename"); + reporter.CPUTypeMismatch(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL, + CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL); +} + +TEST(MachOReaderReporter, HeaderTruncated) { + Reader::Reporter reporter("filename"); + reporter.HeaderTruncated(); +} + +TEST(MachOReaderReporter, LoadCommandRegionTruncated) { + Reader::Reporter reporter("filename"); + reporter.LoadCommandRegionTruncated(); +} + +TEST(MachOReaderReporter, LoadCommandsOverrun) { + Reader::Reporter reporter("filename"); + reporter.LoadCommandsOverrun(10, 9, LC_DYSYMTAB); + reporter.LoadCommandsOverrun(10, 9, 0); +} + +TEST(MachOReaderReporter, LoadCommandTooShort) { + Reader::Reporter reporter("filename"); + reporter.LoadCommandTooShort(11, LC_SYMTAB); +} + +TEST(MachOReaderReporter, SectionsMissing) { + Reader::Reporter reporter("filename"); + reporter.SectionsMissing("segment name"); +} + +TEST(MachOReaderReporter, MisplacedSegmentData) { + Reader::Reporter reporter("filename"); + reporter.MisplacedSegmentData("segment name"); +} + +TEST(MachOReaderReporter, MisplacedSectionData) { + Reader::Reporter reporter("filename"); + reporter.MisplacedSectionData("section name", "segment name"); +} + +TEST(MachOReaderReporter, MisplacedSymbolTable) { + Reader::Reporter reporter("filename"); + reporter.MisplacedSymbolTable(); +} + +TEST(MachOReaderReporter, UnsupportedCPUType) { + Reader::Reporter reporter("filename"); + reporter.UnsupportedCPUType(CPU_TYPE_HPPA); +} + +struct FatReaderFixture { + FatReaderFixture() + : fat(kBigEndian), + reporter("reporter filename"), + reader(&reporter), object_files() { + EXPECT_CALL(reporter, BadHeader()).Times(0); + EXPECT_CALL(reporter, TooShort()).Times(0); + + // here, start, and Mark are file offsets in 'fat'. + fat.start() = 0; + } + // Append a 'fat_arch' entry to 'fat', with the given field values. + void AppendFatArch(cpu_type_t type, cpu_subtype_t subtype, + Label offset, Label size, uint32_t align) { + fat + .B32(type) + .B32(subtype) + .B32(offset) + .B32(size) + .B32(align); + } + // Append |n| dummy 'fat_arch' entries to 'fat'. The cpu type and + // subtype have unrealistic values. + void AppendDummyArchEntries(int n) { + for (int i = 0; i < n; i++) + AppendFatArch(0xb68ad617, 0x715a0840, 0, 0, 1); + } + void ReadFat(bool expect_parse_success = true) { + ASSERT_TRUE(fat.GetContents(&contents)); + fat_bytes = reinterpret_cast<const uint8_t *>(contents.data()); + if (expect_parse_success) { + EXPECT_TRUE(reader.Read(fat_bytes, contents.size())); + size_t fat_files_count; + const SuperFatArch* fat_files = reader.object_files(&fat_files_count); + object_files.resize(fat_files_count); + for (size_t i = 0; i < fat_files_count; ++i) { + EXPECT_TRUE(fat_files[i].ConvertToFatArch(&object_files[i])); + } + } + else + EXPECT_FALSE(reader.Read(fat_bytes, contents.size())); + } + test_assembler::Section fat; + MockFatReaderReporter reporter; + FatReader reader; + string contents; + const uint8_t *fat_bytes; + vector<struct fat_arch> object_files; +}; + +class FatReaderTest: public FatReaderFixture, public Test { }; + +TEST_F(FatReaderTest, BadMagic) { + EXPECT_CALL(reporter, BadHeader()).Times(1); + fat + .B32(0xcafed00d) // magic number (incorrect) + .B32(10); // number of architectures + AppendDummyArchEntries(10); + ReadFat(false); +} + +TEST_F(FatReaderTest, HeaderTooShort) { + EXPECT_CALL(reporter, TooShort()).Times(1); + fat + .B32(0xcafebabe); // magic number + ReadFat(false); +} + +TEST_F(FatReaderTest, ObjectListTooShort) { + EXPECT_CALL(reporter, TooShort()).Times(1); + fat + .B32(0xcafebabe) // magic number + .B32(10); // number of architectures + AppendDummyArchEntries(9); // nine dummy architecture entries... + fat // and a tenth, missing a byte at the end + .B32(0x3d46c8fc) // cpu type + .B32(0x8a7bfb01) // cpu subtype + .B32(0) // offset + .B32(0) // size + .Append(3, '*'); // one byte short of a four-byte alignment + ReadFat(false); +} + +TEST_F(FatReaderTest, DataTooShort) { + EXPECT_CALL(reporter, MisplacedObjectFile()).Times(1); + Label arch_data; + fat + .B32(0xcafebabe) // magic number + .B32(1); // number of architectures + AppendFatArch(0xb4d4a366, 0x4ba4f525, arch_data, 40, 0); + fat + .Mark(&arch_data) // file data begins here + .Append(30, '*'); // only 30 bytes, not 40 as header claims + ReadFat(false); +} + +TEST_F(FatReaderTest, NoObjectFiles) { + fat + .B32(0xcafebabe) // magic number + .B32(0); // number of architectures + ReadFat(); + EXPECT_EQ(0U, object_files.size()); +} + +TEST_F(FatReaderTest, OneObjectFile) { + Label obj1_offset; + fat + .B32(0xcafebabe) // magic number + .B32(1); // number of architectures + // First object file list entry + AppendFatArch(0x5e3a6e91, 0x52ccd852, obj1_offset, 0x42, 0x355b15b2); + // First object file data + fat + .Mark(&obj1_offset) + .Append(0x42, '*'); // dummy contents + ReadFat(); + ASSERT_EQ(1U, object_files.size()); + EXPECT_EQ(0x5e3a6e91, object_files[0].cputype); + EXPECT_EQ(0x52ccd852, object_files[0].cpusubtype); + EXPECT_EQ(obj1_offset.Value(), object_files[0].offset); + EXPECT_EQ(0x42U, object_files[0].size); + EXPECT_EQ(0x355b15b2U, object_files[0].align); +} + +TEST_F(FatReaderTest, ThreeObjectFiles) { + Label obj1, obj2, obj3; + fat + .B32(0xcafebabe) // magic number + .B32(3); // number of architectures + // Three object file list entries. + AppendFatArch(0x0cb92c30, 0x6a159a71, obj1, 0xfb4, 0x2615dbe8); + AppendFatArch(0x0f3f1cbb, 0x6c55e90f, obj2, 0xc31, 0x83af6ffd); + AppendFatArch(0x3717276d, 0x10ecdc84, obj3, 0x4b3, 0x035267d7); + fat + // First object file data + .Mark(&obj1) + .Append(0xfb4, '*') // dummy contents + // Second object file data + .Mark(&obj2) + .Append(0xc31, '%') // dummy contents + // Third object file data + .Mark(&obj3) + .Append(0x4b3, '^'); // dummy contents + + ReadFat(); + + ASSERT_EQ(3U, object_files.size()); + + // First object file. + EXPECT_EQ(0x0cb92c30, object_files[0].cputype); + EXPECT_EQ(0x6a159a71, object_files[0].cpusubtype); + EXPECT_EQ(obj1.Value(), object_files[0].offset); + EXPECT_EQ(0xfb4U, object_files[0].size); + EXPECT_EQ(0x2615dbe8U, object_files[0].align); + + // Second object file. + EXPECT_EQ(0x0f3f1cbb, object_files[1].cputype); + EXPECT_EQ(0x6c55e90f, object_files[1].cpusubtype); + EXPECT_EQ(obj2.Value(), object_files[1].offset); + EXPECT_EQ(0xc31U, object_files[1].size); + EXPECT_EQ(0x83af6ffdU, object_files[1].align); + + // Third object file. + EXPECT_EQ(0x3717276d, object_files[2].cputype); + EXPECT_EQ(0x10ecdc84, object_files[2].cpusubtype); + EXPECT_EQ(obj3.Value(), object_files[2].offset); + EXPECT_EQ(0x4b3U, object_files[2].size); + EXPECT_EQ(0x035267d7U, object_files[2].align); +} + +TEST_F(FatReaderTest, BigEndianMachO32) { + fat.set_endianness(kBigEndian); + fat + .D32(0xfeedface) // Mach-O file magic number + .D32(0x1a9d0518) // cpu type + .D32(0x1b779357) // cpu subtype + .D32(0x009df67e) // file type + .D32(0) // no load commands + .D32(0) // the load commands occupy no bytes + .D32(0x21987a99); // flags + + ReadFat(); + + // FatReader should treat a Mach-O file as if it were a fat binary file + // containing one object file --- the whole thing. + ASSERT_EQ(1U, object_files.size()); + EXPECT_EQ(0x1a9d0518, object_files[0].cputype); + EXPECT_EQ(0x1b779357, object_files[0].cpusubtype); + EXPECT_EQ(0U, object_files[0].offset); + EXPECT_EQ(contents.size(), object_files[0].size); +} + +TEST_F(FatReaderTest, BigEndianMachO64) { + fat.set_endianness(kBigEndian); + fat + .D32(0xfeedfacf) // Mach-O 64-bit file magic number + .D32(0x5aff8487) // cpu type + .D32(0x4c6a57f7) // cpu subtype + .D32(0x4392d2c8) // file type + .D32(0) // no load commands + .D32(0) // the load commands occupy no bytes + .D32(0x1b033eea); // flags + + ReadFat(); + + // FatReader should treat a Mach-O file as if it were a fat binary file + // containing one object file --- the whole thing. + ASSERT_EQ(1U, object_files.size()); + EXPECT_EQ(0x5aff8487, object_files[0].cputype); + EXPECT_EQ(0x4c6a57f7, object_files[0].cpusubtype); + EXPECT_EQ(0U, object_files[0].offset); + EXPECT_EQ(contents.size(), object_files[0].size); +} + +TEST_F(FatReaderTest, LittleEndianMachO32) { + fat.set_endianness(kLittleEndian); + fat + .D32(0xfeedface) // Mach-O file magic number + .D32(0x1a9d0518) // cpu type + .D32(0x1b779357) // cpu subtype + .D32(0x009df67e) // file type + .D32(0) // no load commands + .D32(0) // the load commands occupy no bytes + .D32(0x21987a99); // flags + + ReadFat(); + + // FatReader should treat a Mach-O file as if it were a fat binary file + // containing one object file --- the whole thing. + ASSERT_EQ(1U, object_files.size()); + EXPECT_EQ(0x1a9d0518, object_files[0].cputype); + EXPECT_EQ(0x1b779357, object_files[0].cpusubtype); + EXPECT_EQ(0U, object_files[0].offset); + EXPECT_EQ(contents.size(), object_files[0].size); +} + +TEST_F(FatReaderTest, LittleEndianMachO64) { + fat.set_endianness(kLittleEndian); + fat + .D32(0xfeedfacf) // Mach-O 64-bit file magic number + .D32(0x5aff8487) // cpu type + .D32(0x4c6a57f7) // cpu subtype + .D32(0x4392d2c8) // file type + .D32(0) // no load commands + .D32(0) // the load commands occupy no bytes + .D32(0x1b033eea); // flags + + ReadFat(); + + // FatReader should treat a Mach-O file as if it were a fat binary file + // containing one object file --- the whole thing. + ASSERT_EQ(1U, object_files.size()); + EXPECT_EQ(0x5aff8487, object_files[0].cputype); + EXPECT_EQ(0x4c6a57f7, object_files[0].cpusubtype); + EXPECT_EQ(0U, object_files[0].offset); + EXPECT_EQ(contents.size(), object_files[0].size); +} + +TEST_F(FatReaderTest, IncompleteMach) { + fat.set_endianness(kLittleEndian); + fat + .D32(0xfeedfacf) // Mach-O 64-bit file magic number + .D32(0x5aff8487); // cpu type + // Truncated! + + EXPECT_CALL(reporter, TooShort()).WillOnce(Return()); + + ReadFat(false); +} + + +// General mach_o::Reader tests. + +// Dynamically scoped configuration data. +class WithConfiguration { + public: + // Establish the given parameters as the default for SizedSections + // created within the dynamic scope of this instance. + WithConfiguration(Endianness endianness, size_t word_size) + : endianness_(endianness), word_size_(word_size), saved_(current_) { + current_ = this; + } + ~WithConfiguration() { current_ = saved_; } + static Endianness endianness() { + assert(current_); + return current_->endianness_; + } + static size_t word_size() { + assert(current_); + return current_->word_size_; + } + + private: + // The innermost WithConfiguration in whose dynamic scope we are + // currently executing. + static WithConfiguration *current_; + + // The innermost WithConfiguration whose dynamic scope encloses this + // WithConfiguration. + Endianness endianness_; + size_t word_size_; + WithConfiguration *saved_; +}; + +WithConfiguration *WithConfiguration::current_ = NULL; + +// A test_assembler::Section with a size that we can cite. The start(), +// Here() and Mark() member functions of a SizedSection always represent +// offsets within the overall file. +class SizedSection: public test_assembler::Section { + public: + // Construct a section of the given endianness and word size. + explicit SizedSection(Endianness endianness, size_t word_size) + : test_assembler::Section(endianness), word_size_(word_size) { + assert(word_size_ == 32 || word_size_ == 64); + } + SizedSection() + : test_assembler::Section(WithConfiguration::endianness()), + word_size_(WithConfiguration::word_size()) { + assert(word_size_ == 32 || word_size_ == 64); + } + + // Access/set this section's word size. + size_t word_size() const { return word_size_; } + void set_word_size(size_t word_size) { + assert(word_size_ == 32 || word_size_ == 64); + word_size_ = word_size; + } + + // Return a label representing the size this section will have when it + // is Placed in some containing section. + Label final_size() const { return final_size_; } + + // Append SECTION to the end of this section, and call its Finish member. + // Return a reference to this section. + SizedSection &Place(SizedSection *section) { + assert(section->endianness() == endianness()); + section->Finish(); + section->start() = Here(); + test_assembler::Section::Append(*section); + return *this; + } + + protected: + // Mark this section's contents as complete. For plain SizedSections, we + // set SECTION's start to its position in this section, and its final_size + // label to its current size. Derived classes can extend this as needed + // for their additional semantics. + virtual void Finish() { + final_size_ = Size(); + } + + // The word size for this data: either 32 or 64. + size_t word_size_; + + private: + // This section's final size, set when we are placed in some other + // SizedSection. + Label final_size_; +}; + +// A SizedSection that is loaded into memory at a particular address. +class LoadedSection: public SizedSection { + public: + explicit LoadedSection(Label address = Label()) : address_(address) { } + + // Return a label representing this section's address. + Label address() const { return address_; } + + // Placing a loaded section within a loaded section sets the relationship + // between their addresses. + LoadedSection &Place(LoadedSection *section) { + section->address() = address() + Size(); + SizedSection::Place(section); + return *this; + } + + protected: + // The address at which this section's contents will be loaded. + Label address_; +}; + +// A SizedSection representing a segment load command. +class SegmentLoadCommand: public SizedSection { + public: + SegmentLoadCommand() : section_count_(0) { } + + // Append a segment load command header with the given characteristics. + // The load command will refer to CONTENTS, which must be Placed in the + // file separately, at the desired position. Return a reference to this + // section. + SegmentLoadCommand &Header(const string &name, const LoadedSection &contents, + uint32_t maxprot, uint32_t initprot, + uint32_t flags) { + assert(contents.word_size() == word_size()); + D32(word_size() == 32 ? LC_SEGMENT : LC_SEGMENT_64); + D32(final_size()); + AppendCString(name, 16); + Append(endianness(), word_size() / 8, contents.address()); + Append(endianness(), word_size() / 8, vmsize_); + Append(endianness(), word_size() / 8, contents.start()); + Append(endianness(), word_size() / 8, contents.final_size()); + D32(maxprot); + D32(initprot); + D32(final_section_count_); + D32(flags); + + content_final_size_ = contents.final_size(); + + return *this; + } + + // Return a label representing the size of this segment when loaded into + // memory. If this label is still undefined by the time we place this + // segment, it defaults to the final size of the segment's in-file + // contents. Return a reference to this load command. + Label &vmsize() { return vmsize_; } + + // Add a section entry with the given characteristics to this segment + // load command. Return a reference to this. The section entry will refer + // to CONTENTS, which must be Placed in the segment's contents + // separately, at the desired position. + SegmentLoadCommand &AppendSectionEntry(const string §ion_name, + const string &segment_name, + uint32_t alignment, uint32_t flags, + const LoadedSection &contents) { + AppendCString(section_name, 16); + AppendCString(segment_name, 16); + Append(endianness(), word_size() / 8, contents.address()); + Append(endianness(), word_size() / 8, contents.final_size()); + D32(contents.start()); + D32(alignment); + D32(0); // relocations start + D32(0); // relocations size + D32(flags); + D32(0x93656b95); // reserved1 + D32(0xc35a2473); // reserved2 + if (word_size() == 64) + D32(0x70284b95); // reserved3 + + section_count_++; + + return *this; + } + + protected: + void Finish() { + final_section_count_ = section_count_; + if (!vmsize_.IsKnownConstant()) + vmsize_ = content_final_size_; + SizedSection::Finish(); + } + + private: + // The number of sections that have been added to this segment so far. + size_t section_count_; + + // A label representing the final number of sections this segment will hold. + Label final_section_count_; + + // The size of the contents for this segment present in the file. + Label content_final_size_; + + // A label representing the size of this segment when loaded; this can be + // larger than the size of its file contents, the difference being + // zero-filled. If not set explicitly by calling set_vmsize, this is set + // equal to the size of the contents. + Label vmsize_; +}; + +// A SizedSection holding a list of Mach-O load commands. +class LoadCommands: public SizedSection { + public: + LoadCommands() : command_count_(0) { } + + // Return a label representing the final load command count. + Label final_command_count() const { return final_command_count_; } + + // Increment the command count; return a reference to this section. + LoadCommands &CountCommand() { + command_count_++; + return *this; + } + + // Place COMMAND, containing a load command, at the end of this section. + // Return a reference to this section. + LoadCommands &Place(SizedSection *section) { + SizedSection::Place(section); + CountCommand(); + return *this; + } + + protected: + // Mark this load command list as complete. + void Finish() { + SizedSection::Finish(); + final_command_count_ = command_count_; + } + + private: + // The number of load commands we have added to this file so far. + size_t command_count_; + + // A label representing the final command count. + Label final_command_count_; +}; + +// A SizedSection holding the contents of a Mach-O file. Within a +// MachOFile, the start, Here, and Mark members refer to file offsets. +class MachOFile: public SizedSection { + public: + MachOFile() { + start() = 0; + } + + // Create a Mach-O file header using the given characteristics and load + // command list. This Places COMMANDS immediately after the header. + // Return a reference to this section. + MachOFile &Header(LoadCommands *commands, + cpu_type_t cpu_type = CPU_TYPE_X86, + cpu_subtype_t cpu_subtype = CPU_SUBTYPE_I386_ALL, + FileType file_type = MH_EXECUTE, + uint32_t file_flags = (MH_TWOLEVEL | + MH_DYLDLINK | + MH_NOUNDEFS)) { + D32(word_size() == 32 ? 0xfeedface : 0xfeedfacf); // magic number + D32(cpu_type); // cpu type + D32(cpu_subtype); // cpu subtype + D32(file_type); // file type + D32(commands->final_command_count()); // number of load commands + D32(commands->final_size()); // their size in bytes + D32(file_flags); // flags + if (word_size() == 64) + D32(0x55638b90); // reserved + Place(commands); + return *this; + } +}; + + +struct ReaderFixture { + ReaderFixture() + : reporter("reporter filename"), + reader(&reporter) { + EXPECT_CALL(reporter, BadHeader()).Times(0); + EXPECT_CALL(reporter, CPUTypeMismatch(_, _, _, _)).Times(0); + EXPECT_CALL(reporter, HeaderTruncated()).Times(0); + EXPECT_CALL(reporter, LoadCommandRegionTruncated()).Times(0); + EXPECT_CALL(reporter, LoadCommandsOverrun(_, _, _)).Times(0); + EXPECT_CALL(reporter, LoadCommandTooShort(_, _)).Times(0); + EXPECT_CALL(reporter, SectionsMissing(_)).Times(0); + EXPECT_CALL(reporter, MisplacedSegmentData(_)).Times(0); + EXPECT_CALL(reporter, MisplacedSectionData(_, _)).Times(0); + EXPECT_CALL(reporter, MisplacedSymbolTable()).Times(0); + EXPECT_CALL(reporter, UnsupportedCPUType(_)).Times(0); + + EXPECT_CALL(load_command_handler, UnknownCommand(_, _)).Times(0); + EXPECT_CALL(load_command_handler, SegmentCommand(_)).Times(0); + } + + void ReadFile(MachOFile *file, + bool expect_parse_success, + cpu_type_t expected_cpu_type, + cpu_subtype_t expected_cpu_subtype) { + ASSERT_TRUE(file->GetContents(&file_contents)); + file_bytes = reinterpret_cast<const uint8_t *>(file_contents.data()); + if (expect_parse_success) { + EXPECT_TRUE(reader.Read(file_bytes, + file_contents.size(), + expected_cpu_type, + expected_cpu_subtype)); + } else { + EXPECT_FALSE(reader.Read(file_bytes, + file_contents.size(), + expected_cpu_type, + expected_cpu_subtype)); + } + } + + string file_contents; + const uint8_t *file_bytes; + MockReaderReporter reporter; + Reader reader; + MockLoadCommandHandler load_command_handler; + MockSectionHandler section_handler; +}; + +class ReaderTest: public ReaderFixture, public Test { }; + +TEST_F(ReaderTest, BadMagic) { + WithConfiguration config(kLittleEndian, 32); + const cpu_type_t kCPUType = 0x46b760df; + const cpu_subtype_t kCPUSubType = 0x76a0e7f7; + MachOFile file; + file + .D32(0x67bdebe1) // Not a proper magic number. + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(0) // no load commands + .D32(0) // they occupy no bytes + .D32(0x80e71d64) // flags + .D32(0); // reserved + EXPECT_CALL(reporter, BadHeader()).WillOnce(Return()); + ReadFile(&file, false, CPU_TYPE_ANY, kCPUSubType); +} + +TEST_F(ReaderTest, MismatchedMagic) { + WithConfiguration config(kLittleEndian, 32); + const cpu_type_t kCPUType = CPU_TYPE_I386; + const cpu_subtype_t kCPUSubType = CPU_SUBTYPE_X86_ALL; + MachOFile file; + file + .D32(MH_CIGAM) // Right magic, but winds up wrong + // due to bitswapping + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(0) // no load commands + .D32(0) // they occupy no bytes + .D32(0x80e71d64) // flags + .D32(0); // reserved + EXPECT_CALL(reporter, BadHeader()).WillOnce(Return()); + ReadFile(&file, false, kCPUType, kCPUSubType); +} + +TEST_F(ReaderTest, ShortMagic) { + WithConfiguration config(kBigEndian, 32); + MachOFile file; + file + .D16(0xfeed); // magic number + // truncated! + EXPECT_CALL(reporter, HeaderTruncated()).WillOnce(Return()); + ReadFile(&file, false, CPU_TYPE_ANY, 0); +} + +TEST_F(ReaderTest, ShortHeader) { + WithConfiguration config(kBigEndian, 32); + const cpu_type_t kCPUType = CPU_TYPE_ANY; + const cpu_subtype_t kCPUSubType = 0x76a0e7f7; + MachOFile file; + file + .D32(0xfeedface) // magic number + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(0) // no load commands + .D32(0); // they occupy no bytes + EXPECT_CALL(reporter, HeaderTruncated()).WillOnce(Return()); + ReadFile(&file, false, CPU_TYPE_ANY, kCPUSubType); +} + +TEST_F(ReaderTest, MismatchedCPU) { + WithConfiguration config(kBigEndian, 32); + const cpu_type_t kCPUType = CPU_TYPE_I386; + const cpu_subtype_t kCPUSubType = CPU_SUBTYPE_X86_ALL; + MachOFile file; + file + .D32(MH_MAGIC) // Right magic for PPC (once bitswapped) + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(0) // no load commands + .D32(0) // they occupy no bytes + .D32(0x80e71d64) // flags + .D32(0); // reserved + EXPECT_CALL(reporter, + CPUTypeMismatch(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL, + CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL)) + .WillOnce(Return()); + ReadFile(&file, false, CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL); +} + +TEST_F(ReaderTest, LittleEndian32Bit) { + WithConfiguration config(kLittleEndian, 32); + const cpu_type_t kCPUType = 0x46b760df; + const cpu_subtype_t kCPUSubType = 0x76a0e7f7; + MachOFile file; + file + .D32(0xfeedface) // magic number + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(0) // no load commands + .D32(0) // they occupy no bytes + .D32(0x80e71d64) // flags + .D32(0); // reserved + ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType); + EXPECT_FALSE(reader.bits_64()); + EXPECT_FALSE(reader.big_endian()); + EXPECT_EQ(kCPUType, reader.cpu_type()); + EXPECT_EQ(kCPUSubType, reader.cpu_subtype()); + EXPECT_EQ(FileType(0x149fc717), reader.file_type()); + EXPECT_EQ(FileFlags(0x80e71d64), reader.flags()); +} + +TEST_F(ReaderTest, LittleEndian64Bit) { + WithConfiguration config(kLittleEndian, 64); + const cpu_type_t kCPUType = 0x46b760df; + const cpu_subtype_t kCPUSubType = 0x76a0e7f7; + MachOFile file; + file + .D32(0xfeedfacf) // magic number + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(0) // no load commands + .D32(0) // they occupy no bytes + .D32(0x80e71d64) // flags + .D32(0); // reserved + ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType); + EXPECT_TRUE(reader.bits_64()); + EXPECT_FALSE(reader.big_endian()); + EXPECT_EQ(kCPUType, reader.cpu_type()); + EXPECT_EQ(kCPUSubType, reader.cpu_subtype()); + EXPECT_EQ(FileType(0x149fc717), reader.file_type()); + EXPECT_EQ(FileFlags(0x80e71d64), reader.flags()); +} + +TEST_F(ReaderTest, BigEndian32Bit) { + WithConfiguration config(kBigEndian, 32); + const cpu_type_t kCPUType = 0x46b760df; + const cpu_subtype_t kCPUSubType = 0x76a0e7f7; + MachOFile file; + file + .D32(0xfeedface) // magic number + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(0) // no load commands + .D32(0) // they occupy no bytes + .D32(0x80e71d64) // flags + .D32(0); // reserved + ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType); + EXPECT_FALSE(reader.bits_64()); + EXPECT_TRUE(reader.big_endian()); + EXPECT_EQ(kCPUType, reader.cpu_type()); + EXPECT_EQ(kCPUSubType, reader.cpu_subtype()); + EXPECT_EQ(FileType(0x149fc717), reader.file_type()); + EXPECT_EQ(FileFlags(0x80e71d64), reader.flags()); +} + +TEST_F(ReaderTest, BigEndian64Bit) { + WithConfiguration config(kBigEndian, 64); + const cpu_type_t kCPUType = 0x46b760df; + const cpu_subtype_t kCPUSubType = 0x76a0e7f7; + MachOFile file; + file + .D32(0xfeedfacf) // magic number + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(0) // no load commands + .D32(0) // they occupy no bytes + .D32(0x80e71d64) // flags + .D32(0); // reserved + ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType); + EXPECT_TRUE(reader.bits_64()); + EXPECT_TRUE(reader.big_endian()); + EXPECT_EQ(kCPUType, reader.cpu_type()); + EXPECT_EQ(kCPUSubType, reader.cpu_subtype()); + EXPECT_EQ(FileType(0x149fc717), reader.file_type()); + EXPECT_EQ(FileFlags(0x80e71d64), reader.flags()); +} + + +// Load command tests. + +class LoadCommand: public ReaderFixture, public Test { }; + +TEST_F(LoadCommand, RegionTruncated) { + WithConfiguration config(kBigEndian, 64); + const cpu_type_t kCPUType = 0x46b760df; + const cpu_subtype_t kCPUSubType = 0x76a0e7f7; + MachOFile file; + file + .D32(0xfeedfacf) // magic number + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(1) // one load command + .D32(40) // occupying 40 bytes + .D32(0x80e71d64) // flags + .D32(0) // reserved + .Append(20, 0); // load command region, not as long as + // Mach-O header promised + + EXPECT_CALL(reporter, LoadCommandRegionTruncated()).WillOnce(Return()); + + ReadFile(&file, false, CPU_TYPE_ANY, kCPUSubType); +} + +TEST_F(LoadCommand, None) { + WithConfiguration config(kLittleEndian, 32); + LoadCommands load_commands; + MachOFile file; + file.Header(&load_commands); + + ReadFile(&file, true, CPU_TYPE_X86, CPU_SUBTYPE_I386_ALL); + + EXPECT_FALSE(reader.bits_64()); + EXPECT_FALSE(reader.big_endian()); + EXPECT_EQ(CPU_TYPE_X86, reader.cpu_type()); + EXPECT_EQ(CPU_SUBTYPE_I386_ALL, reader.cpu_subtype()); + EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), reader.file_type()); + EXPECT_EQ(FileFlags(MH_TWOLEVEL | + MH_DYLDLINK | + MH_NOUNDEFS), + FileFlags(reader.flags())); + + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); +} + +TEST_F(LoadCommand, Unknown) { + WithConfiguration config(kBigEndian, 32); + LoadCommands load_commands; + load_commands + .CountCommand() + .D32(0x33293d4a) // unknown load command + .D32(40) // total size in bytes + .Append(32, '*'); // dummy data + MachOFile file; + file.Header(&load_commands); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + EXPECT_FALSE(reader.bits_64()); + EXPECT_TRUE(reader.big_endian()); + EXPECT_EQ(CPU_TYPE_X86, reader.cpu_type()); + EXPECT_EQ(CPU_SUBTYPE_I386_ALL, reader.cpu_subtype()); + EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), reader.file_type()); + EXPECT_EQ(FileFlags(MH_TWOLEVEL | + MH_DYLDLINK | + MH_NOUNDEFS), + reader.flags()); + + ByteBuffer expected; + expected.start = file_bytes + load_commands.start().Value(); + expected.end = expected.start + load_commands.final_size().Value(); + EXPECT_CALL(load_command_handler, UnknownCommand(0x33293d4a, + expected)) + .WillOnce(Return(true)); + + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); +} + +TEST_F(LoadCommand, TypeIncomplete) { + WithConfiguration config(kLittleEndian, 32); + LoadCommands load_commands; + load_commands + .CountCommand() + .Append(3, 0); // load command type, incomplete + + MachOFile file; + file.Header(&load_commands); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + EXPECT_CALL(reporter, LoadCommandsOverrun(1, 0, 0)) + .WillOnce(Return()); + EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler)); +} + +TEST_F(LoadCommand, LengthIncomplete) { + WithConfiguration config(kBigEndian, 64); + LoadCommands load_commands; + load_commands + .CountCommand() + .D32(LC_SEGMENT); // load command + // no length + MachOFile file; + file.Header(&load_commands); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + EXPECT_CALL(reporter, LoadCommandsOverrun(1, 0, LC_SEGMENT)) + .WillOnce(Return()); + EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler)); +} + +TEST_F(LoadCommand, ContentIncomplete) { + WithConfiguration config(kLittleEndian, 64); + LoadCommands load_commands; + load_commands + .CountCommand() + .D32(LC_SEGMENT) // load command + .D32(40) // total size in bytes + .Append(28, '*'); // not enough dummy data + MachOFile file; + file.Header(&load_commands); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + EXPECT_CALL(reporter, LoadCommandsOverrun(1, 0, LC_SEGMENT)) + .WillOnce(Return()); + EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler)); +} + +TEST_F(LoadCommand, SegmentBE32) { + WithConfiguration config(kBigEndian, 32); + LoadedSection segment; + segment.address() = 0x1891139c; + segment.Append(42, '*'); // segment contents + SegmentLoadCommand segment_command; + segment_command + .Header("froon", segment, 0x94d6dd22, 0x8bdbc319, 0x990a16dd); + segment_command.vmsize() = 0xcb76584fU; + LoadCommands load_commands; + load_commands.Place(&segment_command); + MachOFile file; + file + .Header(&load_commands) + .Place(&segment); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_CALL(load_command_handler, SegmentCommand(_)) + .WillOnce(DoAll(SaveArg<0>(&actual_segment), + Return(true))); + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); + + EXPECT_FALSE(actual_segment.bits_64); + EXPECT_EQ("froon", actual_segment.name); + EXPECT_EQ(0x1891139cU, actual_segment.vmaddr); + EXPECT_EQ(0xcb76584fU, actual_segment.vmsize); + EXPECT_EQ(0x94d6dd22U, actual_segment.maxprot); + EXPECT_EQ(0x8bdbc319U, actual_segment.initprot); + EXPECT_EQ(0x990a16ddU, actual_segment.flags); + EXPECT_EQ(0U, actual_segment.nsects); + EXPECT_EQ(0U, actual_segment.section_list.Size()); + EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size()); +} + +TEST_F(LoadCommand, SegmentLE32) { + WithConfiguration config(kLittleEndian, 32); + LoadedSection segment; + segment.address() = 0x4b877866; + segment.Append(42, '*'); // segment contents + SegmentLoadCommand segment_command; + segment_command + .Header("sixteenprecisely", segment, + 0x350759ed, 0x6cf5a62e, 0x990a16dd); + segment_command.vmsize() = 0xcb76584fU; + LoadCommands load_commands; + load_commands.Place(&segment_command); + MachOFile file; + file + .Header(&load_commands) + .Place(&segment); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_CALL(load_command_handler, SegmentCommand(_)) + .WillOnce(DoAll(SaveArg<0>(&actual_segment), + Return(true))); + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); + + EXPECT_FALSE(actual_segment.bits_64); + EXPECT_EQ("sixteenprecisely", actual_segment.name); + EXPECT_EQ(0x4b877866U, actual_segment.vmaddr); + EXPECT_EQ(0xcb76584fU, actual_segment.vmsize); + EXPECT_EQ(0x350759edU, actual_segment.maxprot); + EXPECT_EQ(0x6cf5a62eU, actual_segment.initprot); + EXPECT_EQ(0x990a16ddU, actual_segment.flags); + EXPECT_EQ(0U, actual_segment.nsects); + EXPECT_EQ(0U, actual_segment.section_list.Size()); + EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size()); +} + +TEST_F(LoadCommand, SegmentBE64) { + WithConfiguration config(kBigEndian, 64); + LoadedSection segment; + segment.address() = 0x79f484f77009e511ULL; + segment.Append(42, '*'); // segment contents + SegmentLoadCommand segment_command; + segment_command + .Header("froon", segment, 0x42b45da5, 0x8bdbc319, 0xb2335220); + segment_command.vmsize() = 0x8d92397ce6248abaULL; + LoadCommands load_commands; + load_commands.Place(&segment_command); + MachOFile file; + file + .Header(&load_commands) + .Place(&segment); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_CALL(load_command_handler, SegmentCommand(_)) + .WillOnce(DoAll(SaveArg<0>(&actual_segment), + Return(true))); + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); + + EXPECT_EQ(true, actual_segment.bits_64); + EXPECT_EQ("froon", actual_segment.name); + EXPECT_EQ(0x79f484f77009e511ULL, actual_segment.vmaddr); + EXPECT_EQ(0x8d92397ce6248abaULL, actual_segment.vmsize); + EXPECT_EQ(0x42b45da5U, actual_segment.maxprot); + EXPECT_EQ(0x8bdbc319U, actual_segment.initprot); + EXPECT_EQ(0xb2335220U, actual_segment.flags); + EXPECT_EQ(0U, actual_segment.nsects); + EXPECT_EQ(0U, actual_segment.section_list.Size()); + EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size()); +} + +TEST_F(LoadCommand, SegmentLE64) { + WithConfiguration config(kLittleEndian, 64); + LoadedSection segment; + segment.address() = 0x50c0501dc5922d35ULL; + segment.Append(42, '*'); // segment contents + SegmentLoadCommand segment_command; + segment_command + .Header("sixteenprecisely", segment, + 0x917c339d, 0xdbc446fa, 0xb650b563); + segment_command.vmsize() = 0x84ae73e7c75469bfULL; + LoadCommands load_commands; + load_commands.Place(&segment_command); + MachOFile file; + file + .Header(&load_commands) + .Place(&segment); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_CALL(load_command_handler, SegmentCommand(_)) + .WillOnce(DoAll(SaveArg<0>(&actual_segment), + Return(true))); + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); + + EXPECT_EQ(true, actual_segment.bits_64); + EXPECT_EQ("sixteenprecisely", actual_segment.name); + EXPECT_EQ(0x50c0501dc5922d35ULL, actual_segment.vmaddr); + EXPECT_EQ(0x84ae73e7c75469bfULL, actual_segment.vmsize); + EXPECT_EQ(0x917c339dU, actual_segment.maxprot); + EXPECT_EQ(0xdbc446faU, actual_segment.initprot); + EXPECT_EQ(0xb650b563U, actual_segment.flags); + EXPECT_EQ(0U, actual_segment.nsects); + EXPECT_EQ(0U, actual_segment.section_list.Size()); + EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size()); +} + +TEST_F(LoadCommand, SegmentCommandTruncated) { + WithConfiguration config(kBigEndian, 32); + LoadedSection segment_contents; + segment_contents.Append(20, '*'); // lah di dah + SizedSection command; + command + .D32(LC_SEGMENT) // command type + .D32(command.final_size()) // command size + .AppendCString("too-short", 16) // segment name + .D32(0x9c759211) // vmaddr + .D32(segment_contents.final_size()) // vmsize + .D32(segment_contents.start()) // file offset + .D32(segment_contents.final_size()) // file size + .D32(0x56f28446) // max protection + .D32(0xe7910dcb) // initial protection + .D32(0) // no sections + .Append(3, 0); // flags (one byte short!) + LoadCommands load_commands; + load_commands.Place(&command); + MachOFile file; + file + .Header(&load_commands) + .Place(&segment_contents); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + EXPECT_CALL(reporter, LoadCommandTooShort(0, LC_SEGMENT)) + .WillOnce(Return()); + + EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler)); +} + +TEST_F(LoadCommand, SegmentBadContentOffset) { + WithConfiguration config(kLittleEndian, 32); + // Instead of letting a Place call set the segment's file offset and size, + // set them ourselves, to check that the parser catches invalid offsets + // instead of handing us bogus pointers. + LoadedSection segment; + segment.address() = 0x4db5489c; + segment.start() = 0x7e189e76; // beyond end of file + segment.final_size() = 0x98b9c3ab; + SegmentLoadCommand segment_command; + segment_command + .Header("notmerelyfifteen", segment, 0xcbab25ee, 0x359a20db, 0x68a3933f); + LoadCommands load_commands; + load_commands.Place(&segment_command); + MachOFile file; + file.Header(&load_commands); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + EXPECT_CALL(reporter, MisplacedSegmentData("notmerelyfifteen")) + .WillOnce(Return()); + + EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler)); +} + +TEST_F(LoadCommand, ThreeLoadCommands) { + WithConfiguration config(kBigEndian, 32); + LoadedSection seg1, seg2, seg3; + SegmentLoadCommand cmd1, cmd2, cmd3; + + seg1.Append(128, '@'); + seg1.address() = 0xa7f61ef6; + cmd1.Header("head", seg1, 0x88bf1cc7, 0x889a26a4, 0xe9b80d87); + // Include some dummy data at the end of the load command. Since we + // didn't claim to have any sections, the reader should ignore this. But + // making sure the commands have different lengths ensures that we're + // using the right command's length to advance the LoadCommandIterator. + cmd1.Append(128, '!'); + + seg2.Append(42, '*'); + seg2.address() = 0xc70fc909; + cmd2.Header("thorax", seg2, 0xde7327f4, 0xfdaf771d, 0x65e74b30); + // More dummy data at the end of the load command. + cmd2.Append(32, '^'); + + seg3.Append(42, '%'); + seg3.address() = 0x46b3ab05; + cmd3.Header("abdomen", seg3, 0x7098b70d, 0x8d8d7728, 0x5131419b); + // More dummy data at the end of the load command. + cmd3.Append(64, '&'); + + LoadCommands load_commands; + load_commands.Place(&cmd1).Place(&cmd2).Place(&cmd3); + + MachOFile file; + file.Header(&load_commands).Place(&seg1).Place(&seg2).Place(&seg3); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + { + InSequence s; + EXPECT_CALL(load_command_handler, + SegmentCommand(Field(&Segment::name, "head"))) + .WillOnce(Return(true)); + EXPECT_CALL(load_command_handler, + SegmentCommand(Field(&Segment::name, "thorax"))) + .WillOnce(Return(true)); + EXPECT_CALL(load_command_handler, + SegmentCommand(Field(&Segment::name, "abdomen"))) + .WillOnce(Return(true)); + } + + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); +} + +static inline Matcher<const Section &> MatchSection( + Matcher<bool> bits_64, + Matcher<const string &> section_name, + Matcher<const string &> segment_name, + Matcher<uint64_t> address, + Matcher<uint32_t> alignment, + Matcher<uint32_t> flags, + Matcher<const ByteBuffer &> contents) { + return AllOf(AllOf(Field(&Section::bits_64, bits_64), + Field(&Section::section_name, section_name), + Field(&Section::segment_name, segment_name), + Field(&Section::address, address)), + AllOf(Field(&Section::align, alignment), + Field(&Section::flags, flags), + Field(&Section::contents, contents))); +} + +static inline Matcher<const Section &> MatchSection( + Matcher<bool> bits_64, + Matcher<const string &> section_name, + Matcher<const string &> segment_name, + Matcher<uint64_t> address) { + return AllOf(Field(&Section::bits_64, bits_64), + Field(&Section::section_name, section_name), + Field(&Section::segment_name, segment_name), + Field(&Section::address, address)); +} + +TEST_F(LoadCommand, OneSegmentTwoSections) { + WithConfiguration config(kBigEndian, 64); + + // Create some sections with some data. + LoadedSection section1, section2; + section1.Append("buddha's hand"); + section2.Append("kumquat"); + + // Create a segment to hold them. + LoadedSection segment; + segment.address() = 0xe1d0eeec; + segment.Place(§ion2).Place(§ion1); + + SegmentLoadCommand segment_command; + segment_command + .Header("head", segment, 0x92c9568c, 0xa89f2627, 0x4dc7a1e2) + .AppendSectionEntry("mandarin", "kishu", 12, 0x8cd4604bU, section1) + .AppendSectionEntry("bergamot", "cara cara", 12, 0x98746efaU, section2); + + LoadCommands commands; + commands.Place(&segment_command); + + MachOFile file; + file.Header(&commands).Place(&segment); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_CALL(load_command_handler, SegmentCommand(_)) + .WillOnce(DoAll(SaveArg<0>(&actual_segment), + Return(true))); + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); + + { + InSequence s; + ByteBuffer contents1; + contents1.start = file_bytes + section1.start().Value(); + contents1.end = contents1.start + section1.final_size().Value(); + EXPECT_EQ("buddha's hand", + string(reinterpret_cast<const char *>(contents1.start), + contents1.Size())); + EXPECT_CALL(section_handler, + HandleSection(MatchSection(true, "mandarin", "kishu", + section1.address().Value(), 12, + 0x8cd4604bU, contents1))) + .WillOnce(Return(true)); + + ByteBuffer contents2; + contents2.start = file_bytes + section2.start().Value(); + contents2.end = contents2.start + section2.final_size().Value(); + EXPECT_EQ("kumquat", + string(reinterpret_cast<const char *>(contents2.start), + contents2.Size())); + EXPECT_CALL(section_handler, + HandleSection(MatchSection(true, "bergamot", "cara cara", + section2.address().Value(), 12, + 0x98746efaU, contents2))) + .WillOnce(Return(true)); + } + + EXPECT_TRUE(reader.WalkSegmentSections(actual_segment, §ion_handler)); +} + +TEST_F(LoadCommand, MisplacedSectionBefore) { + WithConfiguration config(kLittleEndian, 64); + + // The segment. + LoadedSection segment; + segment.address() = 0x696d83cc; + segment.Append(10, '0'); + + // The contents of the following sections don't matter, because + // we're not really going to Place them in segment; we're just going + // to set all their labels by hand to get the (impossible) + // configurations we want. + + // A section whose starting offset is before that of its section. + LoadedSection before; + before.Append(10, '1'); + before.start() = segment.start() - 1; + before.address() = segment.address() - 1; + before.final_size() = before.Size(); + + SegmentLoadCommand command; + command + .Header("segment", segment, 0x173baa29, 0x8407275d, 0xed8f7057) + .AppendSectionEntry("before", "segment", 0, 0x686c6921, before); + + LoadCommands commands; + commands.Place(&command); + + MachOFile file; + file.Header(&commands).Place(&segment); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_TRUE(reader.FindSegment("segment", &actual_segment)); + + EXPECT_CALL(reporter, MisplacedSectionData("before", "segment")) + .WillOnce(Return()); + EXPECT_FALSE(reader.WalkSegmentSections(actual_segment, §ion_handler)); +} + +TEST_F(LoadCommand, MisplacedSectionAfter) { + WithConfiguration config(kLittleEndian, 64); + + // The segment. + LoadedSection segment; + segment.address() = 0x696d83cc; + segment.Append(10, '0'); + + // The contents of the following sections don't matter, because + // we're not really going to Place them in segment; we're just going + // to set all their labels by hand to get the (impossible) + // configurations we want. + + // A section whose starting offset is after the end of its section. + LoadedSection after; + after.Append(10, '2'); + after.start() = segment.start() + 11; + after.address() = segment.address() + 11; + after.final_size() = after.Size(); + + SegmentLoadCommand command; + command + .Header("segment", segment, 0x173baa29, 0x8407275d, 0xed8f7057) + .AppendSectionEntry("after", "segment", 0, 0x2ee50124, after); + + LoadCommands commands; + commands.Place(&command); + + MachOFile file; + file.Header(&commands).Place(&segment); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_TRUE(reader.FindSegment("segment", &actual_segment)); + + EXPECT_CALL(reporter, MisplacedSectionData("after", "segment")) + .WillOnce(Return()); + EXPECT_FALSE(reader.WalkSegmentSections(actual_segment, §ion_handler)); +} + +TEST_F(LoadCommand, MisplacedSectionTooBig) { + WithConfiguration config(kLittleEndian, 64); + + // The segment. + LoadedSection segment; + segment.address() = 0x696d83cc; + segment.Append(10, '0'); + + // The contents of the following sections don't matter, because + // we're not really going to Place them in segment; we're just going + // to set all their labels by hand to get the (impossible) + // configurations we want. + + // A section that extends beyond the end of its section. + LoadedSection too_big; + too_big.Append(10, '3'); + too_big.start() = segment.start() + 1; + too_big.address() = segment.address() + 1; + too_big.final_size() = too_big.Size(); + + SegmentLoadCommand command; + command + .Header("segment", segment, 0x173baa29, 0x8407275d, 0xed8f7057) + .AppendSectionEntry("too big", "segment", 0, 0x8b53ae5c, too_big); + + LoadCommands commands; + commands.Place(&command); + + MachOFile file; + file.Header(&commands).Place(&segment); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_TRUE(reader.FindSegment("segment", &actual_segment)); + + EXPECT_CALL(reporter, MisplacedSectionData("too big", "segment")) + .WillOnce(Return()); + EXPECT_FALSE(reader.WalkSegmentSections(actual_segment, §ion_handler)); +} + + +// The segments in a .dSYM bundle's Mach-O file have their file offset +// and size set to zero, but the sections don't. The reader shouldn't +// report an error in this case. +TEST_F(LoadCommand, ZappedSegment) { + WithConfiguration config(kBigEndian, 32); + + // The segment. + LoadedSection segment; + segment.address() = 0x696d83cc; + segment.start() = 0; + segment.final_size() = 0; + + // The section. + LoadedSection section; + section.address() = segment.address(); + section.start() = 0; + section.final_size() = 1000; // extends beyond its segment + + SegmentLoadCommand command; + command + .Header("zapped", segment, 0x0861a5cb, 0x68ccff67, 0x0b66255c) + .AppendSectionEntry("twitching", "zapped", 0, 0x93b3bd42, section); + + LoadCommands commands; + commands.Place(&command); + + MachOFile file; + file.Header(&commands); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_TRUE(reader.FindSegment("zapped", &actual_segment)); + + ByteBuffer zapped_extent(NULL, 0); + EXPECT_CALL(section_handler, + HandleSection(MatchSection(false, "twitching", "zapped", + 0x696d83cc, 0, 0x93b3bd42, + zapped_extent))) + .WillOnce(Return(true)); + + EXPECT_TRUE(reader.WalkSegmentSections(actual_segment, §ion_handler)); +} + +TEST_F(LoadCommand, MapSegmentSections) { + WithConfiguration config(kLittleEndian, 32); + + // Create some sections with some data. + LoadedSection section1, section2, section3, section4; + section1.Append("buddha's hand"); + section2.start() = 0; // Section 2 is an S_ZEROFILL section. + section2.final_size() = 0; + section3.Append("shasta gold"); + section4.Append("satsuma"); + + // Create two segments to hold them. + LoadedSection segment1, segment2; + segment1.address() = 0x13e6c8a9; + segment1.Place(§ion3).Place(§ion1); + segment2.set_word_size(64); + segment2.address() = 0x04d462e2; + segment2.Place(§ion4); + section2.address() = segment2.address() + segment2.Size(); + + SegmentLoadCommand segment_command1, segment_command2; + segment_command1 + .Header("head", segment1, 0x67d955a6, 0x7a61c13e, 0xe3e50c64) + .AppendSectionEntry("mandarin", "head", 12, 0x5bb565d7, section1) + .AppendSectionEntry("bergamot", "head", 12, 0x8620de73, section3); + segment_command2.set_word_size(64); + segment_command2 + .Header("thorax", segment2, 0x7aab2419, 0xe908007f, 0x17961d33) + .AppendSectionEntry("sixteenprecisely", "thorax", + 12, S_ZEROFILL, section2) + .AppendSectionEntry("cara cara", "thorax", 12, 0xb6c5dd8a, section4); + + LoadCommands commands; + commands.Place(&segment_command1).Place(&segment_command2); + + MachOFile file; + file.Header(&commands).Place(&segment1).Place(&segment2); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment segment; + SectionMap section_map; + + EXPECT_FALSE(reader.FindSegment("smoot", &segment)); + + ASSERT_TRUE(reader.FindSegment("thorax", &segment)); + ASSERT_TRUE(reader.MapSegmentSections(segment, §ion_map)); + + EXPECT_FALSE(section_map.find("sixteenpreciselyandthensome") + != section_map.end()); + EXPECT_FALSE(section_map.find("mandarin") != section_map.end()); + ASSERT_TRUE(section_map.find("cara cara") != section_map.end()); + EXPECT_THAT(section_map["cara cara"], + MatchSection(true, "cara cara", "thorax", 0x04d462e2)); + ASSERT_TRUE(section_map.find("sixteenprecisely") + != section_map.end()); + ByteBuffer sixteenprecisely_contents(NULL, 0); + EXPECT_THAT(section_map["sixteenprecisely"], + MatchSection(true, "sixteenprecisely", "thorax", + 0x04d462e2 + 7, 12, S_ZEROFILL, + sixteenprecisely_contents)); + + ASSERT_TRUE(reader.FindSegment("head", &segment)); + ASSERT_TRUE(reader.MapSegmentSections(segment, §ion_map)); + + ASSERT_TRUE(section_map.find("mandarin") != section_map.end()); + EXPECT_THAT(section_map["mandarin"], + MatchSection(false, "mandarin", "head", 0x13e6c8a9 + 11)); + ASSERT_TRUE(section_map.find("bergamot") != section_map.end()); + EXPECT_THAT(section_map["bergamot"], + MatchSection(false, "bergamot", "head", 0x13e6c8a9)); +} + +TEST_F(LoadCommand, FindSegment) { + WithConfiguration config(kBigEndian, 32); + + LoadedSection segment1, segment2, segment3; + segment1.address() = 0xb8ae5752; + segment1.Append("Some contents!"); + segment2.address() = 0xd6b0ce83; + segment2.Append("Different stuff."); + segment3.address() = 0x7374fd2a; + segment3.Append("Further materials."); + + SegmentLoadCommand cmd1, cmd2, cmd3; + cmd1.Header("first", segment1, 0xfadb6932, 0x175bf529, 0x0de790ad); + cmd2.Header("second", segment2, 0xeef716e0, 0xe103a9d7, 0x7d38a8ef); + cmd3.Header("third", segment3, 0xe172b39e, 0x86012f07, 0x080ac94d); + + LoadCommands commands; + commands.Place(&cmd1).Place(&cmd2).Place(&cmd3); + + MachOFile file; + file.Header(&commands).Place(&segment1).Place(&segment2).Place(&segment3); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + + EXPECT_FALSE(reader.FindSegment("murphy", &actual_segment)); + + EXPECT_TRUE(reader.FindSegment("second", &actual_segment)); + EXPECT_EQ(0xd6b0ce83, actual_segment.vmaddr); +} + + +// Symtab tests. + +// A StringAssembler is a class for generating .stabstr sections to present +// as input to the STABS parser. +class StringAssembler: public SizedSection { + public: + // Add the string S to this StringAssembler, and return the string's + // offset within this compilation unit's strings. + size_t Add(const string &s) { + size_t offset = Size(); + AppendCString(s); + return offset; + } +}; + +// A SymbolAssembler is a class for generating .stab sections to present as +// test input for the STABS parser. +class SymbolAssembler: public SizedSection { + public: + // Create a SymbolAssembler that uses StringAssembler for its strings. + explicit SymbolAssembler(StringAssembler *string_assembler) + : string_assembler_(string_assembler), + entry_count_(0) { } + + // Append a STAB entry to the end of this section with the given + // characteristics. NAME is the offset of this entry's name string within + // its compilation unit's portion of the .stabstr section; this can be a + // value generated by a StringAssembler. Return a reference to this + // SymbolAssembler. + SymbolAssembler &Symbol(uint8_t type, uint8_t other, Label descriptor, + Label value, Label name) { + D32(name); + D8(type); + D8(other); + D16(descriptor); + Append(endianness(), word_size_ / 8, value); + entry_count_++; + return *this; + } + + // As above, but automatically add NAME to our StringAssembler. + SymbolAssembler &Symbol(uint8_t type, uint8_t other, Label descriptor, + Label value, const string &name) { + return Symbol(type, other, descriptor, value, string_assembler_->Add(name)); + } + + private: + // The strings for our STABS entries. + StringAssembler *string_assembler_; + + // The number of entries in this compilation unit so far. + size_t entry_count_; +}; + +class Symtab: public ReaderFixture, public Test { }; + +TEST_F(Symtab, Symtab32) { + WithConfiguration config(kLittleEndian, 32); + + StringAssembler strings; + SymbolAssembler symbols(&strings); + symbols + .Symbol(0x52, 0x7c, 0x3470, 0x9bb02e7c, "hrududu") + .Symbol(0x50, 0x90, 0x7520, 0x1122525d, "Frith"); + + SizedSection symtab_command; + symtab_command + .D32(LC_SYMTAB) // command + .D32(symtab_command.final_size()) // size + .D32(symbols.start()) // file offset of symbols + .D32(2) // symbol count + .D32(strings.start()) // file offset of strings + .D32(strings.final_size()); // strings size + + LoadCommands load_commands; + load_commands.Place(&symtab_command); + + MachOFile file; + file.Header(&load_commands).Place(&symbols).Place(&strings); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + ByteBuffer symbols_found, strings_found; + EXPECT_CALL(load_command_handler, SymtabCommand(_, _)) + .WillOnce(DoAll(SaveArg<0>(&symbols_found), + SaveArg<1>(&strings_found), + Return(true))); + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); + + EXPECT_EQ(24U, symbols_found.Size()); + EXPECT_EQ(14U, strings_found.Size()); +} + +TEST_F(Symtab, Symtab64) { + WithConfiguration config(kBigEndian, 64); + + StringAssembler strings; + SymbolAssembler symbols(&strings); + symbols + .Symbol(0xa7, 0xaf, 0x03af, 0x42f3072c74335181ULL, "foo") + .Symbol(0xb0, 0x9a, 0x2aa7, 0x2e2d349b3d5744a0ULL, "bar"); + + SizedSection symtab_command; + symtab_command + .D32(LC_SYMTAB) // command + .D32(symtab_command.final_size()) // size + .D32(symbols.start()) // file offset of symbols + .D32(2) // symbol count + .D32(strings.start()) // file offset of strings + .D32(strings.final_size()); // strings size + + LoadCommands load_commands; + load_commands.Place(&symtab_command); + + MachOFile file; + file.Header(&load_commands).Place(&symbols).Place(&strings); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + ByteBuffer symbols_found, strings_found; + EXPECT_CALL(load_command_handler, SymtabCommand(_, _)) + .WillOnce(DoAll(SaveArg<0>(&symbols_found), + SaveArg<1>(&strings_found), + Return(true))); + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); + + EXPECT_EQ(32U, symbols_found.Size()); + EXPECT_EQ(8U, strings_found.Size()); +} + +TEST_F(Symtab, SymtabMisplacedSymbols) { + WithConfiguration config(kBigEndian, 32); + + StringAssembler strings; + SymbolAssembler symbols(&strings); + symbols + .Symbol(0xa7, 0xaf, 0x03af, 0x42f3072c74335181ULL, "foo") + .Symbol(0xb0, 0x9a, 0x2aa7, 0x2e2d349b3d5744a0ULL, "bar"); + + SizedSection symtab_command; + symtab_command + .D32(LC_SYMTAB) // command + .D32(symtab_command.final_size()) // size + .D32(symbols.start()) // file offset of symbols + .D32(3) // symbol count (too many) + .D32(strings.start()) // file offset of strings + .D32(strings.final_size()); // strings size + + LoadCommands load_commands; + load_commands.Place(&symtab_command); + + MachOFile file; + // Put symbols at end, so the excessive length will be noticed. + file.Header(&load_commands).Place(&strings).Place(&symbols); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + EXPECT_CALL(reporter, MisplacedSymbolTable()).Times(1); + EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler)); +} + +TEST_F(Symtab, SymtabMisplacedStrings) { + WithConfiguration config(kLittleEndian, 32); + + StringAssembler strings; + SymbolAssembler symbols(&strings); + symbols + .Symbol(0xa7, 0xaf, 0x03af, 0x42f3072c74335181ULL, "foo") + .Symbol(0xb0, 0x9a, 0x2aa7, 0x2e2d349b3d5744a0ULL, "bar"); + + SizedSection symtab_command; + symtab_command + .D32(LC_SYMTAB) // command + .D32(symtab_command.final_size()) // size + .D32(symbols.start()) // file offset of symbols + .D32(2) // symbol count + .D32(strings.start()) // file offset of strings + .D32(strings.final_size() + 1); // strings size (too long) + + LoadCommands load_commands; + load_commands.Place(&symtab_command); + + MachOFile file; + // Put strings at end, so the excessive length will be noticed. + file.Header(&load_commands).Place(&symbols).Place(&strings); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + EXPECT_CALL(reporter, MisplacedSymbolTable()).Times(1); + EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler)); +} + diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/macho_utilities.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_utilities.cc new file mode 100644 index 0000000000..f56fe768c1 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_utilities.cc @@ -0,0 +1,155 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// macho_utilties.cc: Utilities for dealing with mach-o files +// +// Author: Dave Camp + +#include "common/mac/byteswap.h" +#include "common/mac/macho_utilities.h" + +#include <mach-o/fat.h> +#include <mach-o/loader.h> + +void breakpad_swap_uuid_command(struct breakpad_uuid_command *uc) { + uc->cmd = ByteSwap(uc->cmd); + uc->cmdsize = ByteSwap(uc->cmdsize); +} + +void breakpad_swap_load_command(struct load_command *lc) { + lc->cmd = ByteSwap(lc->cmd); + lc->cmdsize = ByteSwap(lc->cmdsize); +} + +void breakpad_swap_dylib_command(struct dylib_command *dc) { + dc->cmd = ByteSwap(dc->cmd); + dc->cmdsize = ByteSwap(dc->cmdsize); + + dc->dylib.name.offset = ByteSwap(dc->dylib.name.offset); + dc->dylib.timestamp = ByteSwap(dc->dylib.timestamp); + dc->dylib.current_version = ByteSwap(dc->dylib.current_version); + dc->dylib.compatibility_version = ByteSwap(dc->dylib.compatibility_version); +} + +void breakpad_swap_segment_command(struct segment_command *sc) { + sc->cmd = ByteSwap(sc->cmd); + sc->cmdsize = ByteSwap(sc->cmdsize); + + sc->vmaddr = ByteSwap(sc->vmaddr); + sc->vmsize = ByteSwap(sc->vmsize); + sc->fileoff = ByteSwap(sc->fileoff); + sc->filesize = ByteSwap(sc->filesize); + sc->maxprot = ByteSwap(sc->maxprot); + sc->initprot = ByteSwap(sc->initprot); + sc->nsects = ByteSwap(sc->nsects); + sc->flags = ByteSwap(sc->flags); +} + +void breakpad_swap_segment_command_64(struct segment_command_64 *sg) { + sg->cmd = ByteSwap(sg->cmd); + sg->cmdsize = ByteSwap(sg->cmdsize); + + sg->vmaddr = ByteSwap(sg->vmaddr); + sg->vmsize = ByteSwap(sg->vmsize); + sg->fileoff = ByteSwap(sg->fileoff); + sg->filesize = ByteSwap(sg->filesize); + + sg->maxprot = ByteSwap(sg->maxprot); + sg->initprot = ByteSwap(sg->initprot); + sg->nsects = ByteSwap(sg->nsects); + sg->flags = ByteSwap(sg->flags); +} + +void breakpad_swap_fat_header(struct fat_header *fh) { + fh->magic = ByteSwap(fh->magic); + fh->nfat_arch = ByteSwap(fh->nfat_arch); +} + +void breakpad_swap_fat_arch(struct fat_arch *fa, uint32_t narchs) { + for (uint32_t i = 0; i < narchs; ++i) { + fa[i].cputype = ByteSwap(fa[i].cputype); + fa[i].cpusubtype = ByteSwap(fa[i].cpusubtype); + fa[i].offset = ByteSwap(fa[i].offset); + fa[i].size = ByteSwap(fa[i].size); + fa[i].align = ByteSwap(fa[i].align); + } +} + +void breakpad_swap_mach_header(struct mach_header *mh) { + mh->magic = ByteSwap(mh->magic); + mh->cputype = ByteSwap(mh->cputype); + mh->cpusubtype = ByteSwap(mh->cpusubtype); + mh->filetype = ByteSwap(mh->filetype); + mh->ncmds = ByteSwap(mh->ncmds); + mh->sizeofcmds = ByteSwap(mh->sizeofcmds); + mh->flags = ByteSwap(mh->flags); +} + +void breakpad_swap_mach_header_64(struct mach_header_64 *mh) { + mh->magic = ByteSwap(mh->magic); + mh->cputype = ByteSwap(mh->cputype); + mh->cpusubtype = ByteSwap(mh->cpusubtype); + mh->filetype = ByteSwap(mh->filetype); + mh->ncmds = ByteSwap(mh->ncmds); + mh->sizeofcmds = ByteSwap(mh->sizeofcmds); + mh->flags = ByteSwap(mh->flags); + mh->reserved = ByteSwap(mh->reserved); +} + +void breakpad_swap_section(struct section *s, + uint32_t nsects) { + for (uint32_t i = 0; i < nsects; i++) { + s[i].addr = ByteSwap(s[i].addr); + s[i].size = ByteSwap(s[i].size); + + s[i].offset = ByteSwap(s[i].offset); + s[i].align = ByteSwap(s[i].align); + s[i].reloff = ByteSwap(s[i].reloff); + s[i].nreloc = ByteSwap(s[i].nreloc); + s[i].flags = ByteSwap(s[i].flags); + s[i].reserved1 = ByteSwap(s[i].reserved1); + s[i].reserved2 = ByteSwap(s[i].reserved2); + } +} + +void breakpad_swap_section_64(struct section_64 *s, + uint32_t nsects) { + for (uint32_t i = 0; i < nsects; i++) { + s[i].addr = ByteSwap(s[i].addr); + s[i].size = ByteSwap(s[i].size); + + s[i].offset = ByteSwap(s[i].offset); + s[i].align = ByteSwap(s[i].align); + s[i].reloff = ByteSwap(s[i].reloff); + s[i].nreloc = ByteSwap(s[i].nreloc); + s[i].flags = ByteSwap(s[i].flags); + s[i].reserved1 = ByteSwap(s[i].reserved1); + s[i].reserved2 = ByteSwap(s[i].reserved2); + } +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/macho_utilities.h b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_utilities.h new file mode 100644 index 0000000000..00563a77c8 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_utilities.h @@ -0,0 +1,95 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// macho_utilities.h: Utilities for dealing with mach-o files +// +// Author: Dave Camp + +#ifndef COMMON_MAC_MACHO_UTILITIES_H__ +#define COMMON_MAC_MACHO_UTILITIES_H__ + +#include <mach-o/loader.h> +#include <mach/thread_status.h> + +/* Some #defines and structs that aren't defined in older SDKs */ +#ifndef CPU_ARCH_ABI64 +# define CPU_ARCH_ABI64 0x01000000 +#endif + +#ifndef CPU_TYPE_X86 +# define CPU_TYPE_X86 CPU_TYPE_I386 +#endif + +#ifndef CPU_TYPE_POWERPC64 +# define CPU_TYPE_POWERPC64 (CPU_TYPE_POWERPC | CPU_ARCH_ABI64) +#endif + +#ifndef LC_UUID +# define LC_UUID 0x1b /* the uuid */ +#endif + +// The uuid_command struct/swap routines were added during the 10.4 series. +// Their presence isn't guaranteed. +struct breakpad_uuid_command { + uint32_t cmd; /* LC_UUID */ + uint32_t cmdsize; /* sizeof(struct uuid_command) */ + uint8_t uuid[16]; /* the 128-bit uuid */ +}; + +void breakpad_swap_uuid_command(struct breakpad_uuid_command *uc); + +void breakpad_swap_load_command(struct load_command *lc); + +void breakpad_swap_dylib_command(struct dylib_command *dc); + +// Older SDKs defines thread_state_data_t as an int[] instead +// of the natural_t[] it should be. +typedef natural_t breakpad_thread_state_data_t[THREAD_STATE_MAX]; + +void breakpad_swap_segment_command(struct segment_command *sc); + +// The 64-bit swap routines were added during the 10.4 series, their +// presence isn't guaranteed. +void breakpad_swap_segment_command_64(struct segment_command_64 *sg); + +void breakpad_swap_fat_header(struct fat_header *fh); + +void breakpad_swap_fat_arch(struct fat_arch *fa, uint32_t narchs); + +void breakpad_swap_mach_header(struct mach_header *mh); + +void breakpad_swap_mach_header_64(struct mach_header_64 *mh); + +void breakpad_swap_section(struct section *s, + uint32_t nsects); + +void breakpad_swap_section_64(struct section_64 *s, + uint32_t nsects); + +#endif diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/macho_walker.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_walker.cc new file mode 100644 index 0000000000..a30f52c7e5 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_walker.cc @@ -0,0 +1,272 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// macho_walker.cc: Iterate over the load commands in a mach-o file +// +// See macho_walker.h for documentation +// +// Author: Dan Waylonis + +#include <assert.h> +#include <fcntl.h> +#include <mach-o/arch.h> +#include <mach-o/fat.h> +#include <mach-o/loader.h> +#include <string.h> +#include <unistd.h> + +#include "common/mac/byteswap.h" +#include "common/mac/macho_walker.h" +#include "common/mac/macho_utilities.h" + +namespace MacFileUtilities { + +MachoWalker::MachoWalker(const char *path, LoadCommandCallback callback, + void *context) + : file_(-1), + memory_(NULL), + memory_size_(0), + callback_(callback), + callback_context_(context), + current_header_(NULL), + current_header_size_(0), + current_header_offset_(0) { + file_ = open(path, O_RDONLY); +} + +MachoWalker::MachoWalker(void *memory, size_t size, + LoadCommandCallback callback, void *context) + : file_(-1), + memory_(memory), + memory_size_(size), + callback_(callback), + callback_context_(context), + current_header_(NULL), + current_header_size_(0), + current_header_offset_(0) { +} + +MachoWalker::~MachoWalker() { + if (file_ != -1) + close(file_); +} + +bool MachoWalker::WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) { + cpu_type_t valid_cpu_type = cpu_type; + cpu_subtype_t valid_cpu_subtype = cpu_subtype; + // if |cpu_type| is 0, use the native cpu type. + if (cpu_type == 0) { + const NXArchInfo *arch = NXGetLocalArchInfo(); + assert(arch); + valid_cpu_type = arch->cputype; + valid_cpu_subtype = CPU_SUBTYPE_MULTIPLE; + } + off_t offset; + if (FindHeader(valid_cpu_type, valid_cpu_subtype, offset)) { + if (cpu_type & CPU_ARCH_ABI64) + return WalkHeader64AtOffset(offset); + + return WalkHeaderAtOffset(offset); + } + + return false; +} + +bool MachoWalker::ReadBytes(void *buffer, size_t size, off_t offset) { + if (memory_) { + if (offset < 0) + return false; + bool result = true; + if (offset + size > memory_size_) { + if (static_cast<size_t>(offset) >= memory_size_) + return false; + size = memory_size_ - static_cast<size_t>(offset); + result = false; + } + memcpy(buffer, static_cast<char *>(memory_) + offset, size); + return result; + } else { + return pread(file_, buffer, size, offset) == (ssize_t)size; + } +} + +bool MachoWalker::CurrentHeader(struct mach_header_64 *header, off_t *offset) { + if (current_header_) { + memcpy(header, current_header_, sizeof(mach_header_64)); + *offset = current_header_offset_; + return true; + } + + return false; +} + +bool MachoWalker::FindHeader(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + off_t &offset) { + // Read the magic bytes that's common amongst all mach-o files + uint32_t magic; + if (!ReadBytes(&magic, sizeof(magic), 0)) + return false; + + offset = sizeof(magic); + + // Figure out what type of file we've got + bool is_fat = false; + if (magic == FAT_MAGIC || magic == FAT_CIGAM) { + is_fat = true; + } + else if (magic != MH_MAGIC && magic != MH_CIGAM && magic != MH_MAGIC_64 && + magic != MH_CIGAM_64) { + return false; + } + + if (!is_fat) { + // If we don't have a fat header, check if the cpu type matches the single + // header + struct mach_header header; + if (!ReadBytes(&header, sizeof(header), 0)) + return false; + + if (magic == MH_CIGAM || magic == MH_CIGAM_64) + breakpad_swap_mach_header(&header); + + header.cpusubtype &= ~CPU_SUBTYPE_MASK; + + if (cpu_type != header.cputype || + (cpu_subtype != CPU_SUBTYPE_MULTIPLE && + cpu_subtype != header.cpusubtype)) { + return false; + } + + offset = 0; + return true; + } else { + // Read the fat header and find an appropriate architecture + offset = 0; + struct fat_header fat; + if (!ReadBytes(&fat, sizeof(fat), offset)) + return false; + + if (NXHostByteOrder() != NX_BigEndian) + breakpad_swap_fat_header(&fat); + + offset += sizeof(fat); + + // Search each architecture for the desired one + struct fat_arch arch; + for (uint32_t i = 0; i < fat.nfat_arch; ++i) { + if (!ReadBytes(&arch, sizeof(arch), offset)) + return false; + + if (NXHostByteOrder() != NX_BigEndian) + breakpad_swap_fat_arch(&arch, 1); + + arch.cpusubtype &= ~CPU_SUBTYPE_MASK; + + if (arch.cputype == cpu_type && + (cpu_subtype == CPU_SUBTYPE_MULTIPLE || + arch.cpusubtype == cpu_subtype)) { + offset = arch.offset; + return true; + } + + offset += sizeof(arch); + } + } + + return false; +} + +bool MachoWalker::WalkHeaderAtOffset(off_t offset) { + struct mach_header header; + if (!ReadBytes(&header, sizeof(header), offset)) + return false; + + bool swap = (header.magic == MH_CIGAM); + if (swap) + breakpad_swap_mach_header(&header); + + // Copy the data into the mach_header_64 structure. Since the 32-bit and + // 64-bit only differ in the last field (reserved), this is safe to do. + struct mach_header_64 header64; + memcpy((void *)&header64, (const void *)&header, sizeof(header)); + header64.reserved = 0; + + current_header_ = &header64; + current_header_size_ = sizeof(header); // 32-bit, not 64-bit + current_header_offset_ = offset; + offset += current_header_size_; + bool result = WalkHeaderCore(offset, header.ncmds, swap); + current_header_ = NULL; + current_header_size_ = 0; + current_header_offset_ = 0; + return result; +} + +bool MachoWalker::WalkHeader64AtOffset(off_t offset) { + struct mach_header_64 header; + if (!ReadBytes(&header, sizeof(header), offset)) + return false; + + bool swap = (header.magic == MH_CIGAM_64); + if (swap) + breakpad_swap_mach_header_64(&header); + + current_header_ = &header; + current_header_size_ = sizeof(header); + current_header_offset_ = offset; + offset += current_header_size_; + bool result = WalkHeaderCore(offset, header.ncmds, swap); + current_header_ = NULL; + current_header_size_ = 0; + current_header_offset_ = 0; + return result; +} + +bool MachoWalker::WalkHeaderCore(off_t offset, uint32_t number_of_commands, + bool swap) { + for (uint32_t i = 0; i < number_of_commands; ++i) { + struct load_command cmd; + if (!ReadBytes(&cmd, sizeof(cmd), offset)) + return false; + + if (swap) + breakpad_swap_load_command(&cmd); + + // Call the user callback + if (callback_ && !callback_(this, &cmd, offset, swap, callback_context_)) + break; + + offset += cmd.cmdsize; + } + + return true; +} + +} // namespace MacFileUtilities diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/macho_walker.h b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_walker.h new file mode 100644 index 0000000000..dd535814a1 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_walker.h @@ -0,0 +1,119 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// macho_walker.h: Iterate over the load commands in a mach-o file +// +// Author: Dan Waylonis + +#ifndef COMMON_MAC_MACHO_WALKER_H__ +#define COMMON_MAC_MACHO_WALKER_H__ + +#include <mach/machine.h> +#include <mach-o/loader.h> +#include <sys/types.h> + +namespace MacFileUtilities { + +class MachoWalker { + public: + // A callback function executed when a new load command is read. If no + // further processing of load commands is desired, return false. Otherwise, + // return true. + // |cmd| is the current command, and |offset| is the location relative to the + // beginning of the file (not header) where the command was read. If |swap| + // is set, then any command data (other than the returned load_command) should + // be swapped when read + typedef bool (*LoadCommandCallback)(MachoWalker *walker, load_command *cmd, + off_t offset, bool swap, void *context); + + MachoWalker(const char *path, LoadCommandCallback callback, void *context); + MachoWalker(void *memory, size_t size, LoadCommandCallback callback, + void *context); + ~MachoWalker(); + + // Begin walking the header for |cpu_type| and |cpu_subtype|. If |cpu_type| + // is 0, then the native cpu type is used. Otherwise, accepted values are + // listed in /usr/include/mach/machine.h (e.g., CPU_TYPE_X86 or + // CPU_TYPE_POWERPC). If |cpu_subtype| is CPU_SUBTYPE_MULTIPLE, the match is + // only done on |cpu_type|. + // Returns false if opening the file failed or if the |cpu_type|/|cpu_subtype| + // is not present in the file. + bool WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype); + + // Read |size| bytes from the opened file at |offset| into |buffer| + bool ReadBytes(void *buffer, size_t size, off_t offset); + + // Return the current header and header offset + bool CurrentHeader(struct mach_header_64 *header, off_t *offset); + + private: + // Locate (if any) the header offset for |cpu_type| and return in |offset|. + // Return true if found, false otherwise. + bool FindHeader(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + off_t &offset); + + // Process an individual header starting at |offset| from the start of the + // file. Return true if successful, false otherwise. + bool WalkHeaderAtOffset(off_t offset); + bool WalkHeader64AtOffset(off_t offset); + + // Bottleneck for walking the load commands + bool WalkHeaderCore(off_t offset, uint32_t number_of_commands, bool swap); + + // File descriptor to the opened file + int file_; + + // Memory location to read from. + void *memory_; + + // Size of the memory segment we can read from. + size_t memory_size_; + + // User specified callback & context + LoadCommandCallback callback_; + void *callback_context_; + + // Current header, size, and offset. The mach_header_64 is used for both + // 32-bit and 64-bit headers because they only differ in their last field + // (reserved). By adding the |current_header_size_| and the + // |current_header_offset_|, you can determine the offset in the file just + // after the header. + struct mach_header_64 *current_header_; + unsigned long current_header_size_; + off_t current_header_offset_; + + private: + MachoWalker(const MachoWalker &); + MachoWalker &operator=(const MachoWalker &); +}; + +} // namespace MacFileUtilities + +#endif // COMMON_MAC_MACHO_WALKER_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/moz.build b/toolkit/crashreporter/google-breakpad/src/common/mac/moz.build new file mode 100644 index 0000000000..23fc2207a4 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/moz.build @@ -0,0 +1,27 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +UNIFIED_SOURCES += [ + 'arch_utilities.cc', + 'file_id.cc', + 'macho_id.cc', + 'macho_reader.cc', + 'macho_utilities.cc', + 'macho_walker.cc', +] + +SOURCES += [ + 'bootstrap_compat.cc', + 'HTTPMultipartUpload.m', + 'MachIPC.mm', + 'string_utilities.cc', +] + +Library('breakpad_mac_common_s') + +CMFLAGS += ['-std=c99'] + +include('/toolkit/crashreporter/crashreporter.mozbuild') diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/scoped_task_suspend-inl.h b/toolkit/crashreporter/google-breakpad/src/common/mac/scoped_task_suspend-inl.h new file mode 100644 index 0000000000..d6d1bef971 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/scoped_task_suspend-inl.h @@ -0,0 +1,56 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Inline implementation of ScopedTaskSuspend, which suspends a Mach +// task for the duration of its scope. + +#ifndef GOOGLE_BREAKPAD_COMMON_MAC_SCOPED_TASK_SUSPEND_H_ +#define GOOGLE_BREAKPAD_COMMON_MAC_SCOPED_TASK_SUSPEND_H_ + +#include <mach/mach.h> + +namespace google_breakpad { + +class ScopedTaskSuspend { + public: + explicit ScopedTaskSuspend(mach_port_t target) : target_(target) { + task_suspend(target_); + } + + ~ScopedTaskSuspend() { + task_resume(target_); + } + + private: + mach_port_t target_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_COMMON_MAC_SCOPED_TASK_SUSPEND_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/string_utilities.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/string_utilities.cc new file mode 100644 index 0000000000..07c0f42680 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/string_utilities.cc @@ -0,0 +1,84 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include "common/scoped_ptr.h" +#include "common/mac/string_utilities.h" + +namespace MacStringUtils { + +using google_breakpad::scoped_array; + +std::string ConvertToString(CFStringRef str) { + CFIndex length = CFStringGetLength(str); + std::string result; + + if (!length) + return result; + + CFIndex maxUTF8Length = + CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8); + scoped_array<UInt8> buffer(new UInt8[maxUTF8Length + 1]); + CFIndex actualUTF8Length; + CFStringGetBytes(str, CFRangeMake(0, length), kCFStringEncodingUTF8, 0, + false, buffer.get(), maxUTF8Length, &actualUTF8Length); + buffer[actualUTF8Length] = 0; + result.assign((const char *)buffer.get()); + + return result; +} + +unsigned int IntegerValueAtIndex(string &str, unsigned int idx) { + string digits("0123456789"), temp; + size_t start = 0; + size_t end; + size_t found = 0; + unsigned int result = 0; + + for (; found <= idx; ++found) { + end = str.find_first_not_of(digits, start); + + if (end == string::npos) + end = str.size(); + + temp = str.substr(start, end - start); + + if (found == idx) { + result = atoi(temp.c_str()); + } + + start = str.find_first_of(digits, end + 1); + + if (start == string::npos) + break; + } + + return result; +} + +} // namespace MacStringUtils diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/string_utilities.h b/toolkit/crashreporter/google-breakpad/src/common/mac/string_utilities.h new file mode 100644 index 0000000000..6d89c834e7 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/string_utilities.h @@ -0,0 +1,52 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// string_utilities.h: Utilities for strings for Mac platform + +#ifndef COMMON_MAC_STRING_UTILITIES_H__ +#define COMMON_MAC_STRING_UTILITIES_H__ + +#include <CoreFoundation/CoreFoundation.h> + +#include <string> + +namespace MacStringUtils { + +using std::string; + +// Convert a CoreFoundation string into a std::string +string ConvertToString(CFStringRef str); + +// Return the idx'th decimal integer in str, separated by non-decimal-digits +// E.g., str = 10.4.8, idx = 1 -> 4 +unsigned int IntegerValueAtIndex(string &str, unsigned int idx); + +} // namespace MacStringUtils + +#endif // COMMON_MAC_STRING_UTILITIES_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/super_fat_arch.h b/toolkit/crashreporter/google-breakpad/src/common/mac/super_fat_arch.h new file mode 100644 index 0000000000..501c8652a1 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/super_fat_arch.h @@ -0,0 +1,88 @@ +// Copyright (c) 2015, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Erik Chen <erikchen@chromium.org> + +// super_fat_arch.h: A class to handle 64-bit object files. Has conversions to +// and from struct fat_arch. + +#ifndef BREAKPAD_COMMON_MAC_SUPER_FAT_ARCH_H_ +#define BREAKPAD_COMMON_MAC_SUPER_FAT_ARCH_H_ + +#include <limits> +#include <mach-o/fat.h> +#include <stdint.h> + +// Similar to struct fat_arch, except size-related parameters support +// 64-bits. +class SuperFatArch { + public: + uint32_t cputype; + uint32_t cpusubtype; + uint64_t offset; + uint64_t size; + uint64_t align; + + SuperFatArch() : + cputype(0), + cpusubtype(0), + offset(0), + size(0), + align(0) { + } + + explicit SuperFatArch(const struct fat_arch &arch) : + cputype(arch.cputype), + cpusubtype(arch.cpusubtype), + offset(arch.offset), + size(arch.size), + align(arch.align) { + } + + // Returns false if the conversion cannot be made. + // If the conversion succeeds, the result is placed in |output_arch|. + bool ConvertToFatArch(struct fat_arch* output_arch) const { + if (offset > std::numeric_limits<uint32_t>::max()) + return false; + if (size > std::numeric_limits<uint32_t>::max()) + return false; + if (align > std::numeric_limits<uint32_t>::max()) + return false; + struct fat_arch arch; + arch.cputype = cputype; + arch.cpusubtype = cpusubtype; + arch.offset = offset; + arch.size = size; + arch.align = align; + *output_arch = arch; + return true; + } +}; + +#endif // BREAKPAD_COMMON_MAC_SUPER_FAT_ARCH_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.h b/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.h new file mode 100644 index 0000000000..ce3d9022cd --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.h @@ -0,0 +1,1110 @@ +// +// GTMSenTestCase.h +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +// Portions of this file fall under the following license, marked with +// SENTE_BEGIN - SENTE_END +// +// Copyright (c) 1997-2005, Sen:te (Sente SA). All rights reserved. +// +// Use of this source code is governed by the following license: +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL Sente SA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 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. +// +// Note: this license is equivalent to the FreeBSD license. +// +// This notice may not be removed from this file. + +// Some extra test case macros that would have been convenient for SenTestingKit +// to provide. I didn't stick GTM in front of the Macro names, so that they would +// be easy to remember. + +#import "GTMDefines.h" + +#if (!GTM_IPHONE_SDK) || (GTM_IPHONE_USE_SENTEST) +#import <SenTestingKit/SenTestingKit.h> +#else +#import <Foundation/Foundation.h> +#ifdef __cplusplus +extern "C" { +#endif + +#if defined __clang__ +// gcc and gcc-llvm do not allow you to use STAssert(blah, nil) with nil +// as a description if you have the NS_FORMAT_FUNCTION on. +// clang however will not compile without warnings if you don't have it. +NSString *STComposeString(NSString *, ...) NS_FORMAT_FUNCTION(1, 2); +#else +NSString *STComposeString(NSString *, ...); +#endif // __clang__ + +#ifdef __cplusplus +} +#endif + +#endif // !GTM_IPHONE_SDK || GTM_IPHONE_USE_SENTEST + +// Generates a failure when a1 != noErr +// Args: +// a1: should be either an OSErr or an OSStatus +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNoErr(a1, description, ...) \ +do { \ + @try { \ + OSStatus a1value = (a1); \ + if (a1value != noErr) { \ + NSString *_expression = [NSString stringWithFormat:@"Expected noErr, got %ld for (%s)", (long)a1value, #a1]; \ + [self failWithException:([NSException failureInCondition:_expression \ + isTrue:NO \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \ + } \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == noErr fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while(0) + +// Generates a failure when a1 != a2 +// Args: +// a1: received value. Should be either an OSErr or an OSStatus +// a2: expected value. Should be either an OSErr or an OSStatus +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertErr(a1, a2, description, ...) \ +do { \ + @try { \ + OSStatus a1value = (a1); \ + OSStatus a2value = (a2); \ + if (a1value != a2value) { \ + NSString *_expression = [NSString stringWithFormat:@"Expected %s(%ld) but got %ld for (%s)", #a2, (long)a2value, (long)a1value, #a1]; \ + [self failWithException:([NSException failureInCondition:_expression \ + isTrue:NO \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \ + } \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == (%s) fails", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while(0) + + +// Generates a failure when a1 is NULL +// Args: +// a1: should be a pointer (use STAssertNotNil for an object) +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotNULL(a1, description, ...) \ +do { \ + @try { \ + __typeof__(a1) a1value = (a1); \ + if (a1value == (__typeof__(a1))NULL) { \ + NSString *_expression = [NSString stringWithFormat:@"((%s) != NULL)", #a1]; \ + [self failWithException:([NSException failureInCondition:_expression \ + isTrue:NO \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \ + } \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != NULL fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while(0) + +// Generates a failure when a1 is not NULL +// Args: +// a1: should be a pointer (use STAssertNil for an object) +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNULL(a1, description, ...) \ +do { \ + @try { \ + __typeof__(a1) a1value = (a1); \ + if (a1value != (__typeof__(a1))NULL) { \ + NSString *_expression = [NSString stringWithFormat:@"((%s) == NULL)", #a1]; \ + [self failWithException:([NSException failureInCondition:_expression \ + isTrue:NO \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \ + } \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == NULL fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while(0) + +// Generates a failure when a1 is equal to a2. This test is for C scalars, +// structs and unions. +// Args: +// a1: argument 1 +// a2: argument 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotEquals(a1, a2, description, ...) \ +do { \ + @try { \ + if (strcmp(@encode(__typeof__(a1)), @encode(__typeof__(a2)))) { \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"Type mismatch -- %@", STComposeString(description, ##__VA_ARGS__)]]; \ + } else { \ + __typeof__(a1) a1value = (a1); \ + __typeof__(a2) a2value = (a2); \ + NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \ + NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \ + if ([a1encoded isEqualToValue:a2encoded]) { \ + NSString *_expression = [NSString stringWithFormat:@"((%s) != (%s))", #a1, #a2]; \ + [self failWithException:([NSException failureInCondition:_expression \ + isTrue:NO \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \ + }\ + } \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while(0) + +// Generates a failure when a1 is equal to a2. This test is for objects. +// Args: +// a1: argument 1. object. +// a2: argument 2. object. +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotEqualObjects(a1, a2, description, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + id a2value = (a2); \ + if ( (strcmp(@encode(__typeof__(a1value)), @encode(id)) == 0) && \ + (strcmp(@encode(__typeof__(a2value)), @encode(id)) == 0) && \ + (![(id)a1value isEqual:(id)a2value]) ) continue; \ + [self failWithException:([NSException failureInEqualityBetweenObject:a1value \ + andObject:a2value \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \ + }\ + @catch (id anException) {\ + [self failWithException:([NSException failureInRaise:[NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \ + }\ +} while(0) + +// Generates a failure when a1 is not 'op' to a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertOperation(a1, a2, op, description, ...) \ +do { \ + @try { \ + if (strcmp(@encode(__typeof__(a1)), @encode(__typeof__(a2)))) { \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"Type mismatch -- %@", STComposeString(description, ##__VA_ARGS__)]]; \ + } else { \ + __typeof__(a1) a1value = (a1); \ + __typeof__(a2) a2value = (a2); \ + if (!(a1value op a2value)) { \ + double a1DoubleValue = a1value; \ + double a2DoubleValue = a2value; \ + NSString *_expression = [NSString stringWithFormat:@"(%s (%lg) %s %s (%lg))", #a1, a1DoubleValue, #op, #a2, a2DoubleValue]; \ + [self failWithException:([NSException failureInCondition:_expression \ + isTrue:NO \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \ + } \ + } \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException \ + failureInRaise:[NSString stringWithFormat:@"(%s) %s (%s)", #a1, #op, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while(0) + +// Generates a failure when a1 is not > a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertGreaterThan(a1, a2, description, ...) \ + STAssertOperation(a1, a2, >, description, ##__VA_ARGS__) + +// Generates a failure when a1 is not >= a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertGreaterThanOrEqual(a1, a2, description, ...) \ + STAssertOperation(a1, a2, >=, description, ##__VA_ARGS__) + +// Generates a failure when a1 is not < a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertLessThan(a1, a2, description, ...) \ + STAssertOperation(a1, a2, <, description, ##__VA_ARGS__) + +// Generates a failure when a1 is not <= a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertLessThanOrEqual(a1, a2, description, ...) \ + STAssertOperation(a1, a2, <=, description, ##__VA_ARGS__) + +// Generates a failure when string a1 is not equal to string a2. This call +// differs from STAssertEqualObjects in that strings that are different in +// composition (precomposed vs decomposed) will compare equal if their final +// representation is equal. +// ex O + umlaut decomposed is the same as O + umlaut composed. +// Args: +// a1: string 1 +// a2: string 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertEqualStrings(a1, a2, description, ...) \ +do { \ + @try { \ + id a1value = (a1); \ + id a2value = (a2); \ + if (a1value == a2value) continue; \ + if ([a1value isKindOfClass:[NSString class]] && \ + [a2value isKindOfClass:[NSString class]] && \ + [a1value compare:a2value options:0] == NSOrderedSame) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject:a1value \ + andObject:a2value \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while(0) + +// Generates a failure when string a1 is equal to string a2. This call +// differs from STAssertEqualObjects in that strings that are different in +// composition (precomposed vs decomposed) will compare equal if their final +// representation is equal. +// ex O + umlaut decomposed is the same as O + umlaut composed. +// Args: +// a1: string 1 +// a2: string 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotEqualStrings(a1, a2, description, ...) \ +do { \ + @try { \ + id a1value = (a1); \ + id a2value = (a2); \ + if ([a1value isKindOfClass:[NSString class]] && \ + [a2value isKindOfClass:[NSString class]] && \ + [a1value compare:a2value options:0] != NSOrderedSame) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject:a1value \ + andObject:a2value \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while(0) + +// Generates a failure when c-string a1 is not equal to c-string a2. +// Args: +// a1: string 1 +// a2: string 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertEqualCStrings(a1, a2, description, ...) \ +do { \ + @try { \ + const char* a1value = (a1); \ + const char* a2value = (a2); \ + if (a1value == a2value) continue; \ + if (strcmp(a1value, a2value) == 0) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject:[NSString stringWithUTF8String:a1value] \ + andObject:[NSString stringWithUTF8String:a2value] \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while(0) + +// Generates a failure when c-string a1 is equal to c-string a2. +// Args: +// a1: string 1 +// a2: string 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotEqualCStrings(a1, a2, description, ...) \ +do { \ + @try { \ + const char* a1value = (a1); \ + const char* a2value = (a2); \ + if (strcmp(a1value, a2value) != 0) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject:[NSString stringWithUTF8String:a1value] \ + andObject:[NSString stringWithUTF8String:a2value] \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while(0) + +/*" Generates a failure when a1 is not equal to a2 within + or - accuracy is false. + This test is for GLKit types (GLKVector, GLKMatrix) where small differences + could make these items not exactly equal. Do not use this version directly. + Use the explicit STAssertEqualGLKVectors and STAssertEqualGLKMatrices defined + below. + _{a1 The GLKType on the left.} + _{a2 The GLKType on the right.} + _{accuracy The maximum difference between a1 and a2 for these values to be + considered equal.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ + +#define STInternalAssertEqualGLKVectorsOrMatrices(a1, a2, accuracy, description, ...) \ +do { \ + @try { \ + if (strcmp(@encode(__typeof__(a1)), @encode(__typeof__(a2)))) { \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"Type mismatch -- %@", STComposeString(description, ##__VA_ARGS__)]]; \ + } else { \ + __typeof__(a1) a1GLKValue = (a1); \ + __typeof__(a2) a2GLKValue = (a2); \ + __typeof__(accuracy) accuracyvalue = (accuracy); \ + float *a1FloatValue = ((float*)&a1GLKValue); \ + float *a2FloatValue = ((float*)&a2GLKValue); \ + for (size_t i = 0; i < sizeof(__typeof__(a1)) / sizeof(float); ++i) { \ + float a1value = a1FloatValue[i]; \ + float a2value = a2FloatValue[i]; \ + if (STAbsoluteDifference(a1value, a2value) > accuracyvalue) { \ + NSMutableArray *strings = [NSMutableArray arrayWithCapacity:sizeof(a1) / sizeof(float)]; \ + NSString *string; \ + for (size_t j = 0; j < sizeof(__typeof__(a1)) / sizeof(float); ++j) { \ + string = [NSString stringWithFormat:@"(%0.3f == %0.3f)", a1FloatValue[j], a2FloatValue[j]]; \ + [strings addObject:string]; \ + } \ + string = [strings componentsJoinedByString:@", "]; \ + NSString *desc = STComposeString(description, ##__VA_ARGS__); \ + desc = [NSString stringWithFormat:@"%@ With Accuracy %0.3f: %@", string, (float)accuracyvalue, desc]; \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", desc]]; \ + break; \ + } \ + } \ + } \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while(0) + +#define STAssertEqualGLKVectors(a1, a2, accuracy, description, ...) \ + STInternalAssertEqualGLKVectorsOrMatrices(a1, a2, accuracy, description, ##__VA_ARGS__) + +#define STAssertEqualGLKMatrices(a1, a2, accuracy, description, ...) \ + STInternalAssertEqualGLKVectorsOrMatrices(a1, a2, accuracy, description, ##__VA_ARGS__) + +#define STAssertEqualGLKQuaternions(a1, a2, accuracy, description, ...) \ + STInternalAssertEqualGLKVectorsOrMatrices(a1, a2, accuracy, description, ##__VA_ARGS__) + +#if GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST +// When not using the Xcode provided version, define everything ourselves. + +// SENTE_BEGIN +/*" Generates a failure when !{ [a1 isEqualTo:a2] } is false + (or one is nil and the other is not). + _{a1 The object on the left.} + _{a2 The object on the right.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertEqualObjects(a1, a2, description, ...) \ +do { \ + @try { \ + id a1value = (a1); \ + id a2value = (a2); \ + if (a1value == a2value) continue; \ + if ((strcmp(@encode(__typeof__(a1value)), @encode(id)) == 0) && \ + (strcmp(@encode(__typeof__(a2value)), @encode(id)) == 0) && \ + [(id)a1value isEqual:(id)a2value]) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject:a1value \ + andObject:a2value \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while(0) + + +/*" Generates a failure when a1 is not equal to a2. This test is for + C scalars, structs and unions. + _{a1 The argument on the left.} + _{a2 The argument on the right.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertEquals(a1, a2, description, ...) \ +do { \ + @try { \ + if (strcmp(@encode(__typeof__(a1)), @encode(__typeof__(a2)))) { \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"Type mismatch -- %@", STComposeString(description, ##__VA_ARGS__)]]; \ + } else { \ + __typeof__(a1) a1value = (a1); \ + __typeof__(a2) a2value = (a2); \ + NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \ + NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \ + if (![a1encoded isEqualToValue:a2encoded]) { \ + [self failWithException:[NSException failureInEqualityBetweenValue:a1encoded \ + andValue:a2encoded \ + withAccuracy:nil \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while(0) + +#define STAbsoluteDifference(left,right) (MAX(left,right)-MIN(left,right)) + + +/*" Generates a failure when a1 is not equal to a2 within + or - accuracy is false. + This test is for scalars such as floats and doubles where small differences + could make these items not exactly equal, but also works for all scalars. + _{a1 The scalar on the left.} + _{a2 The scalar on the right.} + _{accuracy The maximum difference between a1 and a2 for these values to be + considered equal.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ + +#define STAssertEqualsWithAccuracy(a1, a2, accuracy, description, ...) \ +do { \ + @try { \ + if (strcmp(@encode(__typeof__(a1)), @encode(__typeof__(a2)))) { \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"Type mismatch -- %@", STComposeString(description, ##__VA_ARGS__)]]; \ + } else { \ + __typeof__(a1) a1value = (a1); \ + __typeof__(a2) a2value = (a2); \ + __typeof__(accuracy) accuracyvalue = (accuracy); \ + if (STAbsoluteDifference(a1value, a2value) > accuracyvalue) { \ + NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \ + NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \ + NSValue *accuracyencoded = [NSValue value:&accuracyvalue withObjCType:@encode(__typeof__(accuracy))]; \ + [self failWithException:[NSException failureInEqualityBetweenValue:a1encoded \ + andValue:a2encoded \ + withAccuracy:accuracyencoded \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while(0) + + + +/*" Generates a failure unconditionally. + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STFail(description, ...) \ +[self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]] + + + +/*" Generates a failure when a1 is not nil. + _{a1 An object.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertNil(a1, description, ...) \ +do { \ + @try { \ + id a1value = (a1); \ + if (a1value != nil) { \ + NSString *_a1 = [NSString stringWithUTF8String:#a1]; \ + NSString *_expression = [NSString stringWithFormat:@"((%@) == nil)", _a1]; \ + [self failWithException:[NSException failureInCondition:_expression \ + isTrue:NO \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == nil fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while(0) + + +/*" Generates a failure when a1 is nil. + _{a1 An object.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertNotNil(a1, description, ...) \ +do { \ + @try { \ + id a1value = (a1); \ + if (a1value == nil) { \ + NSString *_a1 = [NSString stringWithUTF8String:#a1]; \ + NSString *_expression = [NSString stringWithFormat:@"((%@) != nil)", _a1]; \ + [self failWithException:[NSException failureInCondition:_expression \ + isTrue:NO \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != nil fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while(0) + + +/*" Generates a failure when expression evaluates to false. + _{expr The expression that is tested.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertTrue(expr, description, ...) \ +do { \ + BOOL _evaluatedExpression = (expr); \ + if (!_evaluatedExpression) { \ + NSString *_expression = [NSString stringWithUTF8String:#expr]; \ + [self failWithException:[NSException failureInCondition:_expression \ + isTrue:NO \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while (0) + + +/*" Generates a failure when expression evaluates to false and in addition will + generate error messages if an exception is encountered. + _{expr The expression that is tested.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertTrueNoThrow(expr, description, ...) \ +do { \ + @try { \ + BOOL _evaluatedExpression = (expr); \ + if (!_evaluatedExpression) { \ + NSString *_expression = [NSString stringWithUTF8String:#expr]; \ + [self failWithException:[NSException failureInCondition:_expression \ + isTrue:NO \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) ", #expr] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while (0) + + +/*" Generates a failure when the expression evaluates to true. + _{expr The expression that is tested.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertFalse(expr, description, ...) \ +do { \ + BOOL _evaluatedExpression = (expr); \ + if (_evaluatedExpression) { \ + NSString *_expression = [NSString stringWithUTF8String:#expr]; \ + [self failWithException:[NSException failureInCondition:_expression \ + isTrue:YES \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while (0) + + +/*" Generates a failure when the expression evaluates to true and in addition + will generate error messages if an exception is encountered. + _{expr The expression that is tested.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertFalseNoThrow(expr, description, ...) \ +do { \ + @try { \ + BOOL _evaluatedExpression = (expr); \ + if (_evaluatedExpression) { \ + NSString *_expression = [NSString stringWithUTF8String:#expr]; \ + [self failWithException:[NSException failureInCondition:_expression \ + isTrue:YES \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"!(%s) ", #expr] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while (0) + + +/*" Generates a failure when expression does not throw an exception. + _{expression The expression that is evaluated.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent. +"*/ +#define STAssertThrows(expr, description, ...) \ +do { \ + @try { \ + (expr); \ + } \ + @catch (id anException) { \ + continue; \ + } \ + [self failWithException:[NSException failureInRaise:[NSString stringWithUTF8String:#expr] \ + exception:nil \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ +} while (0) + + +/*" Generates a failure when expression does not throw an exception of a + specific class. + _{expression The expression that is evaluated.} + _{specificException The specified class of the exception.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertThrowsSpecific(expr, specificException, description, ...) \ +do { \ + @try { \ + (expr); \ + } \ + @catch (specificException *anException) { \ + continue; \ + } \ + @catch (id anException) { \ + NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description); \ + [self failWithException:[NSException failureInRaise:[NSString stringWithUTF8String:#expr] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \ + continue; \ + } \ + NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description); \ + [self failWithException:[NSException failureInRaise:[NSString stringWithUTF8String:#expr] \ + exception:nil \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \ +} while (0) + + +/*" Generates a failure when expression does not throw an exception of a + specific class with a specific name. Useful for those frameworks like + AppKit or Foundation that throw generic NSException w/specific names + (NSInvalidArgumentException, etc). + _{expression The expression that is evaluated.} + _{specificException The specified class of the exception.} + _{aName The name of the specified exception.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} + +"*/ +#define STAssertThrowsSpecificNamed(expr, specificException, aName, description, ...) \ +do { \ + @try { \ + (expr); \ + } \ + @catch (specificException *anException) { \ + if ([aName isEqualToString:[anException name]]) continue; \ + NSString *_descrip = STComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description); \ + [self failWithException: \ + [NSException failureInRaise:[NSString stringWithUTF8String:#expr] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \ + continue; \ + } \ + @catch (id anException) { \ + NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description); \ + [self failWithException: \ + [NSException failureInRaise:[NSString stringWithUTF8String:#expr] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \ + continue; \ + } \ + NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description); \ + [self failWithException: \ + [NSException failureInRaise:[NSString stringWithUTF8String:#expr] \ + exception:nil \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \ +} while (0) + + +/*" Generates a failure when expression does throw an exception. + _{expression The expression that is evaluated.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertNoThrow(expr, description, ...) \ +do { \ + @try { \ + (expr); \ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithUTF8String:#expr] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while (0) + + +/*" Generates a failure when expression does throw an exception of the specitied + class. Any other exception is okay (i.e. does not generate a failure). + _{expression The expression that is evaluated.} + _{specificException The specified class of the exception.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertNoThrowSpecific(expr, specificException, description, ...) \ +do { \ + @try { \ + (expr); \ + } \ + @catch (specificException *anException) { \ + [self failWithException:[NSException failureInRaise:[NSString stringWithUTF8String:#expr] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + @catch (id anythingElse) { \ + ; \ + } \ +} while (0) + + +/*" Generates a failure when expression does throw an exception of a + specific class with a specific name. Useful for those frameworks like + AppKit or Foundation that throw generic NSException w/specific names + (NSInvalidArgumentException, etc). + _{expression The expression that is evaluated.} + _{specificException The specified class of the exception.} + _{aName The name of the specified exception.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} + +"*/ +#define STAssertNoThrowSpecificNamed(expr, specificException, aName, description, ...) \ +do { \ + @try { \ + (expr); \ + } \ + @catch (specificException *anException) { \ + if ([aName isEqualToString:[anException name]]) { \ + NSString *_descrip = STComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description); \ + [self failWithException: \ + [NSException failureInRaise:[NSString stringWithUTF8String:#expr] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \ + } \ + continue; \ + } \ + @catch (id anythingElse) { \ + ; \ + } \ +} while (0) + + + +@interface NSException (GTMSenTestAdditions) ++ (NSException *)failureInFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... NS_FORMAT_FUNCTION(3, 4); ++ (NSException *)failureInCondition:(NSString *)condition + isTrue:(BOOL)isTrue + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... NS_FORMAT_FUNCTION(5, 6); ++ (NSException *)failureInEqualityBetweenObject:(id)left + andObject:(id)right + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... NS_FORMAT_FUNCTION(5, 6); ++ (NSException *)failureInEqualityBetweenValue:(NSValue *)left + andValue:(NSValue *)right + withAccuracy:(NSValue *)accuracy + inFile:(NSString *)filename + atLine:(int) ineNumber + withDescription:(NSString *)formatString, ... NS_FORMAT_FUNCTION(6, 7); ++ (NSException *)failureInRaise:(NSString *)expression + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... NS_FORMAT_FUNCTION(4, 5); ++ (NSException *)failureInRaise:(NSString *)expression + exception:(NSException *)exception + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... NS_FORMAT_FUNCTION(5, 6); +@end + +// SENTE_END + +@protocol SenTestCase ++ (id)testCaseWithInvocation:(NSInvocation *)anInvocation; +- (id)initWithInvocation:(NSInvocation *)anInvocation; +- (void)setUp; +- (void)invokeTest; +- (void)tearDown; +- (void)performTest; +- (void)failWithException:(NSException*)exception; +- (NSInvocation *)invocation; +- (SEL)selector; ++ (NSArray *)testInvocations; +@end + +@interface SenTestCase : NSObject<SenTestCase> { + @private + NSInvocation *invocation_; +} +@end + +GTM_EXTERN NSString *const SenTestFailureException; + +GTM_EXTERN NSString *const SenTestFilenameKey; +GTM_EXTERN NSString *const SenTestLineNumberKey; + +#endif // GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST + +// All unittest cases in GTM should inherit from GTMTestCase. It makes sure +// to set up our logging system correctly to verify logging calls. +// See GTMUnitTestDevLog.h for details +@interface GTMTestCase : SenTestCase + +// Returns YES if this is an abstract testCase class as opposed to a concrete +// testCase class that you want tests run against. SenTestCase is not designed +// out of the box to handle an abstract class hierarchy descending from it with +// some concrete subclasses. In some cases we want all the "concrete" +// subclasses of an abstract subclass of SenTestCase to run a test, but we don't +// want that test to be run against an instance of an abstract subclass itself. +// By returning "YES" here, the tests defined by this class won't be run against +// an instance of this class. As an example class hierarchy: +// +// FooExtensionTestCase +// GTMTestCase <- ExtensionAbstractTestCase < +// BarExtensionTestCase +// +// So FooExtensionTestCase and BarExtensionTestCase inherit from +// ExtensionAbstractTestCase (and probably FooExtension and BarExtension inherit +// from a class named Extension). We want the tests in ExtensionAbstractTestCase +// to be run as part of FooExtensionTestCase and BarExtensionTestCase, but we +// don't want them run against ExtensionAbstractTestCase. The default +// implementation checks to see if the name of the class contains the word +// "AbstractTest" (case sensitive). ++ (BOOL)isAbstractTestCase; + +@end diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.m b/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.m new file mode 100644 index 0000000000..162f01e97c --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.m @@ -0,0 +1,428 @@ +// +// GTMSenTestCase.m +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMSenTestCase.h" + +#import <unistd.h> +#if GTM_IPHONE_SIMULATOR +#import <objc/message.h> +#endif + +#import "GTMObjC2Runtime.h" +#import "GTMUnitTestDevLog.h" + +#if GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST +#import <stdarg.h> + +@interface NSException (GTMSenTestPrivateAdditions) ++ (NSException *)failureInFile:(NSString *)filename + atLine:(int)lineNumber + reason:(NSString *)reason; +@end + +@implementation NSException (GTMSenTestPrivateAdditions) ++ (NSException *)failureInFile:(NSString *)filename + atLine:(int)lineNumber + reason:(NSString *)reason { + NSDictionary *userInfo = + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInteger:lineNumber], SenTestLineNumberKey, + filename, SenTestFilenameKey, + nil]; + + return [self exceptionWithName:SenTestFailureException + reason:reason + userInfo:userInfo]; +} +@end + +@implementation NSException (GTMSenTestAdditions) + ++ (NSException *)failureInFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason = testDescription; + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInCondition:(NSString *)condition + isTrue:(BOOL)isTrue + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason = [NSString stringWithFormat:@"'%@' should be %s. %@", + condition, isTrue ? "false" : "true", testDescription]; + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInEqualityBetweenObject:(id)left + andObject:(id)right + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason = + [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@", + [left description], [right description], testDescription]; + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInEqualityBetweenValue:(NSValue *)left + andValue:(NSValue *)right + withAccuracy:(NSValue *)accuracy + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason; + if (accuracy) { + reason = + [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@", + left, right, testDescription]; + } else { + reason = + [NSString stringWithFormat:@"'%@' should be equal to '%@' +/-'%@'. %@", + left, right, accuracy, testDescription]; + } + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInRaise:(NSString *)expression + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason = [NSString stringWithFormat:@"'%@' should raise. %@", + expression, testDescription]; + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInRaise:(NSString *)expression + exception:(NSException *)exception + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason; + if ([[exception name] isEqualToString:SenTestFailureException]) { + // it's our exception, assume it has the right description on it. + reason = [exception reason]; + } else { + // not one of our exception, use the exceptions reason and our description + reason = [NSString stringWithFormat:@"'%@' raised '%@'. %@", + expression, [exception reason], testDescription]; + } + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + +@end + +NSString *STComposeString(NSString *formatString, ...) { + NSString *reason = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + reason = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + return reason; +} + +NSString *const SenTestFailureException = @"SenTestFailureException"; +NSString *const SenTestFilenameKey = @"SenTestFilenameKey"; +NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey"; + +@interface SenTestCase (SenTestCasePrivate) +// our method of logging errors ++ (void)printException:(NSException *)exception fromTestName:(NSString *)name; +@end + +@implementation SenTestCase ++ (id)testCaseWithInvocation:(NSInvocation *)anInvocation { + return [[[self alloc] initWithInvocation:anInvocation] autorelease]; +} + +- (id)initWithInvocation:(NSInvocation *)anInvocation { + if ((self = [super init])) { + invocation_ = [anInvocation retain]; + } + return self; +} + +- (void)dealloc { + [invocation_ release]; + [super dealloc]; +} + +- (void)failWithException:(NSException*)exception { + [exception raise]; +} + +- (void)setUp { +} + +- (void)performTest { + @try { + [self invokeTest]; + } @catch (NSException *exception) { + [[self class] printException:exception + fromTestName:NSStringFromSelector([self selector])]; + [exception raise]; + } +} + +- (NSInvocation *)invocation { + return invocation_; +} + +- (SEL)selector { + return [invocation_ selector]; +} + ++ (void)printException:(NSException *)exception fromTestName:(NSString *)name { + NSDictionary *userInfo = [exception userInfo]; + NSString *filename = [userInfo objectForKey:SenTestFilenameKey]; + NSNumber *lineNumber = [userInfo objectForKey:SenTestLineNumberKey]; + NSString *className = NSStringFromClass([self class]); + if ([filename length] == 0) { + filename = @"Unknown.m"; + } + fprintf(stderr, "%s:%ld: error: -[%s %s] : %s\n", + [filename UTF8String], + (long)[lineNumber integerValue], + [className UTF8String], + [name UTF8String], + [[exception reason] UTF8String]); + fflush(stderr); +} + +- (void)invokeTest { + NSException *e = nil; + @try { + // Wrap things in autorelease pools because they may + // have an STMacro in their dealloc which may get called + // when the pool is cleaned up + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + // We don't log exceptions here, instead we let the person that called + // this log the exception. This ensures they are only logged once but the + // outer layers get the exceptions to report counts, etc. + @try { + [self setUp]; + @try { + NSInvocation *invocation = [self invocation]; +#if GTM_IPHONE_SIMULATOR + // We don't call [invocation invokeWithTarget:self]; because of + // Radar 8081169: NSInvalidArgumentException can't be caught + // It turns out that on iOS4 (and 3.2) exceptions thrown inside an + // [invocation invoke] on the simulator cannot be caught. + // http://openradar.appspot.com/8081169 + objc_msgSend(self, [invocation selector]); +#else + [invocation invokeWithTarget:self]; +#endif + } @catch (NSException *exception) { + e = [exception retain]; + } + [self tearDown]; + } @catch (NSException *exception) { + e = [exception retain]; + } + [pool release]; + } @catch (NSException *exception) { + e = [exception retain]; + } + if (e) { + [e autorelease]; + [e raise]; + } +} + +- (void)tearDown { +} + +- (NSString *)description { + // This matches the description OCUnit would return to you + return [NSString stringWithFormat:@"-[%@ %@]", [self class], + NSStringFromSelector([self selector])]; +} + +// Used for sorting methods below +static int MethodSort(id a, id b, void *context) { + NSInvocation *invocationA = a; + NSInvocation *invocationB = b; + const char *nameA = sel_getName([invocationA selector]); + const char *nameB = sel_getName([invocationB selector]); + return strcmp(nameA, nameB); +} + + ++ (NSArray *)testInvocations { + NSMutableArray *invocations = nil; + // Need to walk all the way up the parent classes collecting methods (in case + // a test is a subclass of another test). + Class senTestCaseClass = [SenTestCase class]; + for (Class currentClass = self; + currentClass && (currentClass != senTestCaseClass); + currentClass = class_getSuperclass(currentClass)) { + unsigned int methodCount; + Method *methods = class_copyMethodList(currentClass, &methodCount); + if (methods) { + // This handles disposing of methods for us even if an exception should fly. + [NSData dataWithBytesNoCopy:methods + length:sizeof(Method) * methodCount]; + if (!invocations) { + invocations = [NSMutableArray arrayWithCapacity:methodCount]; + } + for (size_t i = 0; i < methodCount; ++i) { + Method currMethod = methods[i]; + SEL sel = method_getName(currMethod); + char *returnType = NULL; + const char *name = sel_getName(sel); + // If it starts with test, takes 2 args (target and sel) and returns + // void run it. + if (strstr(name, "test") == name) { + returnType = method_copyReturnType(currMethod); + if (returnType) { + // This handles disposing of returnType for us even if an + // exception should fly. Length +1 for the terminator, not that + // the length really matters here, as we never reference inside + // the data block. + [NSData dataWithBytesNoCopy:returnType + length:strlen(returnType) + 1]; + } + } + // TODO: If a test class is a subclass of another, and they reuse the + // same selector name (ie-subclass overrides it), this current loop + // and test here will cause cause it to get invoked twice. To fix this + // the selector would have to be checked against all the ones already + // added, so it only gets done once. + if (returnType // True if name starts with "test" + && strcmp(returnType, @encode(void)) == 0 + && method_getNumberOfArguments(currMethod) == 2) { + NSMethodSignature *sig = [self instanceMethodSignatureForSelector:sel]; + NSInvocation *invocation + = [NSInvocation invocationWithMethodSignature:sig]; + [invocation setSelector:sel]; + [invocations addObject:invocation]; + } + } + } + } + // Match SenTestKit and run everything in alphbetical order. + [invocations sortUsingFunction:MethodSort context:nil]; + return invocations; +} + +@end + +#endif // GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST + +@implementation GTMTestCase : SenTestCase +- (void)invokeTest { + NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; + Class devLogClass = NSClassFromString(@"GTMUnitTestDevLog"); + if (devLogClass) { + [devLogClass performSelector:@selector(enableTracking)]; + [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)]; + + } + [super invokeTest]; + if (devLogClass) { + [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)]; + [devLogClass performSelector:@selector(disableTracking)]; + } + [localPool drain]; +} + ++ (BOOL)isAbstractTestCase { + NSString *name = NSStringFromClass(self); + return [name rangeOfString:@"AbstractTest"].location != NSNotFound; +} + ++ (NSArray *)testInvocations { + NSArray *invocations = nil; + if (![self isAbstractTestCase]) { + invocations = [super testInvocations]; + } + return invocations; +} + +@end diff --git a/toolkit/crashreporter/google-breakpad/src/common/macros.h b/toolkit/crashreporter/google-breakpad/src/common/macros.h new file mode 100644 index 0000000000..14bb3f7bd3 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/macros.h @@ -0,0 +1,45 @@ +// Copyright (c) 2019, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef BREAKPAD_COMMON_MACROS_H_ +#define BREAKPAD_COMMON_MACROS_H_ + +// Ensure that this macro definition stays in a private header file: clang +// suggests the first macro expanding to [[clang::fallthrough]] in its +// diagnostics, so if BP_FALLTHROUGH is visible in code depending on breakpad, +// clang would suggest BP_FALLTHROUGH for code depending on breakpad, instead of +// the client code's own fallthrough macro. +// TODO(thakis): Once everyone uses C++17, use its [[fallthrough]] instead. +#if defined(__clang__) +#define BP_FALLTHROUGH [[clang::fallthrough]] +#else +#define BP_FALLTHROUGH +#endif + +#endif // BREAKPAD_COMMON_MACROS_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/md5.cc b/toolkit/crashreporter/google-breakpad/src/common/md5.cc new file mode 100644 index 0000000000..4f1ac8cad4 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/md5.cc @@ -0,0 +1,251 @@ +/* + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#include <string.h> + +#include "common/md5.h" + +namespace google_breakpad { + +#ifndef WORDS_BIGENDIAN +#define byteReverse(buf, len) /* Nothing */ +#else +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(unsigned char *buf, unsigned longs) +{ + u32 t; + do { + t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(u32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif + +static void MD5Transform(u32 buf[4], u32 const in[16]); + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, size_t len) +{ + u32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((u32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + memcpy(&ctx->in[14], &ctx->bits[0], sizeof(u32)); + memcpy(&ctx->in[15], &ctx->bits[1], sizeof(u32)); + + MD5Transform(ctx->buf, (u32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(u32 buf[4], u32 const in[16]) +{ + u32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +} // namespace google_breakpad + diff --git a/toolkit/crashreporter/google-breakpad/src/common/md5.h b/toolkit/crashreporter/google-breakpad/src/common/md5.h new file mode 100644 index 0000000000..2ab0ab95ae --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/md5.h @@ -0,0 +1,27 @@ +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: liuli@google.com (Liu Li) +#ifndef COMMON_MD5_H__ +#define COMMON_MD5_H__ + +#include <stdint.h> + +namespace google_breakpad { + +typedef uint32_t u32; +typedef uint8_t u8; + +struct MD5Context { + u32 buf[4]; + u32 bits[2]; + u8 in[64]; +}; + +void MD5Init(struct MD5Context *ctx); + +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, size_t len); + +void MD5Final(unsigned char digest[16], struct MD5Context *ctx); + +} // namespace google_breakpad + +#endif // COMMON_MD5_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/memory_allocator.h b/toolkit/crashreporter/google-breakpad/src/common/memory_allocator.h new file mode 100644 index 0000000000..b7f9168c14 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/memory_allocator.h @@ -0,0 +1,252 @@ +// Copyright (c) 2009, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef GOOGLE_BREAKPAD_COMMON_MEMORY_ALLOCATOR_H_ +#define GOOGLE_BREAKPAD_COMMON_MEMORY_ALLOCATOR_H_ + +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/mman.h> + +#include <memory> +#include <vector> + +#if defined(MEMORY_SANITIZER) +#include <sanitizer/msan_interface.h> +#endif + +#if defined(__APPLE__) || defined(__FreeBSD__) +#define sys_mmap mmap +#define sys_munmap munmap +#define MAP_ANONYMOUS MAP_ANON +#else +#include "third_party/lss/linux_syscall_support.h" +#endif + +namespace google_breakpad { + +// This is very simple allocator which fetches pages from the kernel directly. +// Thus, it can be used even when the heap may be corrupted. +// +// There is no free operation. The pages are only freed when the object is +// destroyed. +class PageAllocator { + public: + PageAllocator() + : page_size_(getpagesize()), + last_(NULL), + current_page_(NULL), + page_offset_(0), + pages_allocated_(0) { + } + + ~PageAllocator() { + FreeAll(); + } + + void *Alloc(size_t bytes) { + if (!bytes) + return NULL; + + if (current_page_ && page_size_ - page_offset_ >= bytes) { + uint8_t *const ret = current_page_ + page_offset_; + page_offset_ += bytes; + if (page_offset_ == page_size_) { + page_offset_ = 0; + current_page_ = NULL; + } + + return ret; + } + + const size_t pages = + (bytes + sizeof(PageHeader) + page_size_ - 1) / page_size_; + uint8_t *const ret = GetNPages(pages); + if (!ret) + return NULL; + + page_offset_ = + (page_size_ - (page_size_ * pages - (bytes + sizeof(PageHeader)))) % + page_size_; + current_page_ = page_offset_ ? ret + page_size_ * (pages - 1) : NULL; + + return ret + sizeof(PageHeader); + } + + // Checks whether the page allocator owns the passed-in pointer. + // This method exists for testing pursposes only. + bool OwnsPointer(const void* p) { + for (PageHeader* header = last_; header; header = header->next) { + const char* current = reinterpret_cast<char*>(header); + if ((p >= current) && (p < current + header->num_pages * page_size_)) + return true; + } + + return false; + } + + unsigned long pages_allocated() { return pages_allocated_; } + + private: + uint8_t *GetNPages(size_t num_pages) { + void *a = sys_mmap(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (a == MAP_FAILED) + return NULL; + +#if defined(MEMORY_SANITIZER) + // We need to indicate to MSan that memory allocated through sys_mmap is + // initialized, since linux_syscall_support.h doesn't have MSan hooks. + __msan_unpoison(a, page_size_ * num_pages); +#endif + + struct PageHeader *header = reinterpret_cast<PageHeader*>(a); + header->next = last_; + header->num_pages = num_pages; + last_ = header; + + pages_allocated_ += num_pages; + + return reinterpret_cast<uint8_t*>(a); + } + + void FreeAll() { + PageHeader *next; + + for (PageHeader *cur = last_; cur; cur = next) { + next = cur->next; + sys_munmap(cur, cur->num_pages * page_size_); + } + } + + struct PageHeader { + PageHeader *next; // pointer to the start of the next set of pages. + size_t num_pages; // the number of pages in this set. + }; + + const size_t page_size_; + PageHeader *last_; + uint8_t *current_page_; + size_t page_offset_; + unsigned long pages_allocated_; +}; + +// Wrapper to use with STL containers +template <typename T> +struct PageStdAllocator { + using AllocatorTraits = std::allocator_traits<std::allocator<T>>; + using value_type = typename AllocatorTraits::value_type; + using pointer = typename AllocatorTraits::pointer; + using difference_type = typename AllocatorTraits::difference_type; + using size_type = typename AllocatorTraits::size_type; + + explicit PageStdAllocator(PageAllocator& allocator) : allocator_(allocator), + stackdata_(NULL), + stackdata_size_(0) + {} + + template <class Other> PageStdAllocator(const PageStdAllocator<Other>& other) + : allocator_(other.allocator_), + stackdata_(nullptr), + stackdata_size_(0) + {} + + explicit PageStdAllocator(PageAllocator& allocator, + pointer stackdata, + size_type stackdata_size) : allocator_(allocator), + stackdata_(stackdata), + stackdata_size_(stackdata_size) + {} + + inline pointer allocate(size_type n, const void* = 0) { + const size_type size = sizeof(T) * n; + if (size <= stackdata_size_) { + return stackdata_; + } + return static_cast<pointer>(allocator_.Alloc(size)); + } + + inline void deallocate(pointer, size_type) { + // The PageAllocator doesn't free. + } + + template <typename U> struct rebind { + typedef PageStdAllocator<U> other; + }; + + private: + // Silly workaround for the gcc from Android's ndk (gcc 4.6), which will + // otherwise complain that `other.allocator_` is private in the constructor + // code. + template<typename Other> friend struct PageStdAllocator; + + PageAllocator& allocator_; + pointer stackdata_; + size_type stackdata_size_; +}; + +// A wasteful vector is a std::vector, except that it allocates memory from a +// PageAllocator. It's wasteful because, when resizing, it always allocates a +// whole new array since the PageAllocator doesn't support realloc. +template<class T> +class wasteful_vector : public std::vector<T, PageStdAllocator<T> > { + public: + wasteful_vector(PageAllocator* allocator, unsigned size_hint = 16) + : std::vector<T, PageStdAllocator<T> >(PageStdAllocator<T>(*allocator)) { + std::vector<T, PageStdAllocator<T> >::reserve(size_hint); + } + protected: + wasteful_vector(PageStdAllocator<T> allocator) + : std::vector<T, PageStdAllocator<T> >(allocator) {} +}; + +// auto_wasteful_vector allocates space on the stack for N entries to avoid +// using the PageAllocator for small data, while still allowing for larger data. +template<class T, unsigned int N> +class auto_wasteful_vector : public wasteful_vector<T> { + T stackdata_[N]; + public: + auto_wasteful_vector(PageAllocator* allocator) + : wasteful_vector<T>( + PageStdAllocator<T>(*allocator, + &stackdata_[0], + sizeof(stackdata_))) { + std::vector<T, PageStdAllocator<T> >::reserve(N); + } +}; + +} // namespace google_breakpad + +inline void* operator new(size_t nbytes, + google_breakpad::PageAllocator& allocator) { + return allocator.Alloc(nbytes); +} + +#endif // GOOGLE_BREAKPAD_COMMON_MEMORY_ALLOCATOR_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/memory_allocator_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/memory_allocator_unittest.cc new file mode 100644 index 0000000000..43c86314c6 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/memory_allocator_unittest.cc @@ -0,0 +1,124 @@ +// Copyright (c) 2009, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include "breakpad_googletest_includes.h" +#include "common/memory_allocator.h" + +using namespace google_breakpad; + +namespace { +typedef testing::Test PageAllocatorTest; +} + +TEST(PageAllocatorTest, Setup) { + PageAllocator allocator; + EXPECT_EQ(0U, allocator.pages_allocated()); +} + +TEST(PageAllocatorTest, SmallObjects) { + PageAllocator allocator; + + EXPECT_EQ(0U, allocator.pages_allocated()); + for (unsigned i = 1; i < 1024; ++i) { + uint8_t *p = reinterpret_cast<uint8_t*>(allocator.Alloc(i)); + ASSERT_FALSE(p == NULL); + memset(p, 0, i); + } +} + +TEST(PageAllocatorTest, LargeObject) { + PageAllocator allocator; + + EXPECT_EQ(0U, allocator.pages_allocated()); + uint8_t *p = reinterpret_cast<uint8_t*>(allocator.Alloc(10000)); + ASSERT_FALSE(p == NULL); + EXPECT_EQ(3U, allocator.pages_allocated()); + for (unsigned i = 1; i < 10; ++i) { + uint8_t *p = reinterpret_cast<uint8_t*>(allocator.Alloc(i)); + ASSERT_FALSE(p == NULL); + memset(p, 0, i); + } +} + +namespace { +typedef testing::Test WastefulVectorTest; +} + +TEST(WastefulVectorTest, Setup) { + PageAllocator allocator_; + wasteful_vector<int> v(&allocator_); + ASSERT_TRUE(v.empty()); + ASSERT_EQ(v.size(), 0u); +} + +TEST(WastefulVectorTest, Simple) { + PageAllocator allocator_; + EXPECT_EQ(0U, allocator_.pages_allocated()); + wasteful_vector<unsigned> v(&allocator_); + + for (unsigned i = 0; i < 256; ++i) { + v.push_back(i); + ASSERT_EQ(i, v.back()); + ASSERT_EQ(&v.back(), &v[i]); + } + ASSERT_FALSE(v.empty()); + ASSERT_EQ(v.size(), 256u); + EXPECT_EQ(1U, allocator_.pages_allocated()); + for (unsigned i = 0; i < 256; ++i) + ASSERT_EQ(v[i], i); +} + +TEST(WastefulVectorTest, UsesPageAllocator) { + PageAllocator allocator_; + wasteful_vector<unsigned> v(&allocator_); + EXPECT_EQ(1U, allocator_.pages_allocated()); + + v.push_back(1); + ASSERT_TRUE(allocator_.OwnsPointer(&v[0])); +} + +TEST(WastefulVectorTest, AutoWastefulVector) { + PageAllocator allocator_; + EXPECT_EQ(0U, allocator_.pages_allocated()); + + auto_wasteful_vector<unsigned, 4> v(&allocator_); + EXPECT_EQ(0U, allocator_.pages_allocated()); + + v.push_back(1); + EXPECT_EQ(0U, allocator_.pages_allocated()); + EXPECT_FALSE(allocator_.OwnsPointer(&v[0])); + + v.resize(4); + EXPECT_EQ(0U, allocator_.pages_allocated()); + EXPECT_FALSE(allocator_.OwnsPointer(&v[0])); + + v.resize(10); + EXPECT_EQ(1U, allocator_.pages_allocated()); + EXPECT_TRUE(allocator_.OwnsPointer(&v[0])); +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/memory_range.h b/toolkit/crashreporter/google-breakpad/src/common/memory_range.h new file mode 100644 index 0000000000..41dd2da622 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/memory_range.h @@ -0,0 +1,145 @@ +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// memory_range.h: Define the google_breakpad::MemoryRange class, which +// is a lightweight wrapper with a pointer and a length to encapsulate +// a contiguous range of memory. + +#ifndef COMMON_MEMORY_RANGE_H_ +#define COMMON_MEMORY_RANGE_H_ + +#include <stddef.h> + +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +// A lightweight wrapper with a pointer and a length to encapsulate a +// contiguous range of memory. It provides helper methods for checked +// access of a subrange of the memory. Its implemementation does not +// allocate memory or call into libc functions, and is thus safer to use +// in a crashed environment. +class MemoryRange { + public: + MemoryRange() : data_(NULL), length_(0) {} + + MemoryRange(const void* data, size_t length) { + Set(data, length); + } + + // Returns true if this memory range contains no data. + bool IsEmpty() const { + // Set() guarantees that |length_| is zero if |data_| is NULL. + return length_ == 0; + } + + // Resets to an empty range. + void Reset() { + data_ = NULL; + length_ = 0; + } + + // Sets this memory range to point to |data| and its length to |length|. + void Set(const void* data, size_t length) { + data_ = reinterpret_cast<const uint8_t*>(data); + // Always set |length_| to zero if |data_| is NULL. + length_ = data ? length : 0; + } + + // Returns true if this range covers a subrange of |sub_length| bytes + // at |sub_offset| bytes of this memory range, or false otherwise. + bool Covers(size_t sub_offset, size_t sub_length) const { + // The following checks verify that: + // 1. sub_offset is within [ 0 .. length_ - 1 ] + // 2. sub_offset + sub_length is within + // [ sub_offset .. length_ ] + return sub_offset < length_ && + sub_offset + sub_length >= sub_offset && + sub_offset + sub_length <= length_; + } + + // Returns a raw data pointer to a subrange of |sub_length| bytes at + // |sub_offset| bytes of this memory range, or NULL if the subrange + // is out of bounds. + const void* GetData(size_t sub_offset, size_t sub_length) const { + return Covers(sub_offset, sub_length) ? (data_ + sub_offset) : NULL; + } + + // Same as the two-argument version of GetData() but uses sizeof(DataType) + // as the subrange length and returns an |DataType| pointer for convenience. + template <typename DataType> + const DataType* GetData(size_t sub_offset) const { + return reinterpret_cast<const DataType*>( + GetData(sub_offset, sizeof(DataType))); + } + + // Returns a raw pointer to the |element_index|-th element of an array + // of elements of length |element_size| starting at |sub_offset| bytes + // of this memory range, or NULL if the element is out of bounds. + const void* GetArrayElement(size_t element_offset, + size_t element_size, + unsigned element_index) const { + size_t sub_offset = element_offset + element_index * element_size; + return GetData(sub_offset, element_size); + } + + // Same as the three-argument version of GetArrayElement() but deduces + // the element size using sizeof(ElementType) and returns an |ElementType| + // pointer for convenience. + template <typename ElementType> + const ElementType* GetArrayElement(size_t element_offset, + unsigned element_index) const { + return reinterpret_cast<const ElementType*>( + GetArrayElement(element_offset, sizeof(ElementType), element_index)); + } + + // Returns a subrange of |sub_length| bytes at |sub_offset| bytes of + // this memory range, or an empty range if the subrange is out of bounds. + MemoryRange Subrange(size_t sub_offset, size_t sub_length) const { + return Covers(sub_offset, sub_length) ? + MemoryRange(data_ + sub_offset, sub_length) : MemoryRange(); + } + + // Returns a pointer to the beginning of this memory range. + const uint8_t* data() const { return data_; } + + // Returns the length, in bytes, of this memory range. + size_t length() const { return length_; } + + private: + // Pointer to the beginning of this memory range. + const uint8_t* data_; + + // Length, in bytes, of this memory range. + size_t length_; +}; + +} // namespace google_breakpad + +#endif // COMMON_MEMORY_RANGE_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/memory_range_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/memory_range_unittest.cc new file mode 100644 index 0000000000..f6cf8c8b2a --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/memory_range_unittest.cc @@ -0,0 +1,193 @@ +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// memory_range_unittest.cc: Unit tests for google_breakpad::MemoryRange. + +#include "breakpad_googletest_includes.h" +#include "common/memory_range.h" + +using google_breakpad::MemoryRange; +using testing::Message; + +namespace { + +const uint32_t kBuffer[10] = { 0 }; +const size_t kBufferSize = sizeof(kBuffer); +const uint8_t* kBufferPointer = reinterpret_cast<const uint8_t*>(kBuffer); + +// Test vectors for verifying Covers, GetData, and Subrange. +const struct { + bool valid; + size_t offset; + size_t length; +} kSubranges[] = { + { true, 0, 0 }, + { true, 0, 2 }, + { true, 0, kBufferSize }, + { true, 2, 0 }, + { true, 2, 4 }, + { true, 2, kBufferSize - 2 }, + { true, kBufferSize - 1, 1 }, + { false, kBufferSize, 0 }, + { false, kBufferSize, static_cast<size_t>(-1) }, + { false, kBufferSize + 1, 0 }, + { false, static_cast<size_t>(-1), 2 }, + { false, 1, kBufferSize }, + { false, kBufferSize - 1, 2 }, + { false, 0, static_cast<size_t>(-1) }, + { false, 1, static_cast<size_t>(-1) }, +}; +const size_t kNumSubranges = sizeof(kSubranges) / sizeof(kSubranges[0]); + +// Test vectors for verifying GetArrayElement. +const struct { + size_t offset; + size_t size; + size_t index; + const void* const pointer; +} kElements[] = { + // Valid array elemenets + { 0, 1, 0, kBufferPointer }, + { 0, 1, 1, kBufferPointer + 1 }, + { 0, 1, kBufferSize - 1, kBufferPointer + kBufferSize - 1 }, + { 0, 2, 1, kBufferPointer + 2 }, + { 0, 4, 2, kBufferPointer + 8 }, + { 0, 4, 9, kBufferPointer + 36 }, + { kBufferSize - 1, 1, 0, kBufferPointer + kBufferSize - 1 }, + // Invalid array elemenets + { 0, 1, kBufferSize, NULL }, + { 0, 4, 10, NULL }, + { kBufferSize - 1, 1, 1, NULL }, + { kBufferSize - 1, 2, 0, NULL }, + { kBufferSize, 1, 0, NULL }, +}; +const size_t kNumElements = sizeof(kElements) / sizeof(kElements[0]); + +} // namespace + +TEST(MemoryRangeTest, DefaultConstructor) { + MemoryRange range; + EXPECT_EQ(NULL, range.data()); + EXPECT_EQ(0U, range.length()); +} + +TEST(MemoryRangeTest, ConstructorWithDataAndLength) { + MemoryRange range(kBuffer, kBufferSize); + EXPECT_EQ(kBufferPointer, range.data()); + EXPECT_EQ(kBufferSize, range.length()); +} + +TEST(MemoryRangeTest, Reset) { + MemoryRange range; + range.Reset(); + EXPECT_EQ(NULL, range.data()); + EXPECT_EQ(0U, range.length()); + + range.Set(kBuffer, kBufferSize); + EXPECT_EQ(kBufferPointer, range.data()); + EXPECT_EQ(kBufferSize, range.length()); + + range.Reset(); + EXPECT_EQ(NULL, range.data()); + EXPECT_EQ(0U, range.length()); +} + +TEST(MemoryRangeTest, Set) { + MemoryRange range; + range.Set(kBuffer, kBufferSize); + EXPECT_EQ(kBufferPointer, range.data()); + EXPECT_EQ(kBufferSize, range.length()); + + range.Set(NULL, 0); + EXPECT_EQ(NULL, range.data()); + EXPECT_EQ(0U, range.length()); +} + +TEST(MemoryRangeTest, SubrangeOfEmptyMemoryRange) { + MemoryRange range; + MemoryRange subrange = range.Subrange(0, 10); + EXPECT_EQ(NULL, subrange.data()); + EXPECT_EQ(0U, subrange.length()); +} + +TEST(MemoryRangeTest, SubrangeAndGetData) { + MemoryRange range(kBuffer, kBufferSize); + for (size_t i = 0; i < kNumSubranges; ++i) { + bool valid = kSubranges[i].valid; + size_t sub_offset = kSubranges[i].offset; + size_t sub_length = kSubranges[i].length; + SCOPED_TRACE(Message() << "offset=" << sub_offset + << ", length=" << sub_length); + + MemoryRange subrange = range.Subrange(sub_offset, sub_length); + if (valid) { + EXPECT_TRUE(range.Covers(sub_offset, sub_length)); + EXPECT_EQ(kBufferPointer + sub_offset, + range.GetData(sub_offset, sub_length)); + EXPECT_EQ(kBufferPointer + sub_offset, subrange.data()); + EXPECT_EQ(sub_length, subrange.length()); + } else { + EXPECT_FALSE(range.Covers(sub_offset, sub_length)); + EXPECT_EQ(NULL, range.GetData(sub_offset, sub_length)); + EXPECT_EQ(NULL, subrange.data()); + EXPECT_EQ(0U, subrange.length()); + } + } +} + +TEST(MemoryRangeTest, GetDataWithTemplateType) { + MemoryRange range(kBuffer, kBufferSize); + const char* char_pointer = range.GetData<char>(0); + EXPECT_EQ(reinterpret_cast<const char*>(kBufferPointer), char_pointer); + const int* int_pointer = range.GetData<int>(0); + EXPECT_EQ(reinterpret_cast<const int*>(kBufferPointer), int_pointer); +} + +TEST(MemoryRangeTest, GetArrayElement) { + MemoryRange range(kBuffer, kBufferSize); + for (size_t i = 0; i < kNumElements; ++i) { + size_t element_offset = kElements[i].offset; + size_t element_size = kElements[i].size; + unsigned element_index = kElements[i].index; + const void* const element_pointer = kElements[i].pointer; + SCOPED_TRACE(Message() << "offset=" << element_offset + << ", size=" << element_size + << ", index=" << element_index); + EXPECT_EQ(element_pointer, range.GetArrayElement( + element_offset, element_size, element_index)); + } +} + +TEST(MemoryRangeTest, GetArrayElmentWithTemplateType) { + MemoryRange range(kBuffer, kBufferSize); + const char* char_pointer = range.GetArrayElement<char>(0, 0); + EXPECT_EQ(reinterpret_cast<const char*>(kBufferPointer), char_pointer); + const int* int_pointer = range.GetArrayElement<int>(0, 0); + EXPECT_EQ(reinterpret_cast<const int*>(kBufferPointer), int_pointer); +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/minidump_type_helper.h b/toolkit/crashreporter/google-breakpad/src/common/minidump_type_helper.h new file mode 100644 index 0000000000..5a7d5a6a87 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/minidump_type_helper.h @@ -0,0 +1,56 @@ +// Copyright (c) 2014, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_TYPE_HELPER_H_ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_TYPE_HELPER_H_ + +#include <stdint.h> + +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +template <size_t> +struct MDTypeHelper; + +template <> +struct MDTypeHelper<sizeof(uint32_t)> { + typedef MDRawDebug32 MDRawDebug; + typedef MDRawLinkMap32 MDRawLinkMap; +}; + +template <> +struct MDTypeHelper<sizeof(uint64_t)> { + typedef MDRawDebug64 MDRawDebug; + typedef MDRawLinkMap64 MDRawLinkMap; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_COMMON_MINIDUMP_TYPE_HELPER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/module.cc b/toolkit/crashreporter/google-breakpad/src/common/module.cc new file mode 100644 index 0000000000..a3544029cb --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/module.cc @@ -0,0 +1,387 @@ +// Copyright (c) 2011 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// module.cc: Implement google_breakpad::Module. See module.h. + +#include "common/module.h" + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#include <iostream> +#include <utility> + +namespace google_breakpad { + +using std::dec; +using std::hex; + + +Module::Module(const string &name, const string &os, + const string &architecture, const string &id, + const string &code_id /* = "" */) : + name_(name), + os_(os), + architecture_(architecture), + id_(id), + code_id_(code_id), + load_address_(0) { } + +Module::~Module() { + for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); ++it) + delete it->second; + for (FunctionSet::iterator it = functions_.begin(); + it != functions_.end(); ++it) { + delete *it; + } + for (StackFrameEntrySet::iterator it = stack_frame_entries_.begin(); + it != stack_frame_entries_.end(); ++it) { + delete *it; + } + for (ExternSet::iterator it = externs_.begin(); it != externs_.end(); ++it) + delete *it; +} + +void Module::SetLoadAddress(Address address) { + load_address_ = address; +} + +void Module::SetAddressRanges(const vector<Range>& ranges) { + address_ranges_ = ranges; +} + +void Module::AddFunction(Function *function) { + // FUNC lines must not hold an empty name, so catch the problem early if + // callers try to add one. + assert(!function->name.empty()); + + if (!AddressIsInModule(function->address)) { + return; + } + + // FUNCs are better than PUBLICs as they come with sizes, so remove an extern + // with the same address if present. + Extern ext(function->address); + ExternSet::iterator it_ext = externs_.find(&ext); + if (it_ext == externs_.end() && + architecture_ == "arm" && + (function->address & 0x1) == 0) { + // ARM THUMB functions have bit 0 set. ARM64 does not have THUMB. + Extern arm_thumb_ext(function->address | 0x1); + it_ext = externs_.find(&arm_thumb_ext); + } + if (it_ext != externs_.end()) { + delete *it_ext; + externs_.erase(it_ext); + } +#if _DEBUG + { + // There should be no other PUBLIC symbols that overlap with the function. + for (const Range& range : function->ranges) { + Extern debug_ext(range.address); + ExternSet::iterator it_debug = externs_.lower_bound(&ext); + assert(it_debug == externs_.end() || + (*it_debug)->address >= range.address + range.size); + } + } +#endif + + std::pair<FunctionSet::iterator,bool> ret = functions_.insert(function); + if (!ret.second && (*ret.first != function)) { + // Free the duplicate that was not inserted because this Module + // now owns it. + delete function; + } +} + +void Module::AddFunctions(vector<Function *>::iterator begin, + vector<Function *>::iterator end) { + for (vector<Function *>::iterator it = begin; it != end; ++it) + AddFunction(*it); +} + +void Module::AddStackFrameEntry(StackFrameEntry* stack_frame_entry) { + if (!AddressIsInModule(stack_frame_entry->address)) { + return; + } + + std::pair<StackFrameEntrySet::iterator,bool> ret = + stack_frame_entries_.insert(stack_frame_entry); + if (!ret.second) { + // Free the duplicate that was not inserted because this Module + // now owns it. + delete stack_frame_entry; + } +} + +void Module::AddExtern(Extern *ext) { + if (!AddressIsInModule(ext->address)) { + return; + } + + std::pair<ExternSet::iterator,bool> ret = externs_.insert(ext); + if (!ret.second) { + // Free the duplicate that was not inserted because this Module + // now owns it. + delete ext; + } +} + +void Module::GetFunctions(vector<Function *> *vec, + vector<Function *>::iterator i) { + vec->insert(i, functions_.begin(), functions_.end()); +} + +void Module::GetExterns(vector<Extern *> *vec, + vector<Extern *>::iterator i) { + vec->insert(i, externs_.begin(), externs_.end()); +} + +Module::File *Module::FindFile(const string &name) { + // A tricky bit here. The key of each map entry needs to be a + // pointer to the entry's File's name string. This means that we + // can't do the initial lookup with any operation that would create + // an empty entry for us if the name isn't found (like, say, + // operator[] or insert do), because such a created entry's key will + // be a pointer the string passed as our argument. Since the key of + // a map's value type is const, we can't fix it up once we've + // created our file. lower_bound does the lookup without doing an + // insertion, and returns a good hint iterator to pass to insert. + // Our "destiny" is where we belong, whether we're there or not now. + FileByNameMap::iterator destiny = files_.lower_bound(&name); + if (destiny == files_.end() + || *destiny->first != name) { // Repeated string comparison, boo hoo. + File *file = new File(name); + file->source_id = -1; + destiny = files_.insert(destiny, + FileByNameMap::value_type(&file->name, file)); + } + return destiny->second; +} + +Module::File *Module::FindFile(const char *name) { + string name_string = name; + return FindFile(name_string); +} + +Module::File *Module::FindExistingFile(const string &name) { + FileByNameMap::iterator it = files_.find(&name); + return (it == files_.end()) ? NULL : it->second; +} + +void Module::GetFiles(vector<File *> *vec) { + vec->clear(); + for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); ++it) + vec->push_back(it->second); +} + +void Module::GetStackFrameEntries(vector<StackFrameEntry*>* vec) const { + vec->clear(); + vec->insert(vec->begin(), stack_frame_entries_.begin(), + stack_frame_entries_.end()); +} + +Module::StackFrameEntry* Module::FindStackFrameEntryByAddress(Address address) { + StackFrameEntry search; + search.address = address; + StackFrameEntrySet::iterator it = stack_frame_entries_.upper_bound(&search); + + if (it == stack_frame_entries_.begin()) + return NULL; + + it--; + if ((*it)->address <= address && address < (*it)->address + (*it)->size) + return *it; + + return NULL; +} + +void Module::AssignSourceIds() { + // First, give every source file an id of -1. + for (FileByNameMap::iterator file_it = files_.begin(); + file_it != files_.end(); ++file_it) { + file_it->second->source_id = -1; + } + + // Next, mark all files actually cited by our functions' line number + // info, by setting each one's source id to zero. + for (FunctionSet::const_iterator func_it = functions_.begin(); + func_it != functions_.end(); ++func_it) { + Function *func = *func_it; + for (vector<Line>::iterator line_it = func->lines.begin(); + line_it != func->lines.end(); ++line_it) + line_it->file->source_id = 0; + } + + // Finally, assign source ids to those files that have been marked. + // We could have just assigned source id numbers while traversing + // the line numbers, but doing it this way numbers the files in + // lexicographical order by name, which is neat. + int next_source_id = 0; + for (FileByNameMap::iterator file_it = files_.begin(); + file_it != files_.end(); ++file_it) { + if (!file_it->second->source_id) + file_it->second->source_id = next_source_id++; + } +} + +bool Module::ReportError() { + fprintf(stderr, "error writing symbol file: %s\n", + strerror(errno)); + return false; +} + +bool Module::WriteRuleMap(const RuleMap &rule_map, std::ostream &stream) { + for (RuleMap::const_iterator it = rule_map.begin(); + it != rule_map.end(); ++it) { + if (it != rule_map.begin()) + stream << ' '; + stream << it->first << ": " << it->second; + } + return stream.good(); +} + +bool Module::AddressIsInModule(Address address) const { + if (address_ranges_.empty()) { + return true; + } + for (const auto& segment : address_ranges_) { + if (address >= segment.address && + address < segment.address + segment.size) { + return true; + } + } + return false; +} + +bool Module::Write(std::ostream &stream, SymbolData symbol_data) { + stream << "MODULE " << os_ << " " << architecture_ << " " + << id_ << " " << name_ << "\n"; + if (!stream.good()) + return ReportError(); + + if (!code_id_.empty()) { + stream << "INFO CODE_ID " << code_id_ << "\n"; + } + + if (symbol_data != ONLY_CFI) { + AssignSourceIds(); + + // Write out files. + for (FileByNameMap::iterator file_it = files_.begin(); + file_it != files_.end(); ++file_it) { + File *file = file_it->second; + if (file->source_id >= 0) { + stream << "FILE " << file->source_id << " " << file->name << "\n"; + if (!stream.good()) + return ReportError(); + } + } + + // Write out functions and their lines. + for (FunctionSet::const_iterator func_it = functions_.begin(); + func_it != functions_.end(); ++func_it) { + Function *func = *func_it; + vector<Line>::iterator line_it = func->lines.begin(); + for (auto range_it = func->ranges.cbegin(); + range_it != func->ranges.cend(); ++range_it) { + stream << "FUNC " << hex + << (range_it->address - load_address_) << " " + << range_it->size << " " + << func->parameter_size << " " + << func->name << dec << "\n"; + + if (!stream.good()) + return ReportError(); + + while ((line_it != func->lines.end()) && + (line_it->address >= range_it->address) && + (line_it->address < (range_it->address + range_it->size))) { + stream << hex + << (line_it->address - load_address_) << " " + << line_it->size << " " + << dec + << line_it->number << " " + << line_it->file->source_id << "\n"; + + if (!stream.good()) + return ReportError(); + + ++line_it; + } + } + } + + // Write out 'PUBLIC' records. + for (ExternSet::const_iterator extern_it = externs_.begin(); + extern_it != externs_.end(); ++extern_it) { + Extern *ext = *extern_it; + stream << "PUBLIC " << hex + << (ext->address - load_address_) << " 0 " + << ext->name << dec << "\n"; + } + } + + if (symbol_data != NO_CFI) { + // Write out 'STACK CFI INIT' and 'STACK CFI' records. + StackFrameEntrySet::const_iterator frame_it; + for (frame_it = stack_frame_entries_.begin(); + frame_it != stack_frame_entries_.end(); ++frame_it) { + StackFrameEntry *entry = *frame_it; + stream << "STACK CFI INIT " << hex + << (entry->address - load_address_) << " " + << entry->size << " " << dec; + if (!stream.good() + || !WriteRuleMap(entry->initial_rules, stream)) + return ReportError(); + + stream << "\n"; + + // Write out this entry's delta rules as 'STACK CFI' records. + for (RuleChangeMap::const_iterator delta_it = entry->rule_changes.begin(); + delta_it != entry->rule_changes.end(); ++delta_it) { + stream << "STACK CFI " << hex + << (delta_it->first - load_address_) << " " << dec; + if (!stream.good() + || !WriteRuleMap(delta_it->second, stream)) + return ReportError(); + + stream << "\n"; + } + } + } + + return true; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/module.h b/toolkit/crashreporter/google-breakpad/src/common/module.h new file mode 100644 index 0000000000..3775860526 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/module.h @@ -0,0 +1,376 @@ +// -*- mode: c++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// module.h: Define google_breakpad::Module. A Module holds debugging +// information, and can write that information out as a Breakpad +// symbol file. + +#ifndef COMMON_LINUX_MODULE_H__ +#define COMMON_LINUX_MODULE_H__ + +#include <iostream> +#include <map> +#include <set> +#include <string> +#include <vector> + +#include "common/symbol_data.h" +#include "common/using_std_string.h" +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +using std::set; +using std::vector; +using std::map; + +// A Module represents the contents of a module, and supports methods +// for adding information produced by parsing STABS or DWARF data +// --- possibly both from the same file --- and then writing out the +// unified contents as a Breakpad-format symbol file. +class Module { + public: + // The type of addresses and sizes in a symbol table. + typedef uint64_t Address; + struct File; + struct Function; + struct Line; + struct Extern; + + // Addresses appearing in File, Function, and Line structures are + // absolute, not relative to the the module's load address. That + // is, if the module were loaded at its nominal load address, the + // addresses would be correct. + + // A source file. + struct File { + explicit File(const string &name_input) : name(name_input), source_id(0) {} + + // The name of the source file. + const string name; + + // The file's source id. The Write member function clears this + // field and assigns source ids a fresh, so any value placed here + // before calling Write will be lost. + int source_id; + }; + + // An address range. + struct Range { + Range(const Address address_input, const Address size_input) : + address(address_input), size(size_input) { } + + Address address; + Address size; + }; + + // A function. + struct Function { + Function(const string &name_input, const Address &address_input) : + name(name_input), address(address_input), parameter_size(0) {} + + // For sorting by address. (Not style-guide compliant, but it's + // stupid not to put this in the struct.) + static bool CompareByAddress(const Function *x, const Function *y) { + return x->address < y->address; + } + + // The function's name. + string name; + + // The start address and the address ranges covered by the function. + const Address address; + vector<Range> ranges; + + // The function's parameter size. + Address parameter_size; + + // Source lines belonging to this function, sorted by increasing + // address. + vector<Line> lines; + }; + + // A source line. + struct Line { + // For sorting by address. (Not style-guide compliant, but it's + // stupid not to put this in the struct.) + static bool CompareByAddress(const Module::Line &x, const Module::Line &y) { + return x.address < y.address; + } + + Address address, size; // The address and size of the line's code. + File *file; // The source file. + int number; // The source line number. + }; + + // An exported symbol. + struct Extern { + explicit Extern(const Address &address_input) : address(address_input) {} + const Address address; + string name; + }; + + // A map from register names to postfix expressions that recover + // their their values. This can represent a complete set of rules to + // follow at some address, or a set of changes to be applied to an + // extant set of rules. + typedef map<string, string> RuleMap; + + // A map from addresses to RuleMaps, representing changes that take + // effect at given addresses. + typedef map<Address, RuleMap> RuleChangeMap; + + // A range of 'STACK CFI' stack walking information. An instance of + // this structure corresponds to a 'STACK CFI INIT' record and the + // subsequent 'STACK CFI' records that fall within its range. + struct StackFrameEntry { + // The starting address and number of bytes of machine code this + // entry covers. + Address address, size; + + // The initial register recovery rules, in force at the starting + // address. + RuleMap initial_rules; + + // A map from addresses to rule changes. To find the rules in + // force at a given address, start with initial_rules, and then + // apply the changes given in this map for all addresses up to and + // including the address you're interested in. + RuleChangeMap rule_changes; + }; + + struct FunctionCompare { + bool operator() (const Function *lhs, + const Function *rhs) const { + if (lhs->address == rhs->address) + return lhs->name < rhs->name; + return lhs->address < rhs->address; + } + }; + + struct ExternCompare { + bool operator() (const Extern *lhs, + const Extern *rhs) const { + return lhs->address < rhs->address; + } + }; + + struct StackFrameEntryCompare { + bool operator() (const StackFrameEntry* lhs, + const StackFrameEntry* rhs) const { + return lhs->address < rhs->address; + } + }; + + // Create a new module with the given name, operating system, + // architecture, and ID string. + Module(const string &name, const string &os, const string &architecture, + const string &id, const string &code_id = ""); + ~Module(); + + // Set the module's load address to LOAD_ADDRESS; addresses given + // for functions and lines will be written to the Breakpad symbol + // file as offsets from this address. Construction initializes this + // module's load address to zero: addresses written to the symbol + // file will be the same as they appear in the Function, Line, and + // StackFrameEntry structures. + // + // Note that this member function has no effect on addresses stored + // in the data added to this module; the Write member function + // simply subtracts off the load address from addresses before it + // prints them. Only the last load address given before calling + // Write is used. + void SetLoadAddress(Address load_address); + + // Sets address filtering on elements added to the module. This allows + // libraries with extraneous debug symbols to generate symbol files containing + // only relevant symbols. For example, an LLD-generated partition library may + // contain debug information pertaining to all partitions derived from a + // single "combined" library. Filtering applies only to elements added after + // this method is called. + void SetAddressRanges(const vector<Range>& ranges); + + // Add FUNCTION to the module. FUNCTION's name must not be empty. + // This module owns all Function objects added with this function: + // destroying the module destroys them as well. + void AddFunction(Function *function); + + // Add all the functions in [BEGIN,END) to the module. + // This module owns all Function objects added with this function: + // destroying the module destroys them as well. + void AddFunctions(vector<Function *>::iterator begin, + vector<Function *>::iterator end); + + // Add STACK_FRAME_ENTRY to the module. + // This module owns all StackFrameEntry objects added with this + // function: destroying the module destroys them as well. + void AddStackFrameEntry(StackFrameEntry *stack_frame_entry); + + // Add PUBLIC to the module. + // This module owns all Extern objects added with this function: + // destroying the module destroys them as well. + void AddExtern(Extern *ext); + + // If this module has a file named NAME, return a pointer to it. If + // it has none, then create one and return a pointer to the new + // file. This module owns all File objects created using these + // functions; destroying the module destroys them as well. + File *FindFile(const string &name); + File *FindFile(const char *name); + + // If this module has a file named NAME, return a pointer to it. + // Otherwise, return NULL. + File *FindExistingFile(const string &name); + + // Insert pointers to the functions added to this module at I in + // VEC. The pointed-to Functions are still owned by this module. + // (Since this is effectively a copy of the function list, this is + // mostly useful for testing; other uses should probably get a more + // appropriate interface.) + void GetFunctions(vector<Function *> *vec, vector<Function *>::iterator i); + + // Insert pointers to the externs added to this module at I in + // VEC. The pointed-to Externs are still owned by this module. + // (Since this is effectively a copy of the extern list, this is + // mostly useful for testing; other uses should probably get a more + // appropriate interface.) + void GetExterns(vector<Extern *> *vec, vector<Extern *>::iterator i); + + // Clear VEC and fill it with pointers to the Files added to this + // module, sorted by name. The pointed-to Files are still owned by + // this module. (Since this is effectively a copy of the file list, + // this is mostly useful for testing; other uses should probably get + // a more appropriate interface.) + void GetFiles(vector<File *> *vec); + + // Clear VEC and fill it with pointers to the StackFrameEntry + // objects that have been added to this module. (Since this is + // effectively a copy of the stack frame entry list, this is mostly + // useful for testing; other uses should probably get + // a more appropriate interface.) + void GetStackFrameEntries(vector<StackFrameEntry *> *vec) const; + + // If this module has a StackFrameEntry whose address range covers + // ADDRESS, return it. Otherwise return NULL. + StackFrameEntry* FindStackFrameEntryByAddress(Address address); + + // Find those files in this module that are actually referred to by + // functions' line number data, and assign them source id numbers. + // Set the source id numbers for all other files --- unused by the + // source line data --- to -1. We do this before writing out the + // symbol file, at which point we omit any unused files. + void AssignSourceIds(); + + // Call AssignSourceIds, and write this module to STREAM in the + // breakpad symbol format. Return true if all goes well, or false if + // an error occurs. This method writes out: + // - a header based on the values given to the constructor, + // If symbol_data is not ONLY_CFI then: + // - the source files added via FindFile, + // - the functions added via AddFunctions, each with its lines, + // - all public records, + // If symbol_data is not NO_CFI then: + // - all CFI records. + // Addresses in the output are all relative to the load address + // established by SetLoadAddress. + bool Write(std::ostream &stream, SymbolData symbol_data); + + string name() const { return name_; } + string os() const { return os_; } + string architecture() const { return architecture_; } + string identifier() const { return id_; } + string code_identifier() const { return code_id_; } + + private: + // Report an error that has occurred writing the symbol file, using + // errno to find the appropriate cause. Return false. + static bool ReportError(); + + // Write RULE_MAP to STREAM, in the form appropriate for 'STACK CFI' + // records, without a final newline. Return true if all goes well; + // if an error occurs, return false, and leave errno set. + static bool WriteRuleMap(const RuleMap &rule_map, std::ostream &stream); + + // Returns true of the specified address resides with an specified address + // range, or if no ranges have been specified. + bool AddressIsInModule(Address address) const; + + // Module header entries. + string name_, os_, architecture_, id_, code_id_; + + // The module's nominal load address. Addresses for functions and + // lines are absolute, assuming the module is loaded at this + // address. + Address load_address_; + + // The set of valid address ranges of the module. If specified, attempts to + // add elements residing outside these ranges will be silently filtered. + vector<Range> address_ranges_; + + // Relation for maps whose keys are strings shared with some other + // structure. + struct CompareStringPtrs { + bool operator()(const string *x, const string *y) const { return *x < *y; } + }; + + // A map from filenames to File structures. The map's keys are + // pointers to the Files' names. + typedef map<const string *, File *, CompareStringPtrs> FileByNameMap; + + // A set containing Function structures, sorted by address. + typedef set<Function *, FunctionCompare> FunctionSet; + + // A set containing Extern structures, sorted by address. + typedef set<Extern *, ExternCompare> ExternSet; + + // A set containing StackFrameEntry structures, sorted by address. + typedef set<StackFrameEntry*, StackFrameEntryCompare> StackFrameEntrySet; + + // The module owns all the files and functions that have been added + // to it; destroying the module frees the Files and Functions these + // point to. + FileByNameMap files_; // This module's source files. + FunctionSet functions_; // This module's functions. + + // The module owns all the call frame info entries that have been + // added to it. + StackFrameEntrySet stack_frame_entries_; + + // The module owns all the externs that have been added to it; + // destroying the module frees the Externs these point to. + ExternSet externs_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_MODULE_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/module_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/module_unittest.cc new file mode 100644 index 0000000000..b855f186ba --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/module_unittest.cc @@ -0,0 +1,674 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// module_unittest.cc: Unit tests for google_breakpad::Module. + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <algorithm> +#include <sstream> +#include <string> + +#include "breakpad_googletest_includes.h" +#include "common/module.h" +#include "common/using_std_string.h" + +using google_breakpad::Module; +using std::stringstream; +using std::vector; +using testing::ContainerEq; + +static Module::Function* generate_duplicate_function(const string &name) { + const Module::Address DUP_ADDRESS = 0xd35402aac7a7ad5cULL; + const Module::Address DUP_SIZE = 0x200b26e605f99071ULL; + const Module::Address DUP_PARAMETER_SIZE = 0xf14ac4fed48c4a99ULL; + + Module::Function* function = new Module::Function(name, DUP_ADDRESS); + Module::Range range(DUP_ADDRESS, DUP_SIZE); + function->ranges.push_back(range); + function->parameter_size = DUP_PARAMETER_SIZE; + return function; +} + +#define MODULE_NAME "name with spaces" +#define MODULE_OS "os-name" +#define MODULE_ARCH "architecture" +#define MODULE_ID "id-string" +#define MODULE_CODE_ID "code-id-string" + +TEST(Write, Header) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + m.Write(s, ALL_SYMBOL_DATA); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n", + contents.c_str()); +} + +TEST(Write, HeaderCodeId) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID, MODULE_CODE_ID); + m.Write(s, ALL_SYMBOL_DATA); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "INFO CODE_ID code-id-string\n", + contents.c_str()); +} + +TEST(Write, OneLineFunc) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + Module::File* file = m.FindFile("file_name.cc"); + Module::Function* function = new Module::Function( + "function_name", 0xe165bf8023b9d9abULL); + Module::Range range(0xe165bf8023b9d9abULL, 0x1e4bb0eb1cbf5b09ULL); + function->ranges.push_back(range); + function->parameter_size = 0x772beee89114358aULL; + Module::Line line = { 0xe165bf8023b9d9abULL, 0x1e4bb0eb1cbf5b09ULL, + file, 67519080 }; + function->lines.push_back(line); + m.AddFunction(function); + + m.Write(s, ALL_SYMBOL_DATA); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FILE 0 file_name.cc\n" + "FUNC e165bf8023b9d9ab 1e4bb0eb1cbf5b09 772beee89114358a" + " function_name\n" + "e165bf8023b9d9ab 1e4bb0eb1cbf5b09 67519080 0\n", + contents.c_str()); +} + +TEST(Write, RelativeLoadAddress) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Some source files. We will expect to see them in lexicographic order. + Module::File* file1 = m.FindFile("filename-b.cc"); + Module::File* file2 = m.FindFile("filename-a.cc"); + + // A function. + Module::Function* function = new Module::Function( + "A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)", 0xbec774ea5dd935f3ULL); + Module::Range range(0xbec774ea5dd935f3ULL, 0x2922088f98d3f6fcULL); + function->ranges.push_back(range); + function->parameter_size = 0xe5e9aa008bd5f0d0ULL; + + // Some source lines. The module should not sort these. + Module::Line line1 = { 0xbec774ea5dd935f3ULL, 0x1c2be6d6c5af2611ULL, + file1, 41676901 }; + Module::Line line2 = { 0xdaf35bc123885c04ULL, 0xcf621b8d324d0ebULL, + file2, 67519080 }; + function->lines.push_back(line2); + function->lines.push_back(line1); + + m.AddFunction(function); + + // Some stack information. + Module::StackFrameEntry* entry = new Module::StackFrameEntry(); + entry->address = 0x30f9e5c83323973dULL; + entry->size = 0x49fc9ca7c7c13dc2ULL; + entry->initial_rules[".cfa"] = "he was a handsome man"; + entry->initial_rules["and"] = "what i want to know is"; + entry->rule_changes[0x30f9e5c83323973eULL]["how"] = + "do you like your blueeyed boy"; + entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = "Death"; + m.AddStackFrameEntry(entry); + + // Set the load address. Doing this after adding all the data to + // the module must work fine. + m.SetLoadAddress(0x2ab698b0b6407073ULL); + + m.Write(s, ALL_SYMBOL_DATA); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FILE 0 filename-a.cc\n" + "FILE 1 filename-b.cc\n" + "FUNC 9410dc39a798c580 2922088f98d3f6fc e5e9aa008bd5f0d0" + " A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)\n" + "b03cc3106d47eb91 cf621b8d324d0eb 67519080 0\n" + "9410dc39a798c580 1c2be6d6c5af2611 41676901 1\n" + "STACK CFI INIT 6434d177ce326ca 49fc9ca7c7c13dc2" + " .cfa: he was a handsome man" + " and: what i want to know is\n" + "STACK CFI 6434d177ce326cb" + " Mister: Death" + " how: do you like your blueeyed boy\n", + contents.c_str()); +} + +TEST(Write, OmitUnusedFiles) { + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Create some source files. + Module::File* file1 = m.FindFile("filename1"); + m.FindFile("filename2"); // not used by any line + Module::File* file3 = m.FindFile("filename3"); + + // Create a function. + Module::Function* function = new Module::Function( + "function_name", 0x9b926d464f0b9384ULL); + Module::Range range(0x9b926d464f0b9384ULL, 0x4f524a4ba795e6a6ULL); + function->ranges.push_back(range); + function->parameter_size = 0xbbe8133a6641c9b7ULL; + + // Source files that refer to some files, but not others. + Module::Line line1 = { 0xab415089485e1a20ULL, 0x126e3124979291f2ULL, + file1, 137850127 }; + Module::Line line2 = { 0xb2675b5c3c2ed33fULL, 0x1df77f5551dbd68cULL, + file3, 28113549 }; + function->lines.push_back(line1); + function->lines.push_back(line2); + m.AddFunction(function); + + m.AssignSourceIds(); + + vector<Module::File*> vec; + m.GetFiles(&vec); + EXPECT_EQ((size_t) 3, vec.size()); + EXPECT_STREQ("filename1", vec[0]->name.c_str()); + EXPECT_NE(-1, vec[0]->source_id); + // Expect filename2 not to be used. + EXPECT_STREQ("filename2", vec[1]->name.c_str()); + EXPECT_EQ(-1, vec[1]->source_id); + EXPECT_STREQ("filename3", vec[2]->name.c_str()); + EXPECT_NE(-1, vec[2]->source_id); + + stringstream s; + m.Write(s, ALL_SYMBOL_DATA); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FILE 0 filename1\n" + "FILE 1 filename3\n" + "FUNC 9b926d464f0b9384 4f524a4ba795e6a6 bbe8133a6641c9b7" + " function_name\n" + "ab415089485e1a20 126e3124979291f2 137850127 0\n" + "b2675b5c3c2ed33f 1df77f5551dbd68c 28113549 1\n", + contents.c_str()); +} + +TEST(Write, NoCFI) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Some source files. We will expect to see them in lexicographic order. + Module::File* file1 = m.FindFile("filename.cc"); + + // A function. + Module::Function* function = new Module::Function( + "A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)", 0xbec774ea5dd935f3ULL); + Module::Range range(0xbec774ea5dd935f3ULL, 0x2922088f98d3f6fcULL); + function->ranges.push_back(range); + function->parameter_size = 0xe5e9aa008bd5f0d0ULL; + + // Some source lines. The module should not sort these. + Module::Line line1 = { 0xbec774ea5dd935f3ULL, 0x1c2be6d6c5af2611ULL, + file1, 41676901 }; + function->lines.push_back(line1); + + m.AddFunction(function); + + // Some stack information. + Module::StackFrameEntry* entry = new Module::StackFrameEntry(); + entry->address = 0x30f9e5c83323973dULL; + entry->size = 0x49fc9ca7c7c13dc2ULL; + entry->initial_rules[".cfa"] = "he was a handsome man"; + entry->initial_rules["and"] = "what i want to know is"; + entry->rule_changes[0x30f9e5c83323973eULL]["how"] = + "do you like your blueeyed boy"; + entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = "Death"; + m.AddStackFrameEntry(entry); + + // Set the load address. Doing this after adding all the data to + // the module must work fine. + m.SetLoadAddress(0x2ab698b0b6407073ULL); + + m.Write(s, NO_CFI); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FILE 0 filename.cc\n" + "FUNC 9410dc39a798c580 2922088f98d3f6fc e5e9aa008bd5f0d0" + " A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)\n" + "9410dc39a798c580 1c2be6d6c5af2611 41676901 0\n", + contents.c_str()); +} + +TEST(Construct, AddFunctions) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Two functions. + Module::Function* function1 = new Module::Function( + "_without_form", 0xd35024aa7ca7da5cULL); + Module::Range r1(0xd35024aa7ca7da5cULL, 0x200b26e605f99071ULL); + function1->ranges.push_back(r1); + function1->parameter_size = 0xf14ac4fed48c4a99ULL; + + Module::Function* function2 = new Module::Function( + "_and_void", 0x2987743d0b35b13fULL); + Module::Range r2(0x2987743d0b35b13fULL, 0xb369db048deb3010ULL); + function2->ranges.push_back(r2); + function2->parameter_size = 0x938e556cb5a79988ULL; + + // Put them in a vector. + vector<Module::Function*> vec; + vec.push_back(function1); + vec.push_back(function2); + + m.AddFunctions(vec.begin(), vec.end()); + + m.Write(s, ALL_SYMBOL_DATA); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FUNC 2987743d0b35b13f b369db048deb3010 938e556cb5a79988" + " _and_void\n" + "FUNC d35024aa7ca7da5c 200b26e605f99071 f14ac4fed48c4a99" + " _without_form\n", + contents.c_str()); + + // Check that m.GetFunctions returns the functions we expect. + vec.clear(); + m.GetFunctions(&vec, vec.end()); + EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function1)); + EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function2)); + EXPECT_EQ((size_t) 2, vec.size()); +} + +TEST(Construct, AddFrames) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // First STACK CFI entry, with no initial rules or deltas. + Module::StackFrameEntry* entry1 = new Module::StackFrameEntry(); + entry1->address = 0xddb5f41285aa7757ULL; + entry1->size = 0x1486493370dc5073ULL; + m.AddStackFrameEntry(entry1); + + // Second STACK CFI entry, with initial rules but no deltas. + Module::StackFrameEntry* entry2 = new Module::StackFrameEntry(); + entry2->address = 0x8064f3af5e067e38ULL; + entry2->size = 0x0de2a5ee55509407ULL; + entry2->initial_rules[".cfa"] = "I think that I shall never see"; + entry2->initial_rules["stromboli"] = "a poem lovely as a tree"; + entry2->initial_rules["cannoli"] = "a tree whose hungry mouth is prest"; + m.AddStackFrameEntry(entry2); + + // Third STACK CFI entry, with initial rules and deltas. + Module::StackFrameEntry* entry3 = new Module::StackFrameEntry(); + entry3->address = 0x5e8d0db0a7075c6cULL; + entry3->size = 0x1c7edb12a7aea229ULL; + entry3->initial_rules[".cfa"] = "Whose woods are these"; + entry3->rule_changes[0x47ceb0f63c269d7fULL]["calzone"] = + "the village though"; + entry3->rule_changes[0x47ceb0f63c269d7fULL]["cannoli"] = + "he will not see me stopping here"; + entry3->rule_changes[0x36682fad3763ffffULL]["stromboli"] = + "his house is in"; + entry3->rule_changes[0x36682fad3763ffffULL][".cfa"] = + "I think I know"; + m.AddStackFrameEntry(entry3); + + // Check that Write writes STACK CFI records properly. + m.Write(s, ALL_SYMBOL_DATA); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "STACK CFI INIT 5e8d0db0a7075c6c 1c7edb12a7aea229" + " .cfa: Whose woods are these\n" + "STACK CFI 36682fad3763ffff" + " .cfa: I think I know" + " stromboli: his house is in\n" + "STACK CFI 47ceb0f63c269d7f" + " calzone: the village though" + " cannoli: he will not see me stopping here\n" + "STACK CFI INIT 8064f3af5e067e38 de2a5ee55509407" + " .cfa: I think that I shall never see" + " cannoli: a tree whose hungry mouth is prest" + " stromboli: a poem lovely as a tree\n" + "STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n", + contents.c_str()); + + // Check that GetStackFrameEntries works. + vector<Module::StackFrameEntry*> entries; + m.GetStackFrameEntries(&entries); + ASSERT_EQ(3U, entries.size()); + // Check first entry. + EXPECT_EQ(0x5e8d0db0a7075c6cULL, entries[0]->address); + EXPECT_EQ(0x1c7edb12a7aea229ULL, entries[0]->size); + Module::RuleMap entry1_initial; + entry1_initial[".cfa"] = "Whose woods are these"; + EXPECT_THAT(entries[0]->initial_rules, ContainerEq(entry1_initial)); + Module::RuleChangeMap entry1_changes; + entry1_changes[0x36682fad3763ffffULL][".cfa"] = "I think I know"; + entry1_changes[0x36682fad3763ffffULL]["stromboli"] = "his house is in"; + entry1_changes[0x47ceb0f63c269d7fULL]["calzone"] = "the village though"; + entry1_changes[0x47ceb0f63c269d7fULL]["cannoli"] = + "he will not see me stopping here"; + EXPECT_THAT(entries[0]->rule_changes, ContainerEq(entry1_changes)); + // Check second entry. + EXPECT_EQ(0x8064f3af5e067e38ULL, entries[1]->address); + EXPECT_EQ(0x0de2a5ee55509407ULL, entries[1]->size); + ASSERT_EQ(3U, entries[1]->initial_rules.size()); + Module::RuleMap entry2_initial; + entry2_initial[".cfa"] = "I think that I shall never see"; + entry2_initial["stromboli"] = "a poem lovely as a tree"; + entry2_initial["cannoli"] = "a tree whose hungry mouth is prest"; + EXPECT_THAT(entries[1]->initial_rules, ContainerEq(entry2_initial)); + ASSERT_EQ(0U, entries[1]->rule_changes.size()); + // Check third entry. + EXPECT_EQ(0xddb5f41285aa7757ULL, entries[2]->address); + EXPECT_EQ(0x1486493370dc5073ULL, entries[2]->size); + ASSERT_EQ(0U, entries[2]->initial_rules.size()); + ASSERT_EQ(0U, entries[2]->rule_changes.size()); +} + +TEST(Construct, UniqueFiles) { + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + Module::File* file1 = m.FindFile("foo"); + Module::File* file2 = m.FindFile(string("bar")); + Module::File* file3 = m.FindFile(string("foo")); + Module::File* file4 = m.FindFile("bar"); + EXPECT_NE(file1, file2); + EXPECT_EQ(file1, file3); + EXPECT_EQ(file2, file4); + EXPECT_EQ(file1, m.FindExistingFile("foo")); + EXPECT_TRUE(m.FindExistingFile("baz") == NULL); +} + +TEST(Construct, DuplicateFunctions) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Two functions. + Module::Function* function1 = generate_duplicate_function("_without_form"); + Module::Function* function2 = generate_duplicate_function("_without_form"); + + m.AddFunction(function1); + m.AddFunction(function2); + + m.Write(s, ALL_SYMBOL_DATA); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99" + " _without_form\n", + contents.c_str()); +} + +TEST(Construct, FunctionsWithSameAddress) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Two functions. + Module::Function* function1 = generate_duplicate_function("_without_form"); + Module::Function* function2 = generate_duplicate_function("_and_void"); + + m.AddFunction(function1); + m.AddFunction(function2); + + m.Write(s, ALL_SYMBOL_DATA); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99" + " _and_void\n" + "FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99" + " _without_form\n", + contents.c_str()); +} + +// Externs should be written out as PUBLIC records, sorted by +// address. +TEST(Construct, Externs) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Two externs. + Module::Extern* extern1 = new Module::Extern(0xffff); + extern1->name = "_abc"; + Module::Extern* extern2 = new Module::Extern(0xaaaa); + extern2->name = "_xyz"; + + m.AddExtern(extern1); + m.AddExtern(extern2); + + m.Write(s, ALL_SYMBOL_DATA); + string contents = s.str(); + + EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " " + MODULE_ID " " MODULE_NAME "\n" + "PUBLIC aaaa 0 _xyz\n" + "PUBLIC ffff 0 _abc\n", + contents.c_str()); +} + +// Externs with the same address should only keep the first entry +// added. +TEST(Construct, DuplicateExterns) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Two externs. + Module::Extern* extern1 = new Module::Extern(0xffff); + extern1->name = "_xyz"; + Module::Extern* extern2 = new Module::Extern(0xffff); + extern2->name = "_abc"; + + m.AddExtern(extern1); + m.AddExtern(extern2); + + m.Write(s, ALL_SYMBOL_DATA); + string contents = s.str(); + + EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " " + MODULE_ID " " MODULE_NAME "\n" + "PUBLIC ffff 0 _xyz\n", + contents.c_str()); +} + +// If there exists an extern and a function at the same address, only write +// out the FUNC entry. +TEST(Construct, FunctionsAndExternsWithSameAddress) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Two externs. + Module::Extern* extern1 = new Module::Extern(0xabc0); + extern1->name = "abc"; + Module::Extern* extern2 = new Module::Extern(0xfff0); + extern2->name = "xyz"; + + m.AddExtern(extern1); + m.AddExtern(extern2); + + Module::Function* function = new Module::Function("_xyz", 0xfff0); + Module::Range range(0xfff0, 0x10); + function->ranges.push_back(range); + function->parameter_size = 0; + m.AddFunction(function); + + m.Write(s, ALL_SYMBOL_DATA); + string contents = s.str(); + + EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " " + MODULE_ID " " MODULE_NAME "\n" + "FUNC fff0 10 0 _xyz\n" + "PUBLIC abc0 0 abc\n", + contents.c_str()); +} + +// If there exists an extern and a function at the same address, only write +// out the FUNC entry. For ARM THUMB, the extern that comes from the ELF +// symbol section has bit 0 set. +TEST(Construct, FunctionsAndThumbExternsWithSameAddress) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, "arm", MODULE_ID); + + // Two THUMB externs. + Module::Extern* thumb_extern1 = new Module::Extern(0xabc1); + thumb_extern1->name = "thumb_abc"; + Module::Extern* thumb_extern2 = new Module::Extern(0xfff1); + thumb_extern2->name = "thumb_xyz"; + + Module::Extern* arm_extern1 = new Module::Extern(0xcc00); + arm_extern1->name = "arm_func"; + + m.AddExtern(thumb_extern1); + m.AddExtern(thumb_extern2); + m.AddExtern(arm_extern1); + + // The corresponding function from the DWARF debug data have the actual + // address. + Module::Function* function = new Module::Function("_thumb_xyz", 0xfff0); + Module::Range range(0xfff0, 0x10); + function->ranges.push_back(range); + function->parameter_size = 0; + m.AddFunction(function); + + m.Write(s, ALL_SYMBOL_DATA); + string contents = s.str(); + + EXPECT_STREQ("MODULE " MODULE_OS " arm " + MODULE_ID " " MODULE_NAME "\n" + "FUNC fff0 10 0 _thumb_xyz\n" + "PUBLIC abc1 0 thumb_abc\n" + "PUBLIC cc00 0 arm_func\n", + contents.c_str()); +} + +TEST(Write, OutOfRangeAddresses) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Specify an allowed address range, representing a PT_LOAD segment in a + // module. + vector<Module::Range> address_ranges = { + Module::Range(0x2000ULL, 0x1000ULL), + }; + m.SetAddressRanges(address_ranges); + + // Add three stack frames (one lower, one in, and one higher than the allowed + // address range). Only the middle frame should be captured. + Module::StackFrameEntry* entry1 = new Module::StackFrameEntry(); + entry1->address = 0x1000ULL; + entry1->size = 0x100ULL; + m.AddStackFrameEntry(entry1); + Module::StackFrameEntry* entry2 = new Module::StackFrameEntry(); + entry2->address = 0x2000ULL; + entry2->size = 0x100ULL; + m.AddStackFrameEntry(entry2); + Module::StackFrameEntry* entry3 = new Module::StackFrameEntry(); + entry3->address = 0x3000ULL; + entry3->size = 0x100ULL; + m.AddStackFrameEntry(entry3); + + // Add a function outside the allowed range. + Module::File* file = m.FindFile("file_name.cc"); + Module::Function* function = new Module::Function( + "function_name", 0x4000ULL); + Module::Range range(0x4000ULL, 0x1000ULL); + function->ranges.push_back(range); + function->parameter_size = 0x100ULL; + Module::Line line = { 0x4000ULL, 0x100ULL, file, 67519080 }; + function->lines.push_back(line); + m.AddFunction(function); + + // Add an extern outside the allowed range. + Module::Extern* extern1 = new Module::Extern(0x5000ULL); + extern1->name = "_xyz"; + m.AddExtern(extern1); + + m.Write(s, ALL_SYMBOL_DATA); + + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "STACK CFI INIT 2000 100 \n", + s.str().c_str()); +} + +TEST(Lookup, StackFrameEntries) { + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // First STACK CFI entry, with no initial rules or deltas. + Module::StackFrameEntry *entry1 = new Module::StackFrameEntry(); + entry1->address = 0x2000; + entry1->size = 0x900; + m.AddStackFrameEntry(entry1); + + // Second STACK CFI entry, with initial rules but no deltas. + Module::StackFrameEntry *entry2 = new Module::StackFrameEntry(); + entry2->address = 0x3000; + entry2->size = 0x900; + entry2->initial_rules[".cfa"] = "I think that I shall never see"; + entry2->initial_rules["stromboli"] = "a poem lovely as a tree"; + entry2->initial_rules["cannoli"] = "a tree whose hungry mouth is prest"; + m.AddStackFrameEntry(entry2); + + // Third STACK CFI entry, with initial rules and deltas. + Module::StackFrameEntry *entry3 = new Module::StackFrameEntry(); + entry3->address = 0x1000; + entry3->size = 0x900; + entry3->initial_rules[".cfa"] = "Whose woods are these"; + entry3->rule_changes[0x47ceb0f63c269d7fULL]["calzone"] = + "the village though"; + entry3->rule_changes[0x47ceb0f63c269d7fULL]["cannoli"] = + "he will not see me stopping here"; + entry3->rule_changes[0x36682fad3763ffffULL]["stromboli"] = + "his house is in"; + entry3->rule_changes[0x36682fad3763ffffULL][".cfa"] = + "I think I know"; + m.AddStackFrameEntry(entry3); + + Module::StackFrameEntry* s = m.FindStackFrameEntryByAddress(0x1000); + EXPECT_EQ(entry3, s); + s = m.FindStackFrameEntryByAddress(0x18FF); + EXPECT_EQ(entry3, s); + + s = m.FindStackFrameEntryByAddress(0x1900); + EXPECT_EQ((Module::StackFrameEntry*)NULL, s); + s = m.FindStackFrameEntryByAddress(0x1A00); + EXPECT_EQ((Module::StackFrameEntry*)NULL, s); + + s = m.FindStackFrameEntryByAddress(0x2000); + EXPECT_EQ(entry1, s); + s = m.FindStackFrameEntryByAddress(0x28FF); + EXPECT_EQ(entry1, s); + + s = m.FindStackFrameEntryByAddress(0x3000); + EXPECT_EQ(entry2, s); + s = m.FindStackFrameEntryByAddress(0x38FF); + EXPECT_EQ(entry2, s); + + s = m.FindStackFrameEntryByAddress(0x3900); + EXPECT_EQ((Module::StackFrameEntry*)NULL, s); + s = m.FindStackFrameEntryByAddress(0x3A00); + EXPECT_EQ((Module::StackFrameEntry*)NULL, s); +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/moz.build b/toolkit/crashreporter/google-breakpad/src/common/moz.build new file mode 100644 index 0000000000..d3b55045d1 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/moz.build @@ -0,0 +1,19 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +UNIFIED_SOURCES += [ + 'convert_UTF.cc', + 'string_conversion.cc', +] + +if CONFIG['OS_ARCH'] == 'Darwin': + UNIFIED_SOURCES += [ + 'md5.cc', + ] + +Library('breakpad_common_s') + +include('/toolkit/crashreporter/crashreporter.mozbuild') diff --git a/toolkit/crashreporter/google-breakpad/src/common/path_helper.cc b/toolkit/crashreporter/google-breakpad/src/common/path_helper.cc new file mode 100644 index 0000000000..61a6e31846 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/path_helper.cc @@ -0,0 +1,55 @@ +// Copyright 2017, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include "common/path_helper.h" + +#include <assert.h> +#include <libgen.h> +#include <stdlib.h> +#include <string.h> + +namespace google_breakpad { + +string BaseName(const string& path) { + char* path_tmp = strdup(path.c_str()); + assert(path_tmp); + string result(basename(path_tmp)); + free(path_tmp); + return result; +} + +string DirName(const string& path) { + char* path_tmp = strdup(path.c_str()); + assert(path_tmp); + string result(dirname(path_tmp)); + free(path_tmp); + return result; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/path_helper.h b/toolkit/crashreporter/google-breakpad/src/common/path_helper.h new file mode 100644 index 0000000000..2166ba018a --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/path_helper.h @@ -0,0 +1,44 @@ +// Copyright 2017, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef GOOGLE_BREAKPAD_COMMON_PATH_HELPER_H +#define GOOGLE_BREAKPAD_COMMON_PATH_HELPER_H + +#include <string> + +#include "common/using_std_string.h" + +namespace google_breakpad { + +string BaseName(const string& path); +string DirName(const string& path); + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_COMMON_PATH_HELPER_H diff --git a/toolkit/crashreporter/google-breakpad/src/common/scoped_ptr.h b/toolkit/crashreporter/google-breakpad/src/common/scoped_ptr.h new file mode 100644 index 0000000000..d137c18681 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/scoped_ptr.h @@ -0,0 +1,404 @@ +// Copyright 2013 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Scopers help you manage ownership of a pointer, helping you easily manage the +// a pointer within a scope, and automatically destroying the pointer at the +// end of a scope. There are two main classes you will use, which correspond +// to the operators new/delete and new[]/delete[]. +// +// Example usage (scoped_ptr): +// { +// scoped_ptr<Foo> foo(new Foo("wee")); +// } // foo goes out of scope, releasing the pointer with it. +// +// { +// scoped_ptr<Foo> foo; // No pointer managed. +// foo.reset(new Foo("wee")); // Now a pointer is managed. +// foo.reset(new Foo("wee2")); // Foo("wee") was destroyed. +// foo.reset(new Foo("wee3")); // Foo("wee2") was destroyed. +// foo->Method(); // Foo::Method() called. +// foo.get()->Method(); // Foo::Method() called. +// SomeFunc(foo.release()); // SomeFunc takes ownership, foo no longer +// // manages a pointer. +// foo.reset(new Foo("wee4")); // foo manages a pointer again. +// foo.reset(); // Foo("wee4") destroyed, foo no longer +// // manages a pointer. +// } // foo wasn't managing a pointer, so nothing was destroyed. +// +// Example usage (scoped_array): +// { +// scoped_array<Foo> foo(new Foo[100]); +// foo.get()->Method(); // Foo::Method on the 0th element. +// foo[10].Method(); // Foo::Method on the 10th element. +// } + +#ifndef COMMON_SCOPED_PTR_H_ +#define COMMON_SCOPED_PTR_H_ + +// This is an implementation designed to match the anticipated future TR2 +// implementation of the scoped_ptr class, and its closely-related brethren, +// scoped_array, scoped_ptr_malloc. + +#include <assert.h> +#include <stddef.h> +#include <stdlib.h> + +namespace google_breakpad { + +// A scoped_ptr<T> is like a T*, except that the destructor of scoped_ptr<T> +// automatically deletes the pointer it holds (if any). +// That is, scoped_ptr<T> owns the T object that it points to. +// Like a T*, a scoped_ptr<T> may hold either NULL or a pointer to a T object. +// Also like T*, scoped_ptr<T> is thread-compatible, and once you +// dereference it, you get the threadsafety guarantees of T. +// +// The size of a scoped_ptr is small: +// sizeof(scoped_ptr<C>) == sizeof(C*) +template <class C> +class scoped_ptr { + public: + + // The element type + typedef C element_type; + + // Constructor. Defaults to initializing with NULL. + // There is no way to create an uninitialized scoped_ptr. + // The input parameter must be allocated with new. + explicit scoped_ptr(C* p = NULL) : ptr_(p) { } + + // Destructor. If there is a C object, delete it. + // We don't need to test ptr_ == NULL because C++ does that for us. + ~scoped_ptr() { + enum { type_must_be_complete = sizeof(C) }; + delete ptr_; + } + + // Reset. Deletes the current owned object, if any. + // Then takes ownership of a new object, if given. + // this->reset(this->get()) works. + void reset(C* p = NULL) { + if (p != ptr_) { + enum { type_must_be_complete = sizeof(C) }; + delete ptr_; + ptr_ = p; + } + } + + // Accessors to get the owned object. + // operator* and operator-> will assert() if there is no current object. + C& operator*() const { + assert(ptr_ != NULL); + return *ptr_; + } + C* operator->() const { + assert(ptr_ != NULL); + return ptr_; + } + C* get() const { return ptr_; } + + // Comparison operators. + // These return whether two scoped_ptr refer to the same object, not just to + // two different but equal objects. + bool operator==(C* p) const { return ptr_ == p; } + bool operator!=(C* p) const { return ptr_ != p; } + + // Swap two scoped pointers. + void swap(scoped_ptr& p2) { + C* tmp = ptr_; + ptr_ = p2.ptr_; + p2.ptr_ = tmp; + } + + // Release a pointer. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + C* release() { + C* retVal = ptr_; + ptr_ = NULL; + return retVal; + } + + private: + C* ptr_; + + // Forbid comparison of scoped_ptr types. If C2 != C, it totally doesn't + // make sense, and if C2 == C, it still doesn't make sense because you should + // never have the same object owned by two different scoped_ptrs. + template <class C2> bool operator==(scoped_ptr<C2> const& p2) const; + template <class C2> bool operator!=(scoped_ptr<C2> const& p2) const; + + // Disallow evil constructors + scoped_ptr(const scoped_ptr&); + void operator=(const scoped_ptr&); +}; + +// Free functions +template <class C> +void swap(scoped_ptr<C>& p1, scoped_ptr<C>& p2) { + p1.swap(p2); +} + +template <class C> +bool operator==(C* p1, const scoped_ptr<C>& p2) { + return p1 == p2.get(); +} + +template <class C> +bool operator!=(C* p1, const scoped_ptr<C>& p2) { + return p1 != p2.get(); +} + +// scoped_array<C> is like scoped_ptr<C>, except that the caller must allocate +// with new [] and the destructor deletes objects with delete []. +// +// As with scoped_ptr<C>, a scoped_array<C> either points to an object +// or is NULL. A scoped_array<C> owns the object that it points to. +// scoped_array<T> is thread-compatible, and once you index into it, +// the returned objects have only the threadsafety guarantees of T. +// +// Size: sizeof(scoped_array<C>) == sizeof(C*) +template <class C> +class scoped_array { + public: + + // The element type + typedef C element_type; + + // Constructor. Defaults to intializing with NULL. + // There is no way to create an uninitialized scoped_array. + // The input parameter must be allocated with new []. + explicit scoped_array(C* p = NULL) : array_(p) { } + + // Destructor. If there is a C object, delete it. + // We don't need to test ptr_ == NULL because C++ does that for us. + ~scoped_array() { + enum { type_must_be_complete = sizeof(C) }; + delete[] array_; + } + + // Reset. Deletes the current owned object, if any. + // Then takes ownership of a new object, if given. + // this->reset(this->get()) works. + void reset(C* p = NULL) { + if (p != array_) { + enum { type_must_be_complete = sizeof(C) }; + delete[] array_; + array_ = p; + } + } + + // Get one element of the current object. + // Will assert() if there is no current object, or index i is negative. + C& operator[](ptrdiff_t i) const { + assert(i >= 0); + assert(array_ != NULL); + return array_[i]; + } + + // Get a pointer to the zeroth element of the current object. + // If there is no current object, return NULL. + C* get() const { + return array_; + } + + // Comparison operators. + // These return whether two scoped_array refer to the same object, not just to + // two different but equal objects. + bool operator==(C* p) const { return array_ == p; } + bool operator!=(C* p) const { return array_ != p; } + + // Swap two scoped arrays. + void swap(scoped_array& p2) { + C* tmp = array_; + array_ = p2.array_; + p2.array_ = tmp; + } + + // Release an array. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + C* release() { + C* retVal = array_; + array_ = NULL; + return retVal; + } + + private: + C* array_; + + // Forbid comparison of different scoped_array types. + template <class C2> bool operator==(scoped_array<C2> const& p2) const; + template <class C2> bool operator!=(scoped_array<C2> const& p2) const; + + // Disallow evil constructors + scoped_array(const scoped_array&); + void operator=(const scoped_array&); +}; + +// Free functions +template <class C> +void swap(scoped_array<C>& p1, scoped_array<C>& p2) { + p1.swap(p2); +} + +template <class C> +bool operator==(C* p1, const scoped_array<C>& p2) { + return p1 == p2.get(); +} + +template <class C> +bool operator!=(C* p1, const scoped_array<C>& p2) { + return p1 != p2.get(); +} + +// This class wraps the c library function free() in a class that can be +// passed as a template argument to scoped_ptr_malloc below. +class ScopedPtrMallocFree { + public: + inline void operator()(void* x) const { + free(x); + } +}; + +// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a +// second template argument, the functor used to free the object. + +template<class C, class FreeProc = ScopedPtrMallocFree> +class scoped_ptr_malloc { + public: + + // The element type + typedef C element_type; + + // Constructor. Defaults to initializing with NULL. + // There is no way to create an uninitialized scoped_ptr. + // The input parameter must be allocated with an allocator that matches the + // Free functor. For the default Free functor, this is malloc, calloc, or + // realloc. + explicit scoped_ptr_malloc(C* p = NULL): ptr_(p) {} + + // Destructor. If there is a C object, call the Free functor. + ~scoped_ptr_malloc() { + reset(); + } + + // Reset. Calls the Free functor on the current owned object, if any. + // Then takes ownership of a new object, if given. + // this->reset(this->get()) works. + void reset(C* p = NULL) { + if (ptr_ != p) { + FreeProc free_proc; + free_proc(ptr_); + ptr_ = p; + } + } + + // Get the current object. + // operator* and operator-> will cause an assert() failure if there is + // no current object. + C& operator*() const { + assert(ptr_ != NULL); + return *ptr_; + } + + C* operator->() const { + assert(ptr_ != NULL); + return ptr_; + } + + C* get() const { + return ptr_; + } + + // Comparison operators. + // These return whether a scoped_ptr_malloc and a plain pointer refer + // to the same object, not just to two different but equal objects. + // For compatibility with the boost-derived implementation, these + // take non-const arguments. + bool operator==(C* p) const { + return ptr_ == p; + } + + bool operator!=(C* p) const { + return ptr_ != p; + } + + // Swap two scoped pointers. + void swap(scoped_ptr_malloc & b) { + C* tmp = b.ptr_; + b.ptr_ = ptr_; + ptr_ = tmp; + } + + // Release a pointer. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + C* release() { + C* tmp = ptr_; + ptr_ = NULL; + return tmp; + } + + private: + C* ptr_; + + // no reason to use these: each scoped_ptr_malloc should have its own object + template <class C2, class GP> + bool operator==(scoped_ptr_malloc<C2, GP> const& p) const; + template <class C2, class GP> + bool operator!=(scoped_ptr_malloc<C2, GP> const& p) const; + + // Disallow evil constructors + scoped_ptr_malloc(const scoped_ptr_malloc&); + void operator=(const scoped_ptr_malloc&); +}; + +template<class C, class FP> inline +void swap(scoped_ptr_malloc<C, FP>& a, scoped_ptr_malloc<C, FP>& b) { + a.swap(b); +} + +template<class C, class FP> inline +bool operator==(C* p, const scoped_ptr_malloc<C, FP>& b) { + return p == b.get(); +} + +template<class C, class FP> inline +bool operator!=(C* p, const scoped_ptr_malloc<C, FP>& b) { + return p != b.get(); +} + +} // namespace google_breakpad + +#endif // COMMON_SCOPED_PTR_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/simple_string_dictionary.cc b/toolkit/crashreporter/google-breakpad/src/common/simple_string_dictionary.cc new file mode 100644 index 0000000000..e0a74ceeb4 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/simple_string_dictionary.cc @@ -0,0 +1,45 @@ +// Copyright (c) 2007, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include "common/simple_string_dictionary.h" + +namespace google_breakpad { + +namespace { + +// In C++98 (ISO 14882), section 9.5.1 says that a union cannot have a member +// with a non-trivial ctor, copy ctor, dtor, or assignment operator. Use this +// property to ensure that Entry remains POD. +union Compile_Assert { + NonAllocatingMap<1, 1, 1>::Entry Compile_Assert__entry_must_be_pod; +}; + +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/simple_string_dictionary.h b/toolkit/crashreporter/google-breakpad/src/common/simple_string_dictionary.h new file mode 100644 index 0000000000..9484920537 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/simple_string_dictionary.h @@ -0,0 +1,279 @@ +// Copyright (c) 2007, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef COMMON_SIMPLE_STRING_DICTIONARY_H_ +#define COMMON_SIMPLE_STRING_DICTIONARY_H_ + +#include <assert.h> +#include <string.h> + +#include "common/basictypes.h" + +namespace google_breakpad { + +// Opaque type for the serialized representation of a NonAllocatingMap. One is +// created in NonAllocatingMap::Serialize and can be deserialized using one of +// the constructors. +struct SerializedNonAllocatingMap; + +// NonAllocatingMap is an implementation of a map/dictionary collection that +// uses a fixed amount of storage, so that it does not perform any dynamic +// allocations for its operations. +// +// The actual map storage (the Entry) is guaranteed to be POD, so that it can +// be transmitted over various IPC mechanisms. +// +// The template parameters control the amount of storage used for the key, +// value, and map. The KeySize and ValueSize are measured in bytes, not glyphs, +// and includes space for a \0 byte. This gives space for KeySize-1 and +// ValueSize-1 characters in an entry. NumEntries is the total number of +// entries that will fit in the map. +template <size_t KeySize, size_t ValueSize, size_t NumEntries> +class NonAllocatingMap { + public: + // Constant and publicly accessible versions of the template parameters. + static const size_t key_size = KeySize; + static const size_t value_size = ValueSize; + static const size_t num_entries = NumEntries; + + // An Entry object is a single entry in the map. If the key is a 0-length + // NUL-terminated string, the entry is empty. + struct Entry { + char key[KeySize]; + char value[ValueSize]; + + bool is_active() const { + return key[0] != '\0'; + } + }; + + // An Iterator can be used to iterate over all the active entries in a + // NonAllocatingMap. + class Iterator { + public: + explicit Iterator(const NonAllocatingMap& map) + : map_(map), + current_(0) { + } + + // Returns the next entry in the map, or NULL if at the end of the + // collection. + const Entry* Next() { + while (current_ < map_.num_entries) { + const Entry* entry = &map_.entries_[current_++]; + if (entry->is_active()) { + return entry; + } + } + return NULL; + } + + private: + const NonAllocatingMap& map_; + size_t current_; + + DISALLOW_COPY_AND_ASSIGN(Iterator); + }; + + NonAllocatingMap() : entries_() { + } + + NonAllocatingMap(const NonAllocatingMap& other) { + *this = other; + } + + NonAllocatingMap& operator=(const NonAllocatingMap& other) { + assert(other.key_size == key_size); + assert(other.value_size == value_size); + assert(other.num_entries == num_entries); + if (other.key_size == key_size && other.value_size == value_size && + other.num_entries == num_entries) { + memcpy(entries_, other.entries_, sizeof(entries_)); + } + return *this; + } + + // Constructs a map from its serialized form. |map| should be the out + // parameter from Serialize() and |size| should be its return value. + NonAllocatingMap(const SerializedNonAllocatingMap* map, size_t size) { + assert(size == sizeof(entries_)); + if (size == sizeof(entries_)) { + memcpy(entries_, map, size); + } + } + + // Returns the number of active key/value pairs. The upper limit for this + // is NumEntries. + size_t GetCount() const { + size_t count = 0; + for (size_t i = 0; i < num_entries; ++i) { + if (entries_[i].is_active()) { + ++count; + } + } + return count; + } + + // Given |key|, returns its corresponding |value|. |key| must not be NULL. If + // the key is not found, NULL is returned. + const char* GetValueForKey(const char* key) const { + assert(key); + if (!key) + return NULL; + + size_t index = GetEntryIndexForKey(key); + if (index == num_entries) + return NULL; + + return entries_[index].value; + } + + // Stores |value| into |key|, replacing the existing value if |key| is + // already present. |key| must not be NULL. If |value| is NULL, the key is + // removed from the map. If there is no more space in the map, then the + // operation silently fails. Returns an index into the map that can be used + // to quickly access the entry, or |num_entries| on failure or when clearing + // a key with a null value. + size_t SetKeyValue(const char* key, const char* value) { + if (!value) { + RemoveKey(key); + return num_entries; + } + + assert(key); + if (!key) + return num_entries; + + // Key must not be an empty string. + assert(key[0] != '\0'); + if (key[0] == '\0') + return num_entries; + + size_t entry_index = GetEntryIndexForKey(key); + + // If it does not yet exist, attempt to insert it. + if (entry_index == num_entries) { + for (size_t i = 0; i < num_entries; ++i) { + if (!entries_[i].is_active()) { + entry_index = i; + Entry* entry = &entries_[i]; + + strncpy(entry->key, key, key_size); + entry->key[key_size - 1] = '\0'; + + break; + } + } + } + + // If the map is out of space, entry will be NULL. + if (entry_index == num_entries) + return num_entries; + +#ifndef NDEBUG + // Sanity check that the key only appears once. + int count = 0; + for (size_t i = 0; i < num_entries; ++i) { + if (strncmp(entries_[i].key, key, key_size) == 0) + ++count; + } + assert(count == 1); +#endif + + strncpy(entries_[entry_index].value, value, value_size); + entries_[entry_index].value[value_size - 1] = '\0'; + + return entry_index; + } + + // Sets a value for a key that has already been set with SetKeyValue(), using + // the index returned from that function. + void SetValueAtIndex(size_t index, const char* value) { + assert(index < num_entries); + if (index >= num_entries) + return; + + Entry* entry = &entries_[index]; + assert(entry->key[0] != '\0'); + + strncpy(entry->value, value, value_size); + entry->value[value_size - 1] = '\0'; + } + + // Given |key|, removes any associated value. |key| must not be NULL. If + // the key is not found, this is a noop. This invalidates the index + // returned by SetKeyValue(). + bool RemoveKey(const char* key) { + assert(key); + if (!key) + return false; + + return RemoveAtIndex(GetEntryIndexForKey(key)); + } + + // Removes a value and key using an index that was returned from + // SetKeyValue(). After a call to this function, the index is invalidated. + bool RemoveAtIndex(size_t index) { + if (index >= num_entries) + return false; + + entries_[index].key[0] = '\0'; + entries_[index].value[0] = '\0'; + return true; + } + + // Places a serialized version of the map into |map| and returns the size. + // Both of these should be passed to the deserializing constructor. Note that + // the serialized |map| is scoped to the lifetime of the non-serialized + // instance of this class. The |map| can be copied across IPC boundaries. + size_t Serialize(const SerializedNonAllocatingMap** map) const { + *map = reinterpret_cast<const SerializedNonAllocatingMap*>(entries_); + return sizeof(entries_); + } + + private: + size_t GetEntryIndexForKey(const char* key) const { + for (size_t i = 0; i < num_entries; ++i) { + if (strncmp(key, entries_[i].key, key_size) == 0) { + return i; + } + } + return num_entries; + } + + Entry entries_[NumEntries]; +}; + +// For historical reasons this specialized version is available with the same +// size factors as a previous implementation. +typedef NonAllocatingMap<256, 256, 64> SimpleStringDictionary; + +} // namespace google_breakpad + +#endif // COMMON_SIMPLE_STRING_DICTIONARY_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/simple_string_dictionary_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/simple_string_dictionary_unittest.cc new file mode 100644 index 0000000000..e7b8fd7632 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/simple_string_dictionary_unittest.cc @@ -0,0 +1,339 @@ +// Copyright (c) 2008, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include "breakpad_googletest_includes.h" +#include "common/simple_string_dictionary.h" + +namespace google_breakpad { + +TEST(NonAllocatingMapTest, Entry) { + typedef NonAllocatingMap<5, 9, 15> TestMap; + TestMap map; + + const TestMap::Entry* entry = TestMap::Iterator(map).Next(); + EXPECT_FALSE(entry); + + // Try setting a key/value and then verify. + map.SetKeyValue("key1", "value1"); + entry = TestMap::Iterator(map).Next(); + ASSERT_TRUE(entry); + EXPECT_STREQ(entry->key, "key1"); + EXPECT_STREQ(entry->value, "value1"); + + // Try setting a new value. + map.SetKeyValue("key1", "value3"); + EXPECT_STREQ(entry->value, "value3"); + + // Make sure the key didn't change. + EXPECT_STREQ(entry->key, "key1"); + + // Clear the entry and verify the key and value are empty strings. + map.RemoveKey("key1"); + EXPECT_FALSE(entry->is_active()); + EXPECT_EQ(strlen(entry->key), 0u); + EXPECT_EQ(strlen(entry->value), 0u); +} + +TEST(NonAllocatingMapTest, SimpleStringDictionary) { + // Make a new dictionary + SimpleStringDictionary dict; + + // Set three distinct values on three keys + dict.SetKeyValue("key1", "value1"); + dict.SetKeyValue("key2", "value2"); + dict.SetKeyValue("key3", "value3"); + + EXPECT_NE(dict.GetValueForKey("key1"), "value1"); + EXPECT_NE(dict.GetValueForKey("key2"), "value2"); + EXPECT_NE(dict.GetValueForKey("key3"), "value3"); + EXPECT_EQ(dict.GetCount(), 3u); + // try an unknown key + EXPECT_FALSE(dict.GetValueForKey("key4")); + + // Remove a key + dict.RemoveKey("key3"); + + // Now make sure it's not there anymore + EXPECT_FALSE(dict.GetValueForKey("key3")); + + // Remove by setting value to NULL + dict.SetKeyValue("key2", NULL); + + // Now make sure it's not there anymore + EXPECT_FALSE(dict.GetValueForKey("key2")); +} + +TEST(NonAllocatingMapTest, CopyAndAssign) { + NonAllocatingMap<10, 10, 10> map; + map.SetKeyValue("one", "a"); + map.SetKeyValue("two", "b"); + map.SetKeyValue("three", "c"); + map.RemoveKey("two"); + EXPECT_EQ(2u, map.GetCount()); + + // Test copy. + NonAllocatingMap<10, 10, 10> map_copy(map); + EXPECT_EQ(2u, map_copy.GetCount()); + EXPECT_STREQ("a", map_copy.GetValueForKey("one")); + EXPECT_STREQ("c", map_copy.GetValueForKey("three")); + map_copy.SetKeyValue("four", "d"); + EXPECT_STREQ("d", map_copy.GetValueForKey("four")); + EXPECT_FALSE(map.GetValueForKey("four")); + + // Test assign. + NonAllocatingMap<10, 10, 10> map_assign; + map_assign = map; + EXPECT_EQ(2u, map_assign.GetCount()); + EXPECT_STREQ("a", map_assign.GetValueForKey("one")); + EXPECT_STREQ("c", map_assign.GetValueForKey("three")); + map_assign.SetKeyValue("four", "d"); + EXPECT_STREQ("d", map_assign.GetValueForKey("four")); + EXPECT_FALSE(map.GetValueForKey("four")); + + map.RemoveKey("one"); + EXPECT_FALSE(map.GetValueForKey("one")); + EXPECT_STREQ("a", map_copy.GetValueForKey("one")); + EXPECT_STREQ("a", map_assign.GetValueForKey("one")); +} + +// Add a bunch of values to the dictionary, remove some entries in the middle, +// and then add more. +TEST(NonAllocatingMapTest, Iterator) { + SimpleStringDictionary* dict = new SimpleStringDictionary(); + ASSERT_TRUE(dict); + + char key[SimpleStringDictionary::key_size]; + char value[SimpleStringDictionary::value_size]; + + const int kDictionaryCapacity = SimpleStringDictionary::num_entries; + const int kPartitionIndex = kDictionaryCapacity - 5; + + // We assume at least this size in the tests below + ASSERT_GE(kDictionaryCapacity, 64); + + // We'll keep track of the number of key/value pairs we think should + // be in the dictionary + int expectedDictionarySize = 0; + + // Set a bunch of key/value pairs like key0/value0, key1/value1, ... + for (int i = 0; i < kPartitionIndex; ++i) { + sprintf(key, "key%d", i); + sprintf(value, "value%d", i); + dict->SetKeyValue(key, value); + } + expectedDictionarySize = kPartitionIndex; + + // set a couple of the keys twice (with the same value) - should be nop + dict->SetKeyValue("key2", "value2"); + dict->SetKeyValue("key4", "value4"); + dict->SetKeyValue("key15", "value15"); + + // Remove some random elements in the middle + dict->RemoveKey("key7"); + dict->RemoveKey("key18"); + dict->RemoveKey("key23"); + dict->RemoveKey("key31"); + expectedDictionarySize -= 4; // we just removed four key/value pairs + + // Set some more key/value pairs like key59/value59, key60/value60, ... + for (int i = kPartitionIndex; i < kDictionaryCapacity; ++i) { + sprintf(key, "key%d", i); + sprintf(value, "value%d", i); + dict->SetKeyValue(key, value); + } + expectedDictionarySize += kDictionaryCapacity - kPartitionIndex; + + // Now create an iterator on the dictionary + SimpleStringDictionary::Iterator iter(*dict); + + // We then verify that it iterates through exactly the number of + // key/value pairs we expect, and that they match one-for-one with what we + // would expect. The ordering of the iteration does not matter... + + // used to keep track of number of occurrences found for key/value pairs + int count[kDictionaryCapacity]; + memset(count, 0, sizeof(count)); + + int totalCount = 0; + + const SimpleStringDictionary::Entry* entry; + while ((entry = iter.Next())) { + totalCount++; + + // Extract keyNumber from a string of the form key<keyNumber> + int keyNumber; + sscanf(entry->key, "key%d", &keyNumber); + + // Extract valueNumber from a string of the form value<valueNumber> + int valueNumber; + sscanf(entry->value, "value%d", &valueNumber); + + // The value number should equal the key number since that's how we set them + EXPECT_EQ(keyNumber, valueNumber); + + // Key and value numbers should be in proper range: + // 0 <= keyNumber < kDictionaryCapacity + bool isKeyInGoodRange = + (keyNumber >= 0 && keyNumber < kDictionaryCapacity); + bool isValueInGoodRange = + (valueNumber >= 0 && valueNumber < kDictionaryCapacity); + EXPECT_TRUE(isKeyInGoodRange); + EXPECT_TRUE(isValueInGoodRange); + + if (isKeyInGoodRange && isValueInGoodRange) { + ++count[keyNumber]; + } + } + + // Make sure each of the key/value pairs showed up exactly one time, except + // for the ones which we removed. + for (size_t i = 0; i < kDictionaryCapacity; ++i) { + // Skip over key7, key18, key23, and key31, since we removed them + if (!(i == 7 || i == 18 || i == 23 || i == 31)) { + EXPECT_EQ(count[i], 1); + } + } + + // Make sure the number of iterations matches the expected dictionary size. + EXPECT_EQ(totalCount, expectedDictionarySize); +} + + +TEST(NonAllocatingMapTest, AddRemove) { + NonAllocatingMap<5, 7, 6> map; + map.SetKeyValue("rob", "ert"); + map.SetKeyValue("mike", "pink"); + map.SetKeyValue("mark", "allays"); + + EXPECT_EQ(3u, map.GetCount()); + EXPECT_STREQ("ert", map.GetValueForKey("rob")); + EXPECT_STREQ("pink", map.GetValueForKey("mike")); + EXPECT_STREQ("allays", map.GetValueForKey("mark")); + + map.RemoveKey("mike"); + + EXPECT_EQ(2u, map.GetCount()); + EXPECT_FALSE(map.GetValueForKey("mike")); + + map.SetKeyValue("mark", "mal"); + EXPECT_EQ(2u, map.GetCount()); + EXPECT_STREQ("mal", map.GetValueForKey("mark")); + + map.RemoveKey("mark"); + EXPECT_EQ(1u, map.GetCount()); + EXPECT_FALSE(map.GetValueForKey("mark")); +} + +TEST(NonAllocatingMapTest, Serialize) { + typedef NonAllocatingMap<4, 5, 7> TestMap; + TestMap map; + map.SetKeyValue("one", "abc"); + map.SetKeyValue("two", "def"); + map.SetKeyValue("tre", "hig"); + + EXPECT_STREQ("abc", map.GetValueForKey("one")); + EXPECT_STREQ("def", map.GetValueForKey("two")); + EXPECT_STREQ("hig", map.GetValueForKey("tre")); + + const SerializedNonAllocatingMap* serialized; + size_t size = map.Serialize(&serialized); + + SerializedNonAllocatingMap* serialized_copy = + reinterpret_cast<SerializedNonAllocatingMap*>(malloc(size)); + ASSERT_TRUE(serialized_copy); + memcpy(serialized_copy, serialized, size); + + TestMap deserialized(serialized_copy, size); + free(serialized_copy); + + EXPECT_EQ(3u, deserialized.GetCount()); + EXPECT_STREQ("abc", deserialized.GetValueForKey("one")); + EXPECT_STREQ("def", deserialized.GetValueForKey("two")); + EXPECT_STREQ("hig", deserialized.GetValueForKey("tre")); +} + +// Running out of space shouldn't crash. +TEST(NonAllocatingMapTest, OutOfSpace) { + NonAllocatingMap<3, 2, 2> map; + map.SetKeyValue("a", "1"); + map.SetKeyValue("b", "2"); + map.SetKeyValue("c", "3"); + EXPECT_EQ(2u, map.GetCount()); + EXPECT_FALSE(map.GetValueForKey("c")); +} + +TEST(NonAllocatingMapTest, ByIndex) { + NonAllocatingMap<10, 10, 3> map; + + size_t index1 = map.SetKeyValue("test", "one"); + EXPECT_TRUE(index1 >= 0 && index1 <= map.num_entries); + + size_t index2 = map.SetKeyValue("moo", "foo"); + EXPECT_TRUE(index2 >= 0 && index2 <= map.num_entries); + EXPECT_NE(index1, index2); + + size_t index3 = map.SetKeyValue("blob", "kebab"); + EXPECT_TRUE(index3 >= 0 && index3 <= map.num_entries); + EXPECT_NE(index2, index3); + + size_t index4 = map.SetKeyValue("nogo", "full"); + EXPECT_TRUE(index4 == map.num_entries); + + EXPECT_STREQ("one", map.GetValueForKey("test")); + EXPECT_STREQ("foo", map.GetValueForKey("moo")); + EXPECT_STREQ("kebab", map.GetValueForKey("blob")); + + map.SetValueAtIndex(index2, "booo"); + EXPECT_STREQ("booo", map.GetValueForKey("moo")); + + EXPECT_TRUE(map.RemoveAtIndex(index1)); + EXPECT_FALSE(map.GetValueForKey("test")); + + EXPECT_FALSE(map.RemoveAtIndex(map.num_entries)); + EXPECT_FALSE(map.RemoveAtIndex(9999)); +} + +#ifndef NDEBUG + +TEST(NonAllocatingMapTest, NullKey) { + NonAllocatingMap<4, 6, 6> map; + ASSERT_DEATH(map.SetKeyValue(NULL, "hello"), ""); + + map.SetKeyValue("hi", "there"); + ASSERT_DEATH(map.GetValueForKey(NULL), ""); + EXPECT_STREQ("there", map.GetValueForKey("hi")); + + ASSERT_DEATH(map.GetValueForKey(NULL), ""); + map.RemoveKey("hi"); + EXPECT_EQ(0u, map.GetCount()); +} + +#endif // !NDEBUG + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/solaris/dump_symbols.cc b/toolkit/crashreporter/google-breakpad/src/common/solaris/dump_symbols.cc new file mode 100644 index 0000000000..168d0b2879 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/solaris/dump_symbols.cc @@ -0,0 +1,681 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Author: Alfred Peng + +#include <demangle.h> +#include <fcntl.h> +#include <gelf.h> +#include <link.h> +#include <sys/mman.h> +#include <stab.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <functional> +#include <map> +#include <vector> + +#include "common/scoped_ptr.h" +#include "common/solaris/dump_symbols.h" +#include "common/solaris/file_id.h" +#include "common/solaris/guid_creator.h" + +// This namespace contains helper functions. +namespace { + +using std::make_pair; + +#if defined(_LP64) +typedef Elf64_Sym Elf_Sym; +#else +typedef Elf32_Sym Elf_Sym; +#endif + +// Symbol table entry from stabs. Sun CC specific. +struct slist { + // String table index. + unsigned int n_strx; + // Stab type. + unsigned char n_type; + char n_other; + short n_desc; + unsigned long n_value; +}; + +// Symbol table entry +struct SymbolEntry { + // Offset from the start of the file. + GElf_Addr offset; + // Function size. + GElf_Word size; +}; + +// Infomation of a line. +struct LineInfo { + // Offset from start of the function. + // Load from stab symbol. + GElf_Off rva_to_func; + // Offset from base of the loading binary. + GElf_Off rva_to_base; + // Size of the line. + // The first line: equals to rva_to_func. + // The other lines: the difference of rva_to_func of the line and + // rva_to_func of the previous N_SLINE. + uint32_t size; + // Line number. + uint32_t line_num; +}; + +// Information of a function. +struct FuncInfo { + // Name of the function. + const char *name; + // Offset from the base of the loading address. + GElf_Off rva_to_base; + // Virtual address of the function. + // Load from stab symbol. + GElf_Addr addr; + // Size of the function. + // Equal to rva_to_func of the last function line. + uint32_t size; + // Total size of stack parameters. + uint32_t stack_param_size; + // Line information array. + std::vector<struct LineInfo> line_info; +}; + +// Information of a source file. +struct SourceFileInfo { + // Name of the source file. + const char *name; + // Starting address of the source file. + GElf_Addr addr; + // Id of the source file. + int source_id; + // Functions information. + std::vector<struct FuncInfo> func_info; +}; + +struct CompareString { + bool operator()(const char *s1, const char *s2) const { + return strcmp(s1, s2) < 0; + } +}; + +typedef std::map<const char *, struct SymbolEntry *, CompareString> SymbolMap; + +// Information of a symbol table. +// This is the root of all types of symbol. +struct SymbolInfo { + std::vector<struct SourceFileInfo> source_file_info; + // Symbols information. + SymbolMap symbol_entries; +}; + +// Stab section name. +const char *kStabName = ".stab"; + +// Stab str section name. +const char *kStabStrName = ".stabstr"; + +// Symtab section name. +const char *kSymtabName = ".symtab"; + +// Strtab section name. +const char *kStrtabName = ".strtab"; + +// Default buffer lenght for demangle. +const int demangleLen = 20000; + +// Offset to the string table. +uint64_t stringOffset = 0; + +// Update the offset to the start of the string index of the next +// object module for every N_ENDM stabs. +inline void RecalculateOffset(struct slist* cur_list, char *stabstr) { + while ((--cur_list)->n_strx == 0) ; + stringOffset += cur_list->n_strx; + + char *temp = stabstr + stringOffset; + while (*temp != '\0') { + ++stringOffset; + ++temp; + } + // Skip the extra '\0' + ++stringOffset; +} + +// Demangle using demangle library on Solaris. +std::string Demangle(const char *mangled) { + int status = 0; + std::string str(mangled); + char *demangled = (char *)malloc(demangleLen); + + if (!demangled) { + fprintf(stderr, "no enough memory.\n"); + goto out; + } + + if ((status = cplus_demangle(mangled, demangled, demangleLen)) == + DEMANGLE_ESPACE) { + fprintf(stderr, "incorrect demangle.\n"); + goto out; + } + + str = demangled; + free(demangled); + +out: + return str; +} + +bool WriteFormat(int fd, const char *fmt, ...) { + va_list list; + char buffer[4096]; + ssize_t expected, written; + va_start(list, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, list); + expected = strlen(buffer); + written = write(fd, buffer, strlen(buffer)); + va_end(list); + return expected == written; +} + +bool IsValidElf(const GElf_Ehdr *elf_header) { + return memcmp(elf_header, ELFMAG, SELFMAG) == 0; +} + +static bool FindSectionByName(Elf *elf, const char *name, + int shstrndx, + GElf_Shdr *shdr) { + assert(name != NULL); + + if (strlen(name) == 0) + return false; + + Elf_Scn *scn = NULL; + + while ((scn = elf_nextscn(elf, scn)) != NULL) { + if (gelf_getshdr(scn, shdr) == (GElf_Shdr *)0) { + fprintf(stderr, "failed to read section header: %s\n", elf_errmsg(0)); + return false; + } + + const char *section_name = elf_strptr(elf, shstrndx, shdr->sh_name); + if (!section_name) { + fprintf(stderr, "Section name error: %s\n", elf_errmsg(-1)); + continue; + } + + if (strcmp(section_name, name) == 0) + return true; + } + + return false; +} + +// The parameter size is used for FPO-optimized code, and +// this is all tied up with the debugging data for Windows x86. +// Set it to 0 on Solaris. +int LoadStackParamSize(struct slist *list, + struct slist *list_end, + struct FuncInfo *func_info) { + struct slist *cur_list = list; + int step = 1; + while (cur_list < list_end && cur_list->n_type == N_PSYM) { + ++cur_list; + ++step; + } + + func_info->stack_param_size = 0; + return step; +} + +int LoadLineInfo(struct slist *list, + struct slist *list_end, + struct FuncInfo *func_info) { + struct slist *cur_list = list; + do { + // Skip non line information. + while (cur_list < list_end && cur_list->n_type != N_SLINE) { + // Only exit when got another function, or source file, or end stab. + if (cur_list->n_type == N_FUN || cur_list->n_type == N_SO || + cur_list->n_type == N_ENDM) { + return cur_list - list; + } + ++cur_list; + } + struct LineInfo line; + while (cur_list < list_end && cur_list->n_type == N_SLINE) { + line.rva_to_func = cur_list->n_value; + // n_desc is a signed short + line.line_num = (unsigned short)cur_list->n_desc; + func_info->line_info.push_back(line); + ++cur_list; + } + if (cur_list == list_end && cur_list->n_type == N_ENDM) + break; + } while (list < list_end); + + return cur_list - list; +} + +int LoadFuncSymbols(struct slist *list, + struct slist *list_end, + char *stabstr, + GElf_Word base, + struct SourceFileInfo *source_file_info) { + struct slist *cur_list = list; + assert(cur_list->n_type == N_SO); + ++cur_list; + + source_file_info->func_info.clear(); + while (cur_list < list_end) { + // Go until the function symbol. + while (cur_list < list_end && cur_list->n_type != N_FUN) { + if (cur_list->n_type == N_SO) { + return cur_list - list; + } + ++cur_list; + if (cur_list->n_type == N_ENDM) + RecalculateOffset(cur_list, stabstr); + continue; + } + while (cur_list->n_type == N_FUN) { + struct FuncInfo func_info; + memset(&func_info, 0, sizeof(func_info)); + func_info.name = stabstr + cur_list->n_strx + stringOffset; + // The n_value field is always 0 from stab generated by Sun CC. + // TODO(Alfred): Find the correct value. + func_info.addr = cur_list->n_value; + ++cur_list; + if (cur_list->n_type == N_ENDM) + RecalculateOffset(cur_list, stabstr); + if (cur_list->n_type != N_ESYM && cur_list->n_type != N_ISYM && + cur_list->n_type != N_FUN) { + // Stack parameter size. + cur_list += LoadStackParamSize(cur_list, list_end, &func_info); + // Line info. + cur_list += LoadLineInfo(cur_list, list_end, &func_info); + } + if (cur_list < list_end && cur_list->n_type == N_ENDM) + RecalculateOffset(cur_list, stabstr); + // Functions in this module should have address bigger than the module + // starting address. + // + // These two values are always 0 with Sun CC. + // TODO(Alfred): Get the correct value or remove the condition statement. + if (func_info.addr >= source_file_info->addr) { + source_file_info->func_info.push_back(func_info); + } + } + } + return cur_list - list; +} + +// Compute size and rva information based on symbols loaded from stab section. +bool ComputeSizeAndRVA(struct SymbolInfo *symbols) { + std::vector<struct SourceFileInfo> *sorted_files = + &(symbols->source_file_info); + SymbolMap *symbol_entries = &(symbols->symbol_entries); + for (size_t i = 0; i < sorted_files->size(); ++i) { + struct SourceFileInfo &source_file = (*sorted_files)[i]; + std::vector<struct FuncInfo> *sorted_functions = &(source_file.func_info); + int func_size = sorted_functions->size(); + + for (size_t j = 0; j < func_size; ++j) { + struct FuncInfo &func_info = (*sorted_functions)[j]; + int line_count = func_info.line_info.size(); + + // Discard the ending part of the name. + std::string func_name(func_info.name); + std::string::size_type last_colon = func_name.find_first_of(':'); + if (last_colon != std::string::npos) + func_name = func_name.substr(0, last_colon); + + // Fine the symbol offset from the loading address and size by name. + SymbolMap::const_iterator it = symbol_entries->find(func_name.c_str()); + if (it->second) { + func_info.rva_to_base = it->second->offset; + func_info.size = (line_count == 0) ? 0 : it->second->size; + } else { + func_info.rva_to_base = 0; + func_info.size = 0; + } + + // Compute function and line size. + for (size_t k = 0; k < line_count; ++k) { + struct LineInfo &line_info = func_info.line_info[k]; + + line_info.rva_to_base = line_info.rva_to_func + func_info.rva_to_base; + if (k == line_count - 1) { + line_info.size = func_info.size - line_info.rva_to_func; + } else { + struct LineInfo &next_line = func_info.line_info[k + 1]; + line_info.size = next_line.rva_to_func - line_info.rva_to_func; + } + } // for each line. + } // for each function. + } // for each source file. + for (SymbolMap::iterator it = symbol_entries->begin(); + it != symbol_entries->end(); ++it) { + free(it->second); + } + return true; +} + +bool LoadAllSymbols(const GElf_Shdr *stab_section, + const GElf_Shdr *stabstr_section, + GElf_Word base, + struct SymbolInfo *symbols) { + if (stab_section == NULL || stabstr_section == NULL) + return false; + + char *stabstr = + reinterpret_cast<char *>(stabstr_section->sh_offset + base); + struct slist *lists = + reinterpret_cast<struct slist *>(stab_section->sh_offset + base); + int nstab = stab_section->sh_size / sizeof(struct slist); + int source_id = 0; + + // First pass, load all symbols from the object file. + for (int i = 0; i < nstab; ) { + int step = 1; + struct slist *cur_list = lists + i; + if (cur_list->n_type == N_SO) { + // FUNC <address> <size> <param_stack_size> <function> + struct SourceFileInfo source_file_info; + source_file_info.name = stabstr + cur_list->n_strx + stringOffset; + // The n_value field is always 0 from stab generated by Sun CC. + // TODO(Alfred): Find the correct value. + source_file_info.addr = cur_list->n_value; + if (strchr(source_file_info.name, '.')) + source_file_info.source_id = source_id++; + else + source_file_info.source_id = -1; + step = LoadFuncSymbols(cur_list, lists + nstab - 1, stabstr, + base, &source_file_info); + symbols->source_file_info.push_back(source_file_info); + } + i += step; + } + // Second pass, compute the size of functions and lines. + return ComputeSizeAndRVA(symbols); +} + +bool LoadSymbols(Elf *elf, GElf_Ehdr *elf_header, struct SymbolInfo *symbols, + void *obj_base) { + GElf_Word base = reinterpret_cast<GElf_Word>(obj_base); + + const GElf_Shdr *sections = + reinterpret_cast<GElf_Shdr *>(elf_header->e_shoff + base); + GElf_Shdr stab_section; + if (!FindSectionByName(elf, kStabName, elf_header->e_shstrndx, + &stab_section)) { + fprintf(stderr, "Stab section not found.\n"); + return false; + } + GElf_Shdr stabstr_section; + if (!FindSectionByName(elf, kStabStrName, elf_header->e_shstrndx, + &stabstr_section)) { + fprintf(stderr, "Stabstr section not found.\n"); + return false; + } + GElf_Shdr symtab_section; + if (!FindSectionByName(elf, kSymtabName, elf_header->e_shstrndx, + &symtab_section)) { + fprintf(stderr, "Symtab section not found.\n"); + return false; + } + GElf_Shdr strtab_section; + if (!FindSectionByName(elf, kStrtabName, elf_header->e_shstrndx, + &strtab_section)) { + fprintf(stderr, "Strtab section not found.\n"); + return false; + } + + Elf_Sym *symbol = (Elf_Sym *)((char *)base + symtab_section.sh_offset); + for (int i = 0; i < symtab_section.sh_size/symtab_section.sh_entsize; ++i) { + struct SymbolEntry *symbol_entry = + (struct SymbolEntry *)malloc(sizeof(struct SymbolEntry)); + const char *name = reinterpret_cast<char *>( + strtab_section.sh_offset + (GElf_Word)base + symbol->st_name); + symbol_entry->offset = symbol->st_value; + symbol_entry->size = symbol->st_size; + symbols->symbol_entries.insert(make_pair(name, symbol_entry)); + ++symbol; + } + + + // Load symbols. + return LoadAllSymbols(&stab_section, &stabstr_section, base, symbols); +} + +bool WriteModuleInfo(int fd, GElf_Half arch, const std::string &obj_file) { + const char *arch_name = NULL; + if (arch == EM_386) + arch_name = "x86"; + else if (arch == EM_X86_64) + arch_name = "x86_64"; + else if (arch == EM_SPARC32PLUS) + arch_name = "SPARC_32+"; + else { + printf("Please add more ARCH support\n"); + return false; + } + + unsigned char identifier[16]; + google_breakpad::FileID file_id(obj_file.c_str()); + if (file_id.ElfFileIdentifier(identifier)) { + char identifier_str[40]; + file_id.ConvertIdentifierToString(identifier, + identifier_str, sizeof(identifier_str)); + std::string filename = obj_file; + size_t slash_pos = obj_file.find_last_of("/"); + if (slash_pos != std::string::npos) + filename = obj_file.substr(slash_pos + 1); + return WriteFormat(fd, "MODULE solaris %s %s %s\n", arch_name, + identifier_str, filename.c_str()); + } + return false; +} + +bool WriteSourceFileInfo(int fd, const struct SymbolInfo &symbols) { + for (size_t i = 0; i < symbols.source_file_info.size(); ++i) { + if (symbols.source_file_info[i].source_id != -1) { + const char *name = symbols.source_file_info[i].name; + if (!WriteFormat(fd, "FILE %d %s\n", + symbols.source_file_info[i].source_id, name)) + return false; + } + } + return true; +} + +bool WriteOneFunction(int fd, int source_id, + const struct FuncInfo &func_info){ + // Discard the ending part of the name. + std::string func_name(func_info.name); + std::string::size_type last_colon = func_name.find_last_of(':'); + if (last_colon != std::string::npos) + func_name = func_name.substr(0, last_colon); + func_name = Demangle(func_name.c_str()); + + if (func_info.size <= 0) + return true; + + // rva_to_base could be unsigned long(32 bit) or unsigned long long(64 bit). + if (WriteFormat(fd, "FUNC %llx %x %d %s\n", + (long long)func_info.rva_to_base, + func_info.size, + func_info.stack_param_size, + func_name.c_str())) { + for (size_t i = 0; i < func_info.line_info.size(); ++i) { + const struct LineInfo &line_info = func_info.line_info[i]; + if (line_info.line_num == 0) + return true; + if (!WriteFormat(fd, "%llx %x %d %d\n", + (long long)line_info.rva_to_base, + line_info.size, + line_info.line_num, + source_id)) + return false; + } + return true; + } + return false; +} + +bool WriteFunctionInfo(int fd, const struct SymbolInfo &symbols) { + for (size_t i = 0; i < symbols.source_file_info.size(); ++i) { + const struct SourceFileInfo &file_info = symbols.source_file_info[i]; + for (size_t j = 0; j < file_info.func_info.size(); ++j) { + const struct FuncInfo &func_info = file_info.func_info[j]; + if (!WriteOneFunction(fd, file_info.source_id, func_info)) + return false; + } + } + return true; +} + +bool DumpStabSymbols(int fd, const struct SymbolInfo &symbols) { + return WriteSourceFileInfo(fd, symbols) && + WriteFunctionInfo(fd, symbols); +} + +// +// FDWrapper +// +// Wrapper class to make sure opened file is closed. +// +class FDWrapper { + public: + explicit FDWrapper(int fd) : + fd_(fd) { + } + ~FDWrapper() { + if (fd_ != -1) + close(fd_); + } + int get() { + return fd_; + } + int release() { + int fd = fd_; + fd_ = -1; + return fd; + } + private: + int fd_; +}; + +// +// MmapWrapper +// +// Wrapper class to make sure mapped regions are unmapped. +// +class MmapWrapper { + public: + MmapWrapper(void *mapped_address, size_t mapped_size) : + base_(mapped_address), size_(mapped_size) { + } + ~MmapWrapper() { + if (base_ != NULL) { + assert(size_ > 0); + munmap((char *)base_, size_); + } + } + void release() { + base_ = NULL; + size_ = 0; + } + + private: + void *base_; + size_t size_; +}; + +} // namespace + +namespace google_breakpad { + +class AutoElfEnder { + public: + AutoElfEnder(Elf *elf) : elf_(elf) {} + ~AutoElfEnder() { if (elf_) elf_end(elf_); } + private: + Elf *elf_; +}; + + +bool DumpSymbols::WriteSymbolFile(const std::string &obj_file, int sym_fd) { + if (elf_version(EV_CURRENT) == EV_NONE) { + fprintf(stderr, "elf_version() failed: %s\n", elf_errmsg(0)); + return false; + } + + int obj_fd = open(obj_file.c_str(), O_RDONLY); + if (obj_fd < 0) + return false; + FDWrapper obj_fd_wrapper(obj_fd); + struct stat st; + if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) + return false; + void *obj_base = mmap(NULL, st.st_size, + PROT_READ, MAP_PRIVATE, obj_fd, 0); + if (obj_base == MAP_FAILED) + return false; + MmapWrapper map_wrapper(obj_base, st.st_size); + GElf_Ehdr elf_header; + Elf *elf = elf_begin(obj_fd, ELF_C_READ, NULL); + AutoElfEnder elfEnder(elf); + + if (gelf_getehdr(elf, &elf_header) == (GElf_Ehdr *)NULL) { + fprintf(stderr, "failed to read elf header: %s\n", elf_errmsg(-1)); + return false; + } + + if (!IsValidElf(&elf_header)) { + fprintf(stderr, "header magic doesn't match\n"); + return false; + } + struct SymbolInfo symbols; + if (!LoadSymbols(elf, &elf_header, &symbols, obj_base)) + return false; + // Write to symbol file. + if (WriteModuleInfo(sym_fd, elf_header.e_machine, obj_file) && + DumpStabSymbols(sym_fd, symbols)) + return true; + + return false; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/solaris/dump_symbols.h b/toolkit/crashreporter/google-breakpad/src/common/solaris/dump_symbols.h new file mode 100644 index 0000000000..7f4baadcfc --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/solaris/dump_symbols.h @@ -0,0 +1,49 @@ +// Copyright (c) 2007, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. +// +// dump_symbols.cc: Implements a Solaris stab debugging format dumper. +// +// Author: Alfred Peng + +#ifndef COMMON_SOLARIS_DUMP_SYMBOLS_H__ +#define COMMON_SOLARIS_DUMP_SYMBOLS_H__ + +#include <string> + +namespace google_breakpad { + +class DumpSymbols { + public: + bool WriteSymbolFile(const std::string &obj_file, + int sym_fd); +}; + +} // namespace google_breakpad + +#endif // COMMON_SOLARIS_DUMP_SYMBOLS_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/solaris/file_id.cc b/toolkit/crashreporter/google-breakpad/src/common/solaris/file_id.cc new file mode 100644 index 0000000000..643a14629d --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/solaris/file_id.cc @@ -0,0 +1,197 @@ +// Copyright (c) 2007, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// file_id.cc: Return a unique identifier for a file +// +// See file_id.h for documentation +// +// Author: Alfred Peng + +#include <elf.h> +#include <fcntl.h> +#include <gelf.h> +#include <sys/mman.h> +#include <sys/ksyms.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <cassert> +#include <cstdio> + +#include "common/md5.h" +#include "common/solaris/file_id.h" +#include "common/solaris/message_output.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +class AutoElfEnder { + public: + AutoElfEnder(Elf *elf) : elf_(elf) {} + ~AutoElfEnder() { if (elf_) elf_end(elf_); } + private: + Elf *elf_; +}; + +// Find the text section in elf object file. +// Return the section start address and the size. +static bool FindElfTextSection(int fd, const void *elf_base, + const void **text_start, + int *text_size) { + assert(text_start); + assert(text_size); + + *text_start = NULL; + *text_size = 0; + + if (elf_version(EV_CURRENT) == EV_NONE) { + print_message2(2, "elf_version() failed: %s\n", elf_errmsg(0)); + return false; + } + + GElf_Ehdr elf_header; + lseek(fd, 0L, 0); + Elf *elf = elf_begin(fd, ELF_C_READ, NULL); + AutoElfEnder elfEnder(elf); + + if (gelf_getehdr(elf, &elf_header) == (GElf_Ehdr *)NULL) { + print_message2(2, "failed to read elf header: %s\n", elf_errmsg(-1)); + return false; + } + + if (elf_header.e_ident[EI_MAG0] != ELFMAG0 || + elf_header.e_ident[EI_MAG1] != ELFMAG1 || + elf_header.e_ident[EI_MAG2] != ELFMAG2 || + elf_header.e_ident[EI_MAG3] != ELFMAG3) { + print_message1(2, "header magic doesn't match\n"); + return false; + } + + static const char kTextSectionName[] = ".text"; + const GElf_Shdr *text_section = NULL; + Elf_Scn *scn = NULL; + GElf_Shdr shdr; + + while ((scn = elf_nextscn(elf, scn)) != NULL) { + if (gelf_getshdr(scn, &shdr) == (GElf_Shdr *)0) { + print_message2(2, "failed to read section header: %s\n", elf_errmsg(0)); + return false; + } + + if (shdr.sh_type == SHT_PROGBITS) { + const char *section_name = elf_strptr(elf, elf_header.e_shstrndx, + shdr.sh_name); + if (!section_name) { + print_message2(2, "Section name error: %s\n", elf_errmsg(-1)); + continue; + } + + if (strcmp(section_name, kTextSectionName) == 0) { + text_section = &shdr; + break; + } + } + } + if (text_section != NULL && text_section->sh_size > 0) { + *text_start = (char *)elf_base + text_section->sh_offset; + *text_size = text_section->sh_size; + return true; + } + + return false; +} + +FileID::FileID(const char *path) { + strcpy(path_, path); +} + +class AutoCloser { + public: + AutoCloser(int fd) : fd_(fd) {} + ~AutoCloser() { if (fd_) close(fd_); } + private: + int fd_; +}; + +bool FileID::ElfFileIdentifier(unsigned char identifier[16]) { + int fd = 0; + if ((fd = open(path_, O_RDONLY)) < 0) + return false; + + AutoCloser autocloser(fd); + struct stat st; + if (fstat(fd, &st) != 0 || st.st_size <= 0) + return false; + + void *base = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (base == MAP_FAILED) + return false; + + bool success = false; + const void *text_section = NULL; + int text_size = 0; + + if (FindElfTextSection(fd, base, &text_section, &text_size)) { + MD5Context md5; + MD5Init(&md5); + MD5Update(&md5, (const unsigned char *)text_section, text_size); + MD5Final(identifier, &md5); + success = true; + } + + munmap((char *)base, st.st_size); + return success; +} + +// static +bool FileID::ConvertIdentifierToString(const unsigned char identifier[16], + char *buffer, int buffer_length) { + if (buffer_length < 34) + return false; + + int buffer_idx = 0; + for (int idx = 0; idx < 16; ++idx) { + int hi = (identifier[idx] >> 4) & 0x0F; + int lo = (identifier[idx]) & 0x0F; + + buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi; + buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo; + } + + // Add an extra "0" by the end. + buffer[buffer_idx++] = '0'; + + // NULL terminate + buffer[buffer_idx] = 0; + + return true; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/solaris/file_id.h b/toolkit/crashreporter/google-breakpad/src/common/solaris/file_id.h new file mode 100644 index 0000000000..375e857512 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/solaris/file_id.h @@ -0,0 +1,66 @@ +// Copyright (c) 2007, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// file_id.h: Return a unique identifier for a file +// +// Author: Alfred Peng + +#ifndef COMMON_SOLARIS_FILE_ID_H__ +#define COMMON_SOLARIS_FILE_ID_H__ + +#include <limits.h> + +namespace google_breakpad { + +class FileID { + public: + FileID(const char *path); + ~FileID() {}; + + // Load the identifier for the elf file path specified in the constructor into + // |identifier|. Return false if the identifier could not be created for the + // file. + // The current implementation will return the MD5 hash of the file's bytes. + bool ElfFileIdentifier(unsigned char identifier[16]); + + // Convert the |identifier| data to a NULL terminated string. The string will + // be formatted as a MDCVInfoPDB70 struct. + // The |buffer| should be at least 34 bytes long to receive all of the data + // and termination. Shorter buffers will return false. + static bool ConvertIdentifierToString(const unsigned char identifier[16], + char *buffer, int buffer_length); + + private: + // Storage for the path specified + char path_[PATH_MAX]; +}; + +} // namespace google_breakpad + +#endif // COMMON_SOLARIS_FILE_ID_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/solaris/guid_creator.cc b/toolkit/crashreporter/google-breakpad/src/common/solaris/guid_creator.cc new file mode 100644 index 0000000000..e9e6c6f5d2 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/solaris/guid_creator.cc @@ -0,0 +1,84 @@ +// Copyright (c) 2007, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Author: Alfred Peng + +#include <cassert> +#include <ctime> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "common/solaris/guid_creator.h" + +// +// GUIDGenerator +// +// This class is used to generate random GUID. +// Currently use random number to generate a GUID. This should be OK since +// we don't expect crash to happen very offen. +// +class GUIDGenerator { + public: + GUIDGenerator() { + srandom(time(NULL)); + } + + bool CreateGUID(GUID *guid) const { + guid->data1 = random(); + guid->data2 = (uint16_t)(random()); + guid->data3 = (uint16_t)(random()); + *reinterpret_cast<uint32_t*>(&guid->data4[0]) = random(); + *reinterpret_cast<uint32_t*>(&guid->data4[4]) = random(); + return true; + } +}; + +// Guid generator. +const GUIDGenerator kGuidGenerator; + +bool CreateGUID(GUID *guid) { + return kGuidGenerator.CreateGUID(guid); +} + +// Parse guid to string. +bool GUIDToString(const GUID *guid, char *buf, int buf_len) { + // Should allow more space the the max length of GUID. + assert(buf_len > kGUIDStringLength); + int num = snprintf(buf, buf_len, kGUIDFormatString, + guid->data1, guid->data2, guid->data3, + *reinterpret_cast<const uint32_t *>(&(guid->data4[0])), + *reinterpret_cast<const uint32_t *>(&(guid->data4[4]))); + if (num != kGUIDStringLength) + return false; + + buf[num] = '\0'; + return true; +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/solaris/guid_creator.h b/toolkit/crashreporter/google-breakpad/src/common/solaris/guid_creator.h new file mode 100644 index 0000000000..4aee3a1c24 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/solaris/guid_creator.h @@ -0,0 +1,50 @@ +// Copyright (c) 2007, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Author: Alfred Peng + +#ifndef COMMON_SOLARIS_GUID_CREATOR_H__ +#define COMMON_SOLARIS_GUID_CREATOR_H__ + +#include "google_breakpad/common/minidump_format.h" + +typedef MDGUID GUID; + +// Format string for parsing GUID. +#define kGUIDFormatString "%08x-%04x-%04x-%08x-%08x" +// Length of GUID string. Don't count the ending '\0'. +#define kGUIDStringLength 36 + +// Create a guid. +bool CreateGUID(GUID *guid); + +// Get the string from guid. +bool GUIDToString(const GUID *guid, char *buf, int buf_len); + +#endif // COMMON_SOLARIS_GUID_CREATOR_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/solaris/message_output.h b/toolkit/crashreporter/google-breakpad/src/common/solaris/message_output.h new file mode 100644 index 0000000000..3e3b1d4652 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/solaris/message_output.h @@ -0,0 +1,54 @@ +// Copyright (c) 2007, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Author: Alfred Peng + +#ifndef COMMON_SOLARIS_MESSAGE_OUTPUT_H__ +#define COMMON_SOLARIS_MESSAGE_OUTPUT_H__ + +namespace google_breakpad { + +const int MESSAGE_MAX = 1000; + +// Message output macros. +// snprintf doesn't operate heap on Solaris, while printf and fprintf do. +// Use snprintf here to avoid heap allocation. +#define print_message1(std, message) \ + char buffer[MESSAGE_MAX]; \ + int len = snprintf(buffer, MESSAGE_MAX, message); \ + write(std, buffer, len) + +#define print_message2(std, message, para) \ + char buffer[MESSAGE_MAX]; \ + int len = snprintf(buffer, MESSAGE_MAX, message, para); \ + write(std, buffer, len); + +} // namespace google_breakpad + +#endif // COMMON_SOLARIS_MESSAGE_OUTPUT_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/stabs_reader.cc b/toolkit/crashreporter/google-breakpad/src/common/stabs_reader.cc new file mode 100644 index 0000000000..6019fc7ee2 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/stabs_reader.cc @@ -0,0 +1,315 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// This file implements the google_breakpad::StabsReader class. +// See stabs_reader.h. + +#include "common/stabs_reader.h" + +#include <assert.h> +#include <stab.h> +#include <string.h> + +#include <string> + +#include "common/using_std_string.h" + +using std::vector; + +namespace google_breakpad { + +StabsReader::EntryIterator::EntryIterator(const ByteBuffer *buffer, + bool big_endian, size_t value_size) + : value_size_(value_size), cursor_(buffer, big_endian) { + // Actually, we could handle weird sizes just fine, but they're + // probably mistakes --- expressed in bits, say. + assert(value_size == 4 || value_size == 8); + entry_.index = 0; + Fetch(); +} + +void StabsReader::EntryIterator::Fetch() { + cursor_ + .Read(4, false, &entry_.name_offset) + .Read(1, false, &entry_.type) + .Read(1, false, &entry_.other) + .Read(2, false, &entry_.descriptor) + .Read(value_size_, false, &entry_.value); + entry_.at_end = !cursor_; +} + +StabsReader::StabsReader(const uint8_t *stab, size_t stab_size, + const uint8_t *stabstr, size_t stabstr_size, + bool big_endian, size_t value_size, bool unitized, + StabsHandler *handler) + : entries_(stab, stab_size), + strings_(stabstr, stabstr_size), + iterator_(&entries_, big_endian, value_size), + unitized_(unitized), + handler_(handler), + string_offset_(0), + next_cu_string_offset_(0), + current_source_file_(NULL) { } + +const char *StabsReader::SymbolString() { + ptrdiff_t offset = string_offset_ + iterator_->name_offset; + if (offset < 0 || (size_t) offset >= strings_.Size()) { + handler_->Warning("symbol %d: name offset outside the string section\n", + iterator_->index); + // Return our null string, to keep our promise about all names being + // taken from the string section. + offset = 0; + } + return reinterpret_cast<const char *>(strings_.start + offset); +} + +bool StabsReader::Process() { + while (!iterator_->at_end) { + if (iterator_->type == N_SO) { + if (! ProcessCompilationUnit()) + return false; + } else if (iterator_->type == N_UNDF && unitized_) { + // In unitized STABS (including Linux STABS, and pretty much anything + // else that puts STABS data in sections), at the head of each + // compilation unit's entries there is an N_UNDF stab giving the + // number of symbols in the compilation unit, and the number of bytes + // that compilation unit's strings take up in the .stabstr section. + // Each CU's strings are separate; the n_strx values are offsets + // within the current CU's portion of the .stabstr section. + // + // As an optimization, the GNU linker combines all the + // compilation units into one, with a single N_UNDF at the + // beginning. However, other linkers, like Gold, do not perform + // this optimization. + string_offset_ = next_cu_string_offset_; + next_cu_string_offset_ = iterator_->value; + ++iterator_; + } +#if defined(HAVE_MACH_O_NLIST_H) + // Export symbols in Mach-O binaries look like this. + // This is necessary in order to be able to dump symbols + // from OS X system libraries. + else if ((iterator_->type & N_STAB) == 0 && + (iterator_->type & N_TYPE) == N_SECT) { + ProcessExtern(); + } +#endif + else { + ++iterator_; + } + } + return true; +} + +bool StabsReader::ProcessCompilationUnit() { + assert(!iterator_->at_end && iterator_->type == N_SO); + + // There may be an N_SO entry whose name ends with a slash, + // indicating the directory in which the compilation occurred. + // The build directory defaults to NULL. + const char *build_directory = NULL; + { + const char *name = SymbolString(); + if (name[0] && name[strlen(name) - 1] == '/') { + build_directory = name; + ++iterator_; + } + } + + // We expect to see an N_SO entry with a filename next, indicating + // the start of the compilation unit. + { + if (iterator_->at_end || iterator_->type != N_SO) + return true; + const char *name = SymbolString(); + if (name[0] == '\0') { + // This seems to be a stray end-of-compilation-unit marker; + // consume it, but don't report the end, since we didn't see a + // beginning. + ++iterator_; + return true; + } + current_source_file_ = name; + } + + if (! handler_->StartCompilationUnit(current_source_file_, + iterator_->value, + build_directory)) + return false; + + ++iterator_; + + // The STABS documentation says that some compilers may emit + // additional N_SO entries with names immediately following the + // first, and that they should be ignored. However, the original + // Breakpad STABS reader doesn't ignore them, so we won't either. + + // Process the body of the compilation unit, up to the next N_SO. + while (!iterator_->at_end && iterator_->type != N_SO) { + if (iterator_->type == N_FUN) { + if (! ProcessFunction()) + return false; + } else if (iterator_->type == N_SLINE) { + // Mac OS X STABS place SLINE records before functions. + Line line; + // The value of an N_SLINE entry that appears outside a function is + // the absolute address of the line. + line.address = iterator_->value; + line.filename = current_source_file_; + // The n_desc of a N_SLINE entry is the line number. It's a + // signed 16-bit field; line numbers from 32768 to 65535 are + // stored as n-65536. + line.number = (uint16_t) iterator_->descriptor; + queued_lines_.push_back(line); + ++iterator_; + } else if (iterator_->type == N_SOL) { + current_source_file_ = SymbolString(); + ++iterator_; + } else { + // Ignore anything else. + ++iterator_; + } + } + + // An N_SO with an empty name indicates the end of the compilation + // unit. Default to zero. + uint64_t ending_address = 0; + if (!iterator_->at_end) { + assert(iterator_->type == N_SO); + const char *name = SymbolString(); + if (name[0] == '\0') { + ending_address = iterator_->value; + ++iterator_; + } + } + + if (! handler_->EndCompilationUnit(ending_address)) + return false; + + queued_lines_.clear(); + + return true; +} + +bool StabsReader::ProcessFunction() { + assert(!iterator_->at_end && iterator_->type == N_FUN); + + uint64_t function_address = iterator_->value; + // The STABS string for an N_FUN entry is the name of the function, + // followed by a colon, followed by type information for the + // function. We want to pass the name alone to StartFunction. + const char *stab_string = SymbolString(); + const char *name_end = strchr(stab_string, ':'); + if (! name_end) + name_end = stab_string + strlen(stab_string); + string name(stab_string, name_end - stab_string); + if (! handler_->StartFunction(name, function_address)) + return false; + ++iterator_; + + // If there were any SLINE records given before the function, report them now. + for (vector<Line>::const_iterator it = queued_lines_.begin(); + it != queued_lines_.end(); it++) { + if (!handler_->Line(it->address, it->filename, it->number)) + return false; + } + queued_lines_.clear(); + + while (!iterator_->at_end) { + if (iterator_->type == N_SO || iterator_->type == N_FUN) + break; + else if (iterator_->type == N_SLINE) { + // The value of an N_SLINE entry is the offset of the line from + // the function's start address. + uint64_t line_address = function_address + iterator_->value; + // The n_desc of a N_SLINE entry is the line number. It's a + // signed 16-bit field; line numbers from 32768 to 65535 are + // stored as n-65536. + uint16_t line_number = iterator_->descriptor; + if (! handler_->Line(line_address, current_source_file_, line_number)) + return false; + ++iterator_; + } else if (iterator_->type == N_SOL) { + current_source_file_ = SymbolString(); + ++iterator_; + } else + // Ignore anything else. + ++iterator_; + } + + // We've reached the end of the function. See if we can figure out its + // ending address. + uint64_t ending_address = 0; + if (!iterator_->at_end) { + assert(iterator_->type == N_SO || iterator_->type == N_FUN); + if (iterator_->type == N_FUN) { + const char *symbol_name = SymbolString(); + if (symbol_name[0] == '\0') { + // An N_FUN entry with no name is a terminator for this function; + // its value is the function's size. + ending_address = function_address + iterator_->value; + ++iterator_; + } else { + // An N_FUN entry with a name is the next function, and we can take + // its value as our ending address. Don't advance the iterator, as + // we'll use this symbol to start the next function as well. + ending_address = iterator_->value; + } + } else { + // An N_SO entry could be an end-of-compilation-unit marker, or the + // start of the next compilation unit, but in either case, its value + // is our ending address. We don't advance the iterator; + // ProcessCompilationUnit will decide what to do with this symbol. + ending_address = iterator_->value; + } + } + + if (! handler_->EndFunction(ending_address)) + return false; + + return true; +} + +bool StabsReader::ProcessExtern() { +#if defined(HAVE_MACH_O_NLIST_H) + assert(!iterator_->at_end && + (iterator_->type & N_STAB) == 0 && + (iterator_->type & N_TYPE) == N_SECT); +#endif + + // TODO(mark): only do symbols in the text section? + if (!handler_->Extern(SymbolString(), iterator_->value)) + return false; + + ++iterator_; + return true; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/stabs_reader.h b/toolkit/crashreporter/google-breakpad/src/common/stabs_reader.h new file mode 100644 index 0000000000..98ee2dd53b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/stabs_reader.h @@ -0,0 +1,325 @@ +// -*- mode: c++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// stabs_reader.h: Define StabsReader, a parser for STABS debugging +// information. A description of the STABS debugging format can be +// found at: +// +// http://sourceware.org/gdb/current/onlinedocs/stabs_toc.html +// +// The comments here assume you understand the format. +// +// This parser can handle big-endian and little-endian data, and the symbol +// values may be either 32 or 64 bits long. It handles both STABS in +// sections (as used on Linux) and STABS appearing directly in an +// a.out-like symbol table (as used in Darwin OS X Mach-O files). + +#ifndef COMMON_STABS_READER_H__ +#define COMMON_STABS_READER_H__ + +#include <stddef.h> +#include <stdint.h> + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_MACH_O_NLIST_H +#include <mach-o/nlist.h> +#elif defined(HAVE_A_OUT_H) +#include <a.out.h> +#endif + +#include <string> +#include <vector> + +#include "common/byte_cursor.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +class StabsHandler; + +class StabsReader { + public: + // Create a reader for the STABS debug information whose .stab section is + // being traversed by ITERATOR, and whose .stabstr section is referred to + // by STRINGS. The reader will call the member functions of HANDLER to + // report the information it finds, when the reader's 'Process' member + // function is called. + // + // BIG_ENDIAN should be true if the entries in the .stab section are in + // big-endian form, or false if they are in little-endian form. + // + // VALUE_SIZE should be either 4 or 8, indicating the size of the 'value' + // field in each entry in bytes. + // + // UNITIZED should be true if the STABS data is stored in units with + // N_UNDF headers. This is usually the case for STABS stored in sections, + // like .stab/.stabstr, and usually not the case for STABS stored in the + // actual symbol table; UNITIZED should be true when parsing Linux stabs, + // false when parsing Mac OS X STABS. For details, see: + // http://sourceware.org/gdb/current/onlinedocs/stabs/Stab-Section-Basics.html + // + // Note that, in ELF, the .stabstr section should be found using the + // 'sh_link' field of the .stab section header, not by name. + StabsReader(const uint8_t *stab, size_t stab_size, + const uint8_t *stabstr, size_t stabstr_size, + bool big_endian, size_t value_size, bool unitized, + StabsHandler *handler); + + // Process the STABS data, calling the handler's member functions to + // report what we find. While the handler functions return true, + // continue to process until we reach the end of the section. If we + // processed the entire section and all handlers returned true, + // return true. If any handler returned false, return false. + // + // This is only meant to be called once per StabsReader instance; + // resuming a prior processing pass that stopped abruptly isn't supported. + bool Process(); + + private: + + // An class for walking arrays of STABS entries. This isolates the main + // STABS reader from the exact format (size; endianness) of the entries + // themselves. + class EntryIterator { + public: + // The contents of a STABS entry, adjusted for the host's endianness, + // word size, 'struct nlist' layout, and so on. + struct Entry { + // True if this iterator has reached the end of the entry array. When + // this is set, the other members of this structure are not valid. + bool at_end; + + // The number of this entry within the list. + size_t index; + + // The current entry's name offset. This is the offset within the + // current compilation unit's strings, as establish by the N_UNDF entries. + size_t name_offset; + + // The current entry's type, 'other' field, descriptor, and value. + unsigned char type; + unsigned char other; + short descriptor; + uint64_t value; + }; + + // Create a EntryIterator walking the entries in BUFFER. Treat the + // entries as big-endian if BIG_ENDIAN is true, as little-endian + // otherwise. Assume each entry has a 'value' field whose size is + // VALUE_SIZE. + // + // This would not be terribly clean to extend to other format variations, + // but it's enough to handle Linux and Mac, and we'd like STABS to die + // anyway. + // + // For the record: on Linux, STABS entry values are always 32 bits, + // regardless of the architecture address size (don't ask me why); on + // Mac, they are 32 or 64 bits long. Oddly, the section header's entry + // size for a Linux ELF .stab section varies according to the ELF class + // from 12 to 20 even as the actual entries remain unchanged. + EntryIterator(const ByteBuffer *buffer, bool big_endian, size_t value_size); + + // Move to the next entry. This function's behavior is undefined if + // at_end() is true when it is called. + EntryIterator &operator++() { Fetch(); entry_.index++; return *this; } + + // Dereferencing this iterator produces a reference to an Entry structure + // that holds the current entry's values. The entry is owned by this + // EntryIterator, and will be invalidated at the next call to operator++. + const Entry &operator*() const { return entry_; } + const Entry *operator->() const { return &entry_; } + + private: + // Read the STABS entry at cursor_, and set entry_ appropriately. + void Fetch(); + + // The size of entries' value field, in bytes. + size_t value_size_; + + // A byte cursor traversing buffer_. + ByteCursor cursor_; + + // Values for the entry this iterator refers to. + Entry entry_; + }; + + // A source line, saved to be reported later. + struct Line { + uint64_t address; + const char *filename; + int number; + }; + + // Return the name of the current symbol. + const char *SymbolString(); + + // Process a compilation unit starting at symbol_. Return true + // to continue processing, or false to abort. + bool ProcessCompilationUnit(); + + // Process a function in current_source_file_ starting at symbol_. + // Return true to continue processing, or false to abort. + bool ProcessFunction(); + + // Process an exported function symbol. + // Return true to continue processing, or false to abort. + bool ProcessExtern(); + + // The STABS entries being parsed. + ByteBuffer entries_; + + // The string section to which the entries refer. + ByteBuffer strings_; + + // The iterator walking the STABS entries. + EntryIterator iterator_; + + // True if the data is "unitized"; see the explanation in the comment for + // StabsReader::StabsReader. + bool unitized_; + + StabsHandler *handler_; + + // The offset of the current compilation unit's strings within stabstr_. + size_t string_offset_; + + // The value string_offset_ should have for the next compilation unit, + // as established by N_UNDF entries. + size_t next_cu_string_offset_; + + // The current source file name. + const char *current_source_file_; + + // Mac OS X STABS place SLINE records before functions; we accumulate a + // vector of these until we see the FUN record, and then report them + // after the StartFunction call. + std::vector<Line> queued_lines_; +}; + +// Consumer-provided callback structure for the STABS reader. Clients +// of the STABS reader provide an instance of this structure. The +// reader then invokes the member functions of that instance to report +// the information it finds. +// +// The default definitions of the member functions do nothing, and return +// true so processing will continue. +class StabsHandler { + public: + StabsHandler() { } + virtual ~StabsHandler() { } + + // Some general notes about the handler callback functions: + + // Processing proceeds until the end of the .stabs section, or until + // one of these functions returns false. + + // The addresses given are as reported in the STABS info, without + // regard for whether the module may be loaded at different + // addresses at different times (a shared library, say). When + // processing STABS from an ELF shared library, the addresses given + // all assume the library is loaded at its nominal load address. + // They are *not* offsets from the nominal load address. If you + // want offsets, you must subtract off the library's nominal load + // address. + + // The arguments to these functions named FILENAME are all + // references to strings stored in the .stabstr section. Because + // both the Linux and Solaris linkers factor out duplicate strings + // from the .stabstr section, the consumer can assume that if two + // FILENAME values are different addresses, they represent different + // file names. + // + // Thus, it's safe to use (say) std::map<char *, ...>, which does + // string address comparisons, not string content comparisons. + // Since all the strings are in same array of characters --- the + // .stabstr section --- comparing their addresses produces + // predictable, if not lexicographically meaningful, results. + + // Begin processing a compilation unit whose main source file is + // named FILENAME, and whose base address is ADDRESS. If + // BUILD_DIRECTORY is non-NULL, it is the name of the build + // directory in which the compilation occurred. + virtual bool StartCompilationUnit(const char *filename, uint64_t address, + const char *build_directory) { + return true; + } + + // Finish processing the compilation unit. If ADDRESS is non-zero, + // it is the ending address of the compilation unit. If ADDRESS is + // zero, then the compilation unit's ending address is not + // available, and the consumer must infer it by other means. + virtual bool EndCompilationUnit(uint64_t address) { return true; } + + // Begin processing a function named NAME, whose starting address is + // ADDRESS. This function belongs to the compilation unit that was + // most recently started but not ended. + // + // Note that, unlike filenames, NAME is not a pointer into the + // .stabstr section; this is because the name as it appears in the + // STABS data is followed by type information. The value passed to + // StartFunction is the function name alone. + // + // In languages that use name mangling, like C++, NAME is mangled. + virtual bool StartFunction(const string &name, uint64_t address) { + return true; + } + + // Finish processing the function. If ADDRESS is non-zero, it is + // the ending address for the function. If ADDRESS is zero, then + // the function's ending address is not available, and the consumer + // must infer it by other means. + virtual bool EndFunction(uint64_t address) { return true; } + + // Report that the code at ADDRESS is attributable to line NUMBER of + // the source file named FILENAME. The caller must infer the ending + // address of the line. + virtual bool Line(uint64_t address, const char *filename, int number) { + return true; + } + + // Report that an exported function NAME is present at ADDRESS. + // The size of the function is unknown. + virtual bool Extern(const string &name, uint64_t address) { + return true; + } + + // Report a warning. FORMAT is a printf-like format string, + // specifying how to format the subsequent arguments. + virtual void Warning(const char *format, ...) = 0; +}; + +} // namespace google_breakpad + +#endif // COMMON_STABS_READER_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/stabs_reader_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/stabs_reader_unittest.cc new file mode 100644 index 0000000000..a84da1c4ca --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/stabs_reader_unittest.cc @@ -0,0 +1,611 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// stabs_reader_unittest.cc: Unit tests for google_breakpad::StabsReader. + +#include <assert.h> +#include <errno.h> +#include <stab.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> + +#include <fstream> +#include <iomanip> +#include <iostream> +#include <map> +#include <sstream> +#include <string> + +#include "breakpad_googletest_includes.h" +#include "common/stabs_reader.h" +#include "common/test_assembler.h" +#include "common/using_std_string.h" + +using ::testing::Eq; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::StrEq; +using ::testing::Test; +using ::testing::_; +using google_breakpad::StabsHandler; +using google_breakpad::StabsReader; +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::Section; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; +using std::map; + +namespace { + +// A StringAssembler is a class for generating .stabstr sections to present +// as input to the STABS parser. +class StringAssembler: public Section { + public: + StringAssembler() : in_cu_(false) { StartCU(); } + + // Add the string S to this StringAssembler, and return the string's + // offset within this compilation unit's strings. If S has been added + // already, this returns the offset of its first instance. + size_t Add(const string &s) { + map<string, size_t>::iterator it = added_.find(s); + if (it != added_.end()) + return it->second; + size_t offset = Size() - cu_start_; + AppendCString(s); + added_[s] = offset; + return offset; + } + + // Start a fresh compilation unit string collection. + void StartCU() { + // Ignore duplicate calls to StartCU. Our test data don't always call + // StartCU at all, meaning that our constructor has to take care of it, + // meaning that tests that *do* call StartCU call it twice at the + // beginning. This is not worth smoothing out. + if (in_cu_) return; + + added_.clear(); + cu_start_ = Size(); + + // Each compilation unit's strings start with an empty string. + AppendCString(""); + added_[""] = 0; + + in_cu_ = true; + } + + // Finish off the current CU's strings. + size_t EndCU() { + assert(in_cu_); + in_cu_ = false; + return Size() - cu_start_; + } + + private: + // The offset of the start of this compilation unit's strings. + size_t cu_start_; + + // True if we're in a CU. + bool in_cu_; + + // A map from the strings that have been added to this section to + // their starting indices within their compilation unit. + map<string, size_t> added_; +}; + +// A StabsAssembler is a class for generating .stab sections to present as +// test input for the STABS parser. +class StabsAssembler: public Section { + public: + // Create a StabsAssembler that uses StringAssembler for its strings. + StabsAssembler(StringAssembler *string_assembler) + : Section(string_assembler->endianness()), + string_assembler_(string_assembler), + value_size_(0), + entry_count_(0), + cu_header_(NULL) { } + ~StabsAssembler() { assert(!cu_header_); } + + // Accessor and setter for value_size_. + size_t value_size() const { return value_size_; } + StabsAssembler &set_value_size(size_t value_size) { + value_size_ = value_size; + return *this; + } + + // Append a STAB entry to the end of this section with the given + // characteristics. NAME is the offset of this entry's name string within + // its compilation unit's portion of the .stabstr section; this can be a + // value generated by a StringAssembler. Return a reference to this + // StabsAssembler. + StabsAssembler &Stab(uint8_t type, uint8_t other, Label descriptor, + Label value, Label name) { + D32(name); + D8(type); + D8(other); + D16(descriptor); + Append(endianness(), value_size_, value); + entry_count_++; + return *this; + } + + // As above, but automatically add NAME to our StringAssembler. + StabsAssembler &Stab(uint8_t type, uint8_t other, Label descriptor, + Label value, const string &name) { + return Stab(type, other, descriptor, value, string_assembler_->Add(name)); + } + + // Start a compilation unit named NAME, with an N_UNDF symbol to start + // it, and its own portion of the string section. Return a reference to + // this StabsAssembler. + StabsAssembler &StartCU(const string &name) { + assert(!cu_header_); + cu_header_ = new CUHeader; + string_assembler_->StartCU(); + entry_count_ = 0; + return Stab(N_UNDF, 0, + cu_header_->final_entry_count, + cu_header_->final_string_size, + string_assembler_->Add(name)); + } + + // Close off the current compilation unit. Return a reference to this + // StabsAssembler. + StabsAssembler &EndCU() { + assert(cu_header_); + cu_header_->final_entry_count = entry_count_; + cu_header_->final_string_size = string_assembler_->EndCU(); + delete cu_header_; + cu_header_ = NULL; + return *this; + } + + private: + // Data used in a compilation unit header STAB that we won't know until + // we've finished the compilation unit. + struct CUHeader { + // The final number of entries this compilation unit will hold. + Label final_entry_count; + + // The final size of this compilation unit's strings. + Label final_string_size; + }; + + // The strings for our STABS entries. + StringAssembler *string_assembler_; + + // The size of the 'value' field of stabs entries in this section. + size_t value_size_; + + // The number of entries in this compilation unit so far. + size_t entry_count_; + + // Header labels for this compilation unit, if we've started one but not + // finished it. + CUHeader *cu_header_; +}; + +class MockStabsReaderHandler: public StabsHandler { + public: + MOCK_METHOD3(StartCompilationUnit, + bool(const char *, uint64_t, const char *)); + MOCK_METHOD1(EndCompilationUnit, bool(uint64_t)); + MOCK_METHOD2(StartFunction, bool(const string &, uint64_t)); + MOCK_METHOD1(EndFunction, bool(uint64_t)); + MOCK_METHOD3(Line, bool(uint64_t, const char *, int)); + MOCK_METHOD2(Extern, bool(const string &, uint64_t)); + void Warning(const char *format, ...) { MockWarning(format); } + MOCK_METHOD1(MockWarning, void(const char *)); +}; + +struct StabsFixture { + StabsFixture() : stabs(&strings), unitized(true) { } + + // Create a StabsReader to parse the mock stabs data in stabs and + // strings, and pass the parsed information to mock_handler. Use the + // endianness and value size of stabs to parse the data. If all goes + // well, return the result of calling the reader's Process member + // function. Otherwise, return false. + bool ApplyHandlerToMockStabsData() { + string stabs_contents, stabstr_contents; + if (!stabs.GetContents(&stabs_contents) || + !strings.GetContents(&stabstr_contents)) + return false; + + // Run the parser on the test input, passing whatever we find to HANDLER. + StabsReader reader( + reinterpret_cast<const uint8_t *>(stabs_contents.data()), + stabs_contents.size(), + reinterpret_cast<const uint8_t *>(stabstr_contents.data()), + stabstr_contents.size(), + stabs.endianness() == kBigEndian, stabs.value_size(), unitized, + &mock_handler); + return reader.Process(); + } + + StringAssembler strings; + StabsAssembler stabs; + bool unitized; + MockStabsReaderHandler mock_handler; +}; + +class Stabs: public StabsFixture, public Test { }; + +TEST_F(Stabs, MockStabsInput) { + stabs.set_endianness(kLittleEndian); + stabs.set_value_size(4); + stabs + .Stab(N_SO, 149, 40232, 0x18a2a72bU, "builddir/") + .Stab(N_FUN, 83, 50010, 0x91a5353fU, + "not the SO with source file name we expected ") + .Stab(N_SO, 165, 24791, 0xfe69d23cU, "") + .Stab(N_SO, 184, 34178, 0xca4d883aU, "builddir1/") + .Stab(N_SO, 83, 40859, 0xd2fe5df3U, "file1.c") + .Stab(N_LSYM, 147, 39565, 0x60d4bb8aU, "not the FUN we're looking for") + .Stab(N_FUN, 120, 50271, 0xa049f4b1U, "fun1") + .Stab(N_BINCL, 150, 15694, 0xef65c659U, + "something to ignore in a FUN body") + .Stab(N_SLINE, 147, 4967, 0xd904b3f, "") + .Stab(N_SOL, 177, 56135, 0xbd97b1dcU, "header.h") + .Stab(N_SLINE, 130, 24610, 0x90f145b, "") + .Stab(N_FUN, 45, 32441, 0xbf27cf93U, + "fun2:some stabs type info here:to trim from the name") + .Stab(N_SLINE, 138, 39002, 0x8148b87, "") + .Stab(N_SOL, 60, 49318, 0x1d06e025U, "file1.c") + .Stab(N_SLINE, 29, 52163, 0x6eebbb7, "") + .Stab(N_SO, 167, 4647, 0xd04b7448U, "") + .Stab(N_LSYM, 58, 37837, 0xe6b14d37U, "") + .Stab(N_SO, 152, 7810, 0x11759f10U, "file3.c") + .Stab(N_SO, 218, 12447, 0x11cfe4b5U, ""); + + { + InSequence s; + + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("file1.c"), 0xd2fe5df3U, + StrEq("builddir1/"))) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartFunction(StrEq("fun1"), 0xa049f4b1U)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + Line(0xa049f4b1U + 0xd904b3f, StrEq("file1.c"), 4967)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + Line(0xa049f4b1U + 0x90f145b, StrEq("header.h"), 24610)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0xbf27cf93U)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartFunction(StrEq("fun2"), 0xbf27cf93U)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + Line(0xbf27cf93U + 0x8148b87, StrEq("header.h"), 39002)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + Line(0xbf27cf93U + 0x6eebbb7, StrEq("file1.c"), 52163)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0xd04b7448U)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0xd04b7448U)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartCompilationUnit(StrEq("file3.c"), + 0x11759f10U, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0x11cfe4b5U)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +TEST_F(Stabs, AbruptCU) { + stabs.set_endianness(kBigEndian); + stabs.set_value_size(4); + stabs.Stab(N_SO, 177, 23446, 0xbf10d5e4, "file2-1.c"); + + { + InSequence s; + + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("file2-1.c"), 0xbf10d5e4, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +TEST_F(Stabs, AbruptFunction) { + stabs.set_endianness(kLittleEndian); + stabs.set_value_size(8); + stabs + .Stab(N_SO, 218, 26631, 0xb83ddf10U, "file3-1.c") + .Stab(N_FUN, 113, 24765, 0xbbd4a145U, "fun3_1"); + + { + InSequence s; + + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("file3-1.c"), 0xb83ddf10U, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartFunction(StrEq("fun3_1"), 0xbbd4a145U)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +TEST_F(Stabs, NoCU) { + stabs.set_endianness(kBigEndian); + stabs.set_value_size(8); + stabs.Stab(N_SO, 161, 25673, 0x8f676e7bU, "build-directory/"); + + EXPECT_CALL(mock_handler, StartCompilationUnit(_, _, _)) + .Times(0); + EXPECT_CALL(mock_handler, StartFunction(_, _)) + .Times(0); + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +TEST_F(Stabs, NoCUEnd) { + stabs.set_endianness(kBigEndian); + stabs.set_value_size(8); + stabs + .Stab(N_SO, 116, 58280, 0x2f7493c9U, "file5-1.c") + .Stab(N_SO, 224, 23057, 0xf9f1d50fU, "file5-2.c"); + + { + InSequence s; + + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("file5-1.c"), 0x2f7493c9U, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("file5-2.c"), 0xf9f1d50fU, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +// On systems that store STABS in sections, string offsets are relative to +// the beginning of that compilation unit's strings, marked with N_UNDF +// symbols; see the comments for StabsReader::StabsReader. +TEST_F(Stabs, Unitized) { + stabs.set_endianness(kBigEndian); + stabs.set_value_size(4); + stabs + .StartCU("antimony") + .Stab(N_SO, 49, 26043, 0x7e259f1aU, "antimony") + .Stab(N_FUN, 101, 63253, 0x7fbcccaeU, "arsenic") + .Stab(N_SO, 124, 37175, 0x80b0014cU, "") + .EndCU() + .StartCU("aluminum") + .Stab(N_SO, 72, 23084, 0x86756839U, "aluminum") + .Stab(N_FUN, 59, 3305, 0xa8e120b0U, "selenium") + .Stab(N_SO, 178, 56949, 0xbffff983U, "") + .EndCU(); + + { + InSequence s; + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("antimony"), 0x7e259f1aU, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartFunction(Eq("arsenic"), 0x7fbcccaeU)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0x80b0014cU)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0x80b0014cU)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("aluminum"), 0x86756839U, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartFunction(Eq("selenium"), 0xa8e120b0U)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0xbffff983U)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0xbffff983U)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +// On systems that store STABS entries in the real symbol table, the N_UNDF +// entries have no special meaning, and shouldn't mess up the string +// indices. +TEST_F(Stabs, NonUnitized) { + stabs.set_endianness(kLittleEndian); + stabs.set_value_size(4); + unitized = false; + stabs + .Stab(N_UNDF, 21, 11551, 0x9bad2b2e, "") + .Stab(N_UNDF, 21, 11551, 0x9bad2b2e, "") + .Stab(N_SO, 71, 45139, 0x11a97352, "Tanzania") + .Stab(N_SO, 221, 41976, 0x21a97352, ""); + + { + InSequence s; + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("Tanzania"), + 0x11a97352, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0x21a97352)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +TEST_F(Stabs, FunctionEnd) { + stabs.set_endianness(kLittleEndian); + stabs.set_value_size(8); + stabs + .Stab(N_SO, 102, 62362, 0x52a830d644cd6942ULL, "compilation unit") + // This function is terminated by the start of the next function. + .Stab(N_FUN, 216, 38405, 0xbb5ab70ecdd23bfeULL, "function 1") + // This function is terminated by an explicit end-of-function stab, + // whose value is a size in bytes. + .Stab(N_FUN, 240, 10973, 0xc954de9b8fb3e5e2ULL, "function 2") + .Stab(N_FUN, 14, 36749, 0xc1ab, "") + // This function is terminated by the end of the compilation unit. + .Stab(N_FUN, 143, 64514, 0xdff98c9a35386e1fULL, "function 3") + .Stab(N_SO, 164, 60142, 0xfdacb856e78bbf57ULL, ""); + + { + InSequence s; + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("compilation unit"), + 0x52a830d644cd6942ULL, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + StartFunction(Eq("function 1"), 0xbb5ab70ecdd23bfeULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0xc954de9b8fb3e5e2ULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + StartFunction(Eq("function 2"), 0xc954de9b8fb3e5e2ULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0xc954de9b8fb3e5e2ULL + 0xc1ab)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + StartFunction(Eq("function 3"), 0xdff98c9a35386e1fULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0xfdacb856e78bbf57ULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0xfdacb856e78bbf57ULL)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +// On Mac OS X, SLINE records can appear before the FUN stab to which they +// belong, and their values are absolute addresses, not offsets. +TEST_F(Stabs, LeadingLine) { + stabs.set_endianness(kBigEndian); + stabs.set_value_size(4); + stabs + .Stab(N_SO, 179, 27357, 0x8adabc15, "build directory/") + .Stab(N_SO, 52, 53058, 0x4c7e3bf4, "compilation unit") + .Stab(N_SOL, 165, 12086, 0x6a797ca3, "source file name") + .Stab(N_SLINE, 229, 20015, 0x4cb3d7e0, "") + .Stab(N_SLINE, 89, 43802, 0x4cba8b88, "") + .Stab(N_FUN, 251, 51639, 0xce1b98fa, "rutabaga") + .Stab(N_FUN, 218, 16113, 0x5798, "") + .Stab(N_SO, 52, 53058, 0xd4af4415, ""); + + { + InSequence s; + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("compilation unit"), + 0x4c7e3bf4, StrEq("build directory/"))) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + StartFunction(Eq("rutabaga"), 0xce1b98fa)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + Line(0x4cb3d7e0, StrEq("source file name"), 20015)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + Line(0x4cba8b88, StrEq("source file name"), 43802)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0xce1b98fa + 0x5798)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0xd4af4415)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + + +#if defined(HAVE_MACH_O_NLIST_H) +// These tests have no meaning on non-Mach-O-based systems, as +// only Mach-O uses N_SECT to represent public symbols. +TEST_F(Stabs, OnePublicSymbol) { + stabs.set_endianness(kLittleEndian); + stabs.set_value_size(4); + + const uint32_t kExpectedAddress = 0x9000; + const string kExpectedFunctionName("public_function"); + stabs + .Stab(N_SECT, 1, 0, kExpectedAddress, kExpectedFunctionName); + + { + InSequence s; + EXPECT_CALL(mock_handler, + Extern(StrEq(kExpectedFunctionName), + kExpectedAddress)) + .WillOnce(Return(true)); + } + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +TEST_F(Stabs, TwoPublicSymbols) { + stabs.set_endianness(kLittleEndian); + stabs.set_value_size(4); + + const uint32_t kExpectedAddress1 = 0xB0B0B0B0; + const string kExpectedFunctionName1("public_function"); + const uint32_t kExpectedAddress2 = 0xF0F0F0F0; + const string kExpectedFunctionName2("something else"); + stabs + .Stab(N_SECT, 1, 0, kExpectedAddress1, kExpectedFunctionName1) + .Stab(N_SECT, 1, 0, kExpectedAddress2, kExpectedFunctionName2); + + { + InSequence s; + EXPECT_CALL(mock_handler, + Extern(StrEq(kExpectedFunctionName1), + kExpectedAddress1)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + Extern(StrEq(kExpectedFunctionName2), + kExpectedAddress2)) + .WillOnce(Return(true)); + } + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +#endif + +} // anonymous namespace diff --git a/toolkit/crashreporter/google-breakpad/src/common/stabs_to_module.cc b/toolkit/crashreporter/google-breakpad/src/common/stabs_to_module.cc new file mode 100644 index 0000000000..049a6cc63e --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/stabs_to_module.cc @@ -0,0 +1,199 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// dump_stabs.cc --- implement the StabsToModule class. + +#include <assert.h> +#include <cxxabi.h> +#include <stdarg.h> +#include <stdio.h> + +#include <algorithm> + +#include "common/stabs_to_module.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +// Demangle using abi call. +// Older GCC may not support it. +static string Demangle(const string &mangled) { + int status = 0; + char *demangled = abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status); + if (status == 0 && demangled != NULL) { + string str(demangled); + free(demangled); + return str; + } + return string(mangled); +} + +StabsToModule::~StabsToModule() { + // Free any functions we've accumulated but not added to the module. + for (vector<Module::Function *>::const_iterator func_it = functions_.begin(); + func_it != functions_.end(); func_it++) + delete *func_it; + // Free any function that we're currently within. + delete current_function_; +} + +bool StabsToModule::StartCompilationUnit(const char *name, uint64_t address, + const char *build_directory) { + assert(!in_compilation_unit_); + in_compilation_unit_ = true; + current_source_file_name_ = name; + current_source_file_ = module_->FindFile(name); + comp_unit_base_address_ = address; + boundaries_.push_back(static_cast<Module::Address>(address)); + return true; +} + +bool StabsToModule::EndCompilationUnit(uint64_t address) { + assert(in_compilation_unit_); + in_compilation_unit_ = false; + comp_unit_base_address_ = 0; + current_source_file_ = NULL; + current_source_file_name_ = NULL; + if (address) + boundaries_.push_back(static_cast<Module::Address>(address)); + return true; +} + +bool StabsToModule::StartFunction(const string &name, + uint64_t address) { + assert(!current_function_); + Module::Function *f = new Module::Function(Demangle(name), address); + Module::Range r(address, 0); // We compute this in StabsToModule::Finalize(). + f->ranges.push_back(r); + f->parameter_size = 0; // We don't provide this information. + current_function_ = f; + boundaries_.push_back(static_cast<Module::Address>(address)); + return true; +} + +bool StabsToModule::EndFunction(uint64_t address) { + assert(current_function_); + // Functions in this compilation unit should have address bigger + // than the compilation unit's starting address. There may be a lot + // of duplicated entries for functions in the STABS data. We will + // count on the Module to remove the duplicates. + if (current_function_->address >= comp_unit_base_address_) + functions_.push_back(current_function_); + else + delete current_function_; + current_function_ = NULL; + if (address) + boundaries_.push_back(static_cast<Module::Address>(address)); + return true; +} + +bool StabsToModule::Line(uint64_t address, const char *name, int number) { + assert(current_function_); + assert(current_source_file_); + if (name != current_source_file_name_) { + current_source_file_ = module_->FindFile(name); + current_source_file_name_ = name; + } + Module::Line line; + line.address = address; + line.size = 0; // We compute this in StabsToModule::Finalize(). + line.file = current_source_file_; + line.number = number; + current_function_->lines.push_back(line); + return true; +} + +bool StabsToModule::Extern(const string &name, uint64_t address) { + Module::Extern *ext = new Module::Extern(address); + // Older libstdc++ demangle implementations can crash on unexpected + // input, so be careful about what gets passed in. + if (name.compare(0, 3, "__Z") == 0) { + ext->name = Demangle(name.substr(1)); + } else if (name[0] == '_') { + ext->name = name.substr(1); + } else { + ext->name = name; + } + module_->AddExtern(ext); + return true; +} + +void StabsToModule::Warning(const char *format, ...) { + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} + +void StabsToModule::Finalize() { + // Sort our boundary list, so we can search it quickly. + sort(boundaries_.begin(), boundaries_.end()); + // Sort all functions by address, just for neatness. + sort(functions_.begin(), functions_.end(), + Module::Function::CompareByAddress); + + for (vector<Module::Function *>::const_iterator func_it = functions_.begin(); + func_it != functions_.end(); + func_it++) { + Module::Function *f = *func_it; + // Compute the function f's size. + vector<Module::Address>::const_iterator boundary + = std::upper_bound(boundaries_.begin(), boundaries_.end(), f->address); + if (boundary != boundaries_.end()) + f->ranges[0].size = *boundary - f->address; + else + // If this is the last function in the module, and the STABS + // reader was unable to give us its ending address, then assign + // it a bogus, very large value. This will happen at most once + // per module: since we've added all functions' addresses to the + // boundary table, only one can be the last. + f->ranges[0].size = kFallbackSize; + + // Compute sizes for each of the function f's lines --- if it has any. + if (!f->lines.empty()) { + stable_sort(f->lines.begin(), f->lines.end(), + Module::Line::CompareByAddress); + vector<Module::Line>::iterator last_line = f->lines.end() - 1; + for (vector<Module::Line>::iterator line_it = f->lines.begin(); + line_it != last_line; line_it++) + line_it[0].size = line_it[1].address - line_it[0].address; + // Compute the size of the last line from f's end address. + last_line->size = + (f->ranges[0].address + f->ranges[0].size) - last_line->address; + } + } + // Now that everything has a size, add our functions to the module, and + // dispose of our private list. + module_->AddFunctions(functions_.begin(), functions_.end()); + functions_.clear(); +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/stabs_to_module.h b/toolkit/crashreporter/google-breakpad/src/common/stabs_to_module.h new file mode 100644 index 0000000000..5e04fa7927 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/stabs_to_module.h @@ -0,0 +1,143 @@ +// -*- mode: C++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// dump_stabs.h: Define the StabsToModule class, which receives +// STABS debugging information from a parser and adds it to a Breakpad +// symbol file. + +#ifndef BREAKPAD_COMMON_STABS_TO_MODULE_H_ +#define BREAKPAD_COMMON_STABS_TO_MODULE_H_ + +#include <stdint.h> + +#include <string> +#include <vector> + +#include "common/module.h" +#include "common/stabs_reader.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +using std::vector; + +// A StabsToModule is a handler that receives parsed STABS debugging +// information from a StabsReader, and uses that to populate +// a Module. (All classes are in the google_breakpad namespace.) A +// Module represents the contents of a Breakpad symbol file, and knows +// how to write itself out as such. A StabsToModule thus acts as +// the bridge between STABS and Breakpad data. +// When processing Darwin Mach-O files, this also receives public linker +// symbols, like those found in system libraries. +class StabsToModule: public google_breakpad::StabsHandler { + public: + // Receive parsed debugging information from a StabsReader, and + // store it all in MODULE. + StabsToModule(Module *module) : + module_(module), + in_compilation_unit_(false), + comp_unit_base_address_(0), + current_function_(NULL), + current_source_file_(NULL), + current_source_file_name_(NULL) { } + ~StabsToModule(); + + // The standard StabsHandler virtual member functions. + bool StartCompilationUnit(const char *name, uint64_t address, + const char *build_directory); + bool EndCompilationUnit(uint64_t address); + bool StartFunction(const string &name, uint64_t address); + bool EndFunction(uint64_t address); + bool Line(uint64_t address, const char *name, int number); + bool Extern(const string &name, uint64_t address); + void Warning(const char *format, ...); + + // Do any final processing necessary to make module_ contain all the + // data provided by the STABS reader. + // + // Because STABS does not provide reliable size information for + // functions and lines, we need to make a pass over the data after + // processing all the STABS to compute those sizes. We take care of + // that here. + void Finalize(); + + private: + + // An arbitrary, but very large, size to use for functions whose + // size we can't compute properly. + static const uint64_t kFallbackSize = 0x10000000; + + // The module we're contributing debugging info to. + Module *module_; + + // The functions we've generated so far. We don't add these to + // module_ as we parse them. Instead, we wait until we've computed + // their ending address, and their lines' ending addresses. + // + // We could just stick them in module_ from the outset, but if + // module_ already contains data gathered from other debugging + // formats, that would complicate the size computation. + vector<Module::Function *> functions_; + + // Boundary addresses. STABS doesn't necessarily supply sizes for + // functions and lines, so we need to compute them ourselves by + // finding the next object. + vector<Module::Address> boundaries_; + + // True if we are currently within a compilation unit: we have gotten a + // StartCompilationUnit call, but no matching EndCompilationUnit call + // yet. We use this for sanity checks. + bool in_compilation_unit_; + + // The base address of the current compilation unit. We use this to + // recognize functions we should omit from the symbol file. (If you + // know the details of why we omit these, please patch this + // comment.) + Module::Address comp_unit_base_address_; + + // The function we're currently contributing lines to. + Module::Function *current_function_; + + // The last Module::File we got a line number in. + Module::File *current_source_file_; + + // The pointer in the .stabstr section of the name that + // current_source_file_ is built from. This allows us to quickly + // recognize when the current line is in the same file as the + // previous one (which it usually is). + const char *current_source_file_name_; +}; + +} // namespace google_breakpad + +#endif // BREAKPAD_COMMON_STABS_TO_MODULE_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/stabs_to_module_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/stabs_to_module_unittest.cc new file mode 100644 index 0000000000..aae0047667 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/stabs_to_module_unittest.cc @@ -0,0 +1,258 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// dump_stabs_unittest.cc: Unit tests for StabsToModule. + +#include <vector> + +#include "breakpad_googletest_includes.h" +#include "common/stabs_to_module.h" + +using google_breakpad::Module; +using google_breakpad::StabsToModule; +using std::vector; + +TEST(StabsToModule, SimpleCU) { + Module m("name", "os", "arch", "id"); + StabsToModule h(&m); + + // Feed in a simple compilation unit that defines a function with + // one line. + EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0x9f4d1271e50db93bLL, + "build-directory")); + EXPECT_TRUE(h.StartFunction("function", 0xfde4abbed390c394LL)); + EXPECT_TRUE(h.Line(0xfde4abbed390c394LL, "source-file-name", 174823314)); + EXPECT_TRUE(h.EndFunction(0xfde4abbed390c3a4LL)); + EXPECT_TRUE(h.EndCompilationUnit(0xfee4abbed390c3a4LL)); + h.Finalize(); + + // Now check to see what has been added to the Module. + Module::File *file = m.FindExistingFile("source-file-name"); + ASSERT_TRUE(file != NULL); + + vector<Module::Function *> functions; + m.GetFunctions(&functions, functions.end()); + ASSERT_EQ((size_t) 1, functions.size()); + Module::Function *function = functions[0]; + EXPECT_STREQ("function", function->name.c_str()); + EXPECT_EQ(0xfde4abbed390c394LL, function->address); + EXPECT_EQ(0x10U, function->ranges[0].size); + EXPECT_EQ(0U, function->parameter_size); + ASSERT_EQ((size_t) 1, function->lines.size()); + Module::Line *line = &function->lines[0]; + EXPECT_EQ(0xfde4abbed390c394LL, line->address); + EXPECT_EQ(0x10U, line->size); // derived from EndFunction + EXPECT_TRUE(line->file == file); + EXPECT_EQ(174823314, line->number); +} + +#ifdef __GNUC__ +// Function name mangling can vary by compiler, so only run mangled-name +// tests on GCC for simplicity's sake. +TEST(StabsToModule, Externs) { + Module m("name", "os", "arch", "id"); + StabsToModule h(&m); + + // Feed in a few Extern symbols. + EXPECT_TRUE(h.Extern("_foo", 0xffff)); + EXPECT_TRUE(h.Extern("__Z21dyldGlobalLockAcquirev", 0xaaaa)); + EXPECT_TRUE(h.Extern("_MorphTableGetNextMorphChain", 0x1111)); + h.Finalize(); + + // Now check to see what has been added to the Module. + vector<Module::Extern *> externs; + m.GetExterns(&externs, externs.end()); + ASSERT_EQ((size_t) 3, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_STREQ("MorphTableGetNextMorphChain", extern1->name.c_str()); + EXPECT_EQ((Module::Address)0x1111, extern1->address); + Module::Extern *extern2 = externs[1]; + EXPECT_STREQ("dyldGlobalLockAcquire()", extern2->name.c_str()); + EXPECT_EQ((Module::Address)0xaaaa, extern2->address); + Module::Extern *extern3 = externs[2]; + EXPECT_STREQ("foo", extern3->name.c_str()); + EXPECT_EQ((Module::Address)0xffff, extern3->address); +} +#endif // __GNUC__ + +TEST(StabsToModule, DuplicateFunctionNames) { + Module m("name", "os", "arch", "id"); + StabsToModule h(&m); + + // Compilation unit with one function, mangled name. + EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xf2cfda36ecf7f46cLL, + "build-directory")); + EXPECT_TRUE(h.StartFunction("funcfoo", + 0xf2cfda36ecf7f46dLL)); + EXPECT_TRUE(h.EndFunction(0)); + EXPECT_TRUE(h.StartFunction("funcfoo", + 0xf2cfda36ecf7f46dLL)); + EXPECT_TRUE(h.EndFunction(0)); + EXPECT_TRUE(h.EndCompilationUnit(0)); + + h.Finalize(); + + // Now check to see what has been added to the Module. + Module::File *file = m.FindExistingFile("compilation-unit"); + ASSERT_TRUE(file != NULL); + + vector<Module::Function *> functions; + m.GetFunctions(&functions, functions.end()); + ASSERT_EQ(1U, functions.size()); + + Module::Function *function = functions[0]; + EXPECT_EQ(0xf2cfda36ecf7f46dLL, function->address); + EXPECT_LT(0U, function->ranges[0].size); // should have used dummy size + EXPECT_EQ(0U, function->parameter_size); + ASSERT_EQ(0U, function->lines.size()); +} + +TEST(InferSizes, LineSize) { + Module m("name", "os", "arch", "id"); + StabsToModule h(&m); + + // Feed in a simple compilation unit that defines a function with + // one line. + EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xb4513962eff94e92LL, + "build-directory")); + EXPECT_TRUE(h.StartFunction("function", 0xb4513962eff94e92LL)); + EXPECT_TRUE(h.Line(0xb4513962eff94e92LL, "source-file-name-1", 77396614)); + EXPECT_TRUE(h.Line(0xb4513963eff94e92LL, "source-file-name-2", 87660088)); + EXPECT_TRUE(h.EndFunction(0)); // unknown function end address + EXPECT_TRUE(h.EndCompilationUnit(0)); // unknown CU end address + EXPECT_TRUE(h.StartCompilationUnit("compilation-unit-2", 0xb4523963eff94e92LL, + "build-directory-2")); // next boundary + EXPECT_TRUE(h.EndCompilationUnit(0)); + h.Finalize(); + + // Now check to see what has been added to the Module. + Module::File *file1 = m.FindExistingFile("source-file-name-1"); + ASSERT_TRUE(file1 != NULL); + Module::File *file2 = m.FindExistingFile("source-file-name-2"); + ASSERT_TRUE(file2 != NULL); + + vector<Module::Function *> functions; + m.GetFunctions(&functions, functions.end()); + ASSERT_EQ((size_t) 1, functions.size()); + + Module::Function *function = functions[0]; + EXPECT_STREQ("function", function->name.c_str()); + EXPECT_EQ(0xb4513962eff94e92LL, function->address); + EXPECT_EQ(0x1000100000000ULL, function->ranges[0].size); // inferred from CU end + EXPECT_EQ(0U, function->parameter_size); + ASSERT_EQ((size_t) 2, function->lines.size()); + + Module::Line *line1 = &function->lines[0]; + EXPECT_EQ(0xb4513962eff94e92LL, line1->address); + EXPECT_EQ(0x100000000ULL, line1->size); // derived from EndFunction + EXPECT_TRUE(line1->file == file1); + EXPECT_EQ(77396614, line1->number); + + Module::Line *line2 = &function->lines[1]; + EXPECT_EQ(0xb4513963eff94e92LL, line2->address); + EXPECT_EQ(0x1000000000000ULL, line2->size); // derived from EndFunction + EXPECT_TRUE(line2->file == file2); + EXPECT_EQ(87660088, line2->number); +} + +#ifdef __GNUC__ +// Function name mangling can vary by compiler, so only run mangled-name +// tests on GCC for simplicity's sake. +TEST(FunctionNames, Mangled) { + Module m("name", "os", "arch", "id"); + StabsToModule h(&m); + + // Compilation unit with one function, mangled name. + EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xf2cfda63cef7f46cLL, + "build-directory")); + EXPECT_TRUE(h.StartFunction("_ZNSt6vectorIySaIyEE9push_backERKy", + 0xf2cfda63cef7f46dLL)); + EXPECT_TRUE(h.EndFunction(0)); + EXPECT_TRUE(h.EndCompilationUnit(0)); + + h.Finalize(); + + // Now check to see what has been added to the Module. + Module::File *file = m.FindExistingFile("compilation-unit"); + ASSERT_TRUE(file != NULL); + + vector<Module::Function *> functions; + m.GetFunctions(&functions, functions.end()); + ASSERT_EQ(1U, functions.size()); + + Module::Function *function = functions[0]; + // This is GCC-specific, but we shouldn't be seeing STABS data anywhere + // but Linux. + EXPECT_STREQ("std::vector<unsigned long long, " + "std::allocator<unsigned long long> >::" + "push_back(unsigned long long const&)", + function->name.c_str()); + EXPECT_EQ(0xf2cfda63cef7f46dLL, function->address); + EXPECT_LT(0U, function->ranges[0].size); // should have used dummy size + EXPECT_EQ(0U, function->parameter_size); + ASSERT_EQ(0U, function->lines.size()); +} +#endif // __GNUC__ + +// The GNU toolchain can omit functions that are not used; however, +// when it does so, it doesn't clean up the debugging information that +// refers to them. In STABS, this results in compilation units whose +// SO addresses are zero. +TEST(Omitted, Function) { + Module m("name", "os", "arch", "id"); + StabsToModule h(&m); + + // The StartCompilationUnit and EndCompilationUnit calls may both have an + // address of zero if the compilation unit has had sections removed. + EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0, "build-directory")); + EXPECT_TRUE(h.StartFunction("function", 0x2a133596)); + EXPECT_TRUE(h.EndFunction(0)); + EXPECT_TRUE(h.EndCompilationUnit(0)); +} + +// TODO --- if we actually cared about STABS. Even without these we've +// got full coverage of non-failure source lines in dump_stabs.cc. + +// Line size from next line +// Line size from function end +// Line size from next function start +// line size from cu end +// line size from next cu start +// fallback size is something plausible + +// function size from function end +// function size from next function start +// function size from cu end +// function size from next cu start +// fallback size is something plausible + +// omitting functions outside the compilation unit's address range +// zero-line, one-line, many-line functions diff --git a/toolkit/crashreporter/google-breakpad/src/common/stdio_wrapper.h b/toolkit/crashreporter/google-breakpad/src/common/stdio_wrapper.h new file mode 100644 index 0000000000..a3dd50aab6 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/stdio_wrapper.h @@ -0,0 +1,43 @@ +// Copyright (c) 2016, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef GOOGLE_BREAKPAD_COMMON_STDIO_WRAPPER_H +#define GOOGLE_BREAKPAD_COMMON_STDIO_WRAPPER_H + +#include <stdio.h> + +#if defined(_MSC_VER) && MSC_VER < 1900 +#include <basetsd.h> + +#define snprintf _snprintf +typedef SSIZE_T ssize_t; +#endif + + +#endif // GOOGLE_BREAKPAD_COMMON_STDIO_WRAPPER_H diff --git a/toolkit/crashreporter/google-breakpad/src/common/string_conversion.cc b/toolkit/crashreporter/google-breakpad/src/common/string_conversion.cc new file mode 100644 index 0000000000..11d60a3664 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/string_conversion.cc @@ -0,0 +1,155 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include <string.h> + +#include "common/convert_UTF.h" +#include "common/scoped_ptr.h" +#include "common/string_conversion.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +using std::vector; + +void UTF8ToUTF16(const char *in, vector<uint16_t> *out) { + size_t source_length = strlen(in); + const UTF8 *source_ptr = reinterpret_cast<const UTF8 *>(in); + const UTF8 *source_end_ptr = source_ptr + source_length; + // Erase the contents and zero fill to the expected size + out->clear(); + out->insert(out->begin(), source_length, 0); + uint16_t *target_ptr = &(*out)[0]; + uint16_t *target_end_ptr = target_ptr + out->capacity(); + ConversionResult result = ConvertUTF8toUTF16(&source_ptr, source_end_ptr, + &target_ptr, target_end_ptr, + strictConversion); + + // Resize to be the size of the # of converted characters + NULL + out->resize(result == conversionOK ? target_ptr - &(*out)[0] + 1: 0); +} + +int UTF8ToUTF16Char(const char *in, int in_length, uint16_t out[2]) { + const UTF8 *source_ptr = reinterpret_cast<const UTF8 *>(in); + const UTF8 *source_end_ptr = source_ptr + 1; + uint16_t *target_ptr = out; + uint16_t *target_end_ptr = target_ptr + 2; + out[0] = out[1] = 0; + + // Process one character at a time + while (1) { + ConversionResult result = ConvertUTF8toUTF16(&source_ptr, source_end_ptr, + &target_ptr, target_end_ptr, + strictConversion); + + if (result == conversionOK) + return static_cast<int>(source_ptr - reinterpret_cast<const UTF8 *>(in)); + + // Add another character to the input stream and try again + source_ptr = reinterpret_cast<const UTF8 *>(in); + ++source_end_ptr; + + if (source_end_ptr > reinterpret_cast<const UTF8 *>(in) + in_length) + break; + } + + return 0; +} + +void UTF32ToUTF16(const wchar_t *in, vector<uint16_t> *out) { + size_t source_length = wcslen(in); + const UTF32 *source_ptr = reinterpret_cast<const UTF32 *>(in); + const UTF32 *source_end_ptr = source_ptr + source_length; + // Erase the contents and zero fill to the expected size + out->clear(); + out->insert(out->begin(), source_length, 0); + uint16_t *target_ptr = &(*out)[0]; + uint16_t *target_end_ptr = target_ptr + out->capacity(); + ConversionResult result = ConvertUTF32toUTF16(&source_ptr, source_end_ptr, + &target_ptr, target_end_ptr, + strictConversion); + + // Resize to be the size of the # of converted characters + NULL + out->resize(result == conversionOK ? target_ptr - &(*out)[0] + 1: 0); +} + +void UTF32ToUTF16Char(wchar_t in, uint16_t out[2]) { + const UTF32 *source_ptr = reinterpret_cast<const UTF32 *>(&in); + const UTF32 *source_end_ptr = source_ptr + 1; + uint16_t *target_ptr = out; + uint16_t *target_end_ptr = target_ptr + 2; + out[0] = out[1] = 0; + ConversionResult result = ConvertUTF32toUTF16(&source_ptr, source_end_ptr, + &target_ptr, target_end_ptr, + strictConversion); + + if (result != conversionOK) { + out[0] = out[1] = 0; + } +} + +static inline uint16_t Swap(uint16_t value) { + return (value >> 8) | static_cast<uint16_t>(value << 8); +} + +string UTF16ToUTF8(const vector<uint16_t> &in, bool swap) { + const UTF16 *source_ptr = &in[0]; + scoped_array<uint16_t> source_buffer; + + // If we're to swap, we need to make a local copy and swap each byte pair + if (swap) { + int idx = 0; + source_buffer.reset(new uint16_t[in.size()]); + UTF16 *source_buffer_ptr = source_buffer.get(); + for (vector<uint16_t>::const_iterator it = in.begin(); + it != in.end(); ++it, ++idx) + source_buffer_ptr[idx] = Swap(*it); + + source_ptr = source_buffer.get(); + } + + // The maximum expansion would be 4x the size of the input string. + const UTF16 *source_end_ptr = source_ptr + in.size(); + size_t target_capacity = in.size() * 4; + scoped_array<UTF8> target_buffer(new UTF8[target_capacity]); + UTF8 *target_ptr = target_buffer.get(); + UTF8 *target_end_ptr = target_ptr + target_capacity; + ConversionResult result = ConvertUTF16toUTF8(&source_ptr, source_end_ptr, + &target_ptr, target_end_ptr, + strictConversion); + + if (result == conversionOK) { + const char *targetPtr = reinterpret_cast<const char *>(target_buffer.get()); + return targetPtr; + } + + return ""; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/string_conversion.h b/toolkit/crashreporter/google-breakpad/src/common/string_conversion.h new file mode 100644 index 0000000000..b9ba96a2e1 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/string_conversion.h @@ -0,0 +1,68 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// string_conversion.h: Conversion between different UTF-8/16/32 encodings. + +#ifndef COMMON_STRING_CONVERSION_H__ +#define COMMON_STRING_CONVERSION_H__ + +#include <string> +#include <vector> + +#include "common/using_std_string.h" +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +using std::vector; + +// Convert |in| to UTF-16 into |out|. Use platform byte ordering. If the +// conversion failed, |out| will be zero length. +void UTF8ToUTF16(const char *in, vector<uint16_t> *out); + +// Convert at least one character (up to a maximum of |in_length|) from |in| +// to UTF-16 into |out|. Return the number of characters consumed from |in|. +// Any unused characters in |out| will be initialized to 0. No memory will +// be allocated by this routine. +int UTF8ToUTF16Char(const char *in, int in_length, uint16_t out[2]); + +// Convert |in| to UTF-16 into |out|. Use platform byte ordering. If the +// conversion failed, |out| will be zero length. +void UTF32ToUTF16(const wchar_t *in, vector<uint16_t> *out); + +// Convert |in| to UTF-16 into |out|. Any unused characters in |out| will be +// initialized to 0. No memory will be allocated by this routine. +void UTF32ToUTF16Char(wchar_t in, uint16_t out[2]); + +// Convert |in| to UTF-8. If |swap| is true, swap bytes before converting. +string UTF16ToUTF8(const vector<uint16_t> &in, bool swap); + +} // namespace google_breakpad + +#endif // COMMON_STRING_CONVERSION_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/string_conversion_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/string_conversion_unittest.cc new file mode 100644 index 0000000000..e9f9b55d95 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/string_conversion_unittest.cc @@ -0,0 +1,64 @@ +// Copyright (c) 2019, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// string_conversion_unittest.cc: Unit tests for google_breakpad::UTF* helpers. + +#include <string> +#include <vector> + +#include "breakpad_googletest_includes.h" +#include "common/string_conversion.h" + +using google_breakpad::UTF8ToUTF16; +using google_breakpad::UTF8ToUTF16Char; +using google_breakpad::UTF16ToUTF8; +using std::vector; + +TEST(StringConversionTest, UTF8ToUTF16) { + const char in[] = "aßc"; + vector<uint16_t> out; + vector<uint16_t> exp{'a', 0xdf, 'c', 0}; + UTF8ToUTF16(in, &out); + EXPECT_EQ(4u, out.size()); + EXPECT_EQ(exp, out); +} + +TEST(StringConversionTest, UTF8ToUTF16Char) { + const char in[] = "a"; + uint16_t out[3] = {0xff, 0xff, 0xff}; + EXPECT_EQ(1, UTF8ToUTF16Char(in, 1, out)); + EXPECT_EQ('a', out[0]); + EXPECT_EQ(0, out[1]); + EXPECT_EQ(0xff, out[2]); +} + +TEST(StringConversionTest, UTF16ToUTF8) { + vector<uint16_t> in{'a', 0xdf, 'c', 0}; + EXPECT_EQ("aßc", UTF16ToUTF8(in, false)); +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/symbol_data.h b/toolkit/crashreporter/google-breakpad/src/common/symbol_data.h new file mode 100644 index 0000000000..2cf15a855d --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/symbol_data.h @@ -0,0 +1,42 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2013 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef COMMON_SYMBOL_DATA_H_ +#define COMMON_SYMBOL_DATA_H_ + +// Control what data is used from the symbol file. +enum SymbolData { + ALL_SYMBOL_DATA, + NO_CFI, + ONLY_CFI +}; + +#endif // COMMON_SYMBOL_DATA_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/test_assembler.cc b/toolkit/crashreporter/google-breakpad/src/common/test_assembler.cc new file mode 100644 index 0000000000..1e783b45c5 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/test_assembler.cc @@ -0,0 +1,359 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// test_assembler.cc: Implementation of google_breakpad::TestAssembler. +// See test_assembler.h for details. + +#include "common/test_assembler.h" + +#include <assert.h> +#include <stdio.h> + +#include <iterator> + +namespace google_breakpad { +namespace test_assembler { + +using std::back_insert_iterator; + +Label::Label() : value_(new Binding()) { } +Label::Label(uint64_t value) : value_(new Binding(value)) { } +Label::Label(const Label &label) { + value_ = label.value_; + value_->Acquire(); +} +Label::~Label() { + if (value_->Release()) delete value_; +} + +Label &Label::operator=(uint64_t value) { + value_->Set(NULL, value); + return *this; +} + +Label &Label::operator=(const Label &label) { + value_->Set(label.value_, 0); + return *this; +} + +Label Label::operator+(uint64_t addend) const { + Label l; + l.value_->Set(this->value_, addend); + return l; +} + +Label Label::operator-(uint64_t subtrahend) const { + Label l; + l.value_->Set(this->value_, -subtrahend); + return l; +} + +// When NDEBUG is #defined, assert doesn't evaluate its argument. This +// means you can't simply use assert to check the return value of a +// function with necessary side effects. +// +// ALWAYS_EVALUATE_AND_ASSERT(x) evaluates x regardless of whether +// NDEBUG is #defined; when NDEBUG is not #defined, it further asserts +// that x is true. +#ifdef NDEBUG +#define ALWAYS_EVALUATE_AND_ASSERT(x) x +#else +#define ALWAYS_EVALUATE_AND_ASSERT(x) assert(x) +#endif + +uint64_t Label::operator-(const Label &label) const { + uint64_t offset; + ALWAYS_EVALUATE_AND_ASSERT(IsKnownOffsetFrom(label, &offset)); + return offset; +} + +uint64_t Label::Value() const { + uint64_t v = 0; + ALWAYS_EVALUATE_AND_ASSERT(IsKnownConstant(&v)); + return v; +}; + +bool Label::IsKnownConstant(uint64_t *value_p) const { + Binding *base; + uint64_t addend; + value_->Get(&base, &addend); + if (base != NULL) return false; + if (value_p) *value_p = addend; + return true; +} + +bool Label::IsKnownOffsetFrom(const Label &label, uint64_t *offset_p) const +{ + Binding *label_base, *this_base; + uint64_t label_addend, this_addend; + label.value_->Get(&label_base, &label_addend); + value_->Get(&this_base, &this_addend); + // If this and label are related, Get will find their final + // common ancestor, regardless of how indirect the relation is. This + // comparison also handles the constant vs. constant case. + if (this_base != label_base) return false; + if (offset_p) *offset_p = this_addend - label_addend; + return true; +} + +Label::Binding::Binding() : base_(this), addend_(), reference_count_(1) { } + +Label::Binding::Binding(uint64_t addend) + : base_(NULL), addend_(addend), reference_count_(1) { } + +Label::Binding::~Binding() { + assert(reference_count_ == 0); + if (base_ && base_ != this && base_->Release()) + delete base_; +} + +void Label::Binding::Set(Binding *binding, uint64_t addend) { + if (!base_ && !binding) { + // We're equating two constants. This could be okay. + assert(addend_ == addend); + } else if (!base_) { + // We are a known constant, but BINDING may not be, so turn the + // tables and try to set BINDING's value instead. + binding->Set(NULL, addend_ - addend); + } else { + if (binding) { + // Find binding's final value. Since the final value is always either + // completely unconstrained or a constant, never a reference to + // another variable (otherwise, it wouldn't be final), this + // guarantees we won't create cycles here, even for code like this: + // l = m, m = n, n = l; + uint64_t binding_addend; + binding->Get(&binding, &binding_addend); + addend += binding_addend; + } + + // It seems likely that setting a binding to itself is a bug + // (although I can imagine this might turn out to be helpful to + // permit). + assert(binding != this); + + if (base_ != this) { + // Set the other bindings on our chain as well. Note that this + // is sufficient even though binding relationships form trees: + // All binding operations traverse their chains to the end, and + // all bindings related to us share some tail of our chain, so + // they will see the changes we make here. + base_->Set(binding, addend - addend_); + // We're not going to use base_ any more. + if (base_->Release()) delete base_; + } + + // Adopt BINDING as our base. Note that it should be correct to + // acquire here, after the release above, even though the usual + // reference-counting rules call for acquiring first, and then + // releasing: the self-reference assertion above should have + // complained if BINDING were 'this' or anywhere along our chain, + // so we didn't release BINDING. + if (binding) binding->Acquire(); + base_ = binding; + addend_ = addend; + } +} + +void Label::Binding::Get(Binding **base, uint64_t *addend) { + if (base_ && base_ != this) { + // Recurse to find the end of our reference chain (the root of our + // tree), and then rewrite every binding along the chain to refer + // to it directly, adjusting addends appropriately. (This is why + // this member function isn't this-const.) + Binding *final_base; + uint64_t final_addend; + base_->Get(&final_base, &final_addend); + if (final_base) final_base->Acquire(); + if (base_->Release()) delete base_; + base_ = final_base; + addend_ += final_addend; + } + *base = base_; + *addend = addend_; +} + +template<typename Inserter> +static inline void InsertEndian(test_assembler::Endianness endianness, + size_t size, uint64_t number, Inserter dest) { + assert(size > 0); + if (endianness == kLittleEndian) { + for (size_t i = 0; i < size; i++) { + *dest++ = (char) (number & 0xff); + number >>= 8; + } + } else { + assert(endianness == kBigEndian); + // The loop condition is odd, but it's correct for size_t. + for (size_t i = size - 1; i < size; i--) + *dest++ = (char) ((number >> (i * 8)) & 0xff); + } +} + +Section &Section::Append(Endianness endianness, size_t size, uint64_t number) { + InsertEndian(endianness, size, number, + back_insert_iterator<string>(contents_)); + return *this; +} + +Section &Section::Append(Endianness endianness, size_t size, + const Label &label) { + // If this label's value is known, there's no reason to waste an + // entry in references_ on it. + uint64_t value; + if (label.IsKnownConstant(&value)) + return Append(endianness, size, value); + + // This will get caught when the references are resolved, but it's + // nicer to find out earlier. + assert(endianness != kUnsetEndian); + + references_.push_back(Reference(contents_.size(), endianness, size, label)); + contents_.append(size, 0); + return *this; +} + +#define ENDIANNESS_L kLittleEndian +#define ENDIANNESS_B kBigEndian +#define ENDIANNESS(e) ENDIANNESS_ ## e + +#define DEFINE_SHORT_APPEND_NUMBER_ENDIAN(e, bits) \ + Section &Section::e ## bits(uint ## bits ## _t v) { \ + InsertEndian(ENDIANNESS(e), bits / 8, v, \ + back_insert_iterator<string>(contents_)); \ + return *this; \ + } + +#define DEFINE_SHORT_APPEND_LABEL_ENDIAN(e, bits) \ + Section &Section::e ## bits(const Label &v) { \ + return Append(ENDIANNESS(e), bits / 8, v); \ + } + +// Define L16, B32, and friends. +#define DEFINE_SHORT_APPEND_ENDIAN(e, bits) \ + DEFINE_SHORT_APPEND_NUMBER_ENDIAN(e, bits) \ + DEFINE_SHORT_APPEND_LABEL_ENDIAN(e, bits) + +DEFINE_SHORT_APPEND_LABEL_ENDIAN(L, 8); +DEFINE_SHORT_APPEND_LABEL_ENDIAN(B, 8); +DEFINE_SHORT_APPEND_ENDIAN(L, 16); +DEFINE_SHORT_APPEND_ENDIAN(L, 32); +DEFINE_SHORT_APPEND_ENDIAN(L, 64); +DEFINE_SHORT_APPEND_ENDIAN(B, 16); +DEFINE_SHORT_APPEND_ENDIAN(B, 32); +DEFINE_SHORT_APPEND_ENDIAN(B, 64); + +#define DEFINE_SHORT_APPEND_NUMBER_DEFAULT(bits) \ + Section &Section::D ## bits(uint ## bits ## _t v) { \ + InsertEndian(endianness_, bits / 8, v, \ + back_insert_iterator<string>(contents_)); \ + return *this; \ + } +#define DEFINE_SHORT_APPEND_LABEL_DEFAULT(bits) \ + Section &Section::D ## bits(const Label &v) { \ + return Append(endianness_, bits / 8, v); \ + } +#define DEFINE_SHORT_APPEND_DEFAULT(bits) \ + DEFINE_SHORT_APPEND_NUMBER_DEFAULT(bits) \ + DEFINE_SHORT_APPEND_LABEL_DEFAULT(bits) + +DEFINE_SHORT_APPEND_LABEL_DEFAULT(8) +DEFINE_SHORT_APPEND_DEFAULT(16); +DEFINE_SHORT_APPEND_DEFAULT(32); +DEFINE_SHORT_APPEND_DEFAULT(64); + +Section &Section::Append(const Section §ion) { + size_t base = contents_.size(); + contents_.append(section.contents_); + for (vector<Reference>::const_iterator it = section.references_.begin(); + it != section.references_.end(); it++) + references_.push_back(Reference(base + it->offset, it->endianness, + it->size, it->label)); + return *this; +} + +Section &Section::LEB128(long long value) { + while (value < -0x40 || 0x3f < value) { + contents_ += (value & 0x7f) | 0x80; + if (value < 0) + value = (value >> 7) | ~(((unsigned long long) -1) >> 7); + else + value = (value >> 7); + } + contents_ += value & 0x7f; + return *this; +} + +Section &Section::ULEB128(uint64_t value) { + while (value > 0x7f) { + contents_ += (value & 0x7f) | 0x80; + value = (value >> 7); + } + contents_ += value; + return *this; +} + +Section &Section::Align(size_t alignment, uint8_t pad_byte) { + // ALIGNMENT must be a power of two. + assert(((alignment - 1) & alignment) == 0); + size_t new_size = (contents_.size() + alignment - 1) & ~(alignment - 1); + contents_.append(new_size - contents_.size(), pad_byte); + assert((contents_.size() & (alignment - 1)) == 0); + return *this; +} + +void Section::Clear() { + contents_.clear(); + references_.clear(); +} + +bool Section::GetContents(string *contents) { + // For each label reference, find the label's value, and patch it into + // the section's contents. + for (size_t i = 0; i < references_.size(); i++) { + Reference &r = references_[i]; + uint64_t value; + if (!r.label.IsKnownConstant(&value)) { + fprintf(stderr, "Undefined label #%zu at offset 0x%zx\n", i, r.offset); + return false; + } + assert(r.offset < contents_.size()); + assert(contents_.size() - r.offset >= r.size); + InsertEndian(r.endianness, r.size, value, contents_.begin() + r.offset); + } + contents->clear(); + std::swap(contents_, *contents); + references_.clear(); + return true; +} + +} // namespace test_assembler +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/test_assembler.h b/toolkit/crashreporter/google-breakpad/src/common/test_assembler.h new file mode 100644 index 0000000000..373dbebacd --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/test_assembler.h @@ -0,0 +1,484 @@ +// -*- mode: C++ -*- + +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// test-assembler.h: interface to class for building complex binary streams. + +// To test the Breakpad symbol dumper and processor thoroughly, for +// all combinations of host system and minidump processor +// architecture, we need to be able to easily generate complex test +// data like debugging information and minidump files. +// +// For example, if we want our unit tests to provide full code +// coverage for stack walking, it may be difficult to persuade the +// compiler to generate every possible sort of stack walking +// information that we want to support; there are probably DWARF CFI +// opcodes that GCC never emits. Similarly, if we want to test our +// error handling, we will need to generate damaged minidumps or +// debugging information that (we hope) the client or compiler will +// never produce on its own. +// +// google_breakpad::TestAssembler provides a predictable and +// (relatively) simple way to generate complex formatted data streams +// like minidumps and CFI. Furthermore, because TestAssembler is +// portable, developers without access to (say) Visual Studio or a +// SPARC assembler can still work on test data for those targets. + +#ifndef PROCESSOR_TEST_ASSEMBLER_H_ +#define PROCESSOR_TEST_ASSEMBLER_H_ + +#include <list> +#include <vector> +#include <string> + +#include "common/using_std_string.h" +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +using std::list; +using std::vector; + +namespace test_assembler { + +// A Label represents a value not yet known that we need to store in a +// section. As long as all the labels a section refers to are defined +// by the time we retrieve its contents as bytes, we can use undefined +// labels freely in that section's construction. +// +// A label can be in one of three states: +// - undefined, +// - defined as the sum of some other label and a constant, or +// - a constant. +// +// A label's value never changes, but it can accumulate constraints. +// Adding labels and integers is permitted, and yields a label. +// Subtracting a constant from a label is permitted, and also yields a +// label. Subtracting two labels that have some relationship to each +// other is permitted, and yields a constant. +// +// For example: +// +// Label a; // a's value is undefined +// Label b; // b's value is undefined +// { +// Label c = a + 4; // okay, even though a's value is unknown +// b = c + 4; // also okay; b is now a+8 +// } +// Label d = b - 2; // okay; d == a+6, even though c is gone +// d.Value(); // error: d's value is not yet known +// d - a; // is 6, even though their values are not known +// a = 12; // now b == 20, and d == 18 +// d.Value(); // 18: no longer an error +// b.Value(); // 20 +// d = 10; // error: d is already defined. +// +// Label objects' lifetimes are unconstrained: notice that, in the +// above example, even though a and b are only related through c, and +// c goes out of scope, the assignment to a sets b's value as well. In +// particular, it's not necessary to ensure that a Label lives beyond +// Sections that refer to it. +class Label { + public: + Label(); // An undefined label. + Label(uint64_t value); // A label with a fixed value + Label(const Label &value); // A label equal to another. + ~Label(); + + // Return this label's value; it must be known. + // + // Providing this as a cast operator is nifty, but the conversions + // happen in unexpected places. In particular, ISO C++ says that + // Label + size_t becomes ambigious, because it can't decide whether + // to convert the Label to a uint64_t and then to a size_t, or use + // the overloaded operator that returns a new label, even though the + // former could fail if the label is not yet defined and the latter won't. + uint64_t Value() const; + + Label &operator=(uint64_t value); + Label &operator=(const Label &value); + Label operator+(uint64_t addend) const; + Label operator-(uint64_t subtrahend) const; + uint64_t operator-(const Label &subtrahend) const; + + // We could also provide == and != that work on undefined, but + // related, labels. + + // Return true if this label's value is known. If VALUE_P is given, + // set *VALUE_P to the known value if returning true. + bool IsKnownConstant(uint64_t *value_p = NULL) const; + + // Return true if the offset from LABEL to this label is known. If + // OFFSET_P is given, set *OFFSET_P to the offset when returning true. + // + // You can think of l.KnownOffsetFrom(m, &d) as being like 'd = l-m', + // except that it also returns a value indicating whether the + // subtraction is possible given what we currently know of l and m. + // It can be possible even if we don't know l and m's values. For + // example: + // + // Label l, m; + // m = l + 10; + // l.IsKnownConstant(); // false + // m.IsKnownConstant(); // false + // uint64_t d; + // l.IsKnownOffsetFrom(m, &d); // true, and sets d to -10. + // l-m // -10 + // m-l // 10 + // m.Value() // error: m's value is not known + bool IsKnownOffsetFrom(const Label &label, uint64_t *offset_p = NULL) const; + + private: + // A label's value, or if that is not yet known, how the value is + // related to other labels' values. A binding may be: + // - a known constant, + // - constrained to be equal to some other binding plus a constant, or + // - unconstrained, and free to take on any value. + // + // Many labels may point to a single binding, and each binding may + // refer to another, so bindings and labels form trees whose leaves + // are labels, whose interior nodes (and roots) are bindings, and + // where links point from children to parents. Bindings are + // reference counted, allowing labels to be lightweight, copyable, + // assignable, placed in containers, and so on. + class Binding { + public: + Binding(); + Binding(uint64_t addend); + ~Binding(); + + // Increment our reference count. + void Acquire() { reference_count_++; }; + // Decrement our reference count, and return true if it is zero. + bool Release() { return --reference_count_ == 0; } + + // Set this binding to be equal to BINDING + ADDEND. If BINDING is + // NULL, then set this binding to the known constant ADDEND. + // Update every binding on this binding's chain to point directly + // to BINDING, or to be a constant, with addends adjusted + // appropriately. + void Set(Binding *binding, uint64_t value); + + // Return what we know about the value of this binding. + // - If this binding's value is a known constant, set BASE to + // NULL, and set ADDEND to its value. + // - If this binding is not a known constant but related to other + // bindings, set BASE to the binding at the end of the relation + // chain (which will always be unconstrained), and set ADDEND to the + // value to add to that binding's value to get this binding's + // value. + // - If this binding is unconstrained, set BASE to this, and leave + // ADDEND unchanged. + void Get(Binding **base, uint64_t *addend); + + private: + // There are three cases: + // + // - A binding representing a known constant value has base_ NULL, + // and addend_ equal to the value. + // + // - A binding representing a completely unconstrained value has + // base_ pointing to this; addend_ is unused. + // + // - A binding whose value is related to some other binding's + // value has base_ pointing to that other binding, and addend_ + // set to the amount to add to that binding's value to get this + // binding's value. We only represent relationships of the form + // x = y+c. + // + // Thus, the bind_ links form a chain terminating in either a + // known constant value or a completely unconstrained value. Most + // operations on bindings do path compression: they change every + // binding on the chain to point directly to the final value, + // adjusting addends as appropriate. + Binding *base_; + uint64_t addend_; + + // The number of Labels and Bindings pointing to this binding. + // (When a binding points to itself, indicating a completely + // unconstrained binding, that doesn't count as a reference.) + int reference_count_; + }; + + // This label's value. + Binding *value_; +}; + +inline Label operator+(uint64_t a, const Label &l) { return l + a; } +// Note that int-Label isn't defined, as negating a Label is not an +// operation we support. + +// Conventions for representing larger numbers as sequences of bytes. +enum Endianness { + kBigEndian, // Big-endian: the most significant byte comes first. + kLittleEndian, // Little-endian: the least significant byte comes first. + kUnsetEndian, // used internally +}; + +// A section is a sequence of bytes, constructed by appending bytes +// to the end. Sections have a convenient and flexible set of member +// functions for appending data in various formats: big-endian and +// little-endian signed and unsigned values of different sizes; +// LEB128 and ULEB128 values (see below), and raw blocks of bytes. +// +// If you need to append a value to a section that is not convenient +// to compute immediately, you can create a label, append the +// label's value to the section, and then set the label's value +// later, when it's convenient to do so. Once a label's value is +// known, the section class takes care of updating all previously +// appended references to it. +// +// Once all the labels to which a section refers have had their +// values determined, you can get a copy of the section's contents +// as a string. +// +// Note that there is no specified "start of section" label. This is +// because there are typically several different meanings for "the +// start of a section": the offset of the section within an object +// file, the address in memory at which the section's content appear, +// and so on. It's up to the code that uses the Section class to +// keep track of these explicitly, as they depend on the application. +class Section { + public: + Section(Endianness endianness = kUnsetEndian) + : endianness_(endianness) { }; + + // A base class destructor should be either public and virtual, + // or protected and nonvirtual. + virtual ~Section() { }; + + // Set the default endianness of this section to ENDIANNESS. This + // sets the behavior of the D<N> appending functions. If the + // assembler's default endianness was set, this is the + void set_endianness(Endianness endianness) { + endianness_ = endianness; + } + + // Return the default endianness of this section. + Endianness endianness() const { return endianness_; } + + // Append the SIZE bytes at DATA or the contents of STRING to the + // end of this section. Return a reference to this section. + Section &Append(const uint8_t *data, size_t size) { + contents_.append(reinterpret_cast<const char *>(data), size); + return *this; + }; + Section &Append(const string &data) { + contents_.append(data); + return *this; + }; + + // Append SIZE copies of BYTE to the end of this section. Return a + // reference to this section. + Section &Append(size_t size, uint8_t byte) { + contents_.append(size, (char) byte); + return *this; + } + + // Append NUMBER to this section. ENDIANNESS is the endianness to + // use to write the number. SIZE is the length of the number in + // bytes. Return a reference to this section. + Section &Append(Endianness endianness, size_t size, uint64_t number); + Section &Append(Endianness endianness, size_t size, const Label &label); + + // Append SECTION to the end of this section. The labels SECTION + // refers to need not be defined yet. + // + // Note that this has no effect on any Labels' values, or on + // SECTION. If placing SECTION within 'this' provides new + // constraints on existing labels' values, then it's up to the + // caller to fiddle with those labels as needed. + Section &Append(const Section §ion); + + // Append the contents of DATA as a series of bytes terminated by + // a NULL character. + Section &AppendCString(const string &data) { + Append(data); + contents_ += '\0'; + return *this; + } + + // Append at most SIZE bytes from DATA; if DATA is less than SIZE bytes + // long, pad with '\0' characters. + Section &AppendCString(const string &data, size_t size) { + contents_.append(data, 0, size); + if (data.size() < size) + Append(size - data.size(), 0); + return *this; + } + + // Append VALUE or LABEL to this section, with the given bit width and + // endianness. Return a reference to this section. + // + // The names of these functions have the form <ENDIANNESS><BITWIDTH>: + // <ENDIANNESS> is either 'L' (little-endian, least significant byte first), + // 'B' (big-endian, most significant byte first), or + // 'D' (default, the section's default endianness) + // <BITWIDTH> is 8, 16, 32, or 64. + // + // Since endianness doesn't matter for a single byte, all the + // <BITWIDTH>=8 functions are equivalent. + // + // These can be used to write both signed and unsigned values, as + // the compiler will properly sign-extend a signed value before + // passing it to the function, at which point the function's + // behavior is the same either way. + Section &L8(uint8_t value) { contents_ += value; return *this; } + Section &B8(uint8_t value) { contents_ += value; return *this; } + Section &D8(uint8_t value) { contents_ += value; return *this; } + Section &L16(uint16_t), &L32(uint32_t), &L64(uint64_t), + &B16(uint16_t), &B32(uint32_t), &B64(uint64_t), + &D16(uint16_t), &D32(uint32_t), &D64(uint64_t); + Section &L8(const Label &label), &L16(const Label &label), + &L32(const Label &label), &L64(const Label &label), + &B8(const Label &label), &B16(const Label &label), + &B32(const Label &label), &B64(const Label &label), + &D8(const Label &label), &D16(const Label &label), + &D32(const Label &label), &D64(const Label &label); + + // Append VALUE in a signed LEB128 (Little-Endian Base 128) form. + // + // The signed LEB128 representation of an integer N is a variable + // number of bytes: + // + // - If N is between -0x40 and 0x3f, then its signed LEB128 + // representation is a single byte whose value is N. + // + // - Otherwise, its signed LEB128 representation is (N & 0x7f) | + // 0x80, followed by the signed LEB128 representation of N / 128, + // rounded towards negative infinity. + // + // In other words, we break VALUE into groups of seven bits, put + // them in little-endian order, and then write them as eight-bit + // bytes with the high bit on all but the last. + // + // Note that VALUE cannot be a Label (we would have to implement + // relaxation). + Section &LEB128(long long value); + + // Append VALUE in unsigned LEB128 (Little-Endian Base 128) form. + // + // The unsigned LEB128 representation of an integer N is a variable + // number of bytes: + // + // - If N is between 0 and 0x7f, then its unsigned LEB128 + // representation is a single byte whose value is N. + // + // - Otherwise, its unsigned LEB128 representation is (N & 0x7f) | + // 0x80, followed by the unsigned LEB128 representation of N / + // 128, rounded towards negative infinity. + // + // Note that VALUE cannot be a Label (we would have to implement + // relaxation). + Section &ULEB128(uint64_t value); + + // Jump to the next location aligned on an ALIGNMENT-byte boundary, + // relative to the start of the section. Fill the gap with PAD_BYTE. + // ALIGNMENT must be a power of two. Return a reference to this + // section. + Section &Align(size_t alignment, uint8_t pad_byte = 0); + + // Clear the contents of this section. + void Clear(); + + // Return the current size of the section. + size_t Size() const { return contents_.size(); } + + // Return a label representing the start of the section. + // + // It is up to the user whether this label represents the section's + // position in an object file, the section's address in memory, or + // what have you; some applications may need both, in which case + // this simple-minded interface won't be enough. This class only + // provides a single start label, for use with the Here and Mark + // member functions. + // + // Ideally, we'd provide this in a subclass that actually knows more + // about the application at hand and can provide an appropriate + // collection of start labels. But then the appending member + // functions like Append and D32 would return a reference to the + // base class, not the derived class, and the chaining won't work. + // Since the only value here is in pretty notation, that's a fatal + // flaw. + Label start() const { return start_; } + + // Return a label representing the point at which the next Appended + // item will appear in the section, relative to start(). + Label Here() const { return start_ + Size(); } + + // Set *LABEL to Here, and return a reference to this section. + Section &Mark(Label *label) { *label = Here(); return *this; } + + // If there are no undefined label references left in this + // section, set CONTENTS to the contents of this section, as a + // string, and clear this section. Return true on success, or false + // if there were still undefined labels. + bool GetContents(string *contents); + + private: + // Used internally. A reference to a label's value. + struct Reference { + Reference(size_t set_offset, Endianness set_endianness, size_t set_size, + const Label &set_label) + : offset(set_offset), endianness(set_endianness), size(set_size), + label(set_label) { } + + // The offset of the reference within the section. + size_t offset; + + // The endianness of the reference. + Endianness endianness; + + // The size of the reference. + size_t size; + + // The label to which this is a reference. + Label label; + }; + + // The default endianness of this section. + Endianness endianness_; + + // The contents of the section. + string contents_; + + // References to labels within those contents. + vector<Reference> references_; + + // A label referring to the beginning of the section. + Label start_; +}; + +} // namespace test_assembler +} // namespace google_breakpad + +#endif // PROCESSOR_TEST_ASSEMBLER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/test_assembler_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/test_assembler_unittest.cc new file mode 100644 index 0000000000..94b5a5ce5f --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/test_assembler_unittest.cc @@ -0,0 +1,1662 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> + +// test_assembler_unittest.cc: Unit tests for google_breakpad::TestAssembler. + +#include <string> +#include <string.h> + +#include "breakpad_googletest_includes.h" +#include "common/test_assembler.h" +#include "common/using_std_string.h" + +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::Section; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; +using testing::Test; + +TEST(ConstructLabel, Simple) { + Label l; +} + +TEST(ConstructLabel, Undefined) { + Label l; + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(ConstructLabelDeathTest, Undefined) { + Label l; + ASSERT_DEATH(l.Value(), "IsKnownConstant\\(&v\\)"); +} + +TEST(ConstructLabel, Constant) { + Label l(0x060b9f974eaf301eULL); + uint64_t v; + EXPECT_TRUE(l.IsKnownConstant(&v)); + EXPECT_EQ(v, 0x060b9f974eaf301eULL); + EXPECT_EQ(l.Value(), 0x060b9f974eaf301eULL); +} + +TEST(ConstructLabel, Copy) { + Label l; + Label m(l); + uint64_t v; + EXPECT_TRUE(l.IsKnownOffsetFrom(m, &v)); + EXPECT_EQ(0U, v); +} + +// The left-hand-side of a label assignment can be either +// unconstrained, related, or known. The right-hand-side can be any of +// those, or an integer. +TEST(Assignment, UnconstrainedToUnconstrained) { + Label l, m; + l = m; + EXPECT_EQ(0U, l-m); + EXPECT_TRUE(l.IsKnownOffsetFrom(m)); + uint64_t d; + EXPECT_TRUE(l.IsKnownOffsetFrom(m, &d)); + EXPECT_EQ(0U, d); + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(Assignment, UnconstrainedToRelated) { + Label l, m, n; + l = n; + l = m; + EXPECT_EQ(0U, l-m); + EXPECT_TRUE(l.IsKnownOffsetFrom(m)); + uint64_t d; + EXPECT_TRUE(l.IsKnownOffsetFrom(m, &d)); + EXPECT_EQ(0U, d); + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(Assignment, UnconstrainedToKnown) { + Label l, m; + l = 0x8fd16e55b20a39c1ULL; + l = m; + EXPECT_EQ(0U, l-m); + EXPECT_TRUE(l.IsKnownOffsetFrom(m)); + uint64_t d; + EXPECT_TRUE(l.IsKnownOffsetFrom(m, &d)); + EXPECT_EQ(0U, d); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x8fd16e55b20a39c1ULL, m.Value()); +} + +TEST(Assignment, RelatedToUnconstrained) { + Label l, m, n; + m = n; + l = m; + EXPECT_EQ(0U, l-n); + EXPECT_TRUE(l.IsKnownOffsetFrom(n)); + uint64_t d; + EXPECT_TRUE(l.IsKnownOffsetFrom(n, &d)); + EXPECT_EQ(0U, d); + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(Assignment, RelatedToRelated) { + Label l, m, n, o; + l = n; + m = o; + l = m; + EXPECT_EQ(0U, n-o); + EXPECT_TRUE(n.IsKnownOffsetFrom(o)); + uint64_t d; + EXPECT_TRUE(n.IsKnownOffsetFrom(o, &d)); + EXPECT_EQ(0U, d); + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(Assignment, RelatedToKnown) { + Label l, m, n; + m = n; + l = 0xd2011f8c82ad56f2ULL; + l = m; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0xd2011f8c82ad56f2ULL, l.Value()); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0xd2011f8c82ad56f2ULL, m.Value()); + EXPECT_TRUE(n.IsKnownConstant()); + EXPECT_EQ(0xd2011f8c82ad56f2ULL, n.Value()); +} + +TEST(Assignment, KnownToUnconstrained) { + Label l, m; + m = 0x50b024c0d6073887ULL; + l = m; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0x50b024c0d6073887ULL, l.Value()); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x50b024c0d6073887ULL, m.Value()); +} + +TEST(Assignment, KnownToRelated) { + Label l, m, n; + l = n; + m = 0x5348883655c727e5ULL; + l = m; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0x5348883655c727e5ULL, l.Value()); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x5348883655c727e5ULL, m.Value()); + EXPECT_TRUE(n.IsKnownConstant()); + EXPECT_EQ(0x5348883655c727e5ULL, n.Value()); +} + +TEST(Assignment, KnownToKnown) { + Label l, m; + l = 0x36c209c20987564eULL; + m = 0x36c209c20987564eULL; + l = m; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0x36c209c20987564eULL, l.Value()); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x36c209c20987564eULL, m.Value()); +} + +TEST(Assignment, ConstantToUnconstrained) { + Label l; + l = 0xc02495f4d7f5a957ULL; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0xc02495f4d7f5a957ULL, l.Value()); +} + +TEST(Assignment, ConstantToRelated) { + Label l, m; + l = m; + l = 0x4577901cf275488dULL; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0x4577901cf275488dULL, l.Value()); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x4577901cf275488dULL, m.Value()); +} + +TEST(Assignment, ConstantToKnown) { + Label l; + l = 0xec0b9c369b7e8ea7ULL; + l = 0xec0b9c369b7e8ea7ULL; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0xec0b9c369b7e8ea7ULL, l.Value()); +} + +TEST(AssignmentDeathTest, Self) { + Label l; + ASSERT_DEATH(l = l, "binding != this"); +} + +TEST(AssignmentDeathTest, IndirectCycle) { + Label l, m, n; + l = m; + m = n; + ASSERT_DEATH(n = l, "binding != this"); +} + +TEST(AssignmentDeathTest, Cycle) { + Label l, m, n, o; + l = m; + m = n; + o = n; + ASSERT_DEATH(o = l, "binding != this"); +} + +TEST(Addition, LabelConstant) { + Label l, m; + m = l + 0x5248d93e8bbe9497ULL; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + uint64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(0x5248d93e8bbe9497ULL, d); + EXPECT_FALSE(m.IsKnownConstant()); +} + +TEST(Addition, ConstantLabel) { + Label l, m; + m = 0xf51e94e00d6e3c84ULL + l; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + uint64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(0xf51e94e00d6e3c84ULL, d); + EXPECT_FALSE(m.IsKnownConstant()); +} + +TEST(Addition, KnownLabelConstant) { + Label l, m; + l = 0x16286307042ce0d8ULL; + m = l + 0x3fdddd91306719d7ULL; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + uint64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(0x3fdddd91306719d7ULL, d); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x16286307042ce0d8ULL + 0x3fdddd91306719d7ULL, m.Value()); +} + +TEST(Addition, ConstantKnownLabel) { + Label l, m; + l = 0x50f62d0cdd1031deULL; + m = 0x1b13462d8577c538ULL + l; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + uint64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(0x1b13462d8577c538ULL, d); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x50f62d0cdd1031deULL + 0x1b13462d8577c538ULL, m.Value()); +} + +TEST(Subtraction, LabelConstant) { + Label l, m; + m = l - 0x0620884d21d3138eULL; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + uint64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(-0x0620884d21d3138eULL, d); + EXPECT_FALSE(m.IsKnownConstant()); +} + +TEST(Subtraction, KnownLabelConstant) { + Label l, m; + l = 0x6237fbaf9ef7929eULL; + m = l - 0x317730995d2ab6eeULL; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + uint64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(-0x317730995d2ab6eeULL, d); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x6237fbaf9ef7929eULL - 0x317730995d2ab6eeULL, m.Value()); +} + +TEST(SubtractionDeathTest, LabelLabel) { + Label l, m; + ASSERT_DEATH(l - m, "IsKnownOffsetFrom\\(label, &offset\\)"); +} + +TEST(Subtraction, LabelLabel) { + Label l, m; + l = m + 0x7fa77ec63e28a17aULL; + EXPECT_EQ(0x7fa77ec63e28a17aULL, l - m); + EXPECT_EQ(-0x7fa77ec63e28a17aULL, m - l); +} + +TEST(IsKnownConstant, Undefined) { + Label l; + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(IsKnownConstant, RelatedLabel) { + Label l, m; + l = m; + EXPECT_FALSE(l.IsKnownConstant()); + EXPECT_FALSE(m.IsKnownConstant()); +} + +TEST(IsKnownConstant, Constant) { + Label l; + l = 0xf374b1bdd6a22576ULL; + EXPECT_TRUE(l.IsKnownConstant()); +} + +TEST(IsKnownOffsetFrom, Unrelated) { + Label l, m; + EXPECT_FALSE(l.IsKnownOffsetFrom(m)); +} + +TEST(IsKnownOffsetFrom, Related) { + Label l, m; + l = m; + EXPECT_TRUE(l.IsKnownOffsetFrom(m)); +} + +// Test the construction of chains of related labels, and the +// propagation of values through them. +// +// Although the relations between labels are supposed to behave +// symmetrically --- that is, 'a = b' should put a and b in +// indistinguishable states --- there's a distinction made internally +// between the target (a) and the source (b). +// +// So there are five test axes to cover: +// +// - Do we construct the chain with assignment ("Assign") or with constructors +// ("Construct")? +// +// - Do we set the value of the label at the start of the chain +// ("Start") or the label at the end ("End")? +// +// - Are we testing the propagation of a relationship between variable +// values ("Relation"), or the propagation of a known constant value +// ("Value")? +// +// - Do we set the value before building the chain ("Before") or after +// the chain has been built ("After")? +// +// - Do we add new relationships to the end of the existing chain +// ("Forward") or to the beginning ("Backward")? +// +// Of course, "Construct" and "Backward" can't be combined, which +// eliminates eight combinations, and "Construct", "End", and "Before" +// can't be combined, which eliminates two more, so there are are 22 +// combinations, not 32. + +TEST(LabelChain, AssignStartRelationBeforeForward) { + Label a, b, c, d; + Label x; + a = x; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, AssignStartRelationBeforeBackward) { + Label a, b, c, d; + Label x; + a = x; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, AssignStartRelationAfterForward) { + Label a, b, c, d; + Label x; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + a = x; + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, AssignStartRelationAfterBackward) { + Label a, b, c, d; + Label x; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + a = x; + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, AssignStartValueBeforeForward) { + Label a, b, c, d; + a = 0xa131200190546ac2ULL; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + EXPECT_EQ(0xa131200190546ac2ULL + 0x111U, d.Value()); + EXPECT_EQ(0xa131200190546ac2ULL + 0x11U, c.Value()); + EXPECT_EQ(0xa131200190546ac2ULL + 0x1U, b.Value()); + EXPECT_EQ(0xa131200190546ac2ULL + 0U, a.Value()); +} + +TEST(LabelChain, AssignStartValueBeforeBackward) { + Label a, b, c, d; + a = 0x8da17e1670ad4fa2ULL; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + EXPECT_EQ(0x8da17e1670ad4fa2ULL + 0x111U, d.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL + 0x11U, c.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL + 0x1U, b.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL + 0U, a.Value()); +} + +TEST(LabelChain, AssignStartValueAfterForward) { + Label a, b, c, d; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + a = 0x99b8f51bafd41adaULL; + EXPECT_EQ(0x99b8f51bafd41adaULL + 0x111U, d.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL + 0x11U, c.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL + 0x1U, b.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL + 0U, a.Value()); +} + +TEST(LabelChain, AssignStartValueAfterBackward) { + Label a, b, c, d; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + a = 0xc86ca1d97ab5df6eULL; + EXPECT_EQ(0xc86ca1d97ab5df6eULL + 0x111U, d.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL + 0x11U, c.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL + 0x1U, b.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL + 0U, a.Value()); +} + +TEST(LabelChain, AssignEndRelationBeforeForward) { + Label a, b, c, d; + Label x; + x = d; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + EXPECT_EQ(-(uint64_t)0x111U, a-x); + EXPECT_EQ(-(uint64_t)0x110U, b-x); + EXPECT_EQ(-(uint64_t)0x100U, c-x); + EXPECT_EQ(-(uint64_t)0U, d-x); +} + +TEST(LabelChain, AssignEndRelationBeforeBackward) { + Label a, b, c, d; + Label x; + x = d; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + EXPECT_EQ(-(uint64_t)0x111U, a-x); + EXPECT_EQ(-(uint64_t)0x110U, b-x); + EXPECT_EQ(-(uint64_t)0x100U, c-x); + EXPECT_EQ(-(uint64_t)0U, d-x); +} + +TEST(LabelChain, AssignEndRelationAfterForward) { + Label a, b, c, d; + Label x; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + x = d; + EXPECT_EQ(-(uint64_t)0x111U, a-x); + EXPECT_EQ(-(uint64_t)0x110U, b-x); + EXPECT_EQ(-(uint64_t)0x100U, c-x); + EXPECT_EQ(-(uint64_t)0x000U, d-x); +} + +TEST(LabelChain, AssignEndRelationAfterBackward) { + Label a, b, c, d; + Label x; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + x = d; + EXPECT_EQ(-(uint64_t)0x111U, a-x); + EXPECT_EQ(-(uint64_t)0x110U, b-x); + EXPECT_EQ(-(uint64_t)0x100U, c-x); + EXPECT_EQ(-(uint64_t)0x000U, d-x); +} + +TEST(LabelChain, AssignEndValueBeforeForward) { + Label a, b, c, d; + d = 0xa131200190546ac2ULL; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + EXPECT_EQ(0xa131200190546ac2ULL - 0x111, a.Value()); + EXPECT_EQ(0xa131200190546ac2ULL - 0x110, b.Value()); + EXPECT_EQ(0xa131200190546ac2ULL - 0x100, c.Value()); + EXPECT_EQ(0xa131200190546ac2ULL - 0x000, d.Value()); +} + +TEST(LabelChain, AssignEndValueBeforeBackward) { + Label a, b, c, d; + d = 0x8da17e1670ad4fa2ULL; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + EXPECT_EQ(0x8da17e1670ad4fa2ULL - 0x111, a.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL - 0x110, b.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL - 0x100, c.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL - 0x000, d.Value()); +} + +TEST(LabelChain, AssignEndValueAfterForward) { + Label a, b, c, d; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + d = 0x99b8f51bafd41adaULL; + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x111, a.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x110, b.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x100, c.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x000, d.Value()); +} + +TEST(LabelChain, AssignEndValueAfterBackward) { + Label a, b, c, d; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + d = 0xc86ca1d97ab5df6eULL; + EXPECT_EQ(0xc86ca1d97ab5df6eULL - 0x111, a.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL - 0x110, b.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL - 0x100, c.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL - 0x000, d.Value()); +} + +TEST(LabelChain, ConstructStartRelationBeforeForward) { + Label x; + Label a(x); + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, ConstructStartRelationAfterForward) { + Label x; + Label a; + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + a = x; + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, ConstructStartValueBeforeForward) { + Label a(0x5d234d177d01ccc8ULL); + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + EXPECT_EQ(0x5d234d177d01ccc8ULL + 0x111U, d.Value()); + EXPECT_EQ(0x5d234d177d01ccc8ULL + 0x011U, c.Value()); + EXPECT_EQ(0x5d234d177d01ccc8ULL + 0x001U, b.Value()); + EXPECT_EQ(0x5d234d177d01ccc8ULL + 0x000U, a.Value()); +} + +TEST(LabelChain, ConstructStartValueAfterForward) { + Label a; + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + a = 0xded85d54586e84fcULL; + EXPECT_EQ(0xded85d54586e84fcULL + 0x111U, d.Value()); + EXPECT_EQ(0xded85d54586e84fcULL + 0x011U, c.Value()); + EXPECT_EQ(0xded85d54586e84fcULL + 0x001U, b.Value()); + EXPECT_EQ(0xded85d54586e84fcULL + 0x000U, a.Value()); +} + +TEST(LabelChain, ConstructEndRelationAfterForward) { + Label x; + Label a; + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + x = d; + EXPECT_EQ(-(uint64_t)0x111U, a-x); + EXPECT_EQ(-(uint64_t)0x110U, b-x); + EXPECT_EQ(-(uint64_t)0x100U, c-x); + EXPECT_EQ(-(uint64_t)0x000U, d-x); +} + +TEST(LabelChain, ConstructEndValueAfterForward) { + Label a; + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + d = 0x99b8f51bafd41adaULL; + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x111, a.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x110, b.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x100, c.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x000, d.Value()); +} + +TEST(LabelTree, KnownValue) { + Label l, m, n, o, p; + l = m; + m = n; + o = p; + p = n; + l = 0x536b5de3d468a1b5ULL; + EXPECT_EQ(0x536b5de3d468a1b5ULL, o.Value()); +} + +TEST(LabelTree, Related) { + Label l, m, n, o, p; + l = m - 1; + m = n - 10; + o = p + 100; + p = n + 1000; + EXPECT_EQ(1111U, o - l); +} + +TEST(EquationDeathTest, EqualConstants) { + Label m = 0x0d3962f280f07d24ULL; + Label n = 0x0d3962f280f07d24ULL; + m = n; // no death expected +} + +TEST(EquationDeathTest, EqualIndirectConstants) { + Label m = 0xa347f1e5238fe6a1ULL; + Label n; + Label o = n; + n = 0xa347f1e5238fe6a1ULL; + n = m; // no death expected +} + +TEST(EquationDeathTest, ConstantClash) { + Label m = 0xd4cc0f4f630ec741ULL; + Label n = 0x934cd2d8254fc3eaULL; + ASSERT_DEATH(m = n, "addend_ == addend"); +} + +TEST(EquationDeathTest, IndirectConstantClash) { + Label m = 0xd4cc0f4f630ec741ULL; + Label n, o; + n = o; + o = 0xcfbe3b83ac49ce86ULL; + ASSERT_DEATH(m = n, "addend_ == addend"); +} + +// Assigning to a related label may free the next Binding on its +// chain. This test always passes; it is interesting to memory +// checkers and coverage analysis. +TEST(LabelReferenceCount, AssignmentFree) { + Label l; + { + Label m; + l = m; + } + // This should free m's Binding. + l = 0xca8bae92f0376d4fULL; + ASSERT_EQ(0xca8bae92f0376d4fULL, l.Value()); +} + +// Finding the value of a label may free the Binding it refers to. This test +// always passes; it is interesting to memory checkers and coverage analysis. +TEST(LabelReferenceCount, FindValueFree) { + Label l; + { + Label m, n; + l = m; + m = n; + n = 0x7a0b0c576672daafULL; + // At this point, l's Binding refers to m's Binding, which refers + // to n's binding. + } + // Now, l is the only reference keeping the three Bindings alive. + // Resolving its value should free l's and m's original bindings. + ASSERT_EQ(0x7a0b0c576672daafULL, l.Value()); +} + +TEST(ConstructSection, Simple) { + Section s; +} + +TEST(ConstructSection, WithEndian) { + Section s(kBigEndian); +} + +// A fixture class for TestAssembler::Section tests. +class SectionFixture { + public: + Section section; + string contents; + static const uint8_t data[]; + static const size_t data_size; +}; + +const uint8_t SectionFixture::data[] = { + 0x87, 0x4f, 0x43, 0x67, 0x30, 0xd0, 0xd4, 0x0e +}; + +#define I0() +#define I1(a) { a } +#define I2(a,b) { a,b } +#define I3(a,b,c) { a,b,c } +#define I4(a,b,c,d) { a,b,c,d } +#define I5(a,b,c,d,e) { a,b,c,d,e } +#define I6(a,b,c,d,e,f) { a,b,c,d,e,f } +#define I7(a,b,c,d,e,f,g) { a,b,c,d,e,f,g } +#define I8(a,b,c,d,e,f,g,h) { a,b,c,d,e,f,g,h } +#define I9(a,b,c,d,e,f,g,h,i) { a,b,c,d,e,f,g,h,i } +#define ASSERT_BYTES(s, b) \ + do \ + { \ + static const uint8_t expected_bytes[] = b; \ + ASSERT_EQ(sizeof(expected_bytes), s.size()); \ + ASSERT_TRUE(memcmp(s.data(), (const char *) expected_bytes, \ + sizeof(expected_bytes)) == 0); \ + } \ + while(0) + +class Append: public SectionFixture, public Test { }; + +TEST_F(Append, Bytes) { + section.Append(data, sizeof(data)); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_EQ(sizeof(data), contents.size()); + EXPECT_TRUE(0 == memcmp(contents.data(), (const char *) data, sizeof(data))); +} + +TEST_F(Append, BytesTwice) { + section.Append(data, sizeof(data)); + section.Append(data, sizeof(data)); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_EQ(2 * sizeof(data), contents.size()); + ASSERT_TRUE(0 == memcmp(contents.data(), (const char *) data, sizeof(data))); + ASSERT_TRUE(0 == memcmp(contents.data() + sizeof(data), + (const char *) data, sizeof(data))); +} + +TEST_F(Append, String) { + string s1 = "howdy "; + string s2 = "there"; + section.Append(s1); + section.Append(s2); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_STREQ(contents.c_str(), "howdy there"); +} + +TEST_F(Append, CString) { + section.AppendCString("howdy"); + section.AppendCString(""); + section.AppendCString("there"); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_EQ(string("howdy\0\0there\0", 13), contents); +} + +TEST_F(Append, CStringSize) { + section.AppendCString("howdy", 3); + section.AppendCString("there", 5); + section.AppendCString("fred", 6); + section.AppendCString("natalie", 0); + section.AppendCString("", 10); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_EQ(string("howtherefred\0\0\0\0\0\0\0\0\0\0\0\0", 24), contents); +} + +TEST_F(Append, RepeatedBytes) { + section.Append((size_t) 10, '*'); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_STREQ(contents.c_str(), "**********"); +} + +TEST_F(Append, GeneralLE1) { + section.Append(kLittleEndian, 1, 42); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I1(42)); +} + +TEST_F(Append, GeneralLE2) { + section.Append(kLittleEndian, 2, 0x15a1); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0xa1, 0x15)); +} + +TEST_F(Append, GeneralLE3) { + section.Append(kLittleEndian, 3, 0x59ae8d); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x8d, 0xae, 0x59)); +} + +TEST_F(Append, GeneralLE4) { + section.Append(kLittleEndian, 4, 0x51603c56); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I4(0x56, 0x3c, 0x60, 0x51)); +} + +TEST_F(Append, GeneralLE5) { + section.Append(kLittleEndian, 5, 0x385e2803b4ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0xb4, 0x03, 0x28, 0x5e, 0x38)); +} + +TEST_F(Append, GeneralLE6) { + section.Append(kLittleEndian, 6, 0xc7db9534dd1fULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I6(0x1f, 0xdd, 0x34, 0x95, 0xdb, 0xc7)); +} + +TEST_F(Append, GeneralLE7) { + section.Append(kLittleEndian, 7, 0x1445c9f1b843e6ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I7(0xe6, 0x43, 0xb8, 0xf1, 0xc9, 0x45, 0x14)); +} + +TEST_F(Append, GeneralLE8) { + section.Append(kLittleEndian, 8, 0xaf48019dfe5c01e5ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I8(0xe5, 0x01, 0x5c, 0xfe, 0x9d, 0x01, 0x48, 0xaf)); +} + +TEST_F(Append, GeneralBE1) { + section.Append(kBigEndian, 1, 0xd0ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I1(0xd0)); +} + +TEST_F(Append, GeneralBE2) { + section.Append(kBigEndian, 2, 0x2e7eULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2e, 0x7e)); +} + +TEST_F(Append, GeneralBE3) { + section.Append(kBigEndian, 3, 0x37dad6ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x37, 0xda, 0xd6)); +} + +TEST_F(Append, GeneralBE4) { + section.Append(kBigEndian, 4, 0x715935c7ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I4(0x71, 0x59, 0x35, 0xc7)); +} + +TEST_F(Append, GeneralBE5) { + section.Append(kBigEndian, 5, 0x42baeb02b7ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x42, 0xba, 0xeb, 0x02, 0xb7)); +} + +TEST_F(Append, GeneralBE6) { + section.Append(kBigEndian, 6, 0xf1cdf10e7b18ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I6(0xf1, 0xcd, 0xf1, 0x0e, 0x7b, 0x18)); +} + +TEST_F(Append, GeneralBE7) { + section.Append(kBigEndian, 7, 0xf50a724f0b0d20ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I7(0xf5, 0x0a, 0x72, 0x4f, 0x0b, 0x0d, 0x20)); +} + +TEST_F(Append, GeneralBE8) { + section.Append(kBigEndian, 8, 0xa6b2cb5e98dc9c16ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I8(0xa6, 0xb2, 0xcb, 0x5e, 0x98, 0xdc, 0x9c, 0x16)); +} + +TEST_F(Append, GeneralLE1Label) { + Label l; + section.Append(kLittleEndian, 1, l); + l = 42; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I1(42)); +} + +TEST_F(Append, GeneralLE2Label) { + Label l; + section.Append(kLittleEndian, 2, l); + l = 0x15a1; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0xa1, 0x15)); +} + +TEST_F(Append, GeneralLE3Label) { + Label l; + section.Append(kLittleEndian, 3, l); + l = 0x59ae8d; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x8d, 0xae, 0x59)); +} + +TEST_F(Append, GeneralLE4Label) { + Label l; + section.Append(kLittleEndian, 4, l); + l = 0x51603c56; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I4(0x56, 0x3c, 0x60, 0x51)); +} + +TEST_F(Append, GeneralLE5Label) { + Label l; + section.Append(kLittleEndian, 5, l); + l = 0x385e2803b4ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0xb4, 0x03, 0x28, 0x5e, 0x38)); +} + +TEST_F(Append, GeneralLE6Label) { + Label l; + section.Append(kLittleEndian, 6, l); + l = 0xc7db9534dd1fULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I6(0x1f, 0xdd, 0x34, 0x95, 0xdb, 0xc7)); +} + +TEST_F(Append, GeneralLE7Label) { + Label l; + section.Append(kLittleEndian, 7, l); + l = 0x1445c9f1b843e6ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I7(0xe6, 0x43, 0xb8, 0xf1, 0xc9, 0x45, 0x14)); +} + +TEST_F(Append, GeneralLE8Label) { + Label l; + section.Append(kLittleEndian, 8, l); + l = 0xaf48019dfe5c01e5ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I8(0xe5, 0x01, 0x5c, 0xfe, 0x9d, 0x01, 0x48, 0xaf)); +} + +TEST_F(Append, GeneralBE1Label) { + Label l; + section.Append(kBigEndian, 1, l); + l = 0xd0ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I1(0xd0)); +} + +TEST_F(Append, GeneralBE2Label) { + Label l; + section.Append(kBigEndian, 2, l); + l = 0x2e7eULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2e, 0x7e)); +} + +TEST_F(Append, GeneralBE3Label) { + Label l; + section.Append(kBigEndian, 3, l); + l = 0x37dad6ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x37, 0xda, 0xd6)); +} + +TEST_F(Append, GeneralBE4Label) { + Label l; + section.Append(kBigEndian, 4, l); + l = 0x715935c7ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I4(0x71, 0x59, 0x35, 0xc7)); +} + +TEST_F(Append, GeneralBE5Label) { + Label l; + section.Append(kBigEndian, 5, l); + l = 0x42baeb02b7ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x42, 0xba, 0xeb, 0x02, 0xb7)); +} + +TEST_F(Append, GeneralBE6Label) { + Label l; + section.Append(kBigEndian, 6, l); + l = 0xf1cdf10e7b18ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I6(0xf1, 0xcd, 0xf1, 0x0e, 0x7b, 0x18)); +} + +TEST_F(Append, GeneralBE7Label) { + Label l; + section.Append(kBigEndian, 7, l); + l = 0xf50a724f0b0d20ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I7(0xf5, 0x0a, 0x72, 0x4f, 0x0b, 0x0d, 0x20)); +} + +TEST_F(Append, GeneralBE8Label) { + Label l; + section.Append(kBigEndian, 8, l); + l = 0xa6b2cb5e98dc9c16ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I8(0xa6, 0xb2, 0xcb, 0x5e, 0x98, 0xdc, 0x9c, 0x16)); +} + +TEST_F(Append, B8) { + section.Append(1, 0x2a); + section.B8(0xd3U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0xd3)); +} + +TEST_F(Append, B8Label) { + Label l; + section.Append(1, 0x2a); + section.B8(l); + l = 0x4bU; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0x4b)); +} + +TEST_F(Append, B16) { + section.Append(1, 0x2a); + section.B16(0x472aU); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x47, 0x2a)); +} + +TEST_F(Append, B16Label) { + Label l; + section.Append(1, 0x2a); + section.B16(l); + l = 0x55e8U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x55, 0xe8)); +} + +TEST_F(Append, B32) { + section.Append(1, 0x2a); + section.B32(0xbd412cbcU); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0xbd, 0x41, 0x2c, 0xbc)); +} + +TEST_F(Append, B32Label) { + Label l; + section.Append(1, 0x2a); + section.B32(l); + l = 0x208e37d5U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0x20, 0x8e, 0x37, 0xd5)); +} + +TEST_F(Append, B64) { + section.Append(1, 0x2a); + section.B64(0x3402a013111e68adULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0x34, 0x02, 0xa0, 0x13, 0x11, 0x1e, 0x68, 0xad)); +} + +TEST_F(Append, B64Label) { + Label l; + section.Append(1, 0x2a); + section.B64(l); + l = 0x355dbfbb4ac6d57fULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0x35, 0x5d, 0xbf, 0xbb, 0x4a, 0xc6, 0xd5, 0x7f)); +} + +TEST_F(Append, L8) { + section.Append(1, 0x2a); + section.L8(0x26U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0x26)); +} + +TEST_F(Append, L8Label) { + Label l; + section.Append(1, 0x2a); + section.L8(l); + l = 0xa8U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0xa8)); +} + +TEST_F(Append, L16) { + section.Append(1, 0x2a); + section.L16(0xca6dU); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x6d, 0xca)); +} + +TEST_F(Append, L16Label) { + Label l; + section.Append(1, 0x2a); + section.L16(l); + l = 0xd21fU; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x1f, 0xd2)); +} + +TEST_F(Append, L32) { + section.Append(1, 0x2a); + section.L32(0x558f6181U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0x81, 0x61, 0x8f, 0x55)); +} + +TEST_F(Append, L32Label) { + Label l; + section.Append(1, 0x2a); + section.L32(l); + l = 0x4b810f82U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0x82, 0x0f, 0x81, 0x4b)); +} + +TEST_F(Append, L64) { + section.Append(1, 0x2a); + section.L64(0x564384f7579515bfULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0xbf, 0x15, 0x95, 0x57, 0xf7, 0x84, 0x43, 0x56)); +} + +TEST_F(Append, L64Label) { + Label l; + section.Append(1, 0x2a); + section.L64(l); + l = 0x424b1d020667c8dbULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0xdb, 0xc8, 0x67, 0x06, 0x02, 0x1d, 0x4b, 0x42)); +} + +TEST_F(Append, D8Big) { + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D8(0xe6U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0xe6)); +} + +TEST_F(Append, D8BigLabel) { + Label l; + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D8(l); + l = 0xeeU; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0xee)); +} + +TEST_F(Append, D16Big) { + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D16(0x83b1U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x83, 0xb1)); +} + +TEST_F(Append, D16BigLabel) { + Label l; + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D16(l); + l = 0x5b55U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x5b, 0x55)); +} + +TEST_F(Append, D32Big) { + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D32(0xd0b0e431U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0xd0, 0xb0, 0xe4, 0x31)); +} + +TEST_F(Append, D32BigLabel) { + Label l; + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D32(l); + l = 0x312fb340U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0x31, 0x2f, 0xb3, 0x40)); +} + +TEST_F(Append, D64Big) { + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D64(0xb109843500dbcb16ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0xb1, 0x09, 0x84, 0x35, 0x00, 0xdb, 0xcb, 0x16)); +} + +TEST_F(Append, D64BigLabel) { + Label l; + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D64(l); + l = 0x9a0d61b70f671fd7ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0x9a, 0x0d, 0x61, 0xb7, 0x0f, 0x67, 0x1f, 0xd7)); +} + +TEST_F(Append, D8Little) { + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D8(0x42U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0x42)); +} + +TEST_F(Append, D8LittleLabel) { + Label l; + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D8(l); + l = 0x05U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0x05)); +} + +TEST_F(Append, D16Little) { + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D16(0xc5c5U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0xc5, 0xc5)); +} + +TEST_F(Append, D16LittleLabel) { + Label l; + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D16(l); + l = 0xb620U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x20, 0xb6)); +} + +TEST_F(Append, D32Little) { + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D32(0x1a87d0feU); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0xfe, 0xd0, 0x87, 0x1a)); +} + +TEST_F(Append, D32LittleLabel) { + Label l; + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D32(l); + l = 0xb8012d6bU; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0x6b, 0x2d, 0x01, 0xb8)); +} + +TEST_F(Append, D64Little) { + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D64(0x42de75c61375a1deULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0xde, 0xa1, 0x75, 0x13, 0xc6, 0x75, 0xde, 0x42)); +} + +TEST_F(Append, D64LittleLabel) { + Label l; + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D64(l); + l = 0x8b3bececf3fb5312ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0x12, 0x53, 0xfb, 0xf3, 0xec, 0xec, 0x3b, 0x8b)); +} + +TEST_F(Append, Variety) { + Label a, b, c, d, e, f, g, h; + section.Append(kBigEndian, 1, a) + .Append(kLittleEndian, 8, h) + .Append(kBigEndian, 1, 0x8bULL) + .Append(kLittleEndian, 8, 0x0ea56540448f4439ULL) + .Append(kBigEndian, 2, b) + .Append(kLittleEndian, 7, g) + .Append(kBigEndian, 2, 0xcf15ULL) + .Append(kLittleEndian, 7, 0x29694f04c5724aULL) + .Append(kBigEndian, 3, c) + .Append(kLittleEndian, 6, f) + .Append(kBigEndian, 3, 0x8c3ffdULL) + .Append(kLittleEndian, 6, 0x6f11ba80187aULL) + .Append(kBigEndian, 4, d) + .Append(kLittleEndian, 5, e) + .Append(kBigEndian, 4, 0x2fda2472ULL) + .Append(kLittleEndian, 5, 0x0aa02d423fULL) + .Append(kBigEndian, 5, e) + .Append(kLittleEndian, 4, d) + .Append(kBigEndian, 5, 0x53ba432138ULL) + .Append(kLittleEndian, 4, 0xf139ae60ULL) + .Append(kBigEndian, 6, f) + .Append(kLittleEndian, 3, c) + .Append(kBigEndian, 6, 0x168e436af716ULL) + .Append(kLittleEndian, 3, 0x3ef189ULL) + .Append(kBigEndian, 7, g) + .Append(kLittleEndian, 2, b) + .Append(kBigEndian, 7, 0xacd4ef233e47d9ULL) + .Append(kLittleEndian, 2, 0x5311ULL) + .Append(kBigEndian, 8, h) + .Append(kLittleEndian, 1, a) + .Append(kBigEndian, 8, 0x4668d5f1c93637a1ULL) + .Append(kLittleEndian, 1, 0x65ULL); + a = 0x79ac9bd8aa256b35ULL; + b = 0x22d13097ef86c91cULL; + c = 0xf204968b0a05862fULL; + d = 0x163177f15a0eb4ecULL; + e = 0xbd1b0f1d977f2246ULL; + f = 0x2b0842eee83c6461ULL; + g = 0x92f4b928a4bf875eULL; + h = 0x61a199a8f7286ba6ULL; + ASSERT_EQ(8 * 18U, section.Size()); + ASSERT_TRUE(section.GetContents(&contents)); + + static const uint8_t expected[] = { + 0x35, 0xa6, 0x6b, 0x28, 0xf7, 0xa8, 0x99, 0xa1, 0x61, + 0x8b, 0x39, 0x44, 0x8f, 0x44, 0x40, 0x65, 0xa5, 0x0e, + 0xc9, 0x1c, 0x5e, 0x87, 0xbf, 0xa4, 0x28, 0xb9, 0xf4, + 0xcf, 0x15, 0x4a, 0x72, 0xc5, 0x04, 0x4f, 0x69, 0x29, + 0x05, 0x86, 0x2f, 0x61, 0x64, 0x3c, 0xe8, 0xee, 0x42, + 0x8c, 0x3f, 0xfd, 0x7a, 0x18, 0x80, 0xba, 0x11, 0x6f, + 0x5a, 0x0e, 0xb4, 0xec, 0x46, 0x22, 0x7f, 0x97, 0x1d, + 0x2f, 0xda, 0x24, 0x72, 0x3f, 0x42, 0x2d, 0xa0, 0x0a, + 0x1d, 0x97, 0x7f, 0x22, 0x46, 0xec, 0xb4, 0x0e, 0x5a, + 0x53, 0xba, 0x43, 0x21, 0x38, 0x60, 0xae, 0x39, 0xf1, + 0x42, 0xee, 0xe8, 0x3c, 0x64, 0x61, 0x2f, 0x86, 0x05, + 0x16, 0x8e, 0x43, 0x6a, 0xf7, 0x16, 0x89, 0xf1, 0x3e, + 0xf4, 0xb9, 0x28, 0xa4, 0xbf, 0x87, 0x5e, 0x1c, 0xc9, + 0xac, 0xd4, 0xef, 0x23, 0x3e, 0x47, 0xd9, 0x11, 0x53, + 0x61, 0xa1, 0x99, 0xa8, 0xf7, 0x28, 0x6b, 0xa6, 0x35, + 0x46, 0x68, 0xd5, 0xf1, 0xc9, 0x36, 0x37, 0xa1, 0x65, + }; + + ASSERT_TRUE(0 == memcmp(contents.data(), expected, sizeof(expected))); +} + +TEST_F(Append, Section) { + section.Append("murder"); + { + Section middle; + middle.Append(" she"); + section.Append(middle); + } + section.Append(" wrote"); + EXPECT_EQ(16U, section.Size()); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_STREQ(contents.c_str(), "murder she wrote"); +} + +TEST_F(Append, SectionRefs) { + section.Append("sugar "); + Label l; + { + Section middle; + Label m; + middle.B32(m); + section.Append(middle); + m = 0x66726565; + } + section.Append(" jazz"); + EXPECT_EQ(15U, section.Size()); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_STREQ(contents.c_str(), "sugar free jazz"); +} + +TEST_F(Append, LEB128_0) { + section.LEB128(0); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\0", 1), contents); +} + +TEST_F(Append, LEB128_0x3f) { + section.LEB128(0x3f); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x3f", 1), contents); +} + +TEST_F(Append, LEB128_0x40) { + section.LEB128(0x40); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xc0\x00", 2), contents); +} + +TEST_F(Append, LEB128_0x7f) { + section.LEB128(0x7f); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x00", 2), contents); +} + +TEST_F(Append, LEB128_0x80) { + section.LEB128(0x80); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x01", 2), contents); +} + +TEST_F(Append, LEB128_0xff) { + section.LEB128(0xff); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x01", 2), contents); +} + +TEST_F(Append, LEB128_0x1fff) { + section.LEB128(0x1fff); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x3f", 2), contents); +} + +TEST_F(Append, LEB128_0x2000) { + section.LEB128(0x2000); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\xc0\x00", 3), contents); +} + +TEST_F(Append, LEB128_n1) { + section.LEB128(-1); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x7f", 1), contents); +} + +TEST_F(Append, LEB128_n0x40) { + section.LEB128(-0x40); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x40", 1), contents); +} + +TEST_F(Append, LEB128_n0x41) { + section.LEB128(-0x41); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xbf\x7f", 2), contents); +} + +TEST_F(Append, LEB128_n0x7f) { + section.LEB128(-0x7f); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x81\x7f", 2), contents); +} + +TEST_F(Append, LEB128_n0x80) { + section.LEB128(-0x80); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x7f", 2), contents); +} + +TEST_F(Append, LEB128_n0x2000) { + section.LEB128(-0x2000); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x40", 2), contents); +} + +TEST_F(Append, LEB128_n0x2001) { + section.LEB128(-0x2001); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\xbf\x7f", 3), contents); +} + +TEST_F(Append,ULEB128_0) { + section.ULEB128(0); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\0", 1), contents); +} + +TEST_F(Append,ULEB128_1) { + section.ULEB128(1); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x01", 1), contents); +} + +TEST_F(Append,ULEB128_0x3f) { + section.ULEB128(0x3f); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x3f", 1), contents); +} + +TEST_F(Append,ULEB128_0x40) { + section.ULEB128(0x40); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x40", 1), contents); +} + +TEST_F(Append,ULEB128_0x7f) { + section.ULEB128(0x7f); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x7f", 1), contents); +} + +TEST_F(Append,ULEB128_0x80) { + section.ULEB128(0x80); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x01", 2), contents); +} + +TEST_F(Append,ULEB128_0xff) { + section.ULEB128(0xff); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x01", 2), contents); +} + +TEST_F(Append,ULEB128_0x100) { + section.ULEB128(0x100); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x02", 2), contents); +} + +TEST_F(Append,ULEB128_0x1fff) { + section.ULEB128(0x1fff); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x3f", 2), contents); +} + +TEST_F(Append,ULEB128_0x2000) { + section.ULEB128(0x2000); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x40", 2), contents); +} + +TEST_F(Append,ULEB128_0x3fff) { + section.ULEB128(0x3fff); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x7f", 2), contents); +} + +TEST_F(Append,ULEB128_0x4000) { + section.ULEB128(0x4000); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x80\x01", 3), contents); +} + +TEST_F(Append,ULEB128_12857) { + section.ULEB128(12857); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xb9\x64", 2), contents); +} + +TEST_F(Append, LEBChain) { + section.LEB128(-0x80).ULEB128(12857).Append("*"); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x7f\xb9\x64*", 5), contents); +} + + +class GetContents: public SectionFixture, public Test { }; + +TEST_F(GetContents, Undefined) { + Label l; + section.Append(kLittleEndian, 8, l); + ASSERT_FALSE(section.GetContents(&contents)); +} + +TEST_F(GetContents, ClearsContents) { + section.Append((size_t) 10, '*'); + EXPECT_EQ(10U, section.Size()); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(0U, section.Size()); +} + +TEST_F(GetContents, ClearsReferences) { + Label l; + section.Append(kBigEndian, 1, l); + l = 42; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I1(42)); + ASSERT_TRUE(section.GetContents(&contents)); // should not die +} + +class Miscellanea: public SectionFixture, public Test { }; + +TEST_F(Miscellanea, Clear) { + section.Append("howdy"); + Label l; + section.L32(l); + EXPECT_EQ(9U, section.Size()); + section.Clear(); + EXPECT_EQ(0U, section.Size()); + l = 0x8d231bf0U; + ASSERT_TRUE(section.GetContents(&contents)); // should not die +} + +TEST_F(Miscellanea, Align) { + section.Append("*"); + EXPECT_EQ(1U, section.Size()); + section.Align(4).Append("*"); + EXPECT_EQ(5U, section.Size()); + section.Append("*").Align(2); + EXPECT_EQ(6U, section.Size()); +} + +TEST_F(Miscellanea, AlignPad) { + section.Append("*"); + EXPECT_EQ(1U, section.Size()); + section.Align(4, ' ').Append("*"); + EXPECT_EQ(5U, section.Size()); + section.Append("*").Align(2, ' '); + EXPECT_EQ(6U, section.Size()); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_EQ(string("* **"), contents); +} + +TEST_F(Miscellanea, StartHereMark) { + Label m; + section.Append(42, ' ').Mark(&m).Append(13, '+'); + EXPECT_EQ(42U, m - section.start()); + EXPECT_EQ(42U + 13U, section.Here() - section.start()); + EXPECT_FALSE(section.start().IsKnownConstant()); + EXPECT_FALSE(m.IsKnownConstant()); + EXPECT_FALSE(section.Here().IsKnownConstant()); +} + +TEST_F(Miscellanea, Endianness) { + section.set_endianness(kBigEndian); + EXPECT_EQ(kBigEndian, section.endianness()); + section.set_endianness(kLittleEndian); + EXPECT_EQ(kLittleEndian, section.endianness()); +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/testdata/func-line-pairing.h b/toolkit/crashreporter/google-breakpad/src/common/testdata/func-line-pairing.h new file mode 100644 index 0000000000..05538f961b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/testdata/func-line-pairing.h @@ -0,0 +1,676 @@ +// -*- mode: c++ -*- + +// Test data for pairing functions and lines. +// +// For a pair of functions that are adjacent (10,20),(20,25) and a +// pair that are not (10,15),(20,25), we include a test case for every +// possible arrangement of two lines relative to those functions. We +// include cases only for non-empty ranges, since empty functions and +// lines are dropped before we do any pairing. +// +// Each test case is represented by a macro call of the form: +// +// PAIRING(func1_start, func1_end, func2_start, func2_end, +// line1_start, line1_end, line2_start, line2_end, +// func1_num_lines, func2_num_lines, +// func1_line1_start, func1_line1_end, +// func1_line2_start, func1_line2_end, +// func2_line1_start, func2_line1_end, +// func2_line2_start, func2_line2_end, +// uncovered_funcs, uncovered_lines) +// +// where: +// - funcN_{start,end} is the range of the N'th function +// - lineN_{start,end} is the range of the N'th function +// - funcN_num_lines is the number of source lines that should be +// paired with the N'th function +// - funcN_lineM_{start,end} is the range of the M'th line +// paired with the N'th function, where 0,0 indicates that +// there should be no such line paired +// - uncovered_funcs is the number of functions with area that is +// uncovered by any line, and +// - uncovered_lines is the reverse. + +// func1 func2 line1 line2 num pairing1 pairing2 uncovered +PAIRING(10, 20, 20, 25, 6, 7, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #0 +PAIRING(10, 20, 20, 25, 6, 7, 7, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #1 +PAIRING(10, 20, 20, 25, 6, 7, 7, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #2 +PAIRING(10, 20, 20, 25, 6, 7, 7, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #3 +PAIRING(10, 20, 20, 25, 6, 7, 7, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #4 +PAIRING(10, 20, 20, 25, 6, 7, 7, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #5 +PAIRING(10, 20, 20, 25, 6, 7, 7, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #6 +PAIRING(10, 20, 20, 25, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #7 +PAIRING(10, 20, 20, 25, 6, 7, 8, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #8 +PAIRING(10, 20, 20, 25, 6, 7, 8, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #9 +PAIRING(10, 20, 20, 25, 6, 7, 8, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #10 +PAIRING(10, 20, 20, 25, 6, 7, 8, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #11 +PAIRING(10, 20, 20, 25, 6, 7, 8, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #12 +PAIRING(10, 20, 20, 25, 6, 7, 8, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #13 +PAIRING(10, 20, 20, 25, 6, 7, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #14 +PAIRING(10, 20, 20, 25, 6, 7, 10, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #15 +PAIRING(10, 20, 20, 25, 6, 7, 10, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #16 +PAIRING(10, 20, 20, 25, 6, 7, 10, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #17 +PAIRING(10, 20, 20, 25, 6, 7, 10, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #18 +PAIRING(10, 20, 20, 25, 6, 7, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #19 +PAIRING(10, 20, 20, 25, 6, 7, 11, 20, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #20 +PAIRING(10, 20, 20, 25, 6, 7, 11, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #21 +PAIRING(10, 20, 20, 25, 6, 7, 11, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #22 +PAIRING(10, 20, 20, 25, 6, 7, 11, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #23 +PAIRING(10, 20, 20, 25, 6, 7, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #24 +PAIRING(10, 20, 20, 25, 6, 7, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #25 +PAIRING(10, 20, 20, 25, 6, 7, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #26 +PAIRING(10, 20, 20, 25, 6, 7, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #27 +PAIRING(10, 20, 20, 25, 6, 7, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #28 +PAIRING(10, 20, 20, 25, 6, 7, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #29 +PAIRING(10, 20, 20, 25, 6, 7, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #30 +PAIRING(10, 20, 20, 25, 6, 7, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #31 +PAIRING(10, 20, 20, 25, 6, 10, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #32 +PAIRING(10, 20, 20, 25, 6, 10, 10, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #33 +PAIRING(10, 20, 20, 25, 6, 10, 10, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #34 +PAIRING(10, 20, 20, 25, 6, 10, 10, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #35 +PAIRING(10, 20, 20, 25, 6, 10, 10, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #36 +PAIRING(10, 20, 20, 25, 6, 10, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #37 +PAIRING(10, 20, 20, 25, 6, 10, 11, 20, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #38 +PAIRING(10, 20, 20, 25, 6, 10, 11, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #39 +PAIRING(10, 20, 20, 25, 6, 10, 11, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #40 +PAIRING(10, 20, 20, 25, 6, 10, 11, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #41 +PAIRING(10, 20, 20, 25, 6, 10, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #42 +PAIRING(10, 20, 20, 25, 6, 10, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #43 +PAIRING(10, 20, 20, 25, 6, 10, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #44 +PAIRING(10, 20, 20, 25, 6, 10, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #45 +PAIRING(10, 20, 20, 25, 6, 10, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #46 +PAIRING(10, 20, 20, 25, 6, 10, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #47 +PAIRING(10, 20, 20, 25, 6, 10, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #48 +PAIRING(10, 20, 20, 25, 6, 10, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #49 +PAIRING(10, 20, 20, 25, 6, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 1) // #50 +PAIRING(10, 20, 20, 25, 6, 11, 11, 20, 2, 0, 10, 11, 11, 20, 0, 0, 0, 0, 1, 1) // #51 +PAIRING(10, 20, 20, 25, 6, 11, 11, 21, 2, 1, 10, 11, 11, 20, 20, 21, 0, 0, 1, 1) // #52 +PAIRING(10, 20, 20, 25, 6, 11, 11, 25, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 1) // #53 +PAIRING(10, 20, 20, 25, 6, 11, 11, 26, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 2) // #54 +PAIRING(10, 20, 20, 25, 6, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 1) // #55 +PAIRING(10, 20, 20, 25, 6, 11, 12, 20, 2, 0, 10, 11, 12, 20, 0, 0, 0, 0, 2, 1) // #56 +PAIRING(10, 20, 20, 25, 6, 11, 12, 21, 2, 1, 10, 11, 12, 20, 20, 21, 0, 0, 2, 1) // #57 +PAIRING(10, 20, 20, 25, 6, 11, 12, 25, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 1) // #58 +PAIRING(10, 20, 20, 25, 6, 11, 12, 26, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 2) // #59 +PAIRING(10, 20, 20, 25, 6, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #60 +PAIRING(10, 20, 20, 25, 6, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #61 +PAIRING(10, 20, 20, 25, 6, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #62 +PAIRING(10, 20, 20, 25, 6, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 1) // #63 +PAIRING(10, 20, 20, 25, 6, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #64 +PAIRING(10, 20, 20, 25, 6, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 2) // #65 +PAIRING(10, 20, 20, 25, 6, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #66 +PAIRING(10, 20, 20, 25, 6, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #67 +PAIRING(10, 20, 20, 25, 6, 20, 20, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #68 +PAIRING(10, 20, 20, 25, 6, 20, 20, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #69 +PAIRING(10, 20, 20, 25, 6, 20, 20, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #70 +PAIRING(10, 20, 20, 25, 6, 20, 21, 22, 1, 1, 10, 20, 0, 0, 21, 22, 0, 0, 1, 1) // #71 +PAIRING(10, 20, 20, 25, 6, 20, 21, 25, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 1) // #72 +PAIRING(10, 20, 20, 25, 6, 20, 21, 26, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 2) // #73 +PAIRING(10, 20, 20, 25, 6, 20, 25, 26, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #74 +PAIRING(10, 20, 20, 25, 6, 20, 26, 27, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #75 +PAIRING(10, 20, 20, 25, 6, 21, 21, 22, 1, 2, 10, 20, 0, 0, 20, 21, 21, 22, 1, 1) // #76 +PAIRING(10, 20, 20, 25, 6, 21, 21, 25, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 1) // #77 +PAIRING(10, 20, 20, 25, 6, 21, 21, 26, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 2) // #78 +PAIRING(10, 20, 20, 25, 6, 21, 22, 23, 1, 2, 10, 20, 0, 0, 20, 21, 22, 23, 1, 1) // #79 +PAIRING(10, 20, 20, 25, 6, 21, 22, 25, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 1) // #80 +PAIRING(10, 20, 20, 25, 6, 21, 22, 26, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 2) // #81 +PAIRING(10, 20, 20, 25, 6, 21, 25, 26, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #82 +PAIRING(10, 20, 20, 25, 6, 21, 26, 27, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #83 +PAIRING(10, 20, 20, 25, 6, 25, 25, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #84 +PAIRING(10, 20, 20, 25, 6, 25, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #85 +PAIRING(10, 20, 20, 25, 6, 26, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #86 +PAIRING(10, 20, 20, 25, 6, 26, 27, 28, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #87 +PAIRING(10, 20, 20, 25, 10, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 0) // #88 +PAIRING(10, 20, 20, 25, 10, 11, 11, 20, 2, 0, 10, 11, 11, 20, 0, 0, 0, 0, 1, 0) // #89 +PAIRING(10, 20, 20, 25, 10, 11, 11, 21, 2, 1, 10, 11, 11, 20, 20, 21, 0, 0, 1, 0) // #90 +PAIRING(10, 20, 20, 25, 10, 11, 11, 25, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 0) // #91 +PAIRING(10, 20, 20, 25, 10, 11, 11, 26, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 1) // #92 +PAIRING(10, 20, 20, 25, 10, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 0) // #93 +PAIRING(10, 20, 20, 25, 10, 11, 12, 20, 2, 0, 10, 11, 12, 20, 0, 0, 0, 0, 2, 0) // #94 +PAIRING(10, 20, 20, 25, 10, 11, 12, 21, 2, 1, 10, 11, 12, 20, 20, 21, 0, 0, 2, 0) // #95 +PAIRING(10, 20, 20, 25, 10, 11, 12, 25, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 0) // #96 +PAIRING(10, 20, 20, 25, 10, 11, 12, 26, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 1) // #97 +PAIRING(10, 20, 20, 25, 10, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 0) // #98 +PAIRING(10, 20, 20, 25, 10, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 0) // #99 +PAIRING(10, 20, 20, 25, 10, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #100 +PAIRING(10, 20, 20, 25, 10, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 0) // #101 +PAIRING(10, 20, 20, 25, 10, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 0) // #102 +PAIRING(10, 20, 20, 25, 10, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #103 +PAIRING(10, 20, 20, 25, 10, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #104 +PAIRING(10, 20, 20, 25, 10, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #105 +PAIRING(10, 20, 20, 25, 10, 20, 20, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 0) // #106 +PAIRING(10, 20, 20, 25, 10, 20, 20, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 0) // #107 +PAIRING(10, 20, 20, 25, 10, 20, 20, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #108 +PAIRING(10, 20, 20, 25, 10, 20, 21, 22, 1, 1, 10, 20, 0, 0, 21, 22, 0, 0, 1, 0) // #109 +PAIRING(10, 20, 20, 25, 10, 20, 21, 25, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 0) // #110 +PAIRING(10, 20, 20, 25, 10, 20, 21, 26, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 1) // #111 +PAIRING(10, 20, 20, 25, 10, 20, 25, 26, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #112 +PAIRING(10, 20, 20, 25, 10, 20, 26, 27, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #113 +PAIRING(10, 20, 20, 25, 10, 21, 21, 22, 1, 2, 10, 20, 0, 0, 20, 21, 21, 22, 1, 0) // #114 +PAIRING(10, 20, 20, 25, 10, 21, 21, 25, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 0) // #115 +PAIRING(10, 20, 20, 25, 10, 21, 21, 26, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 1) // #116 +PAIRING(10, 20, 20, 25, 10, 21, 22, 23, 1, 2, 10, 20, 0, 0, 20, 21, 22, 23, 1, 0) // #117 +PAIRING(10, 20, 20, 25, 10, 21, 22, 25, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 0) // #118 +PAIRING(10, 20, 20, 25, 10, 21, 22, 26, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 1) // #119 +PAIRING(10, 20, 20, 25, 10, 21, 25, 26, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #120 +PAIRING(10, 20, 20, 25, 10, 21, 26, 27, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #121 +PAIRING(10, 20, 20, 25, 10, 25, 25, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #122 +PAIRING(10, 20, 20, 25, 10, 25, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #123 +PAIRING(10, 20, 20, 25, 10, 26, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #124 +PAIRING(10, 20, 20, 25, 10, 26, 27, 28, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #125 +PAIRING(10, 20, 20, 25, 11, 12, 12, 13, 2, 0, 11, 12, 12, 13, 0, 0, 0, 0, 2, 0) // #126 +PAIRING(10, 20, 20, 25, 11, 12, 12, 20, 2, 0, 11, 12, 12, 20, 0, 0, 0, 0, 2, 0) // #127 +PAIRING(10, 20, 20, 25, 11, 12, 12, 21, 2, 1, 11, 12, 12, 20, 20, 21, 0, 0, 2, 0) // #128 +PAIRING(10, 20, 20, 25, 11, 12, 12, 25, 2, 1, 11, 12, 12, 20, 20, 25, 0, 0, 1, 0) // #129 +PAIRING(10, 20, 20, 25, 11, 12, 12, 26, 2, 1, 11, 12, 12, 20, 20, 25, 0, 0, 1, 1) // #130 +PAIRING(10, 20, 20, 25, 11, 12, 13, 14, 2, 0, 11, 12, 13, 14, 0, 0, 0, 0, 2, 0) // #131 +PAIRING(10, 20, 20, 25, 11, 12, 13, 20, 2, 0, 11, 12, 13, 20, 0, 0, 0, 0, 2, 0) // #132 +PAIRING(10, 20, 20, 25, 11, 12, 13, 21, 2, 1, 11, 12, 13, 20, 20, 21, 0, 0, 2, 0) // #133 +PAIRING(10, 20, 20, 25, 11, 12, 13, 25, 2, 1, 11, 12, 13, 20, 20, 25, 0, 0, 1, 0) // #134 +PAIRING(10, 20, 20, 25, 11, 12, 13, 26, 2, 1, 11, 12, 13, 20, 20, 25, 0, 0, 1, 1) // #135 +PAIRING(10, 20, 20, 25, 11, 12, 20, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 0) // #136 +PAIRING(10, 20, 20, 25, 11, 12, 20, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 0) // #137 +PAIRING(10, 20, 20, 25, 11, 12, 20, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #138 +PAIRING(10, 20, 20, 25, 11, 12, 21, 22, 1, 1, 11, 12, 0, 0, 21, 22, 0, 0, 2, 0) // #139 +PAIRING(10, 20, 20, 25, 11, 12, 21, 25, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 0) // #140 +PAIRING(10, 20, 20, 25, 11, 12, 21, 26, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 1) // #141 +PAIRING(10, 20, 20, 25, 11, 12, 25, 26, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #142 +PAIRING(10, 20, 20, 25, 11, 12, 26, 27, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #143 +PAIRING(10, 20, 20, 25, 11, 20, 20, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 0) // #144 +PAIRING(10, 20, 20, 25, 11, 20, 20, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 0) // #145 +PAIRING(10, 20, 20, 25, 11, 20, 20, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #146 +PAIRING(10, 20, 20, 25, 11, 20, 21, 22, 1, 1, 11, 20, 0, 0, 21, 22, 0, 0, 2, 0) // #147 +PAIRING(10, 20, 20, 25, 11, 20, 21, 25, 1, 1, 11, 20, 0, 0, 21, 25, 0, 0, 2, 0) // #148 +PAIRING(10, 20, 20, 25, 11, 20, 21, 26, 1, 1, 11, 20, 0, 0, 21, 25, 0, 0, 2, 1) // #149 +PAIRING(10, 20, 20, 25, 11, 20, 25, 26, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #150 +PAIRING(10, 20, 20, 25, 11, 20, 26, 27, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #151 +PAIRING(10, 20, 20, 25, 11, 21, 21, 22, 1, 2, 11, 20, 0, 0, 20, 21, 21, 22, 2, 0) // #152 +PAIRING(10, 20, 20, 25, 11, 21, 21, 25, 1, 2, 11, 20, 0, 0, 20, 21, 21, 25, 1, 0) // #153 +PAIRING(10, 20, 20, 25, 11, 21, 21, 26, 1, 2, 11, 20, 0, 0, 20, 21, 21, 25, 1, 1) // #154 +PAIRING(10, 20, 20, 25, 11, 21, 22, 23, 1, 2, 11, 20, 0, 0, 20, 21, 22, 23, 2, 0) // #155 +PAIRING(10, 20, 20, 25, 11, 21, 22, 25, 1, 2, 11, 20, 0, 0, 20, 21, 22, 25, 2, 0) // #156 +PAIRING(10, 20, 20, 25, 11, 21, 22, 26, 1, 2, 11, 20, 0, 0, 20, 21, 22, 25, 2, 1) // #157 +PAIRING(10, 20, 20, 25, 11, 21, 25, 26, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #158 +PAIRING(10, 20, 20, 25, 11, 21, 26, 27, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #159 +PAIRING(10, 20, 20, 25, 11, 25, 25, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #160 +PAIRING(10, 20, 20, 25, 11, 25, 26, 27, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #161 +PAIRING(10, 20, 20, 25, 11, 26, 26, 27, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #162 +PAIRING(10, 20, 20, 25, 11, 26, 27, 28, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #163 +PAIRING(10, 20, 20, 25, 20, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 0) // #164 +PAIRING(10, 20, 20, 25, 20, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 0) // #165 +PAIRING(10, 20, 20, 25, 20, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #166 +PAIRING(10, 20, 20, 25, 20, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 0) // #167 +PAIRING(10, 20, 20, 25, 20, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 0) // #168 +PAIRING(10, 20, 20, 25, 20, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #169 +PAIRING(10, 20, 20, 25, 20, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #170 +PAIRING(10, 20, 20, 25, 20, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #171 +PAIRING(10, 20, 20, 25, 20, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #172 +PAIRING(10, 20, 20, 25, 20, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #173 +PAIRING(10, 20, 20, 25, 20, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #174 +PAIRING(10, 20, 20, 25, 20, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #175 +PAIRING(10, 20, 20, 25, 21, 22, 22, 23, 0, 2, 0, 0, 0, 0, 21, 22, 22, 23, 2, 0) // #176 +PAIRING(10, 20, 20, 25, 21, 22, 22, 25, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 0) // #177 +PAIRING(10, 20, 20, 25, 21, 22, 22, 26, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 1) // #178 +PAIRING(10, 20, 20, 25, 21, 22, 23, 24, 0, 2, 0, 0, 0, 0, 21, 22, 23, 24, 2, 0) // #179 +PAIRING(10, 20, 20, 25, 21, 22, 23, 25, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 0) // #180 +PAIRING(10, 20, 20, 25, 21, 22, 23, 26, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 1) // #181 +PAIRING(10, 20, 20, 25, 21, 22, 25, 26, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #182 +PAIRING(10, 20, 20, 25, 21, 22, 26, 27, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #183 +PAIRING(10, 20, 20, 25, 21, 25, 25, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #184 +PAIRING(10, 20, 20, 25, 21, 25, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #185 +PAIRING(10, 20, 20, 25, 21, 26, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #186 +PAIRING(10, 20, 20, 25, 21, 26, 27, 28, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #187 +PAIRING(10, 20, 20, 25, 25, 26, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #188 +PAIRING(10, 20, 20, 25, 25, 26, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #189 +PAIRING(10, 20, 20, 25, 26, 27, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #190 +PAIRING(10, 20, 20, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #191 +PAIRING(10, 15, 20, 25, 6, 7, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #192 +PAIRING(10, 15, 20, 25, 6, 7, 7, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #193 +PAIRING(10, 15, 20, 25, 6, 7, 7, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #194 +PAIRING(10, 15, 20, 25, 6, 7, 7, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #195 +PAIRING(10, 15, 20, 25, 6, 7, 7, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #196 +PAIRING(10, 15, 20, 25, 6, 7, 7, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #197 +PAIRING(10, 15, 20, 25, 6, 7, 7, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #198 +PAIRING(10, 15, 20, 25, 6, 7, 7, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #199 +PAIRING(10, 15, 20, 25, 6, 7, 7, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #200 +PAIRING(10, 15, 20, 25, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #201 +PAIRING(10, 15, 20, 25, 6, 7, 8, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #202 +PAIRING(10, 15, 20, 25, 6, 7, 8, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #203 +PAIRING(10, 15, 20, 25, 6, 7, 8, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #204 +PAIRING(10, 15, 20, 25, 6, 7, 8, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #205 +PAIRING(10, 15, 20, 25, 6, 7, 8, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #206 +PAIRING(10, 15, 20, 25, 6, 7, 8, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #207 +PAIRING(10, 15, 20, 25, 6, 7, 8, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #208 +PAIRING(10, 15, 20, 25, 6, 7, 8, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #209 +PAIRING(10, 15, 20, 25, 6, 7, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #210 +PAIRING(10, 15, 20, 25, 6, 7, 10, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #211 +PAIRING(10, 15, 20, 25, 6, 7, 10, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #212 +PAIRING(10, 15, 20, 25, 6, 7, 10, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #213 +PAIRING(10, 15, 20, 25, 6, 7, 10, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #214 +PAIRING(10, 15, 20, 25, 6, 7, 10, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #215 +PAIRING(10, 15, 20, 25, 6, 7, 10, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #216 +PAIRING(10, 15, 20, 25, 6, 7, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #217 +PAIRING(10, 15, 20, 25, 6, 7, 11, 15, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #218 +PAIRING(10, 15, 20, 25, 6, 7, 11, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #219 +PAIRING(10, 15, 20, 25, 6, 7, 11, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #220 +PAIRING(10, 15, 20, 25, 6, 7, 11, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #221 +PAIRING(10, 15, 20, 25, 6, 7, 11, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #222 +PAIRING(10, 15, 20, 25, 6, 7, 11, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #223 +PAIRING(10, 15, 20, 25, 6, 7, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #224 +PAIRING(10, 15, 20, 25, 6, 7, 15, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #225 +PAIRING(10, 15, 20, 25, 6, 7, 15, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #226 +PAIRING(10, 15, 20, 25, 6, 7, 15, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #227 +PAIRING(10, 15, 20, 25, 6, 7, 15, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #228 +PAIRING(10, 15, 20, 25, 6, 7, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #229 +PAIRING(10, 15, 20, 25, 6, 7, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #230 +PAIRING(10, 15, 20, 25, 6, 7, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #231 +PAIRING(10, 15, 20, 25, 6, 7, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #232 +PAIRING(10, 15, 20, 25, 6, 7, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #233 +PAIRING(10, 15, 20, 25, 6, 7, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #234 +PAIRING(10, 15, 20, 25, 6, 7, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #235 +PAIRING(10, 15, 20, 25, 6, 7, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #236 +PAIRING(10, 15, 20, 25, 6, 7, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #237 +PAIRING(10, 15, 20, 25, 6, 7, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #238 +PAIRING(10, 15, 20, 25, 6, 7, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #239 +PAIRING(10, 15, 20, 25, 6, 7, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #240 +PAIRING(10, 15, 20, 25, 6, 7, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #241 +PAIRING(10, 15, 20, 25, 6, 10, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #242 +PAIRING(10, 15, 20, 25, 6, 10, 10, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #243 +PAIRING(10, 15, 20, 25, 6, 10, 10, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #244 +PAIRING(10, 15, 20, 25, 6, 10, 10, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #245 +PAIRING(10, 15, 20, 25, 6, 10, 10, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #246 +PAIRING(10, 15, 20, 25, 6, 10, 10, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #247 +PAIRING(10, 15, 20, 25, 6, 10, 10, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #248 +PAIRING(10, 15, 20, 25, 6, 10, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #249 +PAIRING(10, 15, 20, 25, 6, 10, 11, 15, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #250 +PAIRING(10, 15, 20, 25, 6, 10, 11, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #251 +PAIRING(10, 15, 20, 25, 6, 10, 11, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #252 +PAIRING(10, 15, 20, 25, 6, 10, 11, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #253 +PAIRING(10, 15, 20, 25, 6, 10, 11, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #254 +PAIRING(10, 15, 20, 25, 6, 10, 11, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #255 +PAIRING(10, 15, 20, 25, 6, 10, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #256 +PAIRING(10, 15, 20, 25, 6, 10, 15, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #257 +PAIRING(10, 15, 20, 25, 6, 10, 15, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #258 +PAIRING(10, 15, 20, 25, 6, 10, 15, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #259 +PAIRING(10, 15, 20, 25, 6, 10, 15, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #260 +PAIRING(10, 15, 20, 25, 6, 10, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #261 +PAIRING(10, 15, 20, 25, 6, 10, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #262 +PAIRING(10, 15, 20, 25, 6, 10, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #263 +PAIRING(10, 15, 20, 25, 6, 10, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #264 +PAIRING(10, 15, 20, 25, 6, 10, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #265 +PAIRING(10, 15, 20, 25, 6, 10, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #266 +PAIRING(10, 15, 20, 25, 6, 10, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #267 +PAIRING(10, 15, 20, 25, 6, 10, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #268 +PAIRING(10, 15, 20, 25, 6, 10, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #269 +PAIRING(10, 15, 20, 25, 6, 10, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #270 +PAIRING(10, 15, 20, 25, 6, 10, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #271 +PAIRING(10, 15, 20, 25, 6, 10, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #272 +PAIRING(10, 15, 20, 25, 6, 10, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #273 +PAIRING(10, 15, 20, 25, 6, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 1) // #274 +PAIRING(10, 15, 20, 25, 6, 11, 11, 15, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #275 +PAIRING(10, 15, 20, 25, 6, 11, 11, 16, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 2) // #276 +PAIRING(10, 15, 20, 25, 6, 11, 11, 20, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #277 +PAIRING(10, 15, 20, 25, 6, 11, 11, 21, 2, 1, 10, 11, 11, 15, 20, 21, 0, 0, 1, 2) // #278 +PAIRING(10, 15, 20, 25, 6, 11, 11, 25, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 2) // #279 +PAIRING(10, 15, 20, 25, 6, 11, 11, 26, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 2) // #280 +PAIRING(10, 15, 20, 25, 6, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 1) // #281 +PAIRING(10, 15, 20, 25, 6, 11, 12, 15, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #282 +PAIRING(10, 15, 20, 25, 6, 11, 12, 16, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 2) // #283 +PAIRING(10, 15, 20, 25, 6, 11, 12, 20, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #284 +PAIRING(10, 15, 20, 25, 6, 11, 12, 21, 2, 1, 10, 11, 12, 15, 20, 21, 0, 0, 2, 2) // #285 +PAIRING(10, 15, 20, 25, 6, 11, 12, 25, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 2) // #286 +PAIRING(10, 15, 20, 25, 6, 11, 12, 26, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 2) // #287 +PAIRING(10, 15, 20, 25, 6, 11, 15, 16, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #288 +PAIRING(10, 15, 20, 25, 6, 11, 15, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #289 +PAIRING(10, 15, 20, 25, 6, 11, 15, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 2) // #290 +PAIRING(10, 15, 20, 25, 6, 11, 15, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #291 +PAIRING(10, 15, 20, 25, 6, 11, 15, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #292 +PAIRING(10, 15, 20, 25, 6, 11, 16, 17, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #293 +PAIRING(10, 15, 20, 25, 6, 11, 16, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #294 +PAIRING(10, 15, 20, 25, 6, 11, 16, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 2) // #295 +PAIRING(10, 15, 20, 25, 6, 11, 16, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #296 +PAIRING(10, 15, 20, 25, 6, 11, 16, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #297 +PAIRING(10, 15, 20, 25, 6, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #298 +PAIRING(10, 15, 20, 25, 6, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #299 +PAIRING(10, 15, 20, 25, 6, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #300 +PAIRING(10, 15, 20, 25, 6, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 1) // #301 +PAIRING(10, 15, 20, 25, 6, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #302 +PAIRING(10, 15, 20, 25, 6, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 2) // #303 +PAIRING(10, 15, 20, 25, 6, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #304 +PAIRING(10, 15, 20, 25, 6, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #305 +PAIRING(10, 15, 20, 25, 6, 15, 15, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #306 +PAIRING(10, 15, 20, 25, 6, 15, 15, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #307 +PAIRING(10, 15, 20, 25, 6, 15, 15, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #308 +PAIRING(10, 15, 20, 25, 6, 15, 15, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #309 +PAIRING(10, 15, 20, 25, 6, 15, 15, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #310 +PAIRING(10, 15, 20, 25, 6, 15, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #311 +PAIRING(10, 15, 20, 25, 6, 15, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #312 +PAIRING(10, 15, 20, 25, 6, 15, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #313 +PAIRING(10, 15, 20, 25, 6, 15, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #314 +PAIRING(10, 15, 20, 25, 6, 15, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #315 +PAIRING(10, 15, 20, 25, 6, 15, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #316 +PAIRING(10, 15, 20, 25, 6, 15, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #317 +PAIRING(10, 15, 20, 25, 6, 15, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #318 +PAIRING(10, 15, 20, 25, 6, 15, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #319 +PAIRING(10, 15, 20, 25, 6, 15, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #320 +PAIRING(10, 15, 20, 25, 6, 15, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #321 +PAIRING(10, 15, 20, 25, 6, 15, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #322 +PAIRING(10, 15, 20, 25, 6, 15, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #323 +PAIRING(10, 15, 20, 25, 6, 16, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #324 +PAIRING(10, 15, 20, 25, 6, 16, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #325 +PAIRING(10, 15, 20, 25, 6, 16, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #326 +PAIRING(10, 15, 20, 25, 6, 16, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #327 +PAIRING(10, 15, 20, 25, 6, 16, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #328 +PAIRING(10, 15, 20, 25, 6, 16, 17, 18, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #329 +PAIRING(10, 15, 20, 25, 6, 16, 17, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #330 +PAIRING(10, 15, 20, 25, 6, 16, 17, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #331 +PAIRING(10, 15, 20, 25, 6, 16, 17, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #332 +PAIRING(10, 15, 20, 25, 6, 16, 17, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #333 +PAIRING(10, 15, 20, 25, 6, 16, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #334 +PAIRING(10, 15, 20, 25, 6, 16, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #335 +PAIRING(10, 15, 20, 25, 6, 16, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #336 +PAIRING(10, 15, 20, 25, 6, 16, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #337 +PAIRING(10, 15, 20, 25, 6, 16, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #338 +PAIRING(10, 15, 20, 25, 6, 16, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #339 +PAIRING(10, 15, 20, 25, 6, 16, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #340 +PAIRING(10, 15, 20, 25, 6, 16, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #341 +PAIRING(10, 15, 20, 25, 6, 20, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #342 +PAIRING(10, 15, 20, 25, 6, 20, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #343 +PAIRING(10, 15, 20, 25, 6, 20, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #344 +PAIRING(10, 15, 20, 25, 6, 20, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #345 +PAIRING(10, 15, 20, 25, 6, 20, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #346 +PAIRING(10, 15, 20, 25, 6, 20, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #347 +PAIRING(10, 15, 20, 25, 6, 20, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #348 +PAIRING(10, 15, 20, 25, 6, 20, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #349 +PAIRING(10, 15, 20, 25, 6, 21, 21, 22, 1, 2, 10, 15, 0, 0, 20, 21, 21, 22, 1, 1) // #350 +PAIRING(10, 15, 20, 25, 6, 21, 21, 25, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 1) // #351 +PAIRING(10, 15, 20, 25, 6, 21, 21, 26, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 2) // #352 +PAIRING(10, 15, 20, 25, 6, 21, 22, 23, 1, 2, 10, 15, 0, 0, 20, 21, 22, 23, 1, 1) // #353 +PAIRING(10, 15, 20, 25, 6, 21, 22, 25, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 1) // #354 +PAIRING(10, 15, 20, 25, 6, 21, 22, 26, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 2) // #355 +PAIRING(10, 15, 20, 25, 6, 21, 25, 26, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #356 +PAIRING(10, 15, 20, 25, 6, 21, 26, 27, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #357 +PAIRING(10, 15, 20, 25, 6, 25, 25, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #358 +PAIRING(10, 15, 20, 25, 6, 25, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #359 +PAIRING(10, 15, 20, 25, 6, 26, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #360 +PAIRING(10, 15, 20, 25, 6, 26, 27, 28, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #361 +PAIRING(10, 15, 20, 25, 10, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 0) // #362 +PAIRING(10, 15, 20, 25, 10, 11, 11, 15, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 0) // #363 +PAIRING(10, 15, 20, 25, 10, 11, 11, 16, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #364 +PAIRING(10, 15, 20, 25, 10, 11, 11, 20, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 0) // #365 +PAIRING(10, 15, 20, 25, 10, 11, 11, 21, 2, 1, 10, 11, 11, 15, 20, 21, 0, 0, 1, 1) // #366 +PAIRING(10, 15, 20, 25, 10, 11, 11, 25, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 1) // #367 +PAIRING(10, 15, 20, 25, 10, 11, 11, 26, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 1) // #368 +PAIRING(10, 15, 20, 25, 10, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 0) // #369 +PAIRING(10, 15, 20, 25, 10, 11, 12, 15, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 0) // #370 +PAIRING(10, 15, 20, 25, 10, 11, 12, 16, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #371 +PAIRING(10, 15, 20, 25, 10, 11, 12, 20, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 0) // #372 +PAIRING(10, 15, 20, 25, 10, 11, 12, 21, 2, 1, 10, 11, 12, 15, 20, 21, 0, 0, 2, 1) // #373 +PAIRING(10, 15, 20, 25, 10, 11, 12, 25, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 1) // #374 +PAIRING(10, 15, 20, 25, 10, 11, 12, 26, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 1) // #375 +PAIRING(10, 15, 20, 25, 10, 11, 15, 16, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #376 +PAIRING(10, 15, 20, 25, 10, 11, 15, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #377 +PAIRING(10, 15, 20, 25, 10, 11, 15, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #378 +PAIRING(10, 15, 20, 25, 10, 11, 15, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #379 +PAIRING(10, 15, 20, 25, 10, 11, 15, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #380 +PAIRING(10, 15, 20, 25, 10, 11, 16, 17, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #381 +PAIRING(10, 15, 20, 25, 10, 11, 16, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #382 +PAIRING(10, 15, 20, 25, 10, 11, 16, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #383 +PAIRING(10, 15, 20, 25, 10, 11, 16, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #384 +PAIRING(10, 15, 20, 25, 10, 11, 16, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #385 +PAIRING(10, 15, 20, 25, 10, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 0) // #386 +PAIRING(10, 15, 20, 25, 10, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 0) // #387 +PAIRING(10, 15, 20, 25, 10, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #388 +PAIRING(10, 15, 20, 25, 10, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 0) // #389 +PAIRING(10, 15, 20, 25, 10, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 0) // #390 +PAIRING(10, 15, 20, 25, 10, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #391 +PAIRING(10, 15, 20, 25, 10, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #392 +PAIRING(10, 15, 20, 25, 10, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #393 +PAIRING(10, 15, 20, 25, 10, 15, 15, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #394 +PAIRING(10, 15, 20, 25, 10, 15, 15, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #395 +PAIRING(10, 15, 20, 25, 10, 15, 15, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #396 +PAIRING(10, 15, 20, 25, 10, 15, 15, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #397 +PAIRING(10, 15, 20, 25, 10, 15, 15, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #398 +PAIRING(10, 15, 20, 25, 10, 15, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #399 +PAIRING(10, 15, 20, 25, 10, 15, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #400 +PAIRING(10, 15, 20, 25, 10, 15, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #401 +PAIRING(10, 15, 20, 25, 10, 15, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #402 +PAIRING(10, 15, 20, 25, 10, 15, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #403 +PAIRING(10, 15, 20, 25, 10, 15, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 0) // #404 +PAIRING(10, 15, 20, 25, 10, 15, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 0) // #405 +PAIRING(10, 15, 20, 25, 10, 15, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #406 +PAIRING(10, 15, 20, 25, 10, 15, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 0) // #407 +PAIRING(10, 15, 20, 25, 10, 15, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 0) // #408 +PAIRING(10, 15, 20, 25, 10, 15, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #409 +PAIRING(10, 15, 20, 25, 10, 15, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #410 +PAIRING(10, 15, 20, 25, 10, 15, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #411 +PAIRING(10, 15, 20, 25, 10, 16, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #412 +PAIRING(10, 15, 20, 25, 10, 16, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #413 +PAIRING(10, 15, 20, 25, 10, 16, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #414 +PAIRING(10, 15, 20, 25, 10, 16, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #415 +PAIRING(10, 15, 20, 25, 10, 16, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #416 +PAIRING(10, 15, 20, 25, 10, 16, 17, 18, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #417 +PAIRING(10, 15, 20, 25, 10, 16, 17, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #418 +PAIRING(10, 15, 20, 25, 10, 16, 17, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #419 +PAIRING(10, 15, 20, 25, 10, 16, 17, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #420 +PAIRING(10, 15, 20, 25, 10, 16, 17, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #421 +PAIRING(10, 15, 20, 25, 10, 16, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #422 +PAIRING(10, 15, 20, 25, 10, 16, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #423 +PAIRING(10, 15, 20, 25, 10, 16, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #424 +PAIRING(10, 15, 20, 25, 10, 16, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #425 +PAIRING(10, 15, 20, 25, 10, 16, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #426 +PAIRING(10, 15, 20, 25, 10, 16, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #427 +PAIRING(10, 15, 20, 25, 10, 16, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #428 +PAIRING(10, 15, 20, 25, 10, 16, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #429 +PAIRING(10, 15, 20, 25, 10, 20, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 0) // #430 +PAIRING(10, 15, 20, 25, 10, 20, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 0) // #431 +PAIRING(10, 15, 20, 25, 10, 20, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #432 +PAIRING(10, 15, 20, 25, 10, 20, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 0) // #433 +PAIRING(10, 15, 20, 25, 10, 20, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 0) // #434 +PAIRING(10, 15, 20, 25, 10, 20, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #435 +PAIRING(10, 15, 20, 25, 10, 20, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #436 +PAIRING(10, 15, 20, 25, 10, 20, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #437 +PAIRING(10, 15, 20, 25, 10, 21, 21, 22, 1, 2, 10, 15, 0, 0, 20, 21, 21, 22, 1, 1) // #438 +PAIRING(10, 15, 20, 25, 10, 21, 21, 25, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 1) // #439 +PAIRING(10, 15, 20, 25, 10, 21, 21, 26, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 2) // #440 +PAIRING(10, 15, 20, 25, 10, 21, 22, 23, 1, 2, 10, 15, 0, 0, 20, 21, 22, 23, 1, 1) // #441 +PAIRING(10, 15, 20, 25, 10, 21, 22, 25, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 1) // #442 +PAIRING(10, 15, 20, 25, 10, 21, 22, 26, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 2) // #443 +PAIRING(10, 15, 20, 25, 10, 21, 25, 26, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #444 +PAIRING(10, 15, 20, 25, 10, 21, 26, 27, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #445 +PAIRING(10, 15, 20, 25, 10, 25, 25, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #446 +PAIRING(10, 15, 20, 25, 10, 25, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #447 +PAIRING(10, 15, 20, 25, 10, 26, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #448 +PAIRING(10, 15, 20, 25, 10, 26, 27, 28, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #449 +PAIRING(10, 15, 20, 25, 11, 12, 12, 13, 2, 0, 11, 12, 12, 13, 0, 0, 0, 0, 2, 0) // #450 +PAIRING(10, 15, 20, 25, 11, 12, 12, 15, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 0) // #451 +PAIRING(10, 15, 20, 25, 11, 12, 12, 16, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 1) // #452 +PAIRING(10, 15, 20, 25, 11, 12, 12, 20, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 0) // #453 +PAIRING(10, 15, 20, 25, 11, 12, 12, 21, 2, 1, 11, 12, 12, 15, 20, 21, 0, 0, 2, 1) // #454 +PAIRING(10, 15, 20, 25, 11, 12, 12, 25, 2, 1, 11, 12, 12, 15, 20, 25, 0, 0, 1, 1) // #455 +PAIRING(10, 15, 20, 25, 11, 12, 12, 26, 2, 1, 11, 12, 12, 15, 20, 25, 0, 0, 1, 1) // #456 +PAIRING(10, 15, 20, 25, 11, 12, 13, 14, 2, 0, 11, 12, 13, 14, 0, 0, 0, 0, 2, 0) // #457 +PAIRING(10, 15, 20, 25, 11, 12, 13, 15, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 0) // #458 +PAIRING(10, 15, 20, 25, 11, 12, 13, 16, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 1) // #459 +PAIRING(10, 15, 20, 25, 11, 12, 13, 20, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 0) // #460 +PAIRING(10, 15, 20, 25, 11, 12, 13, 21, 2, 1, 11, 12, 13, 15, 20, 21, 0, 0, 2, 1) // #461 +PAIRING(10, 15, 20, 25, 11, 12, 13, 25, 2, 1, 11, 12, 13, 15, 20, 25, 0, 0, 1, 1) // #462 +PAIRING(10, 15, 20, 25, 11, 12, 13, 26, 2, 1, 11, 12, 13, 15, 20, 25, 0, 0, 1, 1) // #463 +PAIRING(10, 15, 20, 25, 11, 12, 15, 16, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #464 +PAIRING(10, 15, 20, 25, 11, 12, 15, 20, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #465 +PAIRING(10, 15, 20, 25, 11, 12, 15, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 1) // #466 +PAIRING(10, 15, 20, 25, 11, 12, 15, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #467 +PAIRING(10, 15, 20, 25, 11, 12, 15, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #468 +PAIRING(10, 15, 20, 25, 11, 12, 16, 17, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #469 +PAIRING(10, 15, 20, 25, 11, 12, 16, 20, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #470 +PAIRING(10, 15, 20, 25, 11, 12, 16, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 1) // #471 +PAIRING(10, 15, 20, 25, 11, 12, 16, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #472 +PAIRING(10, 15, 20, 25, 11, 12, 16, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #473 +PAIRING(10, 15, 20, 25, 11, 12, 20, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 0) // #474 +PAIRING(10, 15, 20, 25, 11, 12, 20, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 0) // #475 +PAIRING(10, 15, 20, 25, 11, 12, 20, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #476 +PAIRING(10, 15, 20, 25, 11, 12, 21, 22, 1, 1, 11, 12, 0, 0, 21, 22, 0, 0, 2, 0) // #477 +PAIRING(10, 15, 20, 25, 11, 12, 21, 25, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 0) // #478 +PAIRING(10, 15, 20, 25, 11, 12, 21, 26, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 1) // #479 +PAIRING(10, 15, 20, 25, 11, 12, 25, 26, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #480 +PAIRING(10, 15, 20, 25, 11, 12, 26, 27, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #481 +PAIRING(10, 15, 20, 25, 11, 15, 15, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #482 +PAIRING(10, 15, 20, 25, 11, 15, 15, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #483 +PAIRING(10, 15, 20, 25, 11, 15, 15, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #484 +PAIRING(10, 15, 20, 25, 11, 15, 15, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #485 +PAIRING(10, 15, 20, 25, 11, 15, 15, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #486 +PAIRING(10, 15, 20, 25, 11, 15, 16, 17, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #487 +PAIRING(10, 15, 20, 25, 11, 15, 16, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #488 +PAIRING(10, 15, 20, 25, 11, 15, 16, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #489 +PAIRING(10, 15, 20, 25, 11, 15, 16, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #490 +PAIRING(10, 15, 20, 25, 11, 15, 16, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #491 +PAIRING(10, 15, 20, 25, 11, 15, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 0) // #492 +PAIRING(10, 15, 20, 25, 11, 15, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 0) // #493 +PAIRING(10, 15, 20, 25, 11, 15, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #494 +PAIRING(10, 15, 20, 25, 11, 15, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 0) // #495 +PAIRING(10, 15, 20, 25, 11, 15, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 0) // #496 +PAIRING(10, 15, 20, 25, 11, 15, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #497 +PAIRING(10, 15, 20, 25, 11, 15, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #498 +PAIRING(10, 15, 20, 25, 11, 15, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #499 +PAIRING(10, 15, 20, 25, 11, 16, 16, 17, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #500 +PAIRING(10, 15, 20, 25, 11, 16, 16, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #501 +PAIRING(10, 15, 20, 25, 11, 16, 16, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #502 +PAIRING(10, 15, 20, 25, 11, 16, 16, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #503 +PAIRING(10, 15, 20, 25, 11, 16, 16, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #504 +PAIRING(10, 15, 20, 25, 11, 16, 17, 18, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #505 +PAIRING(10, 15, 20, 25, 11, 16, 17, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #506 +PAIRING(10, 15, 20, 25, 11, 16, 17, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #507 +PAIRING(10, 15, 20, 25, 11, 16, 17, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #508 +PAIRING(10, 15, 20, 25, 11, 16, 17, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #509 +PAIRING(10, 15, 20, 25, 11, 16, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #510 +PAIRING(10, 15, 20, 25, 11, 16, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #511 +PAIRING(10, 15, 20, 25, 11, 16, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #512 +PAIRING(10, 15, 20, 25, 11, 16, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 1) // #513 +PAIRING(10, 15, 20, 25, 11, 16, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #514 +PAIRING(10, 15, 20, 25, 11, 16, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 2) // #515 +PAIRING(10, 15, 20, 25, 11, 16, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #516 +PAIRING(10, 15, 20, 25, 11, 16, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #517 +PAIRING(10, 15, 20, 25, 11, 20, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 0) // #518 +PAIRING(10, 15, 20, 25, 11, 20, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 0) // #519 +PAIRING(10, 15, 20, 25, 11, 20, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #520 +PAIRING(10, 15, 20, 25, 11, 20, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 0) // #521 +PAIRING(10, 15, 20, 25, 11, 20, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 0) // #522 +PAIRING(10, 15, 20, 25, 11, 20, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #523 +PAIRING(10, 15, 20, 25, 11, 20, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #524 +PAIRING(10, 15, 20, 25, 11, 20, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #525 +PAIRING(10, 15, 20, 25, 11, 21, 21, 22, 1, 2, 11, 15, 0, 0, 20, 21, 21, 22, 2, 1) // #526 +PAIRING(10, 15, 20, 25, 11, 21, 21, 25, 1, 2, 11, 15, 0, 0, 20, 21, 21, 25, 1, 1) // #527 +PAIRING(10, 15, 20, 25, 11, 21, 21, 26, 1, 2, 11, 15, 0, 0, 20, 21, 21, 25, 1, 2) // #528 +PAIRING(10, 15, 20, 25, 11, 21, 22, 23, 1, 2, 11, 15, 0, 0, 20, 21, 22, 23, 2, 1) // #529 +PAIRING(10, 15, 20, 25, 11, 21, 22, 25, 1, 2, 11, 15, 0, 0, 20, 21, 22, 25, 2, 1) // #530 +PAIRING(10, 15, 20, 25, 11, 21, 22, 26, 1, 2, 11, 15, 0, 0, 20, 21, 22, 25, 2, 2) // #531 +PAIRING(10, 15, 20, 25, 11, 21, 25, 26, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #532 +PAIRING(10, 15, 20, 25, 11, 21, 26, 27, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #533 +PAIRING(10, 15, 20, 25, 11, 25, 25, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #534 +PAIRING(10, 15, 20, 25, 11, 25, 26, 27, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #535 +PAIRING(10, 15, 20, 25, 11, 26, 26, 27, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #536 +PAIRING(10, 15, 20, 25, 11, 26, 27, 28, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #537 +PAIRING(10, 15, 20, 25, 15, 16, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #538 +PAIRING(10, 15, 20, 25, 15, 16, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #539 +PAIRING(10, 15, 20, 25, 15, 16, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #540 +PAIRING(10, 15, 20, 25, 15, 16, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #541 +PAIRING(10, 15, 20, 25, 15, 16, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #542 +PAIRING(10, 15, 20, 25, 15, 16, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #543 +PAIRING(10, 15, 20, 25, 15, 16, 17, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #544 +PAIRING(10, 15, 20, 25, 15, 16, 17, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #545 +PAIRING(10, 15, 20, 25, 15, 16, 17, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #546 +PAIRING(10, 15, 20, 25, 15, 16, 17, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #547 +PAIRING(10, 15, 20, 25, 15, 16, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #548 +PAIRING(10, 15, 20, 25, 15, 16, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #549 +PAIRING(10, 15, 20, 25, 15, 16, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #550 +PAIRING(10, 15, 20, 25, 15, 16, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #551 +PAIRING(10, 15, 20, 25, 15, 16, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #552 +PAIRING(10, 15, 20, 25, 15, 16, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #553 +PAIRING(10, 15, 20, 25, 15, 16, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #554 +PAIRING(10, 15, 20, 25, 15, 16, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #555 +PAIRING(10, 15, 20, 25, 15, 20, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #556 +PAIRING(10, 15, 20, 25, 15, 20, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #557 +PAIRING(10, 15, 20, 25, 15, 20, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #558 +PAIRING(10, 15, 20, 25, 15, 20, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #559 +PAIRING(10, 15, 20, 25, 15, 20, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #560 +PAIRING(10, 15, 20, 25, 15, 20, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #561 +PAIRING(10, 15, 20, 25, 15, 20, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #562 +PAIRING(10, 15, 20, 25, 15, 20, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #563 +PAIRING(10, 15, 20, 25, 15, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 1) // #564 +PAIRING(10, 15, 20, 25, 15, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #565 +PAIRING(10, 15, 20, 25, 15, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 2) // #566 +PAIRING(10, 15, 20, 25, 15, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 1) // #567 +PAIRING(10, 15, 20, 25, 15, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #568 +PAIRING(10, 15, 20, 25, 15, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 2) // #569 +PAIRING(10, 15, 20, 25, 15, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #570 +PAIRING(10, 15, 20, 25, 15, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #571 +PAIRING(10, 15, 20, 25, 15, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #572 +PAIRING(10, 15, 20, 25, 15, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #573 +PAIRING(10, 15, 20, 25, 15, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #574 +PAIRING(10, 15, 20, 25, 15, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #575 +PAIRING(10, 15, 20, 25, 16, 17, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #576 +PAIRING(10, 15, 20, 25, 16, 17, 17, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #577 +PAIRING(10, 15, 20, 25, 16, 17, 17, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #578 +PAIRING(10, 15, 20, 25, 16, 17, 17, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #579 +PAIRING(10, 15, 20, 25, 16, 17, 17, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #580 +PAIRING(10, 15, 20, 25, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #581 +PAIRING(10, 15, 20, 25, 16, 17, 18, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #582 +PAIRING(10, 15, 20, 25, 16, 17, 18, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #583 +PAIRING(10, 15, 20, 25, 16, 17, 18, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #584 +PAIRING(10, 15, 20, 25, 16, 17, 18, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #585 +PAIRING(10, 15, 20, 25, 16, 17, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #586 +PAIRING(10, 15, 20, 25, 16, 17, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #587 +PAIRING(10, 15, 20, 25, 16, 17, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #588 +PAIRING(10, 15, 20, 25, 16, 17, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #589 +PAIRING(10, 15, 20, 25, 16, 17, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #590 +PAIRING(10, 15, 20, 25, 16, 17, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #591 +PAIRING(10, 15, 20, 25, 16, 17, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #592 +PAIRING(10, 15, 20, 25, 16, 17, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #593 +PAIRING(10, 15, 20, 25, 16, 20, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #594 +PAIRING(10, 15, 20, 25, 16, 20, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #595 +PAIRING(10, 15, 20, 25, 16, 20, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #596 +PAIRING(10, 15, 20, 25, 16, 20, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #597 +PAIRING(10, 15, 20, 25, 16, 20, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #598 +PAIRING(10, 15, 20, 25, 16, 20, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #599 +PAIRING(10, 15, 20, 25, 16, 20, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #600 +PAIRING(10, 15, 20, 25, 16, 20, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #601 +PAIRING(10, 15, 20, 25, 16, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 1) // #602 +PAIRING(10, 15, 20, 25, 16, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #603 +PAIRING(10, 15, 20, 25, 16, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 2) // #604 +PAIRING(10, 15, 20, 25, 16, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 1) // #605 +PAIRING(10, 15, 20, 25, 16, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #606 +PAIRING(10, 15, 20, 25, 16, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 2) // #607 +PAIRING(10, 15, 20, 25, 16, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #608 +PAIRING(10, 15, 20, 25, 16, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #609 +PAIRING(10, 15, 20, 25, 16, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #610 +PAIRING(10, 15, 20, 25, 16, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #611 +PAIRING(10, 15, 20, 25, 16, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #612 +PAIRING(10, 15, 20, 25, 16, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #613 +PAIRING(10, 15, 20, 25, 20, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 0) // #614 +PAIRING(10, 15, 20, 25, 20, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 0) // #615 +PAIRING(10, 15, 20, 25, 20, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #616 +PAIRING(10, 15, 20, 25, 20, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 0) // #617 +PAIRING(10, 15, 20, 25, 20, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 0) // #618 +PAIRING(10, 15, 20, 25, 20, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #619 +PAIRING(10, 15, 20, 25, 20, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #620 +PAIRING(10, 15, 20, 25, 20, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #621 +PAIRING(10, 15, 20, 25, 20, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #622 +PAIRING(10, 15, 20, 25, 20, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #623 +PAIRING(10, 15, 20, 25, 20, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #624 +PAIRING(10, 15, 20, 25, 20, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #625 +PAIRING(10, 15, 20, 25, 21, 22, 22, 23, 0, 2, 0, 0, 0, 0, 21, 22, 22, 23, 2, 0) // #626 +PAIRING(10, 15, 20, 25, 21, 22, 22, 25, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 0) // #627 +PAIRING(10, 15, 20, 25, 21, 22, 22, 26, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 1) // #628 +PAIRING(10, 15, 20, 25, 21, 22, 23, 24, 0, 2, 0, 0, 0, 0, 21, 22, 23, 24, 2, 0) // #629 +PAIRING(10, 15, 20, 25, 21, 22, 23, 25, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 0) // #630 +PAIRING(10, 15, 20, 25, 21, 22, 23, 26, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 1) // #631 +PAIRING(10, 15, 20, 25, 21, 22, 25, 26, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #632 +PAIRING(10, 15, 20, 25, 21, 22, 26, 27, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #633 +PAIRING(10, 15, 20, 25, 21, 25, 25, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #634 +PAIRING(10, 15, 20, 25, 21, 25, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #635 +PAIRING(10, 15, 20, 25, 21, 26, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #636 +PAIRING(10, 15, 20, 25, 21, 26, 27, 28, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #637 +PAIRING(10, 15, 20, 25, 25, 26, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #638 +PAIRING(10, 15, 20, 25, 25, 26, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #639 +PAIRING(10, 15, 20, 25, 26, 27, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #640 +PAIRING(10, 15, 20, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #641 diff --git a/toolkit/crashreporter/google-breakpad/src/common/tests/auto_tempdir.h b/toolkit/crashreporter/google-breakpad/src/common/tests/auto_tempdir.h new file mode 100644 index 0000000000..1df88db8bd --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/tests/auto_tempdir.h @@ -0,0 +1,100 @@ +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Utility class for creating a temporary directory for unit tests +// that is deleted in the destructor. +#ifndef GOOGLE_BREAKPAD_COMMON_TESTS_AUTO_TEMPDIR +#define GOOGLE_BREAKPAD_COMMON_TESTS_AUTO_TEMPDIR + +#include <dirent.h> +#include <sys/types.h> + +#include <string> + +#include "breakpad_googletest_includes.h" +#include "common/using_std_string.h" + +#if !defined(__ANDROID__) +#define TEMPDIR "/tmp" +#else +#define TEMPDIR "/data/local/tmp" +#include "common/android/testing/mkdtemp.h" +#endif + +namespace google_breakpad { + +class AutoTempDir { + public: + AutoTempDir() { + char temp_dir[] = TEMPDIR "/breakpad.XXXXXX"; + EXPECT_TRUE(mkdtemp(temp_dir) != NULL); + path_.assign(temp_dir); + } + + ~AutoTempDir() { + DeleteRecursively(path_); + } + + const string& path() const { + return path_; + } + + private: + void DeleteRecursively(const string& path) { + // First remove any files in the dir + DIR* dir = opendir(path.c_str()); + if (!dir) + return; + + dirent* entry; + while ((entry = readdir(dir)) != NULL) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + string entry_path = path + "/" + entry->d_name; + struct stat stats; + EXPECT_TRUE(lstat(entry_path.c_str(), &stats) == 0); + if (S_ISDIR(stats.st_mode)) + DeleteRecursively(entry_path); + else + EXPECT_TRUE(unlink(entry_path.c_str()) == 0); + } + EXPECT_TRUE(closedir(dir) == 0); + EXPECT_TRUE(rmdir(path.c_str()) == 0); + } + + // prevent copy construction and assignment + AutoTempDir(const AutoTempDir&); + AutoTempDir& operator=(const AutoTempDir&); + + string path_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_COMMON_TESTS_AUTO_TEMPDIR diff --git a/toolkit/crashreporter/google-breakpad/src/common/tests/file_utils.cc b/toolkit/crashreporter/google-breakpad/src/common/tests/file_utils.cc new file mode 100644 index 0000000000..1c041777cb --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/tests/file_utils.cc @@ -0,0 +1,153 @@ +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// file_utils.cc: Implement utility functions for file manipulation. +// See file_utils.h for details. + +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "common/linux/eintr_wrapper.h" +#include "common/tests/file_utils.h" + +namespace google_breakpad { + +bool CopyFile(const char* from_path, const char* to_path) { + int infile = HANDLE_EINTR(open(from_path, O_RDONLY)); + if (infile < 0) { + perror("open"); + return false; + } + + int outfile = HANDLE_EINTR(creat(to_path, 0666)); + if (outfile < 0) { + perror("creat"); + if (IGNORE_EINTR(close(infile)) < 0) { + perror("close"); + } + return false; + } + + char buffer[1024]; + bool result = true; + + while (result) { + ssize_t bytes_read = HANDLE_EINTR(read(infile, buffer, sizeof(buffer))); + if (bytes_read < 0) { + perror("read"); + result = false; + break; + } + if (bytes_read == 0) + break; + ssize_t bytes_written_per_read = 0; + do { + ssize_t bytes_written_partial = HANDLE_EINTR(write( + outfile, + &buffer[bytes_written_per_read], + bytes_read - bytes_written_per_read)); + if (bytes_written_partial < 0) { + perror("write"); + result = false; + break; + } + bytes_written_per_read += bytes_written_partial; + } while (bytes_written_per_read < bytes_read); + } + + if (IGNORE_EINTR(close(infile)) == -1) { + perror("close"); + result = false; + } + if (IGNORE_EINTR(close(outfile)) == -1) { + perror("close"); + result = false; + } + + return result; +} + +bool ReadFile(const char* path, void* buffer, ssize_t* buffer_size) { + int fd = HANDLE_EINTR(open(path, O_RDONLY)); + if (fd == -1) { + perror("open"); + return false; + } + + bool ok = true; + if (buffer && buffer_size && *buffer_size > 0) { + memset(buffer, 0, sizeof(*buffer_size)); + *buffer_size = HANDLE_EINTR(read(fd, buffer, *buffer_size)); + if (*buffer_size == -1) { + perror("read"); + ok = false; + } + } + if (IGNORE_EINTR(close(fd)) == -1) { + perror("close"); + ok = false; + } + return ok; +} + +bool WriteFile(const char* path, const void* buffer, size_t buffer_size) { + int fd = HANDLE_EINTR(open(path, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXU)); + if (fd == -1) { + perror("open"); + return false; + } + + bool ok = true; + if (buffer) { + size_t bytes_written_total = 0; + ssize_t bytes_written_partial = 0; + const char* data = reinterpret_cast<const char*>(buffer); + while (bytes_written_total < buffer_size) { + bytes_written_partial = + HANDLE_EINTR(write(fd, data + bytes_written_total, + buffer_size - bytes_written_total)); + if (bytes_written_partial < 0) { + perror("write"); + ok = false; + break; + } + bytes_written_total += bytes_written_partial; + } + } + if (IGNORE_EINTR(close(fd)) == -1) { + perror("close"); + ok = false; + } + return ok; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/tests/file_utils.h b/toolkit/crashreporter/google-breakpad/src/common/tests/file_utils.h new file mode 100644 index 0000000000..c98a9bfa8b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/tests/file_utils.h @@ -0,0 +1,52 @@ +// Copyright (c) 2011, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// file_utils.h: Define utility functions for file manipulation, which +// are used for testing. + +#ifndef COMMON_TESTS_FILE_UTILS_H_ +#define COMMON_TESTS_FILE_UTILS_H_ + +namespace google_breakpad { + +// Copies a file from |from_path| to |to_path|. Returns true on success. +bool CopyFile(const char* from_path, const char* to_path); + +// Reads the content of a file at |path| into |buffer|. |buffer_size| specifies +// the size of |buffer| in bytes and returns the number of bytes read from the +// file on success. Returns true on success. +bool ReadFile(const char* path, void* buffer, ssize_t* buffer_size); + +// Writes |buffer_size| bytes of the content in |buffer| to a file at |path|. +// Returns true on success. +bool WriteFile(const char* path, const void* buffer, size_t buffer_size); + +} // namespace google_breakpad + +#endif // COMMON_TESTS_FILE_UTILS_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/unordered.h b/toolkit/crashreporter/google-breakpad/src/common/unordered.h new file mode 100644 index 0000000000..c9cbd58546 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/unordered.h @@ -0,0 +1,62 @@ +// 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Include this file to use unordered_map and unordered_set. If tr1 +// or C++11 is not available, you can switch to using hash_set and +// hash_map by defining BP_USE_HASH_SET. + +#ifndef COMMON_UNORDERED_H_ +#define COMMON_UNORDERED_H_ + +#if defined(BP_USE_HASH_SET) +#include <hash_map> +#include <hash_set> + +// For hash<string>. +#include "util/hash/hash.h" + +template <class T, class U, class H = __gnu_cxx::hash<T> > +struct unordered_map : public __gnu_cxx::hash_map<T, U, H> {}; +template <class T, class H = __gnu_cxx::hash<T> > +struct unordered_set : public __gnu_cxx::hash_set<T, H> {}; + +#elif defined(_LIBCPP_VERSION) // c++11 +#include <unordered_map> +#include <unordered_set> +using std::unordered_map; +using std::unordered_set; + +#else // Fallback to tr1::unordered +#include <tr1/unordered_map> +#include <tr1/unordered_set> +using std::tr1::unordered_map; +using std::tr1::unordered_set; +#endif + +#endif // COMMON_UNORDERED_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/using_std_string.h b/toolkit/crashreporter/google-breakpad/src/common/using_std_string.h new file mode 100644 index 0000000000..13c1da59cc --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/using_std_string.h @@ -0,0 +1,65 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2012, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Original author: Ivan Penkov + +// using_std_string.h: Allows building this code in environments where +// global string (::string) exists. +// +// The problem: +// ------------- +// Let's say you want to build this code in an environment where a global +// string type is defined (i.e. ::string). Now, let's suppose that ::string +// is different that std::string and you'd like to have the option to easily +// choose between the two string types. Ideally you'd like to control which +// string type is chosen by simply #defining an identifier. +// +// The solution: +// ------------- +// #define HAS_GLOBAL_STRING somewhere in a global header file and then +// globally replace std::string with string. Then include this header +// file everywhere where string is used. If you want to revert back to +// using std::string, simply remove the #define (HAS_GLOBAL_STRING). + +#ifndef THIRD_PARTY_BREAKPAD_SRC_COMMON_USING_STD_STRING_H_ +#define THIRD_PARTY_BREAKPAD_SRC_COMMON_USING_STD_STRING_H_ + +#ifdef HAS_GLOBAL_STRING + typedef ::string google_breakpad_string; +#else + using std::string; + typedef std::string google_breakpad_string; +#endif + +// Inicates that type google_breakpad_string is defined +#define HAS_GOOGLE_BREAKPAD_STRING + +#endif // THIRD_PARTY_BREAKPAD_SRC_COMMON_USING_STD_STRING_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/common_windows.gyp b/toolkit/crashreporter/google-breakpad/src/common/windows/common_windows.gyp new file mode 100644 index 0000000000..5f7594b161 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/common_windows.gyp @@ -0,0 +1,112 @@ +# Copyright 2013 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. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# 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. + +{ + 'includes': [ + '../../build/common.gypi', + ], + 'targets': [ + { + 'target_name': 'dia_sdk', + 'type': 'none', + 'all_dependent_settings': { + 'include_dirs': [ + '<(DEPTH)', + '$(VSInstallDir)/DIA SDK/include', + ], + 'msvs_settings': { + 'VCLinkerTool': { + 'AdditionalDependencies': [ + 'diaguids.lib', + 'imagehlp.lib', + ], + }, + }, + 'configurations': { + 'x86_Base': { + 'msvs_settings': { + 'VCLinkerTool': { + 'AdditionalLibraryDirectories': + ['$(VSInstallDir)/DIA SDK/lib'], + }, + }, + }, + 'x64_Base': { + 'msvs_settings': { + 'VCLinkerTool': { + 'AdditionalLibraryDirectories': + ['$(VSInstallDir)/DIA SDK/lib/amd64'], + }, + }, + }, + }, + }, + }, + { + 'target_name': 'common_windows_lib', + 'type': 'static_library', + 'sources': [ + 'dia_util.cc', + 'dia_util.h', + 'guid_string.cc', + 'guid_string.h', + 'http_upload.cc', + 'http_upload.h', + 'module_info.h', + 'omap.cc', + 'omap.h', + 'omap_internal.h', + 'pdb_source_line_writer.cc', + 'pdb_source_line_writer.h', + 'pe_source_line_writer.cc', + 'pe_source_line_writer.h', + 'pe_util.h', + 'pe_util.cc', + 'string_utils.cc', + 'string_utils-inl.h', + 'symbol_collector_client.cc', + 'symbol_collector_client.h', + ], + 'dependencies': [ + 'dia_sdk', + ], + }, + { + 'target_name': 'common_windows_unittests', + 'type': 'executable', + 'sources': [ + 'omap_unittest.cc', + ], + 'dependencies': [ + '<(DEPTH)/client/windows/unittests/testing.gyp:gmock', + '<(DEPTH)/client/windows/unittests/testing.gyp:gtest', + 'common_windows_lib', + ], + }, + ], +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/dia_util.cc b/toolkit/crashreporter/google-breakpad/src/common/windows/dia_util.cc new file mode 100644 index 0000000000..ed8cb5b658 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/dia_util.cc @@ -0,0 +1,92 @@ +// Copyright 2013 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.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// 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.
+
+#include "common/windows/dia_util.h"
+
+#include <atlbase.h>
+
+namespace google_breakpad {
+
+bool FindDebugStream(const wchar_t* name,
+ IDiaSession* session,
+ IDiaEnumDebugStreamData** debug_stream) {
+ CComPtr<IDiaEnumDebugStreams> enum_debug_streams;
+ if (FAILED(session->getEnumDebugStreams(&enum_debug_streams))) {
+ fprintf(stderr, "IDiaSession::getEnumDebugStreams failed\n");
+ return false;
+ }
+
+ CComPtr<IDiaEnumDebugStreamData> temp_debug_stream;
+ ULONG fetched = 0;
+ while (SUCCEEDED(enum_debug_streams->Next(1, &temp_debug_stream, &fetched)) &&
+ fetched == 1) {
+ CComBSTR stream_name;
+ if (FAILED(temp_debug_stream->get_name(&stream_name))) {
+ fprintf(stderr, "IDiaEnumDebugStreamData::get_name failed\n");
+ return false;
+ }
+
+ // Found the stream?
+ if (wcsncmp((LPWSTR)stream_name, name, stream_name.Length()) == 0) {
+ *debug_stream = temp_debug_stream.Detach();
+ return true;
+ }
+
+ temp_debug_stream.Release();
+ }
+
+ // No table was found.
+ return false;
+}
+
+bool FindTable(REFIID iid, IDiaSession* session, void** table) {
+ // Get the table enumerator.
+ CComPtr<IDiaEnumTables> enum_tables;
+ if (FAILED(session->getEnumTables(&enum_tables))) {
+ fprintf(stderr, "IDiaSession::getEnumTables failed\n");
+ return false;
+ }
+
+ // Iterate through the tables.
+ CComPtr<IDiaTable> temp_table;
+ ULONG fetched = 0;
+ while (SUCCEEDED(enum_tables->Next(1, &temp_table, &fetched)) &&
+ fetched == 1) {
+ void* temp = NULL;
+ if (SUCCEEDED(temp_table->QueryInterface(iid, &temp))) {
+ *table = temp;
+ return true;
+ }
+ temp_table.Release();
+ }
+
+ // The table was not found.
+ return false;
+}
+
+} // namespace google_breakpad
\ No newline at end of file diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/dia_util.h b/toolkit/crashreporter/google-breakpad/src/common/windows/dia_util.h new file mode 100644 index 0000000000..b9e0df2d56 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/dia_util.h @@ -0,0 +1,64 @@ +// Copyright 2013 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Utilities for loading debug streams and tables from a PDB file. + +#ifndef COMMON_WINDOWS_DIA_UTIL_H_ +#define COMMON_WINDOWS_DIA_UTIL_H_ + +#include <Windows.h> +#include <dia2.h> + +namespace google_breakpad { + +// Find the debug stream of the given |name| in the given |session|. Returns +// true on success, false on error of if the stream does not exist. On success +// the stream will be returned via |debug_stream|. +bool FindDebugStream(const wchar_t* name, + IDiaSession* session, + IDiaEnumDebugStreamData** debug_stream); + +// Finds the first table implementing the COM interface with ID |iid| in the +// given |session|. Returns true on success, false on error or if no such +// table is found. On success the table will be returned via |table|. +bool FindTable(REFIID iid, IDiaSession* session, void** table); + +// A templated version of FindTable. Finds the first table implementing type +// |InterfaceType| in the given |session|. Returns true on success, false on +// error or if no such table is found. On success the table will be returned via +// |table|. +template<typename InterfaceType> +bool FindTable(IDiaSession* session, InterfaceType** table) { + return FindTable(__uuidof(InterfaceType), + session, + reinterpret_cast<void**>(table)); +} + +} // namespace google_breakpad + +#endif // COMMON_WINDOWS_DIA_UTIL_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/guid_string.cc b/toolkit/crashreporter/google-breakpad/src/common/windows/guid_string.cc new file mode 100644 index 0000000000..b7f877e66e --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/guid_string.cc @@ -0,0 +1,76 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// guid_string.cc: Convert GUIDs to strings. +// +// See guid_string.h for documentation. + +#include <wchar.h> + +#include "common/windows/string_utils-inl.h" + +#include "common/windows/guid_string.h" + +namespace google_breakpad { + +// static +wstring GUIDString::GUIDToWString(GUID *guid) { + wchar_t guid_string[37]; + swprintf( + guid_string, sizeof(guid_string) / sizeof(guid_string[0]), + L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], + guid->Data4[3], guid->Data4[4], guid->Data4[5], + guid->Data4[6], guid->Data4[7]); + + // remove when VC++7.1 is no longer supported + guid_string[sizeof(guid_string) / sizeof(guid_string[0]) - 1] = L'\0'; + + return wstring(guid_string); +} + +// static +wstring GUIDString::GUIDToSymbolServerWString(GUID *guid) { + wchar_t guid_string[33]; + swprintf( + guid_string, sizeof(guid_string) / sizeof(guid_string[0]), + L"%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X", + guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], + guid->Data4[3], guid->Data4[4], guid->Data4[5], + guid->Data4[6], guid->Data4[7]); + + // remove when VC++7.1 is no longer supported + guid_string[sizeof(guid_string) / sizeof(guid_string[0]) - 1] = L'\0'; + + return wstring(guid_string); +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/guid_string.h b/toolkit/crashreporter/google-breakpad/src/common/windows/guid_string.h new file mode 100644 index 0000000000..48a5c1d370 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/guid_string.h @@ -0,0 +1,58 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// guid_string.cc: Convert GUIDs to strings. + +#ifndef COMMON_WINDOWS_GUID_STRING_H_ +#define COMMON_WINDOWS_GUID_STRING_H_ + +#include <guiddef.h> + +#include <string> + +namespace google_breakpad { + +using std::wstring; + +class GUIDString { + public: + // Converts guid to a string in the format recommended by RFC 4122 and + // returns the string. + static wstring GUIDToWString(GUID *guid); + + // Converts guid to a string formatted as uppercase hexadecimal, with + // no separators, and returns the string. This is the format used for + // symbol server identifiers, although identifiers have an age tacked + // on to the string. + static wstring GUIDToSymbolServerWString(GUID *guid); +}; + +} // namespace google_breakpad + +#endif // COMMON_WINDOWS_GUID_STRING_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.cc b/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.cc new file mode 100644 index 0000000000..5df17e1a88 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.cc @@ -0,0 +1,493 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include <assert.h> + +// Disable exception handler warnings. +#pragma warning(disable:4530) + +#include <fstream> +#include <vector> + +#include "common/windows/string_utils-inl.h" + +#include "common/windows/http_upload.h" + +namespace { + using std::string; + using std::wstring; + using std::map; + using std::vector; + using std::ifstream; + using std::ios; + + const wchar_t kUserAgent[] = L"Breakpad/1.0 (Windows)"; + + // Helper class which closes an internet handle when it goes away + class AutoInternetHandle { + public: + explicit AutoInternetHandle(HINTERNET handle) : handle_(handle) {} + ~AutoInternetHandle() { + if (handle_) { + InternetCloseHandle(handle_); + } + } + + HINTERNET get() { return handle_; } + + private: + HINTERNET handle_; + }; + + wstring UTF8ToWide(const string &utf8) { + if (utf8.length() == 0) { + return wstring(); + } + + // compute the length of the buffer we'll need + int charcount = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, NULL, 0); + + if (charcount == 0) { + return wstring(); + } + + // convert + wchar_t* buf = new wchar_t[charcount]; + MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, buf, charcount); + wstring result(buf); + delete[] buf; + return result; + } + + string WideToMBCP(const wstring &wide, unsigned int cp) { + if (wide.length() == 0) { + return string(); + } + + // compute the length of the buffer we'll need + int charcount = WideCharToMultiByte(cp, 0, wide.c_str(), -1, + NULL, 0, NULL, NULL); + if (charcount == 0) { + return string(); + } + + // convert + char *buf = new char[charcount]; + WideCharToMultiByte(cp, 0, wide.c_str(), -1, buf, charcount, + NULL, NULL); + + string result(buf); + delete[] buf; + return result; + } + + bool GetFileContents(const wstring &filename, vector<char> *contents) { + bool rv = false; + // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a + // wchar_t* filename, so use _wfopen directly in that case. For VC8 and + // later, _wfopen has been deprecated in favor of _wfopen_s, which does + // not exist in earlier versions, so let the ifstream open the file itself. + // GCC doesn't support wide file name and opening on FILE* requires ugly + // hacks, so fallback to multi byte file. +#ifdef _MSC_VER + ifstream file; + file.open(filename.c_str(), ios::binary); +#else // GCC + ifstream file(WideToMBCP(filename, CP_ACP).c_str(), ios::binary); +#endif // _MSC_VER >= 1400 + if (file.is_open()) { + file.seekg(0, ios::end); + std::streamoff length = file.tellg(); + // Check for loss of data when converting lenght from std::streamoff into + // std::vector<char>::size_type + std::vector<char>::size_type vector_size = + static_cast<std::vector<char>::size_type>(length); + if (static_cast<std::streamoff>(vector_size) == length) { + contents->resize(vector_size); + if (length != 0) { + file.seekg(0, ios::beg); + file.read(&((*contents)[0]), length); + } + rv = true; + } + file.close(); + } + return rv; + } + + // Converts a UTF16 string to UTF8. + string WideToUTF8(const wstring &wide) { + return WideToMBCP(wide, CP_UTF8); + } + + bool ReadResponse(HINTERNET request, wstring *response) { + bool has_content_length_header = false; + wchar_t content_length[32]; + DWORD content_length_size = sizeof(content_length); + DWORD claimed_size = 0; + string response_body; + + if (HttpQueryInfo(request, HTTP_QUERY_CONTENT_LENGTH, + static_cast<LPVOID>(&content_length), + &content_length_size, 0)) { + has_content_length_header = true; + claimed_size = wcstol(content_length, NULL, 10); + response_body.reserve(claimed_size); + } + + DWORD bytes_available; + DWORD total_read = 0; + BOOL return_code; + + while (((return_code = InternetQueryDataAvailable(request, &bytes_available, + 0, 0)) != 0) && bytes_available > 0) { + vector<char> response_buffer(bytes_available); + DWORD size_read; + + return_code = InternetReadFile(request, + &response_buffer[0], + bytes_available, &size_read); + + if (return_code && size_read > 0) { + total_read += size_read; + response_body.append(&response_buffer[0], size_read); + } + else { + break; + } + } + + bool succeeded = return_code && (!has_content_length_header || + (total_read == claimed_size)); + if (succeeded && response) { + *response = UTF8ToWide(response_body); + } + + return succeeded; + } + + bool SendRequestInner( + const wstring& url, + const wstring& http_method, + const wstring& content_type_header, + const string& request_body, + int* timeout_ms, + wstring* response_body, + int* response_code) { + if (response_code) { + *response_code = 0; + } + + // Break up the URL and make sure we can handle it + wchar_t scheme[16], host[256], path[1024]; + URL_COMPONENTS components; + memset(&components, 0, sizeof(components)); + components.dwStructSize = sizeof(components); + components.lpszScheme = scheme; + components.dwSchemeLength = sizeof(scheme) / sizeof(scheme[0]); + components.lpszHostName = host; + components.dwHostNameLength = sizeof(host) / sizeof(host[0]); + components.lpszUrlPath = path; + components.dwUrlPathLength = sizeof(path) / sizeof(path[0]); + if (!InternetCrackUrl(url.c_str(), static_cast<DWORD>(url.size()), + 0, &components)) { + DWORD err = GetLastError(); + wprintf(L"%d\n", err); + return false; + } + bool secure = false; + if (wcscmp(scheme, L"https") == 0) { + secure = true; + } + else if (wcscmp(scheme, L"http") != 0) { + return false; + } + + AutoInternetHandle internet(InternetOpen(kUserAgent, + INTERNET_OPEN_TYPE_PRECONFIG, + NULL, // proxy name + NULL, // proxy bypass + 0)); // flags + if (!internet.get()) { + return false; + } + + AutoInternetHandle connection(InternetConnect(internet.get(), + host, + components.nPort, + NULL, // user name + NULL, // password + INTERNET_SERVICE_HTTP, + 0, // flags + NULL)); // context + if (!connection.get()) { + return false; + } + + DWORD http_open_flags = secure ? INTERNET_FLAG_SECURE : 0; + http_open_flags |= INTERNET_FLAG_NO_COOKIES; + AutoInternetHandle request(HttpOpenRequest(connection.get(), + http_method.c_str(), + path, + NULL, // version + NULL, // referer + NULL, // agent type + http_open_flags, + NULL)); // context + if (!request.get()) { + return false; + } + + if (!content_type_header.empty()) { + HttpAddRequestHeaders(request.get(), + content_type_header.c_str(), + static_cast<DWORD>(-1), + HTTP_ADDREQ_FLAG_ADD); + } + + if (timeout_ms) { + if (!InternetSetOption(request.get(), + INTERNET_OPTION_SEND_TIMEOUT, + timeout_ms, + sizeof(*timeout_ms))) { + fwprintf(stderr, L"Could not unset send timeout, continuing...\n"); + } + + if (!InternetSetOption(request.get(), + INTERNET_OPTION_RECEIVE_TIMEOUT, + timeout_ms, + sizeof(*timeout_ms))) { + fwprintf(stderr, L"Could not unset receive timeout, continuing...\n"); + } + } + + if (!HttpSendRequest(request.get(), NULL, 0, + const_cast<char *>(request_body.data()), + static_cast<DWORD>(request_body.size()))) { + return false; + } + + // The server indicates a successful upload with HTTP status 200. + wchar_t http_status[4]; + DWORD http_status_size = sizeof(http_status); + if (!HttpQueryInfo(request.get(), HTTP_QUERY_STATUS_CODE, + static_cast<LPVOID>(&http_status), &http_status_size, + 0)) { + return false; + } + + int http_response = wcstol(http_status, NULL, 10); + if (response_code) { + *response_code = http_response; + } + + bool result = (http_response == 200); + + if (result) { + result = ReadResponse(request.get(), response_body); + } + + return result; + } + + wstring GenerateMultipartBoundary() { + // The boundary has 27 '-' characters followed by 16 hex digits + static const wchar_t kBoundaryPrefix[] = L"---------------------------"; + static const int kBoundaryLength = 27 + 16 + 1; + + // Generate some random numbers to fill out the boundary + int r0 = rand(); + int r1 = rand(); + + wchar_t temp[kBoundaryLength]; + swprintf(temp, kBoundaryLength, L"%s%08X%08X", kBoundaryPrefix, r0, r1); + + // remove when VC++7.1 is no longer supported + temp[kBoundaryLength - 1] = L'\0'; + + return wstring(temp); + } + + wstring GenerateMultipartPostRequestHeader(const wstring &boundary) { + wstring header = L"Content-Type: multipart/form-data; boundary="; + header += boundary; + return header; + } + + bool AppendFileToRequestBody( + const wstring& file_part_name, + const wstring& filename, + string* request_body) { + string file_part_name_utf8 = WideToUTF8(file_part_name); + if (file_part_name_utf8.empty()) { + return false; + } + + string filename_utf8 = WideToUTF8(filename); + if (filename_utf8.empty()) { + return false; + } + + request_body->append("Content-Disposition: form-data; " + "name=\"" + file_part_name_utf8 + "\"; " + "filename=\"" + filename_utf8 + "\"\r\n"); + request_body->append("Content-Type: application/octet-stream\r\n"); + request_body->append("\r\n"); + + vector<char> contents; + if (!GetFileContents(filename, &contents)) { + return false; + } + + if (!contents.empty()) { + request_body->append(&(contents[0]), contents.size()); + } + request_body->append("\r\n"); + + return true; + } + + bool GenerateRequestBody(const string ¶meters, + const map<wstring, wstring> &files, + const wstring &boundary, + string *request_body) { + string boundary_str = WideToUTF8(boundary); + if (boundary_str.empty()) { + return false; + } + + request_body->clear(); + + // Append the extra data as a single JSON form entry + request_body->append("--" + boundary_str + "\r\n"); + request_body->append( + "Content-Disposition: form-data; " + "name=\"extra\"; " + "filename=\"extra.json\"\r\n"); + request_body->append("Content-Type: application/json\r\n"); + request_body->append("\r\n"); + + if (!parameters.empty()) { + request_body->append(parameters); + } + request_body->append("\r\n"); + + // Now append each upload file as a binary (octet-stream) part + for (map<wstring, wstring>::const_iterator pos = files.begin(); + pos != files.end(); ++pos) { + request_body->append("--" + boundary_str + "\r\n"); + + if (!AppendFileToRequestBody(pos->first, pos->second, request_body)) { + return false; + } + } + request_body->append("--" + boundary_str + "--\r\n"); + return true; + } +} + +namespace google_breakpad { + bool HTTPUpload::SendPutRequest( + const wstring& url, + const wstring& path, + int* timeout_ms, + wstring* response_body, + int* response_code) { + string request_body; + if (!AppendFileToRequestBody(L"symbol_file", path, &request_body)) { + return false; + } + + return SendRequestInner( + url, + L"PUT", + L"", + request_body, + timeout_ms, + response_body, + response_code); + } + + bool HTTPUpload::SendGetRequest( + const wstring& url, + int* timeout_ms, + wstring* response_body, + int* response_code) { + return SendRequestInner( + url, + L"GET", + L"", + "", + timeout_ms, + response_body, + response_code); + } + + bool HTTPUpload::SendMultipartPostRequest( + const wstring& url, + const string& parameters, + const map<wstring, wstring>& files, + int* timeout_ms, + wstring* response_body, + int* response_code) { + wstring boundary = GenerateMultipartBoundary(); + wstring content_type_header = GenerateMultipartPostRequestHeader(boundary); + + string request_body; + if (!GenerateRequestBody(parameters, files, boundary, &request_body)) { + return false; + } + + return SendRequestInner( + url, + L"POST", + content_type_header, + request_body, + timeout_ms, + response_body, + response_code); + } + + bool HTTPUpload::SendSimplePostRequest( + const wstring& url, + const wstring& body, + const wstring& content_type, + int *timeout_ms, + wstring *response_body, + int *response_code) { + return SendRequestInner( + url, + L"POST", + content_type, + WideToUTF8(body), + timeout_ms, + response_body, + response_code); + } +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.h b/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.h new file mode 100644 index 0000000000..1e47f5829f --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.h @@ -0,0 +1,125 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// HTTPUpload provides a "nice" API to send a multipart HTTP(S) POST +// request using wininet. It currently supports requests that contain +// parameters encoded in a JSON string, and a file to upload. + +#ifndef COMMON_WINDOWS_HTTP_UPLOAD_H_ +#define COMMON_WINDOWS_HTTP_UPLOAD_H_ + +#pragma warning(push) +// Disable exception handler warnings. +#pragma warning(disable : 4530) + +#include <windows.h> +#include <wininet.h> + +#include <map> + +namespace google_breakpad { + +using std::map; +using std::string; +using std::wstring; + +class HTTPUpload { + public: + // Sends a PUT request containing the data in |path| to the given + // URL. + // Only HTTP(S) URLs are currently supported. Returns true on success. + // If the request is successful and response_body is non-NULL, + // the response body will be returned in response_body. + // If response_code is non-NULL, it will be set to the HTTP response code + // received (or 0 if the request failed before getting an HTTP response). + static bool SendPutRequest( + const wstring& url, + const wstring& path, + int* timeout_ms, + wstring* response_body, + int* response_code); + + // Sends a GET request to the given URL. + // Only HTTP(S) URLs are currently supported. Returns true on success. + // If the request is successful and response_body is non-NULL, + // the response body will be returned in response_body. + // If response_code is non-NULL, it will be set to the HTTP response code + // received (or 0 if the request failed before getting an HTTP response). + static bool SendGetRequest( + const wstring& url, + int* timeout_ms, + wstring* response_body, + int* response_code); + + // Sends the given sets of parameters and files as a multipart POST + // request to the given URL. + // Each key in |files| is the name of the file part of the request + // (i.e. it corresponds to the name= attribute on an <input type="file">. + // Parameters are specified as a JSON-encoded string in |parameters|. + // Only HTTP(S) URLs are currently supported. Returns true on success. + // If the request is successful and response_body is non-NULL, + // the response body will be returned in response_body. + // If response_code is non-NULL, it will be set to the HTTP response code + // received (or 0 if the request failed before getting an HTTP response). + static bool SendMultipartPostRequest( + const wstring& url, + const string& parameters, + const map<wstring, wstring>& files, + int *timeout_ms, + wstring *response_body, + int *response_code); + + // Sends a POST request, with the body set to |body|, to the given URL. + // Only HTTP(S) URLs are currently supported. Returns true on success. + // If the request is successful and response_body is non-NULL, + // the response body will be returned in response_body. + // If response_code is non-NULL, it will be set to the HTTP response code + // received (or 0 if the request failed before getting an HTTP response). + static bool SendSimplePostRequest( + const wstring& url, + const wstring& body, + const wstring& content_type, + int *timeout_ms, + wstring *response_body, + int *response_code); + + private: + // No instances of this class should be created. + // Disallow all constructors, destructors, and operator=. + HTTPUpload(); + explicit HTTPUpload(const HTTPUpload &); + void operator=(const HTTPUpload &); + ~HTTPUpload(); +}; + +} // namespace google_breakpad + +#pragma warning(pop) + +#endif // COMMON_WINDOWS_HTTP_UPLOAD_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/module_info.h b/toolkit/crashreporter/google-breakpad/src/common/windows/module_info.h new file mode 100644 index 0000000000..3dccc80882 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/module_info.h @@ -0,0 +1,75 @@ +// Copyright (c) 2019, 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.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// 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.
+
+#ifndef COMMON_WINDOWS_MODULE_INFO_H_
+#define COMMON_WINDOWS_MODULE_INFO_H_
+
+#include <string>
+
+namespace google_breakpad {
+
+using std::wstring;
+// A structure that carries information that identifies a module.
+struct PDBModuleInfo {
+public:
+ // The basename of the pe/pdb file from which information was loaded.
+ wstring debug_file;
+
+ // The module's identifier. For recent pe/pdb files, the identifier consists
+ // of the pe/pdb's guid, in uppercase hexadecimal form without any dashes
+ // or separators, followed immediately by the pe/pdb's age, also in
+ // uppercase hexadecimal form. For older pe/pdb files which have no guid,
+ // the identifier is the pe/pdb's 32-bit signature value, in zero-padded
+ // hexadecimal form, followed immediately by the pe/pdb's age, in lowercase
+ // hexadecimal form.
+ wstring debug_identifier;
+
+ // A string identifying the cpu that the pe/pdb is associated with.
+ // Currently, this may be "x86" or "unknown".
+ wstring cpu;
+};
+
+// A structure that carries information that identifies a PE file,
+// either an EXE or a DLL.
+struct PEModuleInfo {
+ // The basename of the PE file.
+ wstring code_file;
+
+ // The PE file's code identifier, which consists of its timestamp
+ // and file size concatenated together into a single hex string.
+ // (The fields IMAGE_OPTIONAL_HEADER::SizeOfImage and
+ // IMAGE_FILE_HEADER::TimeDateStamp, as defined in the ImageHlp
+ // documentation.) This is not well documented, if it's documented
+ // at all, but it's what symstore does and what DbgHelp supports.
+ wstring code_identifier;
+};
+
+} // namespace google_breakpad
+
+#endif // COMMON_WINDOWS_MODULE_INFO_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/objs.mozbuild b/toolkit/crashreporter/google-breakpad/src/common/windows/objs.mozbuild new file mode 100644 index 0000000000..5fcdae5a35 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/objs.mozbuild @@ -0,0 +1,15 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +lobjs_common = [ + 'guid_string.cc', + 'string_utils.cc', +] + +subdir = 'toolkit/crashreporter/google-breakpad/src/common/windows' +objs_common = [ + '/%s/%s' % (subdir, s) for s in lobjs_common +] diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/omap.cc b/toolkit/crashreporter/google-breakpad/src/common/windows/omap.cc new file mode 100644 index 0000000000..ba3ce86b8b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/omap.cc @@ -0,0 +1,716 @@ +// Copyright 2013 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// This contains a suite of tools for transforming symbol information when +// when that information has been extracted from a PDB containing OMAP +// information. + +// OMAP information is a lightweight description of a mapping between two +// address spaces. It consists of two streams, each of them a vector 2-tuples. +// The OMAPTO stream contains tuples of the form +// +// (RVA in transformed image, RVA in original image) +// +// while the OMAPFROM stream contains tuples of the form +// +// (RVA in original image, RVA in transformed image) +// +// The entries in each vector are sorted by the first value of the tuple, and +// the lengths associated with a mapping are implicit as the distance between +// two successive addresses in the vector. + +// Consider a trivial 10-byte function described by the following symbol: +// +// Function: RVA 0x00001000, length 10, "foo" +// +// Now consider the same function, but with 5-bytes of instrumentation injected +// at offset 5. The OMAP streams describing this would look like: +// +// OMAPTO : [ [0x00001000, 0x00001000], +// [0x00001005, 0xFFFFFFFF], +// [0x0000100a, 0x00001005] ] +// OMAPFROM: [ [0x00001000, 0x00001000], +// [0x00001005, 0x0000100a] ] +// +// In this case the injected code has been marked as not originating in the +// source image, and thus it will have no symbol information at all. However, +// the injected code may also be associated with an original address range; +// for example, when prepending instrumentation to a basic block the +// instrumentation can be labelled as originating from the same source BB such +// that symbol resolution will still find the appropriate source code line +// number. In this case the OMAP stream would look like: +// +// OMAPTO : [ [0x00001000, 0x00001000], +// [0x00001005, 0x00001005], +// [0x0000100a, 0x00001005] ] +// OMAPFROM: [ [0x00001000, 0x00001000], +// [0x00001005, 0x0000100a] ] +// +// Suppose we asked DIA to lookup the symbol at location 0x0000100a of the +// instrumented image. It would first run this through the OMAPTO table and +// translate that address to 0x00001005. It would then lookup the symbol +// at that address and return the symbol for the function "foo". This is the +// correct result. +// +// However, if we query DIA for the length and address of the symbol it will +// tell us that it has length 10 and is at RVA 0x00001000. The location is +// correct, but the length doesn't take into account the 5-bytes of injected +// code. Symbol resolution works (starting from an instrumented address, +// mapping to an original address, and looking up a symbol), but the symbol +// metadata is incorrect. +// +// If we dump the symbols using DIA they will have their addresses +// appropriately transformed and reflect positions in the instrumented image. +// However, if we try to do a lookup using those symbols resolution can fail. +// For example, the address 0x0000100a will not map to the symbol for "foo", +// because DIA tells us it is at location 0x00001000 (correct) with length +// 10 (incorrect). The problem is one of order of operations: in this case +// we're attempting symbol resolution by looking up an instrumented address +// in the table of translated symbols. +// +// One way to handle this is to dump the OMAP information as part of the +// breakpad symbols. This requires the rest of the toolchain to be aware of +// OMAP information and to use it when present prior to performing lookup. The +// other option is to properly transform the symbols (updating length as well as +// position) so that resolution will work as expected for translated addresses. +// This is transparent to the rest of the toolchain. + +#include "common/windows/omap.h" + +#include <atlbase.h> + +#include <algorithm> +#include <cassert> +#include <set> + +#include "common/windows/dia_util.h" + +namespace google_breakpad { + +namespace { + +static const wchar_t kOmapToDebugStreamName[] = L"OMAPTO"; +static const wchar_t kOmapFromDebugStreamName[] = L"OMAPFROM"; + +// Dependending on where this is used in breakpad we sometimes get min/max from +// windef, and other times from algorithm. To get around this we simply +// define our own min/max functions. +template<typename T> +const T& Min(const T& t1, const T& t2) { return t1 < t2 ? t1 : t2; } +template<typename T> +const T& Max(const T& t1, const T& t2) { return t1 > t2 ? t1 : t2; } + +// It makes things more readable to have two different OMAP types. We cast +// normal OMAPs into these. They must be the same size as the OMAP structure +// for this to work, hence the static asserts. +struct OmapOrigToTran { + DWORD rva_original; + DWORD rva_transformed; +}; +struct OmapTranToOrig { + DWORD rva_transformed; + DWORD rva_original; +}; +static_assert(sizeof(OmapOrigToTran) == sizeof(OMAP), + "OmapOrigToTran must have same size as OMAP."); +static_assert(sizeof(OmapTranToOrig) == sizeof(OMAP), + "OmapTranToOrig must have same size as OMAP."); +typedef std::vector<OmapOrigToTran> OmapFromTable; +typedef std::vector<OmapTranToOrig> OmapToTable; + +// Used for sorting and searching through a Mapping. +bool MappedRangeOriginalLess(const MappedRange& lhs, const MappedRange& rhs) { + if (lhs.rva_original < rhs.rva_original) + return true; + if (lhs.rva_original > rhs.rva_original) + return false; + return lhs.length < rhs.length; +} +bool MappedRangeMappedLess(const MappedRange& lhs, const MappedRange& rhs) { + if (lhs.rva_transformed < rhs.rva_transformed) + return true; + if (lhs.rva_transformed > rhs.rva_transformed) + return false; + return lhs.length < rhs.length; +} + +// Used for searching through the EndpointIndexMap. +bool EndpointIndexLess(const EndpointIndex& ei1, const EndpointIndex& ei2) { + return ei1.endpoint < ei2.endpoint; +} + +// Finds the debug stream with the given |name| in the given |session|, and +// populates |table| with its contents. Casts the data directly into OMAP +// structs. +bool FindAndLoadOmapTable(const wchar_t* name, + IDiaSession* session, + OmapTable* table) { + assert(name != NULL); + assert(session != NULL); + assert(table != NULL); + + CComPtr<IDiaEnumDebugStreamData> stream; + if (!FindDebugStream(name, session, &stream)) + return false; + assert(stream.p != NULL); + + LONG count = 0; + if (FAILED(stream->get_Count(&count))) { + fprintf(stderr, "IDiaEnumDebugStreamData::get_Count failed for stream " + "\"%ws\"\n", name); + return false; + } + + // Get the length of the stream in bytes. + DWORD bytes_read = 0; + ULONG count_read = 0; + if (FAILED(stream->Next(count, 0, &bytes_read, NULL, &count_read))) { + fprintf(stderr, "IDiaEnumDebugStreamData::Next failed while reading " + "length of stream \"%ws\"\n", name); + return false; + } + + // Ensure it's consistent with the OMAP data type. + DWORD bytes_expected = count * sizeof(OmapTable::value_type); + if (count * sizeof(OmapTable::value_type) != bytes_read) { + fprintf(stderr, "DIA debug stream \"%ws\" has an unexpected length", name); + return false; + } + + // Read the table. + table->resize(count); + bytes_read = 0; + count_read = 0; + if (FAILED(stream->Next(count, bytes_expected, &bytes_read, + reinterpret_cast<BYTE*>(&table->at(0)), + &count_read))) { + fprintf(stderr, "IDiaEnumDebugStreamData::Next failed while reading " + "data from stream \"%ws\"\n", name); + return false; + } + + return true; +} + +// This determines the original image length by looking through the segment +// table. +bool GetOriginalImageLength(IDiaSession* session, DWORD* image_length) { + assert(session != NULL); + assert(image_length != NULL); + + CComPtr<IDiaEnumSegments> enum_segments; + if (!FindTable(session, &enum_segments)) + return false; + assert(enum_segments.p != NULL); + + DWORD temp_image_length = 0; + CComPtr<IDiaSegment> segment; + ULONG fetched = 0; + while (SUCCEEDED(enum_segments->Next(1, &segment, &fetched)) && + fetched == 1) { + assert(segment.p != NULL); + + DWORD rva = 0; + DWORD length = 0; + DWORD frame = 0; + if (FAILED(segment->get_relativeVirtualAddress(&rva)) || + FAILED(segment->get_length(&length)) || + FAILED(segment->get_frame(&frame))) { + fprintf(stderr, "Failed to get basic properties for IDiaSegment\n"); + return false; + } + + if (frame > 0) { + DWORD segment_end = rva + length; + if (segment_end > temp_image_length) + temp_image_length = segment_end; + } + segment.Release(); + } + + *image_length = temp_image_length; + return true; +} + +// Detects regions of the original image that have been removed in the +// transformed image, and sets the 'removed' property on all mapped ranges +// immediately preceding a gap. The mapped ranges must be sorted by +// 'rva_original'. +void FillInRemovedLengths(Mapping* mapping) { + assert(mapping != NULL); + + // Find and fill gaps. We do this with two sweeps. We first sweep forward + // looking for gaps. When we identify a gap we then sweep forward with a + // second scan and set the 'removed' property for any intervals that + // immediately precede the gap. + // + // Gaps are typically between two successive intervals, but not always: + // + // Range 1: --------------- + // Range 2: ------- + // Range 3: ------------- + // Gap : ****** + // + // In the above example the gap is between range 1 and range 3. A forward + // sweep finds the gap, and a second forward sweep identifies that range 1 + // immediately precedes the gap and sets its 'removed' property. + + size_t fill = 0; + DWORD rva_front = 0; + for (size_t find = 0; find < mapping->size(); ++find) { +#ifndef NDEBUG + // We expect the mapped ranges to be sorted by 'rva_original'. + if (find > 0) { + assert(mapping->at(find - 1).rva_original <= + mapping->at(find).rva_original); + } +#endif + + if (rva_front < mapping->at(find).rva_original) { + // We've found a gap. Fill it in by setting the 'removed' property for + // any affected intervals. + DWORD removed = mapping->at(find).rva_original - rva_front; + for (; fill < find; ++fill) { + if (mapping->at(fill).rva_original + mapping->at(fill).length != + rva_front) { + continue; + } + + // This interval ends right where the gap starts. It needs to have its + // 'removed' information filled in. + mapping->at(fill).removed = removed; + } + } + + // Advance the front that indicates the covered portion of the image. + rva_front = mapping->at(find).rva_original + mapping->at(find).length; + } +} + +// Builds a unified view of the mapping between the original and transformed +// image space by merging OMAPTO and OMAPFROM data. +void BuildMapping(const OmapData& omap_data, Mapping* mapping) { + assert(mapping != NULL); + + mapping->clear(); + + if (omap_data.omap_from.empty() || omap_data.omap_to.empty()) + return; + + // The names 'omap_to' and 'omap_from' are awfully confusing, so we make + // ourselves more explicit here. This cast is only safe because the underlying + // types have the exact same size. + const OmapToTable& tran2orig = + reinterpret_cast<const OmapToTable&>(omap_data.omap_to); + const OmapFromTable& orig2tran = reinterpret_cast<const OmapFromTable&>( + omap_data.omap_from); + + // Handle the range of data at the beginning of the image. This is not usually + // specified by the OMAP data. + if (tran2orig[0].rva_transformed > 0 && orig2tran[0].rva_original > 0) { + DWORD header_transformed = tran2orig[0].rva_transformed; + DWORD header_original = orig2tran[0].rva_original; + DWORD header = Min(header_transformed, header_original); + + MappedRange mr = {}; + mr.length = header; + mr.injected = header_transformed - header; + mr.removed = header_original - header; + mapping->push_back(mr); + } + + // Convert the implied lengths to explicit lengths, and infer which content + // has been injected into the transformed image. Injected content is inferred + // as regions of the transformed address space that does not map back to + // known valid content in the original image. + for (size_t i = 0; i < tran2orig.size(); ++i) { + const OmapTranToOrig& o1 = tran2orig[i]; + + // This maps to content that is outside the original image, thus it + // describes injected content. We can skip this entry. + if (o1.rva_original >= omap_data.length_original) + continue; + + // Calculate the length of the current OMAP entry. This is implicit as the + // distance between successive |rva| values, capped at the end of the + // original image. + DWORD length = 0; + if (i + 1 < tran2orig.size()) { + const OmapTranToOrig& o2 = tran2orig[i + 1]; + + // We expect the table to be sorted by rva_transformed. + assert(o1.rva_transformed <= o2.rva_transformed); + + length = o2.rva_transformed - o1.rva_transformed; + if (o1.rva_original + length > omap_data.length_original) { + length = omap_data.length_original - o1.rva_original; + } + } else { + length = omap_data.length_original - o1.rva_original; + } + + // Zero-length entries don't describe anything and can be ignored. + if (length == 0) + continue; + + // Any gaps in the transformed address-space are due to injected content. + if (!mapping->empty()) { + MappedRange& prev_mr = mapping->back(); + prev_mr.injected += o1.rva_transformed - + (prev_mr.rva_transformed + prev_mr.length); + } + + MappedRange mr = {}; + mr.rva_original = o1.rva_original; + mr.rva_transformed = o1.rva_transformed; + mr.length = length; + mapping->push_back(mr); + } + + // Sort based on the original image addresses. + std::sort(mapping->begin(), mapping->end(), MappedRangeOriginalLess); + + // Fill in the 'removed' lengths by looking for gaps in the coverage of the + // original address space. + FillInRemovedLengths(mapping); + + return; +} + +void BuildEndpointIndexMap(ImageMap* image_map) { + assert(image_map != NULL); + + if (image_map->mapping.size() == 0) + return; + + const Mapping& mapping = image_map->mapping; + EndpointIndexMap& eim = image_map->endpoint_index_map; + + // Get the unique set of interval endpoints. + std::set<DWORD> endpoints; + for (size_t i = 0; i < mapping.size(); ++i) { + endpoints.insert(mapping[i].rva_original); + endpoints.insert(mapping[i].rva_original + + mapping[i].length + + mapping[i].removed); + } + + // Use the endpoints to initialize the secondary search structure for the + // mapping. + eim.resize(endpoints.size()); + std::set<DWORD>::const_iterator it = endpoints.begin(); + for (size_t i = 0; it != endpoints.end(); ++it, ++i) { + eim[i].endpoint = *it; + eim[i].index = mapping.size(); + } + + // For each endpoint we want the smallest index of any interval containing + // it. We iterate over the intervals and update the indices associated with + // each interval endpoint contained in the current interval. In the general + // case of an arbitrary set of intervals this is O(n^2), but the structure of + // OMAP data makes this O(n). + for (size_t i = 0; i < mapping.size(); ++i) { + EndpointIndex ei1 = { mapping[i].rva_original, 0 }; + EndpointIndexMap::iterator it1 = std::lower_bound( + eim.begin(), eim.end(), ei1, EndpointIndexLess); + + EndpointIndex ei2 = { mapping[i].rva_original + mapping[i].length + + mapping[i].removed, 0 }; + EndpointIndexMap::iterator it2 = std::lower_bound( + eim.begin(), eim.end(), ei2, EndpointIndexLess); + + for (; it1 != it2; ++it1) + it1->index = Min(i, it1->index); + } +} + +void BuildSubsequentRVAMap(const OmapData &omap_data, + std::map<DWORD, DWORD> *subsequent) { + assert(subsequent->empty()); + const OmapFromTable &orig2tran = + reinterpret_cast<const OmapFromTable &>(omap_data.omap_from); + + if (orig2tran.empty()) + return; + + for (size_t i = 0; i < orig2tran.size() - 1; ++i) { + // Expect that orig2tran is sorted. + if (orig2tran[i].rva_original >= orig2tran[i + 1].rva_original) { + fprintf(stderr, "OMAP 'from' table unexpectedly unsorted\n"); + subsequent->clear(); + return; + } + subsequent->insert(std::make_pair(orig2tran[i].rva_original, + orig2tran[i + 1].rva_original)); + } +} + +// Clips the given mapped range. +void ClipMappedRangeOriginal(const AddressRange& clip_range, + MappedRange* mapped_range) { + assert(mapped_range != NULL); + + // The clipping range is entirely outside of the mapped range. + if (clip_range.end() <= mapped_range->rva_original || + mapped_range->rva_original + mapped_range->length + + mapped_range->removed <= clip_range.rva) { + mapped_range->length = 0; + mapped_range->injected = 0; + mapped_range->removed = 0; + return; + } + + // Clip the left side. + if (mapped_range->rva_original < clip_range.rva) { + DWORD clip_left = clip_range.rva - mapped_range->rva_original; + mapped_range->rva_original += clip_left; + mapped_range->rva_transformed += clip_left; + + if (clip_left > mapped_range->length) { + // The left clipping boundary entirely erases the content section of the + // range. + DWORD trim = clip_left - mapped_range->length; + mapped_range->length = 0; + mapped_range->injected -= Min(trim, mapped_range->injected); + // We know that trim <= mapped_range->remove. + mapped_range->removed -= trim; + } else { + // The left clipping boundary removes some, but not all, of the content. + // As such it leaves the removed/injected component intact. + mapped_range->length -= clip_left; + } + } + + // Clip the right side. + DWORD end_original = mapped_range->rva_original + mapped_range->length; + if (clip_range.end() < end_original) { + // The right clipping boundary lands in the 'content' section of the range, + // entirely clearing the injected/removed portion. + DWORD clip_right = end_original - clip_range.end(); + mapped_range->length -= clip_right; + mapped_range->injected = 0; + mapped_range->removed = 0; + return; + } else { + // The right clipping boundary is outside of the content, but may affect + // the removed/injected portion of the range. + DWORD end_removed = end_original + mapped_range->removed; + if (clip_range.end() < end_removed) + mapped_range->removed = clip_range.end() - end_original; + + DWORD end_injected = end_original + mapped_range->injected; + if (clip_range.end() < end_injected) + mapped_range->injected = clip_range.end() - end_original; + } + + return; +} + +} // namespace + +int AddressRange::Compare(const AddressRange& rhs) const { + if (end() <= rhs.rva) + return -1; + if (rhs.end() <= rva) + return 1; + return 0; +} + +bool GetOmapDataAndDisableTranslation(IDiaSession* session, + OmapData* omap_data) { + assert(session != NULL); + assert(omap_data != NULL); + + CComPtr<IDiaAddressMap> address_map; + if (FAILED(session->QueryInterface(&address_map))) { + fprintf(stderr, "IDiaSession::QueryInterface(IDiaAddressMap) failed\n"); + return false; + } + assert(address_map.p != NULL); + + BOOL omap_enabled = FALSE; + if (FAILED(address_map->get_addressMapEnabled(&omap_enabled))) { + fprintf(stderr, "IDiaAddressMap::get_addressMapEnabled failed\n"); + return false; + } + + if (!omap_enabled) { + // We indicate the non-presence of OMAP data by returning empty tables. + omap_data->omap_from.clear(); + omap_data->omap_to.clear(); + omap_data->length_original = 0; + return true; + } + + // OMAP data is present. Disable translation. + if (FAILED(address_map->put_addressMapEnabled(FALSE))) { + fprintf(stderr, "IDiaAddressMap::put_addressMapEnabled failed\n"); + return false; + } + + // Read the OMAP streams. + if (!FindAndLoadOmapTable(kOmapFromDebugStreamName, + session, + &omap_data->omap_from)) { + return false; + } + if (!FindAndLoadOmapTable(kOmapToDebugStreamName, + session, + &omap_data->omap_to)) { + return false; + } + + // Get the lengths of the address spaces. + if (!GetOriginalImageLength(session, &omap_data->length_original)) + return false; + + return true; +} + +void BuildImageMap(const OmapData& omap_data, ImageMap* image_map) { + assert(image_map != NULL); + + BuildMapping(omap_data, &image_map->mapping); + BuildEndpointIndexMap(image_map); + BuildSubsequentRVAMap(omap_data, &image_map->subsequent_rva_block); +} + +void MapAddressRange(const ImageMap& image_map, + const AddressRange& original_range, + AddressRangeVector* mapped_ranges) { + assert(mapped_ranges != NULL); + + const Mapping& map = image_map.mapping; + + // Handle the trivial case of an empty image_map. This means that there is + // no transformation to be applied, and we can simply return the original + // range. + if (map.empty()) { + mapped_ranges->push_back(original_range); + return; + } + + // If we get a query of length 0 we need to handle it by using a non-zero + // query length. + AddressRange query_range(original_range); + if (query_range.length == 0) + query_range.length = 1; + + // Find the range of intervals that can potentially intersect our query range. + size_t imin = 0; + size_t imax = 0; + { + // The index of the earliest possible range that can affect is us done by + // searching through the secondary indexing structure. + const EndpointIndexMap& eim = image_map.endpoint_index_map; + EndpointIndex q1 = { query_range.rva, 0 }; + EndpointIndexMap::const_iterator it1 = std::lower_bound( + eim.begin(), eim.end(), q1, EndpointIndexLess); + if (it1 == eim.end()) { + imin = map.size(); + } else { + // Backup to find the interval that contains our query point. + if (it1 != eim.begin() && query_range.rva < it1->endpoint) + --it1; + imin = it1->index; + } + + // The first range that can't possibly intersect us is found by searching + // through the image map directly as it is already sorted by interval start + // point. + MappedRange q2 = { query_range.end(), 0 }; + Mapping::const_iterator it2 = std::lower_bound( + map.begin(), map.end(), q2, MappedRangeOriginalLess); + imax = it2 - map.begin(); + } + + // Find all intervals that intersect the query range. + Mapping temp_map; + for (size_t i = imin; i < imax; ++i) { + MappedRange mr = map[i]; + ClipMappedRangeOriginal(query_range, &mr); + if (mr.length + mr.injected > 0) + temp_map.push_back(mr); + } + + // If there are no intersecting ranges then the query range has been removed + // from the image in question. + if (temp_map.empty()) + return; + + // Sort based on transformed addresses. + std::sort(temp_map.begin(), temp_map.end(), MappedRangeMappedLess); + + // Zero-length queries can't actually be merged. We simply output the set of + // unique RVAs that correspond to the query RVA. + if (original_range.length == 0) { + mapped_ranges->push_back(AddressRange(temp_map[0].rva_transformed, 0)); + for (size_t i = 1; i < temp_map.size(); ++i) { + if (temp_map[i].rva_transformed > mapped_ranges->back().rva) + mapped_ranges->push_back(AddressRange(temp_map[i].rva_transformed, 0)); + } + return; + } + + // Merge any ranges that are consecutive in the mapped image. We merge over + // injected content if it makes ranges contiguous, but we ignore any injected + // content at the tail end of a range. This allows us to detect symbols that + // have been lengthened by injecting content in the middle. However, it + // misses the case where content has been injected at the head or the tail. + // The problem is that it doesn't know whether to attribute it to the + // preceding or following symbol. It is up to the author of the transform to + // output explicit OMAP info in these cases to ensure full coverage of the + // transformed address space. + DWORD rva_begin = temp_map[0].rva_transformed; + DWORD rva_cur_content = rva_begin + temp_map[0].length; + DWORD rva_cur_injected = rva_cur_content + temp_map[0].injected; + for (size_t i = 1; i < temp_map.size(); ++i) { + if (rva_cur_injected < temp_map[i].rva_transformed) { + // This marks the end of a continuous range in the image. Output the + // current range and start a new one. + if (rva_begin < rva_cur_content) { + mapped_ranges->push_back( + AddressRange(rva_begin, rva_cur_content - rva_begin)); + } + rva_begin = temp_map[i].rva_transformed; + } + + rva_cur_content = temp_map[i].rva_transformed + temp_map[i].length; + rva_cur_injected = rva_cur_content + temp_map[i].injected; + } + + // Output the range in progress. + if (rva_begin < rva_cur_content) { + mapped_ranges->push_back( + AddressRange(rva_begin, rva_cur_content - rva_begin)); + } + + return; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/omap.h b/toolkit/crashreporter/google-breakpad/src/common/windows/omap.h new file mode 100644 index 0000000000..bc293afb51 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/omap.h @@ -0,0 +1,72 @@ +// Copyright 2013 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Provides an API for mapping symbols through OMAP information, if a PDB file +// is augmented with it. This allows breakpad to work with addresses in +// transformed images by transforming the symbols themselves, rather than +// transforming addresses prior to querying symbols (the way it is typically +// done by Windows-native tools, including the DIA). + +#ifndef COMMON_WINDOWS_OMAP_H_ +#define COMMON_WINDOWS_OMAP_H_ + +#include "common/windows/omap_internal.h" + +namespace google_breakpad { + +// If the given session contains OMAP data this extracts it, populating +// |omap_data|, and then disabling automatic translation for the session. +// OMAP data is present in the PDB if |omap_data| is not empty. This returns +// true on success, false otherwise. +bool GetOmapDataAndDisableTranslation(IDiaSession* dia_session, + OmapData* omap_data); + +// Given raw OMAP data builds an ImageMap. This can be used to query individual +// image ranges using MapAddressRange. +// |omap_data|| is the OMAP data extracted from the PDB. +// |image_map| will be populated with a description of the image mapping. If +// |omap_data| is empty then this will also be empty. +void BuildImageMap(const OmapData& omap_data, ImageMap* image_map); + +// Given an address range in the original image space determines how exactly it +// has been tranformed. +// |omap_data| is the OMAP data extracted from the PDB, which must not be +// empty. +// |original_range| is the address range in the original image being queried. +// |mapped_ranges| will be populated with a full description of the mapping. +// They may be disjoint in the transformed image so a vector is needed to +// fully represent the mapping. This will be appended to if it is not +// empty. If |omap_data| is empty then |mapped_ranges| will simply be +// populated with a copy of |original_range| (the identity transform). +void MapAddressRange(const ImageMap& image_map, + const AddressRange& original_range, + AddressRangeVector* mapped_ranges); + +} // namespace google_breakpad + +#endif // COMMON_WINDOWS_OMAP_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/omap_internal.h b/toolkit/crashreporter/google-breakpad/src/common/windows/omap_internal.h new file mode 100644 index 0000000000..2a4713d934 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/omap_internal.h @@ -0,0 +1,140 @@ +// Copyright 2013 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// Declares internal implementation details for functionality in omap.h and +// omap.cc. + +#ifndef COMMON_WINDOWS_OMAP_INTERNAL_H_ +#define COMMON_WINDOWS_OMAP_INTERNAL_H_ + +#include <windows.h> +#include <dia2.h> + +#include <map> +#include <vector> + +namespace google_breakpad { + +// The OMAP struct is defined by debughlp.h, which doesn't play nicely with +// imagehlp.h. We simply redefine it. +struct OMAP { + DWORD rva; + DWORD rvaTo; +}; +static_assert(sizeof(OMAP) == 8, "Wrong size for OMAP structure."); +typedef std::vector<OMAP> OmapTable; + +// This contains the OMAP data extracted from an image. +struct OmapData { + // The table of OMAP entries describing the transformation from the + // original image to the transformed image. + OmapTable omap_from; + // The table of OMAP entries describing the transformation from the + // instrumented image to the original image. + OmapTable omap_to; + // The length of the original untransformed image. + DWORD length_original; + + OmapData() : length_original(0) { } +}; + +// This represents a range of addresses in an image. +struct AddressRange { + DWORD rva; + DWORD length; + + AddressRange() : rva(0), length(0) { } + AddressRange(DWORD rva, DWORD length) : rva(rva), length(length) { } + + // Returns the end address of this range. + DWORD end() const { return rva + length; } + + // Addreses only compare as less-than or greater-than if they are not + // overlapping. Otherwise, they compare equal. + int Compare(const AddressRange& rhs) const; + bool operator<(const AddressRange& rhs) const { return Compare(rhs) == -1; } + bool operator>(const AddressRange& rhs) const { return Compare(rhs) == 1; } + + // Equality operators compare exact values. + bool operator==(const AddressRange& rhs) const { + return rva == rhs.rva && length == rhs.length; + } + bool operator!=(const AddressRange& rhs) const { return !((*this) == rhs); } +}; + +typedef std::vector<AddressRange> AddressRangeVector; + +// This represents an address range in an original image, and its corresponding +// range in the transformed image. +struct MappedRange { + // An address in the original image. + DWORD rva_original; + // The corresponding addresses in the transformed image. + DWORD rva_transformed; + // The length of the address range. + DWORD length; + // It is possible for code to be injected into a transformed image, for which + // there is no corresponding code in the original image. If this range of + // transformed image is immediately followed by such injected code we maintain + // a record of its length here. + DWORD injected; + // It is possible for code to be removed from the original image. This happens + // for things like padding between blocks. There is no actual content lost, + // but the spacing between items may be lost. This keeps track of any removed + // content immediately following the |original| range. + DWORD removed; +}; +// A vector of mapped ranges is used as a more useful representation of +// OMAP data. +typedef std::vector<MappedRange> Mapping; + +// Used as a secondary search structure accompanying a Mapping. +struct EndpointIndex { + DWORD endpoint; + size_t index; +}; +typedef std::vector<EndpointIndex> EndpointIndexMap; + +// An ImageMap is vector of mapped ranges, plus a secondary index into it for +// doing interval searches. (An interval tree would also work, but is overkill +// because we don't need insertion and deletion.) +struct ImageMap { + // This is a description of the mapping between original and transformed + // image, sorted by addresses in the original image. + Mapping mapping; + // For all interval endpoints in |mapping| this stores the minimum index of + // an interval in |mapping| that contains the endpoint. Useful for doing + // interval intersection queries. + EndpointIndexMap endpoint_index_map; + + std::map<DWORD, DWORD> subsequent_rva_block; +}; + +} // namespace google_breakpad + +#endif // COMMON_WINDOWS_OMAP_INTERNAL_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/omap_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/windows/omap_unittest.cc new file mode 100644 index 0000000000..7fe66bd406 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/omap_unittest.cc @@ -0,0 +1,329 @@ +// Copyright 2013 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.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// 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.
+
+// Unittests for OMAP related functions.
+
+#include "common/windows/omap.h"
+
+#include "breakpad_googletest_includes.h"
+
+namespace google_breakpad {
+
+// Equality operators for ContainerEq. These must be outside of the anonymous
+// namespace in order for them to be found.
+bool operator==(const MappedRange& mr1, const MappedRange& mr2) {
+ return mr1.rva_original == mr2.rva_original &&
+ mr1.rva_transformed == mr2.rva_transformed &&
+ mr1.length == mr2.length &&
+ mr1.injected == mr2.injected &&
+ mr1.removed == mr2.removed;
+}
+bool operator==(const EndpointIndex& ei1, const EndpointIndex& ei2) {
+ return ei1.endpoint == ei2.endpoint && ei1.index == ei2.index;
+}
+
+// Pretty printers for more meaningful error messages. Also need to be outside
+// the anonymous namespace.
+std::ostream& operator<<(std::ostream& os, const MappedRange& mr) {
+ os << "MappedRange(rva_original=" << mr.rva_original
+ << ", rva_transformed=" << mr.rva_transformed
+ << ", length=" << mr.length
+ << ", injected=" << mr.injected
+ << ", removed=" << mr.removed << ")";
+ return os;
+}
+std::ostream& operator<<(std::ostream& os, const EndpointIndex& ei) {
+ os << "EndpointIndex(endpoint=" << ei.endpoint
+ << ", index=" << ei.index << ")";
+ return os;
+}
+std::ostream& operator<<(std::ostream& os, const AddressRange& ar) {
+ os << "AddressRange(rva=" << ar.rva << ", length=" << ar.length << ")";
+ return os;
+}
+
+namespace {
+
+OMAP CreateOmap(DWORD rva, DWORD rvaTo) {
+ OMAP o = { rva, rvaTo };
+ return o;
+}
+
+MappedRange CreateMappedRange(DWORD rva_original,
+ DWORD rva_transformed,
+ DWORD length,
+ DWORD injected,
+ DWORD removed) {
+ MappedRange mr = { rva_original, rva_transformed, length, injected, removed };
+ return mr;
+}
+
+EndpointIndex CreateEndpointIndex(DWORD endpoint, size_t index) {
+ EndpointIndex ei = { endpoint, index };
+ return ei;
+}
+
+// (C is removed)
+// Original : A B C D E F G H
+// Transformed: A B D F E * H1 G1 G2 H2
+// (* is injected, G is copied, H is split)
+// A is implied.
+
+// Layout of the original image.
+const AddressRange B(100, 15);
+const AddressRange C(B.end(), 10);
+const AddressRange D(C.end(), 25);
+const AddressRange E(D.end(), 10);
+const AddressRange F(E.end(), 40);
+const AddressRange G(F.end(), 3);
+const AddressRange H(G.end(), 7);
+
+// Layout of the transformed image.
+const AddressRange Bt(100, 15);
+const AddressRange Dt(Bt.end(), 20); // D is shortened.
+const AddressRange Ft(Dt.end(), F.length);
+const AddressRange Et(Ft.end(), E.length);
+const AddressRange injected(Et.end(), 5);
+const AddressRange H1t(injected.end(), 4); // H is split.
+const AddressRange G1t(H1t.end(), G.length); // G is copied.
+const AddressRange G2t(G1t.end(), G.length); // G is copied.
+const AddressRange H2t(G2t.end(), 3); // H is split.
+
+class BuildImageMapTest : public testing::Test {
+ public:
+ static const DWORD kInvalidAddress = 0xFFFFFFFF;
+
+ void InitOmapData() {
+ omap_data.length_original = H.end();
+
+ // Build the OMAPTO vector (from transformed to original).
+ omap_data.omap_to.push_back(CreateOmap(Bt.rva, B.rva));
+ omap_data.omap_to.push_back(CreateOmap(Dt.rva, D.rva));
+ omap_data.omap_to.push_back(CreateOmap(Ft.rva, F.rva));
+ omap_data.omap_to.push_back(CreateOmap(Et.rva, E.rva));
+ omap_data.omap_to.push_back(CreateOmap(injected.rva, kInvalidAddress));
+ omap_data.omap_to.push_back(CreateOmap(H1t.rva, H.rva));
+ omap_data.omap_to.push_back(CreateOmap(G1t.rva, G.rva));
+ omap_data.omap_to.push_back(CreateOmap(G2t.rva, G.rva));
+ omap_data.omap_to.push_back(CreateOmap(H2t.rva, H.rva + H1t.length));
+ omap_data.omap_to.push_back(CreateOmap(H2t.end(), kInvalidAddress));
+
+ // Build the OMAPFROM vector (from original to transformed).
+ omap_data.omap_from.push_back(CreateOmap(B.rva, Bt.rva));
+ omap_data.omap_from.push_back(CreateOmap(C.rva, kInvalidAddress));
+ omap_data.omap_from.push_back(CreateOmap(D.rva, Dt.rva));
+ omap_data.omap_from.push_back(CreateOmap(E.rva, Et.rva));
+ omap_data.omap_from.push_back(CreateOmap(F.rva, Ft.rva));
+ omap_data.omap_from.push_back(CreateOmap(G.rva, G1t.rva));
+ omap_data.omap_from.push_back(CreateOmap(H.rva, H1t.rva));
+ omap_data.omap_from.push_back(CreateOmap(H.rva + H1t.length, H2t.rva));
+ omap_data.omap_from.push_back(CreateOmap(H.end(), kInvalidAddress));
+ }
+
+ OmapData omap_data;
+};
+
+} // namespace
+
+TEST_F(BuildImageMapTest, EmptyImageMapOnEmptyOmapData) {
+ ASSERT_EQ(0u, omap_data.omap_from.size());
+ ASSERT_EQ(0u, omap_data.omap_to.size());
+ ASSERT_EQ(0u, omap_data.length_original);
+
+ ImageMap image_map;
+ BuildImageMap(omap_data, &image_map);
+ EXPECT_EQ(0u, image_map.mapping.size());
+ EXPECT_EQ(0u, image_map.endpoint_index_map.size());
+}
+
+TEST_F(BuildImageMapTest, ImageMapIsCorrect) {
+ InitOmapData();
+ ASSERT_LE(0u, omap_data.omap_from.size());
+ ASSERT_LE(0u, omap_data.omap_to.size());
+ ASSERT_LE(0u, omap_data.length_original);
+
+ ImageMap image_map;
+ BuildImageMap(omap_data, &image_map);
+ EXPECT_LE(9u, image_map.mapping.size());
+ EXPECT_LE(9u, image_map.endpoint_index_map.size());
+
+ Mapping mapping;
+ mapping.push_back(CreateMappedRange(0, 0, B.rva, 0, 0));
+ // C is removed, and it originally comes immediately after B.
+ mapping.push_back(CreateMappedRange(B.rva, Bt.rva, B.length, 0, C.length));
+ // D is shortened by a length of 5.
+ mapping.push_back(CreateMappedRange(D.rva, Dt.rva, Dt.length, 0, 5));
+ // The injected content comes immediately after E in the transformed image.
+ mapping.push_back(CreateMappedRange(E.rva, Et.rva, E.length, injected.length,
+ 0));
+ mapping.push_back(CreateMappedRange(F.rva, Ft.rva, F.length, 0, 0));
+ // G is copied so creates two entries.
+ mapping.push_back(CreateMappedRange(G.rva, G1t.rva, G.length, 0, 0));
+ mapping.push_back(CreateMappedRange(G.rva, G2t.rva, G.length, 0, 0));
+ // H is split, so create two entries.
+ mapping.push_back(CreateMappedRange(H.rva, H1t.rva, H1t.length, 0, 0));
+ mapping.push_back(CreateMappedRange(H.rva + H1t.length, H2t.rva, H2t.length,
+ 0, 0));
+ EXPECT_THAT(mapping,
+ testing::ContainerEq(image_map.mapping));
+
+ EndpointIndexMap endpoint_index_map;
+ endpoint_index_map.push_back(CreateEndpointIndex(0, 0));
+ endpoint_index_map.push_back(CreateEndpointIndex(B.rva, 1));
+ endpoint_index_map.push_back(CreateEndpointIndex(D.rva, 2));
+ endpoint_index_map.push_back(CreateEndpointIndex(E.rva, 3));
+ endpoint_index_map.push_back(CreateEndpointIndex(F.rva, 4));
+ // G is duplicated so 2 ranges map back to it, hence the skip from 5 to 7.
+ endpoint_index_map.push_back(CreateEndpointIndex(G.rva, 5));
+ // H is split so we expect 2 endpoints to show up attributed to it.
+ endpoint_index_map.push_back(CreateEndpointIndex(H.rva, 7));
+ endpoint_index_map.push_back(CreateEndpointIndex(H.rva + H1t.length, 8));
+ endpoint_index_map.push_back(CreateEndpointIndex(H.end(), 9));
+ EXPECT_THAT(endpoint_index_map,
+ testing::ContainerEq(image_map.endpoint_index_map));
+}
+
+namespace {
+
+class MapAddressRangeTest : public BuildImageMapTest {
+ public:
+ typedef BuildImageMapTest Super;
+ virtual void SetUp() {
+ Super::SetUp();
+ InitOmapData();
+ BuildImageMap(omap_data, &image_map);
+ }
+
+ ImageMap image_map;
+
+ private:
+ using BuildImageMapTest::InitOmapData;
+ using BuildImageMapTest::omap_data;
+};
+
+} // namespace
+
+TEST_F(MapAddressRangeTest, EmptyImageMapReturnsIdentity) {
+ ImageMap im;
+ AddressRangeVector mapped_ranges;
+ AddressRange ar(0, 1024);
+ MapAddressRange(im, ar, &mapped_ranges);
+ EXPECT_EQ(1u, mapped_ranges.size());
+ EXPECT_EQ(ar, mapped_ranges[0]);
+}
+
+TEST_F(MapAddressRangeTest, MapOutOfImage) {
+ AddressRangeVector mapped_ranges;
+ MapAddressRange(image_map, AddressRange(H.end() + 10, 10), &mapped_ranges);
+ EXPECT_EQ(0u, mapped_ranges.size());
+}
+
+TEST_F(MapAddressRangeTest, MapIdentity) {
+ AddressRangeVector mapped_ranges;
+ MapAddressRange(image_map, B, &mapped_ranges);
+ EXPECT_EQ(1u, mapped_ranges.size());
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(B));
+}
+
+TEST_F(MapAddressRangeTest, MapReorderedContiguous) {
+ AddressRangeVector mapped_ranges;
+
+ AddressRange DEF(D.rva, F.end() - D.rva);
+ MapAddressRange(image_map, DEF, &mapped_ranges);
+ EXPECT_EQ(1u, mapped_ranges.size());
+
+ AddressRange DFEt(Dt.rva, Et.end() - Dt.rva);
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(DFEt));
+}
+
+TEST_F(MapAddressRangeTest, MapEmptySingle) {
+ AddressRangeVector mapped_ranges;
+ MapAddressRange(image_map, AddressRange(D.rva, 0), &mapped_ranges);
+ EXPECT_EQ(1u, mapped_ranges.size());
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(AddressRange(Dt.rva, 0)));
+}
+
+TEST_F(MapAddressRangeTest, MapEmptyCopied) {
+ AddressRangeVector mapped_ranges;
+ MapAddressRange(image_map, AddressRange(G.rva, 0), &mapped_ranges);
+ EXPECT_EQ(2u, mapped_ranges.size());
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(AddressRange(G1t.rva, 0),
+ AddressRange(G2t.rva, 0)));
+}
+
+TEST_F(MapAddressRangeTest, MapCopiedContiguous) {
+ AddressRangeVector mapped_ranges;
+ MapAddressRange(image_map, G, &mapped_ranges);
+ EXPECT_EQ(1u, mapped_ranges.size());
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(
+ AddressRange(G1t.rva, G2t.end() - G1t.rva)));
+}
+
+TEST_F(MapAddressRangeTest, MapSplitDiscontiguous) {
+ AddressRangeVector mapped_ranges;
+ MapAddressRange(image_map, H, &mapped_ranges);
+ EXPECT_EQ(2u, mapped_ranges.size());
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(H1t, H2t));
+}
+
+TEST_F(MapAddressRangeTest, MapInjected) {
+ AddressRangeVector mapped_ranges;
+
+ AddressRange EFGH(E.rva, H.end() - E.rva);
+ MapAddressRange(image_map, EFGH, &mapped_ranges);
+ EXPECT_EQ(1u, mapped_ranges.size());
+
+ AddressRange FEHGGHt(Ft.rva, H2t.end() - Ft.rva);
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(FEHGGHt));
+}
+
+TEST_F(MapAddressRangeTest, MapRemovedEntirely) {
+ AddressRangeVector mapped_ranges;
+ MapAddressRange(image_map, C, &mapped_ranges);
+ EXPECT_EQ(0u, mapped_ranges.size());
+}
+
+TEST_F(MapAddressRangeTest, MapRemovedPartly) {
+ AddressRangeVector mapped_ranges;
+ MapAddressRange(image_map, D, &mapped_ranges);
+ EXPECT_EQ(1u, mapped_ranges.size());
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(Dt));
+}
+
+TEST_F(MapAddressRangeTest, MapFull) {
+ AddressRangeVector mapped_ranges;
+
+ AddressRange AH(0, H.end());
+ MapAddressRange(image_map, AH, &mapped_ranges);
+ EXPECT_EQ(1u, mapped_ranges.size());
+
+ AddressRange AHt(0, H2t.end());
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(AHt));
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.cc b/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.cc new file mode 100644 index 0000000000..4030a2e953 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.cc @@ -0,0 +1,1194 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include "common/windows/pdb_source_line_writer.h" + +#include <windows.h> +#include <winnt.h> +#include <atlbase.h> +#include <dia2.h> +#include <diacreate.h> +#include <ImageHlp.h> +#include <stdio.h> + +#include <algorithm> +#include <limits> +#include <map> +#include <set> +#include <utility> + +#include "common/windows/dia_util.h" +#include "common/windows/guid_string.h" +#include "common/windows/pe_util.h" +#include "common/windows/string_utils-inl.h" + +// This constant may be missing from DbgHelp.h. See the documentation for +// IDiaSymbol::get_undecoratedNameEx. +#ifndef UNDNAME_NO_ECSU +#define UNDNAME_NO_ECSU 0x8000 // Suppresses enum/class/struct/union. +#endif // UNDNAME_NO_ECSU + +namespace google_breakpad { + +namespace { + +using std::vector; + +// The symbol (among possibly many) selected to represent an rva. +struct SelectedSymbol { + SelectedSymbol(const CComPtr<IDiaSymbol>& symbol, bool is_public) + : symbol(symbol), is_public(is_public), is_multiple(false) {} + + // The symbol to use for an rva. + CComPtr<IDiaSymbol> symbol; + // Whether this is a public or function symbol. + bool is_public; + // Whether the rva has multiple associated symbols. An rva will correspond to + // multiple symbols in the case of linker identical symbol folding. + bool is_multiple; +}; + +// Maps rva to the symbol to use for that address. +typedef std::map<DWORD, SelectedSymbol> SymbolMap; + +// Record this in the map as the selected symbol for the rva if it satisfies the +// necessary conditions. +void MaybeRecordSymbol(DWORD rva, + const CComPtr<IDiaSymbol> symbol, + bool is_public, + SymbolMap* map) { + SymbolMap::iterator loc = map->find(rva); + if (loc == map->end()) { + map->insert(std::make_pair(rva, SelectedSymbol(symbol, is_public))); + return; + } + + // Prefer function symbols to public symbols. + if (is_public && !loc->second.is_public) { + return; + } + + loc->second.is_multiple = true; + + // Take the 'least' symbol by lexicographical order of the decorated name. We + // use the decorated rather than undecorated name because computing the latter + // is expensive. + BSTR current_name, new_name; + loc->second.symbol->get_name(¤t_name); + symbol->get_name(&new_name); + if (wcscmp(new_name, current_name) < 0) { + loc->second.symbol = symbol; + loc->second.is_public = is_public; + } +} + + + +bool SymbolsMatch(IDiaSymbol* a, IDiaSymbol* b) { + DWORD a_section, a_offset, b_section, b_offset; + if (FAILED(a->get_addressSection(&a_section)) || + FAILED(a->get_addressOffset(&a_offset)) || + FAILED(b->get_addressSection(&b_section)) || + FAILED(b->get_addressOffset(&b_offset))) + return false; + return a_section == b_section && a_offset == b_offset; +} + +bool CreateDiaDataSourceInstance(CComPtr<IDiaDataSource> &data_source) { + if (SUCCEEDED(data_source.CoCreateInstance(CLSID_DiaSource))) { + return true; + } + + class DECLSPEC_UUID("B86AE24D-BF2F-4ac9-B5A2-34B14E4CE11D") DiaSource100; + class DECLSPEC_UUID("761D3BCD-1304-41D5-94E8-EAC54E4AC172") DiaSource110; + class DECLSPEC_UUID("3BFCEA48-620F-4B6B-81F7-B9AF75454C7D") DiaSource120; + class DECLSPEC_UUID("E6756135-1E65-4D17-8576-610761398C3C") DiaSource140; + + // If the CoCreateInstance call above failed, msdia*.dll is not registered. + // We can try loading the DLL corresponding to the #included DIA SDK, but + // the DIA headers don't provide a version. Lets try to figure out which DIA + // version we're compiling against by comparing CLSIDs. + const wchar_t *msdia_dll = nullptr; + if (CLSID_DiaSource == _uuidof(DiaSource100)) { + msdia_dll = L"msdia100.dll"; + } else if (CLSID_DiaSource == _uuidof(DiaSource110)) { + msdia_dll = L"msdia110.dll"; + } else if (CLSID_DiaSource == _uuidof(DiaSource120)) { + msdia_dll = L"msdia120.dll"; + } else if (CLSID_DiaSource == _uuidof(DiaSource140)) { + msdia_dll = L"msdia140.dll"; + } + + if (msdia_dll && + SUCCEEDED(NoRegCoCreate(msdia_dll, CLSID_DiaSource, IID_IDiaDataSource, + reinterpret_cast<void **>(&data_source)))) { + return true; + } + + return false; +} + +} // namespace + +PDBSourceLineWriter::PDBSourceLineWriter() : output_(NULL) { +} + +PDBSourceLineWriter::~PDBSourceLineWriter() { + Close(); +} + +bool PDBSourceLineWriter::SetCodeFile(const wstring &exe_file) { + if (code_file_.empty()) { + code_file_ = exe_file; + return true; + } + // Setting a different code file path is an error. It is success only if the + // file paths are the same. + return exe_file == code_file_; +} + +bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) { + Close(); + code_file_.clear(); + + if (FAILED(CoInitialize(NULL))) { + fprintf(stderr, "CoInitialize failed\n"); + return false; + } + + CComPtr<IDiaDataSource> data_source; + if (!CreateDiaDataSourceInstance(data_source)) { + const int kGuidSize = 64; + wchar_t classid[kGuidSize] = {0}; + StringFromGUID2(CLSID_DiaSource, classid, kGuidSize); + fprintf(stderr, "CoCreateInstance CLSID_DiaSource %S failed " + "(msdia*.dll unregistered?)\n", classid); + return false; + } + + switch (format) { + case PDB_FILE: + if (FAILED(data_source->loadDataFromPdb(file.c_str()))) { + fprintf(stderr, "loadDataFromPdb failed for %ws\n", file.c_str()); + return false; + } + break; + case EXE_FILE: + if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) { + fprintf(stderr, "loadDataForExe failed for %ws\n", file.c_str()); + return false; + } + code_file_ = file; + break; + case ANY_FILE: + if (FAILED(data_source->loadDataFromPdb(file.c_str()))) { + if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) { + fprintf(stderr, "loadDataForPdb and loadDataFromExe failed for %ws\n", + file.c_str()); + return false; + } + code_file_ = file; + } + break; + default: + fprintf(stderr, "Unknown file format\n"); + return false; + } + + if (FAILED(data_source->openSession(&session_))) { + fprintf(stderr, "openSession failed\n"); + } + + return true; +} + +bool PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *lines) { + // The line number format is: + // <rva> <line number> <source file id> + CComPtr<IDiaLineNumber> line; + ULONG count; + + while (SUCCEEDED(lines->Next(1, &line, &count)) && count == 1) { + DWORD rva; + if (FAILED(line->get_relativeVirtualAddress(&rva))) { + fprintf(stderr, "failed to get line rva\n"); + return false; + } + + DWORD length; + if (FAILED(line->get_length(&length))) { + fprintf(stderr, "failed to get line code length\n"); + return false; + } + + DWORD dia_source_id; + if (FAILED(line->get_sourceFileId(&dia_source_id))) { + fprintf(stderr, "failed to get line source file id\n"); + return false; + } + // duplicate file names are coalesced to share one ID + DWORD source_id = GetRealFileID(dia_source_id); + + DWORD line_num; + if (FAILED(line->get_lineNumber(&line_num))) { + fprintf(stderr, "failed to get line number\n"); + return false; + } + + AddressRangeVector ranges; + MapAddressRange(image_map_, AddressRange(rva, length), &ranges); + for (size_t i = 0; i < ranges.size(); ++i) { + fprintf(output_, "%lx %lx %lu %lu\n", ranges[i].rva, ranges[i].length, + line_num, source_id); + } + line.Release(); + } + return true; +} + +bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function, + IDiaSymbol *block, + bool has_multiple_symbols) { + // The function format is: + // FUNC <address> <length> <param_stack_size> <function> + DWORD rva; + if (FAILED(block->get_relativeVirtualAddress(&rva))) { + fprintf(stderr, "couldn't get rva\n"); + return false; + } + + ULONGLONG length; + if (FAILED(block->get_length(&length))) { + fprintf(stderr, "failed to get function length\n"); + return false; + } + + if (length == 0) { + // Silently ignore zero-length functions, which can infrequently pop up. + return true; + } + + CComBSTR name; + int stack_param_size; + if (!GetSymbolFunctionName(function, &name, &stack_param_size)) { + return false; + } + + // If the decorated name didn't give the parameter size, try to + // calculate it. + if (stack_param_size < 0) { + stack_param_size = GetFunctionStackParamSize(function); + } + + AddressRangeVector ranges; + MapAddressRange(image_map_, AddressRange(rva, static_cast<DWORD>(length)), + &ranges); + for (size_t i = 0; i < ranges.size(); ++i) { + const char* optional_multiple_field = has_multiple_symbols ? "m " : ""; + fprintf(output_, "FUNC %s%lx %lx %x %ws\n", optional_multiple_field, + ranges[i].rva, ranges[i].length, stack_param_size, name.m_str); + } + + CComPtr<IDiaEnumLineNumbers> lines; + if (FAILED(session_->findLinesByRVA(rva, DWORD(length), &lines))) { + return false; + } + + if (!PrintLines(lines)) { + return false; + } + return true; +} + +bool PDBSourceLineWriter::PrintSourceFiles() { + CComPtr<IDiaSymbol> global; + if (FAILED(session_->get_globalScope(&global))) { + fprintf(stderr, "get_globalScope failed\n"); + return false; + } + + CComPtr<IDiaEnumSymbols> compilands; + if (FAILED(global->findChildren(SymTagCompiland, NULL, + nsNone, &compilands))) { + fprintf(stderr, "findChildren failed\n"); + return false; + } + + CComPtr<IDiaSymbol> compiland; + ULONG count; + while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) { + CComPtr<IDiaEnumSourceFiles> source_files; + if (FAILED(session_->findFile(compiland, NULL, nsNone, &source_files))) { + return false; + } + CComPtr<IDiaSourceFile> file; + while (SUCCEEDED(source_files->Next(1, &file, &count)) && count == 1) { + DWORD file_id; + if (FAILED(file->get_uniqueId(&file_id))) { + return false; + } + + CComBSTR file_name; + if (FAILED(file->get_fileName(&file_name))) { + return false; + } + + wstring file_name_string(file_name); + if (!FileIDIsCached(file_name_string)) { + // this is a new file name, cache it and output a FILE line. + CacheFileID(file_name_string, file_id); + fwprintf(output_, L"FILE %d %ws\n", file_id, file_name_string.c_str()); + } else { + // this file name has already been seen, just save this + // ID for later lookup. + StoreDuplicateFileID(file_name_string, file_id); + } + file.Release(); + } + compiland.Release(); + } + return true; +} + +bool PDBSourceLineWriter::PrintFunctions() { + ULONG count = 0; + DWORD rva = 0; + CComPtr<IDiaSymbol> global; + HRESULT hr; + + if (FAILED(session_->get_globalScope(&global))) { + fprintf(stderr, "get_globalScope failed\n"); + return false; + } + + CComPtr<IDiaEnumSymbols> symbols = NULL; + + // Find all function symbols first. + SymbolMap rva_symbol; + hr = global->findChildren(SymTagFunction, NULL, nsNone, &symbols); + + if (SUCCEEDED(hr)) { + CComPtr<IDiaSymbol> symbol = NULL; + + while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) { + if (SUCCEEDED(symbol->get_relativeVirtualAddress(&rva))) { + // Potentially record this as the canonical symbol for this rva. + MaybeRecordSymbol(rva, symbol, false, &rva_symbol); + } else { + fprintf(stderr, "get_relativeVirtualAddress failed on the symbol\n"); + return false; + } + + symbol.Release(); + } + + symbols.Release(); + } + + // Find all public symbols and record public symbols that are not also private + // symbols. + hr = global->findChildren(SymTagPublicSymbol, NULL, nsNone, &symbols); + + if (SUCCEEDED(hr)) { + CComPtr<IDiaSymbol> symbol = NULL; + + while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) { + if (SUCCEEDED(symbol->get_relativeVirtualAddress(&rva))) { + // Potentially record this as the canonical symbol for this rva. + MaybeRecordSymbol(rva, symbol, true, &rva_symbol); + } else { + fprintf(stderr, "get_relativeVirtualAddress failed on the symbol\n"); + return false; + } + + symbol.Release(); + } + + symbols.Release(); + } + + // For each rva, dump the selected symbol at the address. + SymbolMap::iterator it; + for (it = rva_symbol.begin(); it != rva_symbol.end(); ++it) { + CComPtr<IDiaSymbol> symbol = it->second.symbol; + // Only print public symbols if there is no function symbol for the address. + if (!it->second.is_public) { + if (!PrintFunction(symbol, symbol, it->second.is_multiple)) + return false; + } else { + if (!PrintCodePublicSymbol(symbol, it->second.is_multiple)) + return false; + } + } + + // When building with PGO, the compiler can split functions into + // "hot" and "cold" blocks, and move the "cold" blocks out to separate + // pages, so the function can be noncontiguous. To find these blocks, + // we have to iterate over all the compilands, and then find blocks + // that are children of them. We can then find the lexical parents + // of those blocks and print out an extra FUNC line for blocks + // that are not contained in their parent functions. + CComPtr<IDiaEnumSymbols> compilands; + if (FAILED(global->findChildren(SymTagCompiland, NULL, + nsNone, &compilands))) { + fprintf(stderr, "findChildren failed on the global\n"); + return false; + } + + CComPtr<IDiaSymbol> compiland; + while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) { + CComPtr<IDiaEnumSymbols> blocks; + if (FAILED(compiland->findChildren(SymTagBlock, NULL, + nsNone, &blocks))) { + fprintf(stderr, "findChildren failed on a compiland\n"); + return false; + } + + CComPtr<IDiaSymbol> block; + while (SUCCEEDED(blocks->Next(1, &block, &count)) && count == 1) { + // find this block's lexical parent function + CComPtr<IDiaSymbol> parent; + DWORD tag; + if (SUCCEEDED(block->get_lexicalParent(&parent)) && + SUCCEEDED(parent->get_symTag(&tag)) && + tag == SymTagFunction) { + // now get the block's offset and the function's offset and size, + // and determine if the block is outside of the function + DWORD func_rva, block_rva; + ULONGLONG func_length; + if (SUCCEEDED(block->get_relativeVirtualAddress(&block_rva)) && + SUCCEEDED(parent->get_relativeVirtualAddress(&func_rva)) && + SUCCEEDED(parent->get_length(&func_length))) { + if (block_rva < func_rva || block_rva > (func_rva + func_length)) { + if (!PrintFunction(parent, block, false)) { + return false; + } + } + } + } + parent.Release(); + block.Release(); + } + blocks.Release(); + compiland.Release(); + } + + global.Release(); + return true; +} + +#undef max + +bool PDBSourceLineWriter::PrintFrameDataUsingPDB() { + // It would be nice if it were possible to output frame data alongside the + // associated function, as is done with line numbers, but the DIA API + // doesn't make it possible to get the frame data in that way. + + CComPtr<IDiaEnumFrameData> frame_data_enum; + if (!FindTable(session_, &frame_data_enum)) + return false; + + DWORD last_type = std::numeric_limits<DWORD>::max(); + DWORD last_rva = std::numeric_limits<DWORD>::max(); + DWORD last_code_size = 0; + DWORD last_prolog_size = std::numeric_limits<DWORD>::max(); + + CComPtr<IDiaFrameData> frame_data; + ULONG count = 0; + while (SUCCEEDED(frame_data_enum->Next(1, &frame_data, &count)) && + count == 1) { + DWORD type; + if (FAILED(frame_data->get_type(&type))) + return false; + + DWORD rva; + if (FAILED(frame_data->get_relativeVirtualAddress(&rva))) + return false; + + DWORD code_size; + if (FAILED(frame_data->get_lengthBlock(&code_size))) + return false; + + DWORD prolog_size; + if (FAILED(frame_data->get_lengthProlog(&prolog_size))) + return false; + + // parameter_size is the size of parameters passed on the stack. If any + // parameters are not passed on the stack (such as in registers), their + // sizes will not be included in parameter_size. + DWORD parameter_size; + if (FAILED(frame_data->get_lengthParams(¶meter_size))) + return false; + + DWORD saved_register_size; + if (FAILED(frame_data->get_lengthSavedRegisters(&saved_register_size))) + return false; + + DWORD local_size; + if (FAILED(frame_data->get_lengthLocals(&local_size))) + return false; + + // get_maxStack can return S_FALSE, just use 0 in that case. + DWORD max_stack_size = 0; + if (FAILED(frame_data->get_maxStack(&max_stack_size))) + return false; + + // get_programString can return S_FALSE, indicating that there is no + // program string. In that case, check whether %ebp is used. + HRESULT program_string_result; + CComBSTR program_string; + if (FAILED(program_string_result = frame_data->get_program( + &program_string))) { + return false; + } + + // get_allocatesBasePointer can return S_FALSE, treat that as though + // %ebp is not used. + BOOL allocates_base_pointer = FALSE; + if (program_string_result != S_OK) { + if (FAILED(frame_data->get_allocatesBasePointer( + &allocates_base_pointer))) { + return false; + } + } + + // Only print out a line if type, rva, code_size, or prolog_size have + // changed from the last line. It is surprisingly common (especially in + // system library PDBs) for DIA to return a series of identical + // IDiaFrameData objects. For kernel32.pdb from Windows XP SP2 on x86, + // this check reduces the size of the dumped symbol file by a third. + if (type != last_type || rva != last_rva || code_size != last_code_size || + prolog_size != last_prolog_size) { + // The prolog and the code portions of the frame have to be treated + // independently as they may have independently changed in size, or may + // even have been split. + // NOTE: If epilog size is ever non-zero, we have to do something + // similar with it. + + // Figure out where the prolog bytes have landed. + AddressRangeVector prolog_ranges; + if (prolog_size > 0) { + MapAddressRange(image_map_, AddressRange(rva, prolog_size), + &prolog_ranges); + } + + // And figure out where the code bytes have landed. + AddressRangeVector code_ranges; + MapAddressRange(image_map_, + AddressRange(rva + prolog_size, + code_size - prolog_size), + &code_ranges); + + struct FrameInfo { + DWORD rva; + DWORD code_size; + DWORD prolog_size; + }; + std::vector<FrameInfo> frame_infos; + + // Special case: The prolog and the code bytes remain contiguous. This is + // only done for compactness of the symbol file, and we could actually + // be outputting independent frame info for the prolog and code portions. + if (prolog_ranges.size() == 1 && code_ranges.size() == 1 && + prolog_ranges[0].end() == code_ranges[0].rva) { + FrameInfo fi = { prolog_ranges[0].rva, + prolog_ranges[0].length + code_ranges[0].length, + prolog_ranges[0].length }; + frame_infos.push_back(fi); + } else { + // Otherwise we output the prolog and code frame info independently. + for (size_t i = 0; i < prolog_ranges.size(); ++i) { + FrameInfo fi = { prolog_ranges[i].rva, + prolog_ranges[i].length, + prolog_ranges[i].length }; + frame_infos.push_back(fi); + } + for (size_t i = 0; i < code_ranges.size(); ++i) { + FrameInfo fi = { code_ranges[i].rva, code_ranges[i].length, 0 }; + frame_infos.push_back(fi); + } + } + + for (size_t i = 0; i < frame_infos.size(); ++i) { + const FrameInfo& fi(frame_infos[i]); + fprintf(output_, "STACK WIN %lx %lx %lx %lx %x %lx %lx %lx %lx %d ", + type, fi.rva, fi.code_size, fi.prolog_size, + 0 /* epilog_size */, parameter_size, saved_register_size, + local_size, max_stack_size, program_string_result == S_OK); + if (program_string_result == S_OK) { + fprintf(output_, "%ws\n", program_string.m_str); + } else { + fprintf(output_, "%d\n", allocates_base_pointer); + } + } + + last_type = type; + last_rva = rva; + last_code_size = code_size; + last_prolog_size = prolog_size; + } + + frame_data.Release(); + } + + return true; +} + +bool PDBSourceLineWriter::PrintFrameDataUsingEXE() { + if (code_file_.empty() && !FindPEFile()) { + fprintf(stderr, "Couldn't locate EXE or DLL file.\n"); + return false; + } + + return PrintPEFrameData(code_file_, output_); +} + +bool PDBSourceLineWriter::PrintFrameData() { + PDBModuleInfo info; + if (GetModuleInfo(&info) && info.cpu == L"x86_64") { + return PrintFrameDataUsingEXE(); + } else { + return PrintFrameDataUsingPDB(); + } + return false; +} + +bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol, + bool has_multiple_symbols) { + BOOL is_code; + if (FAILED(symbol->get_code(&is_code))) { + return false; + } + if (!is_code) { + return true; + } + + DWORD rva; + if (FAILED(symbol->get_relativeVirtualAddress(&rva))) { + return false; + } + + CComBSTR name; + int stack_param_size; + if (!GetSymbolFunctionName(symbol, &name, &stack_param_size)) { + return false; + } + + AddressRangeVector ranges; + MapAddressRange(image_map_, AddressRange(rva, 1), &ranges); + for (size_t i = 0; i < ranges.size(); ++i) { + const char* optional_multiple_field = has_multiple_symbols ? "m " : ""; + fprintf(output_, "PUBLIC %s%lx %x %ws\n", optional_multiple_field, + ranges[i].rva, stack_param_size > 0 ? stack_param_size : 0, + name.m_str); + } + + // Now walk the function in the original untranslated space, asking DIA + // what function is at that location, stepping through OMAP blocks. If + // we're still in the same function, emit another entry, because the + // symbol could have been split into multiple pieces. If we've gotten to + // another symbol in the original address space, then we're done for + // this symbol. See https://crbug.com/678874. + for (;;) { + // This steps to the next block in the original image. Simply doing + // rva++ would also be correct, but would emit tons of unnecessary + // entries. + rva = image_map_.subsequent_rva_block[rva]; + if (rva == 0) + break; + + CComPtr<IDiaSymbol> next_sym = NULL; + LONG displacement; + if (FAILED(session_->findSymbolByRVAEx(rva, SymTagPublicSymbol, &next_sym, + &displacement))) { + break; + } + + if (!SymbolsMatch(symbol, next_sym)) + break; + + AddressRangeVector next_ranges; + MapAddressRange(image_map_, AddressRange(rva, 1), &next_ranges); + for (size_t i = 0; i < next_ranges.size(); ++i) { + fprintf(output_, "PUBLIC %lx %x %ws\n", next_ranges[i].rva, + stack_param_size > 0 ? stack_param_size : 0, name.m_str); + } + } + + return true; +} + +bool PDBSourceLineWriter::PrintPDBInfo() { + PDBModuleInfo info; + if (!GetModuleInfo(&info)) { + return false; + } + + // Hard-code "windows" for the OS because that's the only thing that makes + // sense for PDB files. (This might not be strictly correct for Windows CE + // support, but we don't care about that at the moment.) + fprintf(output_, "MODULE windows %ws %ws %ws\n", + info.cpu.c_str(), info.debug_identifier.c_str(), + info.debug_file.c_str()); + + return true; +} + +bool PDBSourceLineWriter::PrintPEInfo() { + PEModuleInfo info; + if (!GetPEInfo(&info)) { + return false; + } + + fprintf(output_, "INFO CODE_ID %ws %ws\n", + info.code_identifier.c_str(), + info.code_file.c_str()); + return true; +} + +// wcstol_positive_strict is sort of like wcstol, but much stricter. string +// should be a buffer pointing to a null-terminated string containing only +// decimal digits. If the entire string can be converted to an integer +// without overflowing, and there are no non-digit characters before the +// result is set to the value and this function returns true. Otherwise, +// this function returns false. This is an alternative to the strtol, atoi, +// and scanf families, which are not as strict about input and in some cases +// don't provide a good way for the caller to determine if a conversion was +// successful. +static bool wcstol_positive_strict(wchar_t *string, int *result) { + int value = 0; + for (wchar_t *c = string; *c != '\0'; ++c) { + int last_value = value; + value *= 10; + // Detect overflow. + if (value / 10 != last_value || value < 0) { + return false; + } + if (*c < '0' || *c > '9') { + return false; + } + unsigned int c_value = *c - '0'; + last_value = value; + value += c_value; + // Detect overflow. + if (value < last_value) { + return false; + } + // Forbid leading zeroes unless the string is just "0". + if (value == 0 && *(c+1) != '\0') { + return false; + } + } + *result = value; + return true; +} + +bool PDBSourceLineWriter::FindPEFile() { + CComPtr<IDiaSymbol> global; + if (FAILED(session_->get_globalScope(&global))) { + fprintf(stderr, "get_globalScope failed\n"); + return false; + } + + CComBSTR symbols_file; + if (SUCCEEDED(global->get_symbolsFileName(&symbols_file))) { + wstring file(symbols_file); + + // Look for an EXE or DLL file. + const wchar_t *extensions[] = { L"exe", L"dll" }; + for (size_t i = 0; i < sizeof(extensions) / sizeof(extensions[0]); i++) { + size_t dot_pos = file.find_last_of(L"."); + if (dot_pos != wstring::npos) { + file.replace(dot_pos + 1, wstring::npos, extensions[i]); + // Check if this file exists. + if (GetFileAttributesW(file.c_str()) != INVALID_FILE_ATTRIBUTES) { + code_file_ = file; + return true; + } + } + } + } + + return false; +} + +// static +bool PDBSourceLineWriter::GetSymbolFunctionName(IDiaSymbol *function, + BSTR *name, + int *stack_param_size) { + *stack_param_size = -1; + const DWORD undecorate_options = UNDNAME_NO_MS_KEYWORDS | + UNDNAME_NO_FUNCTION_RETURNS | + UNDNAME_NO_ALLOCATION_MODEL | + UNDNAME_NO_ALLOCATION_LANGUAGE | + UNDNAME_NO_THISTYPE | + UNDNAME_NO_ACCESS_SPECIFIERS | + UNDNAME_NO_THROW_SIGNATURES | + UNDNAME_NO_MEMBER_TYPE | + UNDNAME_NO_RETURN_UDT_MODEL | + UNDNAME_NO_ECSU; + + // Use get_undecoratedNameEx to get readable C++ names with arguments. + if (function->get_undecoratedNameEx(undecorate_options, name) != S_OK) { + if (function->get_name(name) != S_OK) { + fprintf(stderr, "failed to get function name\n"); + return false; + } + + // It's possible for get_name to return an empty string, so + // special-case that. + if (wcscmp(*name, L"") == 0) { + SysFreeString(*name); + // dwarf_cu_to_module.cc uses "<name omitted>", so match that. + *name = SysAllocString(L"<name omitted>"); + return true; + } + + // If a name comes from get_name because no undecorated form existed, + // it's already formatted properly to be used as output. Don't do any + // additional processing. + // + // MSVC7's DIA seems to not undecorate names in as many cases as MSVC8's. + // This will result in calling get_name for some C++ symbols, so + // all of the parameter and return type information may not be included in + // the name string. + } else { + // C++ uses a bogus "void" argument for functions and methods that don't + // take any parameters. Take it out of the undecorated name because it's + // ugly and unnecessary. + const wchar_t *replace_string = L"(void)"; + const size_t replace_length = wcslen(replace_string); + const wchar_t *replacement_string = L"()"; + size_t length = wcslen(*name); + if (length >= replace_length) { + wchar_t *name_end = *name + length - replace_length; + if (wcscmp(name_end, replace_string) == 0) { + WindowsStringUtils::safe_wcscpy(name_end, replace_length, + replacement_string); + length = wcslen(*name); + } + } + + // Undecorate names used for stdcall and fastcall. These names prefix + // the identifier with '_' (stdcall) or '@' (fastcall) and suffix it + // with '@' followed by the number of bytes of parameters, in decimal. + // If such a name is found, take note of the size and undecorate it. + // Only do this for names that aren't C++, which is determined based on + // whether the undecorated name contains any ':' or '(' characters. + if (!wcschr(*name, ':') && !wcschr(*name, '(') && + (*name[0] == '_' || *name[0] == '@')) { + wchar_t *last_at = wcsrchr(*name + 1, '@'); + if (last_at && wcstol_positive_strict(last_at + 1, stack_param_size)) { + // If this function adheres to the fastcall convention, it accepts up + // to the first 8 bytes of parameters in registers (%ecx and %edx). + // We're only interested in the stack space used for parameters, so + // so subtract 8 and don't let the size go below 0. + if (*name[0] == '@') { + if (*stack_param_size > 8) { + *stack_param_size -= 8; + } else { + *stack_param_size = 0; + } + } + + // Undecorate the name by moving it one character to the left in its + // buffer, and terminating it where the last '@' had been. + WindowsStringUtils::safe_wcsncpy(*name, length, + *name + 1, last_at - *name - 1); + } else if (*name[0] == '_') { + // This symbol's name is encoded according to the cdecl rules. The + // name doesn't end in a '@' character followed by a decimal positive + // integer, so it's not a stdcall name. Strip off the leading + // underscore. + WindowsStringUtils::safe_wcsncpy(*name, length, *name + 1, length); + } + } + } + + return true; +} + +// static +int PDBSourceLineWriter::GetFunctionStackParamSize(IDiaSymbol *function) { + // This implementation is highly x86-specific. + + // Gather the symbols corresponding to data. + CComPtr<IDiaEnumSymbols> data_children; + if (FAILED(function->findChildren(SymTagData, NULL, nsNone, + &data_children))) { + return 0; + } + + // lowest_base is the lowest %ebp-relative byte offset used for a parameter. + // highest_end is one greater than the highest offset (i.e. base + length). + // Stack parameters are assumed to be contiguous, because in reality, they + // are. + int lowest_base = INT_MAX; + int highest_end = INT_MIN; + + CComPtr<IDiaSymbol> child; + DWORD count; + while (SUCCEEDED(data_children->Next(1, &child, &count)) && count == 1) { + // If any operation fails at this point, just proceed to the next child. + // Use the next_child label instead of continue because child needs to + // be released before it's reused. Declare constructable/destructable + // types early to avoid gotos that cross initializations. + CComPtr<IDiaSymbol> child_type; + + // DataIsObjectPtr is only used for |this|. Because |this| can be passed + // as a stack parameter, look for it in addition to traditional + // parameters. + DWORD child_kind; + if (FAILED(child->get_dataKind(&child_kind)) || + (child_kind != DataIsParam && child_kind != DataIsObjectPtr)) { + goto next_child; + } + + // Only concentrate on register-relative parameters. Parameters may also + // be enregistered (passed directly in a register), but those don't + // consume any stack space, so they're not of interest. + DWORD child_location_type; + if (FAILED(child->get_locationType(&child_location_type)) || + child_location_type != LocIsRegRel) { + goto next_child; + } + + // Of register-relative parameters, the only ones that make any sense are + // %ebp- or %esp-relative. Note that MSVC's debugging information always + // gives parameters as %ebp-relative even when a function doesn't use a + // traditional frame pointer and stack parameters are accessed relative to + // %esp, so just look for %ebp-relative parameters. If you wanted to + // access parameters, you'd probably want to treat these %ebp-relative + // offsets as if they were relative to %esp before a function's prolog + // executed. + DWORD child_register; + if (FAILED(child->get_registerId(&child_register)) || + child_register != CV_REG_EBP) { + goto next_child; + } + + LONG child_register_offset; + if (FAILED(child->get_offset(&child_register_offset))) { + goto next_child; + } + + // IDiaSymbol::get_type can succeed but still pass back a NULL value. + if (FAILED(child->get_type(&child_type)) || !child_type) { + goto next_child; + } + + ULONGLONG child_length; + if (FAILED(child_type->get_length(&child_length))) { + goto next_child; + } + + // Extra scope to avoid goto jumping over variable initialization + { + int child_end = child_register_offset + static_cast<ULONG>(child_length); + if (child_register_offset < lowest_base) { + lowest_base = child_register_offset; + } + if (child_end > highest_end) { + highest_end = child_end; + } + } + +next_child: + child.Release(); + } + + int param_size = 0; + // Make sure lowest_base isn't less than 4, because [%esp+4] is the lowest + // possible address to find a stack parameter before executing a function's + // prolog (see above). Some optimizations cause parameter offsets to be + // lower than 4, but we're not concerned with those because we're only + // looking for parameters contained in addresses higher than where the + // return address is stored. + if (lowest_base < 4) { + lowest_base = 4; + } + if (highest_end > lowest_base) { + // All stack parameters are pushed as at least 4-byte quantities. If the + // last type was narrower than 4 bytes, promote it. This assumes that all + // parameters' offsets are 4-byte-aligned, which is always the case. Only + // worry about the last type, because we're not summing the type sizes, + // just looking at the lowest and highest offsets. + int remainder = highest_end % 4; + if (remainder) { + highest_end += 4 - remainder; + } + + param_size = highest_end - lowest_base; + } + + return param_size; +} + +bool PDBSourceLineWriter::WriteSymbols(FILE *symbol_file) { + output_ = symbol_file; + + // Load the OMAP information, and disable auto-translation of addresses in + // preference of doing it ourselves. + OmapData omap_data; + if (!GetOmapDataAndDisableTranslation(session_, &omap_data)) + return false; + BuildImageMap(omap_data, &image_map_); + + bool ret = PrintPDBInfo(); + // This is not a critical piece of the symbol file. + PrintPEInfo(); + ret = ret && + PrintSourceFiles() && + PrintFunctions() && + PrintFrameData(); + + output_ = NULL; + return ret; +} + +void PDBSourceLineWriter::Close() { + if (session_ != nullptr) { + session_.Release(); + } +} + +bool PDBSourceLineWriter::GetModuleInfo(PDBModuleInfo *info) { + if (!info) { + return false; + } + + info->debug_file.clear(); + info->debug_identifier.clear(); + info->cpu.clear(); + + CComPtr<IDiaSymbol> global; + if (FAILED(session_->get_globalScope(&global))) { + return false; + } + + DWORD machine_type; + // get_machineType can return S_FALSE. + if (global->get_machineType(&machine_type) == S_OK) { + // The documentation claims that get_machineType returns a value from + // the CV_CPU_TYPE_e enumeration, but that's not the case. + // Instead, it returns one of the IMAGE_FILE_MACHINE values as + // defined here: + // http://msdn.microsoft.com/en-us/library/ms680313%28VS.85%29.aspx + info->cpu = FileHeaderMachineToCpuString(static_cast<WORD>(machine_type)); + } else { + // Unexpected, but handle gracefully. + info->cpu = L"unknown"; + } + + // DWORD* and int* are not compatible. This is clean and avoids a cast. + DWORD age; + if (FAILED(global->get_age(&age))) { + return false; + } + + bool uses_guid; + if (!UsesGUID(&uses_guid)) { + return false; + } + + if (uses_guid) { + GUID guid; + if (FAILED(global->get_guid(&guid))) { + return false; + } + + info->debug_identifier = GenerateDebugIdentifier(age, guid); + } else { + DWORD signature; + if (FAILED(global->get_signature(&signature))) { + return false; + } + + info->debug_identifier = GenerateDebugIdentifier(age, signature); + } + + CComBSTR debug_file_string; + if (FAILED(global->get_symbolsFileName(&debug_file_string))) { + return false; + } + info->debug_file = + WindowsStringUtils::GetBaseName(wstring(debug_file_string)); + + return true; +} + +bool PDBSourceLineWriter::GetPEInfo(PEModuleInfo *info) { + if (!info) { + return false; + } + + if (code_file_.empty() && !FindPEFile()) { + fprintf(stderr, "Couldn't locate EXE or DLL file.\n"); + return false; + } + + return ReadPEInfo(code_file_, info); +} + +bool PDBSourceLineWriter::UsesGUID(bool *uses_guid) { + if (!uses_guid) + return false; + + CComPtr<IDiaSymbol> global; + if (FAILED(session_->get_globalScope(&global))) + return false; + + GUID guid; + if (FAILED(global->get_guid(&guid))) + return false; + + DWORD signature; + if (FAILED(global->get_signature(&signature))) + return false; + + // There are two possibilities for guid: either it's a real 128-bit GUID + // as identified in a code module by a new-style CodeView record, or it's + // a 32-bit signature (timestamp) as identified by an old-style record. + // See MDCVInfoPDB70 and MDCVInfoPDB20 in minidump_format.h. + // + // Because DIA doesn't provide a way to directly determine whether a module + // uses a GUID or a 32-bit signature, this code checks whether the first 32 + // bits of guid are the same as the signature, and if the rest of guid is + // zero. If so, then with a pretty high degree of certainty, there's an + // old-style CodeView record in use. This method will only falsely find an + // an old-style CodeView record if a real 128-bit GUID has its first 32 + // bits set the same as the module's signature (timestamp) and the rest of + // the GUID is set to 0. This is highly unlikely. + + GUID signature_guid = {signature}; // 0-initializes other members + *uses_guid = !IsEqualGUID(guid, signature_guid); + return true; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.h b/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.h new file mode 100644 index 0000000000..c0adf29f24 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.h @@ -0,0 +1,226 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// PDBSourceLineWriter uses a pdb file produced by Visual C++ to output +// a line/address map for use with BasicSourceLineResolver. + +#ifndef COMMON_WINDOWS_PDB_SOURCE_LINE_WRITER_H_ +#define COMMON_WINDOWS_PDB_SOURCE_LINE_WRITER_H_ + +#include <atlcomcli.h> + +#include <string> +#include <unordered_map> + +#include "common/windows/module_info.h" +#include "common/windows/omap.h" + +struct IDiaEnumLineNumbers; +struct IDiaSession; +struct IDiaSymbol; + +namespace google_breakpad { + +using std::wstring; +using std::unordered_map; + +class PDBSourceLineWriter { + public: + enum FileFormat { + PDB_FILE, // a .pdb file containing debug symbols + EXE_FILE, // a .exe or .dll file + ANY_FILE // try PDB_FILE and then EXE_FILE + }; + + explicit PDBSourceLineWriter(); + ~PDBSourceLineWriter(); + + // Opens the given file. For executable files, the corresponding pdb + // file must be available; Open will be if it is not. + // If there is already a pdb file open, it is automatically closed. + // Returns true on success. + bool Open(const wstring &file, FileFormat format); + + // Closes the current pdb file and its associated resources. + void Close(); + + // Sets the code file full path. This is optional for 32-bit modules. It is + // also optional for 64-bit modules when there is an executable file stored + // in the same directory as the PDB file. It is only required for 64-bit + // modules when the executable file is not in the same location as the PDB + // file and it must be called after Open() and before WriteMap(). + // If Open() was called for an executable file, then it is an error to call + // SetCodeFile() with a different file path and it will return false. + bool SetCodeFile(const wstring &exe_file); + + // Writes a Breakpad symbol file from the current pdb file to |symbol_file|. + // Returns true on success. + bool WriteSymbols(FILE *symbol_file); + + // Retrieves information about the module's debugging file. Returns + // true on success and false on failure. + bool GetModuleInfo(PDBModuleInfo *info); + + // Retrieves information about the module's PE file. Returns + // true on success and false on failure. + bool GetPEInfo(PEModuleInfo *info); + + // Sets uses_guid to true if the opened file uses a new-style CodeView + // record with a 128-bit GUID, or false if the opened file uses an old-style + // CodeView record. When no GUID is available, a 32-bit signature should be + // used to identify the module instead. If the information cannot be + // determined, this method returns false. + bool UsesGUID(bool *uses_guid); + + private: + // Outputs the line/address pairs for each line in the enumerator. + // Returns true on success. + bool PrintLines(IDiaEnumLineNumbers *lines); + + // Outputs a function address and name, followed by its source line list. + // block can be the same object as function, or it can be a reference to a + // code block that is lexically part of this function, but resides at a + // separate address. If has_multiple_symbols is true, this function's + // instructions correspond to multiple symbols. Returns true on success. + bool PrintFunction(IDiaSymbol *function, IDiaSymbol *block, + bool has_multiple_symbols); + + // Outputs all functions as described above. Returns true on success. + bool PrintFunctions(); + + // Outputs all of the source files in the session's pdb file. + // Returns true on success. + bool PrintSourceFiles(); + + // Outputs all of the frame information necessary to construct stack + // backtraces in the absence of frame pointers. For x86 data stored in + // .pdb files. Returns true on success. + bool PrintFrameDataUsingPDB(); + + // Outputs all of the frame information necessary to construct stack + // backtraces in the absence of frame pointers. For x64 data stored in + // .exe, .dll files. Returns true on success. + bool PrintFrameDataUsingEXE(); + + // Outputs all of the frame information necessary to construct stack + // backtraces in the absence of frame pointers. Returns true on success. + bool PrintFrameData(); + + // Outputs a single public symbol address and name, if the symbol corresponds + // to a code address. Returns true on success. If symbol is does not + // correspond to code, returns true without outputting anything. If + // has_multiple_symbols is true, the symbol corresponds to a code address and + // the instructions correspond to multiple symbols. + bool PrintCodePublicSymbol(IDiaSymbol *symbol, bool has_multiple_symbols); + + // Outputs a line identifying the PDB file that is being dumped, along with + // its uuid and age. + bool PrintPDBInfo(); + + // Outputs a line identifying the PE file corresponding to the PDB + // file that is being dumped, along with its code identifier, + // which consists of its timestamp and file size. + bool PrintPEInfo(); + + // Returns true if this filename has already been seen, + // and an ID is stored for it, or false if it has not. + bool FileIDIsCached(const wstring &file) { + return unique_files_.find(file) != unique_files_.end(); + } + + // Cache this filename and ID for later reuse. + void CacheFileID(const wstring &file, DWORD id) { + unique_files_[file] = id; + } + + // Store this ID in the cache as a duplicate for this filename. + void StoreDuplicateFileID(const wstring &file, DWORD id) { + unordered_map<wstring, DWORD>::iterator iter = unique_files_.find(file); + if (iter != unique_files_.end()) { + // map this id to the previously seen one + file_ids_[id] = iter->second; + } + } + + // Given a file's unique ID, return the ID that should be used to + // reference it. There may be multiple files with identical filenames + // but different unique IDs. The cache attempts to coalesce these into + // one ID per unique filename. + DWORD GetRealFileID(DWORD id) { + unordered_map<DWORD, DWORD>::iterator iter = file_ids_.find(id); + if (iter == file_ids_.end()) + return id; + return iter->second; + } + + // Find the PE file corresponding to the loaded PDB file, and + // set the code_file_ member. Returns false on failure. + bool FindPEFile(); + + // Returns the function name for a symbol. If possible, the name is + // undecorated. If the symbol's decorated form indicates the size of + // parameters on the stack, this information is returned in stack_param_size. + // Returns true on success. If the symbol doesn't encode parameter size + // information, stack_param_size is set to -1. + static bool GetSymbolFunctionName(IDiaSymbol *function, BSTR *name, + int *stack_param_size); + + // Returns the number of bytes of stack space used for a function's + // parameters. function must have the tag SymTagFunction. In the event of + // a failure, returns 0, which is also a valid number of bytes. + static int GetFunctionStackParamSize(IDiaSymbol *function); + + // The filename of the PE file corresponding to the currently-open + // pdb file. + wstring code_file_; + + // The session for the currently-open pdb file. + CComPtr<IDiaSession> session_; + + // The current output file for this WriteMap invocation. + FILE *output_; + + // There may be many duplicate filenames with different IDs. + // This maps from the DIA "unique ID" to a single ID per unique + // filename. + unordered_map<DWORD, DWORD> file_ids_; + // This maps unique filenames to file IDs. + unordered_map<wstring, DWORD> unique_files_; + + // This is used for calculating post-transform symbol addresses and lengths. + ImageMap image_map_; + + // Disallow copy ctor and operator= + PDBSourceLineWriter(const PDBSourceLineWriter&); + void operator=(const PDBSourceLineWriter&); +}; + +} // namespace google_breakpad + +#endif // COMMON_WINDOWS_PDB_SOURCE_LINE_WRITER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/pe_source_line_writer.cc b/toolkit/crashreporter/google-breakpad/src/common/windows/pe_source_line_writer.cc new file mode 100644 index 0000000000..cb6cc71396 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/pe_source_line_writer.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2019, 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.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// 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.
+
+#include "common/windows/pe_source_line_writer.h"
+
+#include "common/windows/pe_util.h"
+
+namespace google_breakpad {
+PESourceLineWriter::PESourceLineWriter(const wstring& pe_file) :
+ pe_file_(pe_file) {
+}
+
+PESourceLineWriter::~PESourceLineWriter() {
+}
+
+bool PESourceLineWriter::WriteSymbols(FILE* symbol_file) {
+ PDBModuleInfo module_info;
+ if (!GetModuleInfo(&module_info)) {
+ return false;
+ }
+ // Hard-code "windows" for the OS because that's the only thing that makes
+ // sense for PDB files. (This might not be strictly correct for Windows CE
+ // support, but we don't care about that at the moment.)
+ fprintf(symbol_file, "MODULE windows %ws %ws %ws\n",
+ module_info.cpu.c_str(), module_info.debug_identifier.c_str(),
+ module_info.debug_file.c_str());
+
+ PEModuleInfo pe_info;
+ if (!GetPEInfo(&pe_info)) {
+ return false;
+ }
+ fprintf(symbol_file, "INFO CODE_ID %ws %ws\n",
+ pe_info.code_identifier.c_str(),
+ pe_info.code_file.c_str());
+
+ if (!PrintPEFrameData(pe_file_, symbol_file)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool PESourceLineWriter::GetModuleInfo(PDBModuleInfo* info) {
+ return ReadModuleInfo(pe_file_, info);
+}
+
+bool PESourceLineWriter::GetPEInfo(PEModuleInfo* info) {
+ return ReadPEInfo(pe_file_, info);
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/pe_source_line_writer.h b/toolkit/crashreporter/google-breakpad/src/common/windows/pe_source_line_writer.h new file mode 100644 index 0000000000..2bf1d4fd2b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/pe_source_line_writer.h @@ -0,0 +1,69 @@ +// Copyright (c) 2019, 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.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// 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.
+
+#ifndef COMMON_WINDOWS_PE_SOURCE_LINE_WRITER_H_
+#define COMMON_WINDOWS_PE_SOURCE_LINE_WRITER_H_
+
+#include <string>
+
+#include "common/basictypes.h"
+#include "common/windows/module_info.h"
+
+namespace google_breakpad {
+
+using std::wstring;
+
+// PESourceLineWriter uses a pe file produced by Visual C++ to output
+// a line/address map for use with BasicSourceLineResolver.
+// NOTE: Only supports PE32+ format, ie. a 64bit PE file.
+class PESourceLineWriter {
+public:
+ explicit PESourceLineWriter(const wstring& pe_file);
+ ~PESourceLineWriter();
+
+ // Writes Breakpad symbols from the pe file to |symbol_file|.
+ // Returns true on success.
+ bool WriteSymbols(FILE* symbol_file);
+
+ // Retrieves information about the module. Returns true on success.
+ bool GetModuleInfo(PDBModuleInfo* info);
+
+ // Retrieves information about the module's PE file. Returns + // true on success.
+ bool GetPEInfo(PEModuleInfo* info);
+
+private:
+ const wstring pe_file_;
+
+ DISALLOW_COPY_AND_ASSIGN(PESourceLineWriter);
+};
+
+} // namespace google_breakpad
+
+#endif // COMMON_WINDOWS_PE_SOURCE_LINE_WRITER_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/pe_util.cc b/toolkit/crashreporter/google-breakpad/src/common/windows/pe_util.cc new file mode 100644 index 0000000000..6fa63fa3b5 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/pe_util.cc @@ -0,0 +1,407 @@ +// Copyright (c) 2019, 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.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// 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.
+
+#include "pe_util.h"
+
+#include <windows.h>
+#include <winnt.h>
+#include <atlbase.h>
+#include <ImageHlp.h>
+
+#include <functional>
+
+#include "common/windows/string_utils-inl.h"
+#include "common/windows/guid_string.h"
+
+namespace {
+
+/*
+ * Not defined in WinNT.h for some reason. Definitions taken from:
+ * http://uninformed.org/index.cgi?v=4&a=1&p=13
+ *
+ */
+typedef unsigned char UBYTE;
+
+#if !defined(_WIN64)
+#define UNW_FLAG_EHANDLER 0x01
+#define UNW_FLAG_UHANDLER 0x02
+#define UNW_FLAG_CHAININFO 0x04
+#endif
+
+union UnwindCode {
+ struct {
+ UBYTE offset_in_prolog;
+ UBYTE unwind_operation_code : 4;
+ UBYTE operation_info : 4;
+ };
+ USHORT frame_offset;
+};
+
+enum UnwindOperationCodes {
+ UWOP_PUSH_NONVOL = 0, /* info == register number */
+ UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */
+ UWOP_ALLOC_SMALL, /* info == size of allocation / 8 - 1 */
+ UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
+ UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */
+ UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
+ // XXX: these are missing from MSDN!
+ // See: http://www.osronline.com/ddkx/kmarch/64bitamd_4rs7.htm
+ UWOP_SAVE_XMM,
+ UWOP_SAVE_XMM_FAR,
+ UWOP_SAVE_XMM128, /* info == XMM reg number, offset in next slot */
+ UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
+ UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */
+};
+
+// See: http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
+// Note: some fields removed as we don't use them.
+struct UnwindInfo {
+ UBYTE version : 3;
+ UBYTE flags : 5;
+ UBYTE size_of_prolog;
+ UBYTE count_of_codes;
+ UBYTE frame_register : 4;
+ UBYTE frame_offset : 4;
+ UnwindCode unwind_code[1];
+};
+
+struct CV_INFO_PDB70 {
+ ULONG cv_signature;
+ GUID signature;
+ ULONG age;
+ CHAR pdb_filename[ANYSIZE_ARRAY];
+};
+
+#define CV_SIGNATURE_RSDS 'SDSR'
+
+// A helper class to scope a PLOADED_IMAGE.
+class AutoImage {
+public:
+ explicit AutoImage(PLOADED_IMAGE img) : img_(img) {}
+ ~AutoImage() {
+ if (img_)
+ ImageUnload(img_);
+ }
+
+ operator PLOADED_IMAGE() { return img_; }
+ PLOADED_IMAGE operator->() { return img_; }
+
+private:
+ PLOADED_IMAGE img_;
+};
+} // namespace
+
+namespace google_breakpad {
+
+using std::unique_ptr;
+using google_breakpad::GUIDString;
+
+bool ReadModuleInfo(const wstring & pe_file, PDBModuleInfo * info) {
+ // Convert wchar to native charset because ImageLoad only takes
+ // a PSTR as input.
+ string img_file;
+ if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {
+ fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",
+ pe_file.c_str());
+ return false;
+ }
+
+ AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));
+ if (!img) {
+ fprintf(stderr, "Failed to load %s\n", img_file.c_str());
+ return false;
+ }
+
+ info->cpu = FileHeaderMachineToCpuString(
+ img->FileHeader->FileHeader.Machine);
+
+ PIMAGE_OPTIONAL_HEADER64 optional_header =
+ &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader;
+
+ // Search debug directories for a guid signature & age
+ DWORD debug_rva = optional_header->
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
+ DWORD debug_size = optional_header->
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
+ PIMAGE_DEBUG_DIRECTORY debug_directories =
+ static_cast<PIMAGE_DEBUG_DIRECTORY>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ debug_rva,
+ &img->LastRvaSection));
+
+ for (DWORD i = 0; i < debug_size / sizeof(*debug_directories); i++) {
+ if (debug_directories[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW ||
+ debug_directories[i].SizeOfData < sizeof(CV_INFO_PDB70)) {
+ continue;
+ }
+
+ struct CV_INFO_PDB70* cv_info = static_cast<CV_INFO_PDB70*>(ImageRvaToVa(
+ img->FileHeader,
+ img->MappedAddress,
+ debug_directories[i].AddressOfRawData,
+ &img->LastRvaSection));
+ if (cv_info->cv_signature != CV_SIGNATURE_RSDS) {
+ continue;
+ }
+
+ info->debug_identifier = GenerateDebugIdentifier(cv_info->age,
+ cv_info->signature);
+
+ // This code assumes that the pdb_filename is stored as ASCII without
+ // multibyte characters, but it's not clear if that's true.
+ size_t debug_file_length = strnlen_s(cv_info->pdb_filename, MAX_PATH);
+ if (debug_file_length < 0 || debug_file_length >= MAX_PATH) {
+ fprintf(stderr, "PE debug directory is corrupt.\n");
+ return false;
+ }
+ std::string debug_file(cv_info->pdb_filename, debug_file_length);
+ if (!WindowsStringUtils::safe_mbstowcs(debug_file, &info->debug_file)) {
+ fprintf(stderr, "PDB filename '%s' contains unrecognized characters.\n",
+ debug_file.c_str());
+ return false;
+ }
+ info->debug_file = WindowsStringUtils::GetBaseName(info->debug_file);
+
+ return true;
+ }
+
+ fprintf(stderr, "Image is missing debug information.\n");
+ return false;
+}
+
+bool ReadPEInfo(const wstring & pe_file, PEModuleInfo * info) {
+ // Convert wchar to native charset because ImageLoad only takes
+ // a PSTR as input.
+ string img_file;
+ if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {
+ fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",
+ pe_file.c_str());
+ return false;
+ }
+
+ AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));
+ if (!img) {
+ fprintf(stderr, "Failed to open PE file: %S\n", pe_file.c_str());
+ return false;
+ }
+
+ info->code_file = WindowsStringUtils::GetBaseName(pe_file);
+
+ // The date and time that the file was created by the linker.
+ DWORD TimeDateStamp = img->FileHeader->FileHeader.TimeDateStamp;
+ // The size of the file in bytes, including all headers.
+ DWORD SizeOfImage = 0;
+ PIMAGE_OPTIONAL_HEADER64 opt =
+ &((PIMAGE_NT_HEADERS64)img->FileHeader)->OptionalHeader;
+ if (opt->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ // 64-bit PE file.
+ SizeOfImage = opt->SizeOfImage;
+ }
+ else {
+ // 32-bit PE file.
+ SizeOfImage = img->FileHeader->OptionalHeader.SizeOfImage;
+ }
+ wchar_t code_identifier[32];
+ swprintf(code_identifier,
+ sizeof(code_identifier) / sizeof(code_identifier[0]),
+ L"%08X%X", TimeDateStamp, SizeOfImage);
+ info->code_identifier = code_identifier;
+
+ return true;
+}
+
+bool PrintPEFrameData(const wstring & pe_file, FILE * out_file)
+{
+ // Convert wchar to native charset because ImageLoad only takes
+ // a PSTR as input.
+ string img_file;
+ if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {
+ fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",
+ pe_file.c_str());
+ return false;
+ }
+
+ AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));
+ if (!img) {
+ fprintf(stderr, "Failed to load %s\n", img_file.c_str());
+ return false;
+ }
+ PIMAGE_OPTIONAL_HEADER64 optional_header =
+ &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader;
+ if (optional_header->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ fprintf(stderr, "Not a PE32+ image\n");
+ return false;
+ }
+
+ // Read Exception Directory
+ DWORD exception_rva = optional_header->
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
+ DWORD exception_size = optional_header->
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
+ PIMAGE_RUNTIME_FUNCTION_ENTRY funcs =
+ static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ exception_rva,
+ &img->LastRvaSection));
+ for (DWORD i = 0; i < exception_size / sizeof(*funcs); i++) {
+ DWORD unwind_rva = funcs[i].UnwindInfoAddress;
+ // handle chaining
+ while (unwind_rva & 0x1) {
+ unwind_rva ^= 0x1;
+ PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func =
+ static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ unwind_rva,
+ &img->LastRvaSection));
+ unwind_rva = chained_func->UnwindInfoAddress;
+ }
+
+ UnwindInfo *unwind_info = static_cast<UnwindInfo *>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ unwind_rva,
+ &img->LastRvaSection));
+
+ DWORD stack_size = 8; // minimal stack size is 8 for RIP
+ DWORD rip_offset = 8;
+ do {
+ for (UBYTE c = 0; c < unwind_info->count_of_codes; c++) {
+ UnwindCode *unwind_code = &unwind_info->unwind_code[c];
+ switch (unwind_code->unwind_operation_code) {
+ case UWOP_PUSH_NONVOL: {
+ stack_size += 8;
+ break;
+ }
+ case UWOP_ALLOC_LARGE: {
+ if (unwind_code->operation_info == 0) {
+ c++;
+ if (c < unwind_info->count_of_codes)
+ stack_size += (unwind_code + 1)->frame_offset * 8;
+ }
+ else {
+ c += 2;
+ if (c < unwind_info->count_of_codes)
+ stack_size += (unwind_code + 1)->frame_offset |
+ ((unwind_code + 2)->frame_offset << 16);
+ }
+ break;
+ }
+ case UWOP_ALLOC_SMALL: {
+ stack_size += unwind_code->operation_info * 8 + 8;
+ break;
+ }
+ case UWOP_SET_FPREG:
+ case UWOP_SAVE_XMM:
+ case UWOP_SAVE_XMM_FAR:
+ break;
+ case UWOP_SAVE_NONVOL:
+ case UWOP_SAVE_XMM128: {
+ c++; // skip slot with offset
+ break;
+ }
+ case UWOP_SAVE_NONVOL_FAR:
+ case UWOP_SAVE_XMM128_FAR: {
+ c += 2; // skip 2 slots with offset
+ break;
+ }
+ case UWOP_PUSH_MACHFRAME: {
+ if (unwind_code->operation_info) {
+ stack_size += 88;
+ }
+ else {
+ stack_size += 80;
+ }
+ rip_offset += 80;
+ break;
+ }
+ }
+ }
+ if (unwind_info->flags & UNW_FLAG_CHAININFO) {
+ PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func =
+ reinterpret_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
+ (unwind_info->unwind_code +
+ ((unwind_info->count_of_codes + 1) & ~1)));
+
+ unwind_info = static_cast<UnwindInfo *>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ chained_func->UnwindInfoAddress,
+ &img->LastRvaSection));
+ }
+ else {
+ unwind_info = NULL;
+ }
+ } while (unwind_info);
+ fprintf(out_file, "STACK CFI INIT %lx %lx .cfa: $rsp .ra: .cfa %lu - ^\n",
+ funcs[i].BeginAddress,
+ funcs[i].EndAddress - funcs[i].BeginAddress, rip_offset);
+ fprintf(out_file, "STACK CFI %lx .cfa: $rsp %lu +\n",
+ funcs[i].BeginAddress, stack_size);
+ }
+
+ return true;
+}
+
+wstring GenerateDebugIdentifier(DWORD age, GUID signature)
+{
+ // Use the same format that the MS symbol server uses in filesystem
+ // hierarchies.
+ wchar_t age_string[9];
+ swprintf(age_string, sizeof(age_string) / sizeof(age_string[0]),
+ L"%x", age);
+
+ // remove when VC++7.1 is no longer supported
+ age_string[sizeof(age_string) / sizeof(age_string[0]) - 1] = L'\0';
+
+ wstring debug_identifier = GUIDString::GUIDToSymbolServerWString(&signature);
+ debug_identifier.append(age_string);
+
+ return debug_identifier;
+}
+
+wstring GenerateDebugIdentifier(DWORD age, DWORD signature)
+{
+ // Use the same format that the MS symbol server uses in filesystem
+ // hierarchies.
+ wchar_t identifier_string[17];
+ swprintf(identifier_string,
+ sizeof(identifier_string) / sizeof(identifier_string[0]),
+ L"%08X%x", signature, age);
+
+ // remove when VC++7.1 is no longer supported
+ identifier_string[sizeof(identifier_string) /
+ sizeof(identifier_string[0]) - 1] = L'\0';
+
+ return wstring(identifier_string);
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/pe_util.h b/toolkit/crashreporter/google-breakpad/src/common/windows/pe_util.h new file mode 100644 index 0000000000..634ba29343 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/pe_util.h @@ -0,0 +1,78 @@ +// Copyright (c) 2019, 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.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// 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.
+
+#ifndef COMMON_WINDOWS_PE_UTIL_H_
+#define COMMON_WINDOWS_PE_UTIL_H_
+
+#include <windows.h>
+
+#include "common/windows/module_info.h"
+
+namespace google_breakpad {
+
+using std::wstring;
+
+// Reads |pe_file| and populates |info|. Returns true on success.
+// Only supports PE32+ format, ie. a 64bit PE file.
+// Will fail if |pe_file| does not contain a valid CodeView record.
+bool ReadModuleInfo(const wstring& pe_file, PDBModuleInfo* info);
+
+// Reads |pe_file| and populates |info|. Returns true on success.
+bool ReadPEInfo(const wstring& pe_file, PEModuleInfo* info);
+
+// Reads |pe_file| and prints frame data (aka. unwind info) to |out_file|.
+// Only supports PE32+ format, ie. a 64bit PE file.
+bool PrintPEFrameData(const wstring& pe_file, FILE* out_file);
+
+// Combines a GUID |signature| and DWORD |age| to create a Breakpad debug
+// identifier.
+wstring GenerateDebugIdentifier(DWORD age, GUID signature);
+
+// Combines a DWORD |signature| and DWORD |age| to create a Breakpad debug
+// identifier.
+wstring GenerateDebugIdentifier(DWORD age, DWORD signature);
+
+// Converts |machine| enum value to the corresponding string used by Breakpad.
+// The enum is IMAGE_FILE_MACHINE_*, contained in winnt.h.
+constexpr const wchar_t* FileHeaderMachineToCpuString(WORD machine) {
+ switch (machine) {
+ case IMAGE_FILE_MACHINE_I386: {
+ return L"x86";
+ }
+ case IMAGE_FILE_MACHINE_IA64:
+ case IMAGE_FILE_MACHINE_AMD64: {
+ return L"x86_64";
+ }
+ default: { return L"unknown"; }
+ }
+}
+
+} // namespace google_breakpad
+
+#endif // COMMON_WINDOWS_PE_UTIL_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/string_utils-inl.h b/toolkit/crashreporter/google-breakpad/src/common/windows/string_utils-inl.h new file mode 100644 index 0000000000..9b63607268 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/string_utils-inl.h @@ -0,0 +1,142 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +// string_utils-inl.h: Safer string manipulation on Windows, supporting +// pre-MSVC8 environments. + +#ifndef COMMON_WINDOWS_STRING_UTILS_INL_H_ +#define COMMON_WINDOWS_STRING_UTILS_INL_H_ + +#include <stdarg.h> +#include <wchar.h> + +#include <string> + +// The "ll" printf format size specifier corresponding to |long long| was +// intrudced in MSVC8. Earlier versions did not provide this size specifier, +// but "I64" can be used to print 64-bit types. Don't use "I64" where "ll" +// is available, in the event of oddball systems where |long long| is not +// 64 bits wide. +#if _MSC_VER >= 1400 // MSVC 2005/8 +#define WIN_STRING_FORMAT_LL "ll" +#else // MSC_VER >= 1400 +#define WIN_STRING_FORMAT_LL "I64" +#endif // MSC_VER >= 1400 + +// A nonconforming version of swprintf, without the length argument, was +// included with the CRT prior to MSVC8. Although a conforming version was +// also available via an overload, it is not reliably chosen. _snwprintf +// behaves as a standards-confirming swprintf should, so force the use of +// _snwprintf when using older CRTs. +#if _MSC_VER < 1400 // MSVC 2005/8 +#define swprintf _snwprintf +#else +// For MSVC8 and newer, swprintf_s is the recommended method. Conveniently, +// it takes the same argument list as swprintf. +#define swprintf swprintf_s +#endif // MSC_VER < 1400 + +namespace google_breakpad { + +using std::string; +using std::wstring; + +class WindowsStringUtils { + public: + // Roughly equivalent to MSVC8's wcscpy_s, except pre-MSVC8, this does + // not fail if source is longer than destination_size. The destination + // buffer is always 0-terminated. + static void safe_wcscpy(wchar_t *destination, size_t destination_size, + const wchar_t *source); + + // Roughly equivalent to MSVC8's wcsncpy_s, except that _TRUNCATE cannot + // be passed directly, and pre-MSVC8, this will not fail if source or count + // are longer than destination_size. The destination buffer is always + // 0-terminated. + static void safe_wcsncpy(wchar_t *destination, size_t destination_size, + const wchar_t *source, size_t count); + + // Performs multi-byte to wide character conversion on C++ strings, using + // mbstowcs_s (MSVC8) or mbstowcs (pre-MSVC8). Returns false on failure, + // without setting wcs. + static bool safe_mbstowcs(const string &mbs, wstring *wcs); + + // The inverse of safe_mbstowcs. + static bool safe_wcstombs(const wstring &wcs, string *mbs); + + // Returns the base name of a file, e.g. strips off the path. + static wstring GetBaseName(const wstring &filename); + + private: + // Disallow instantiation and other object-based operations. + WindowsStringUtils(); + WindowsStringUtils(const WindowsStringUtils&); + ~WindowsStringUtils(); + void operator=(const WindowsStringUtils&); +}; + +// static +inline void WindowsStringUtils::safe_wcscpy(wchar_t *destination, + size_t destination_size, + const wchar_t *source) { +#if _MSC_VER >= 1400 // MSVC 2005/8 + wcscpy_s(destination, destination_size, source); +#else // _MSC_VER >= 1400 + // Pre-MSVC 2005/8 doesn't have wcscpy_s. Simulate it with wcsncpy. + // wcsncpy doesn't 0-terminate the destination buffer if the source string + // is longer than size. Ensure that the destination is 0-terminated. + wcsncpy(destination, source, destination_size); + if (destination && destination_size) + destination[destination_size - 1] = 0; +#endif // _MSC_VER >= 1400 +} + +// static +inline void WindowsStringUtils::safe_wcsncpy(wchar_t *destination, + size_t destination_size, + const wchar_t *source, + size_t count) { +#if _MSC_VER >= 1400 // MSVC 2005/8 + wcsncpy_s(destination, destination_size, source, count); +#else // _MSC_VER >= 1400 + // Pre-MSVC 2005/8 doesn't have wcsncpy_s. Simulate it with wcsncpy. + // wcsncpy doesn't 0-terminate the destination buffer if the source string + // is longer than size. Ensure that the destination is 0-terminated. + if (destination_size < count) + count = destination_size; + + wcsncpy(destination, source, count); + if (destination && count) + destination[count - 1] = 0; +#endif // _MSC_VER >= 1400 +} + +} // namespace google_breakpad + +#endif // COMMON_WINDOWS_STRING_UTILS_INL_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/string_utils.cc b/toolkit/crashreporter/google-breakpad/src/common/windows/string_utils.cc new file mode 100644 index 0000000000..272800035c --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/string_utils.cc @@ -0,0 +1,133 @@ +// Copyright (c) 2006, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#include <cassert> +#include <vector> + +#include "common/windows/string_utils-inl.h" + +namespace google_breakpad { + +// static +wstring WindowsStringUtils::GetBaseName(const wstring &filename) { + wstring base_name(filename); + size_t slash_pos = base_name.find_last_of(L"/\\"); + if (slash_pos != wstring::npos) { + base_name.erase(0, slash_pos + 1); + } + return base_name; +} + +// static +bool WindowsStringUtils::safe_mbstowcs(const string &mbs, wstring *wcs) { + assert(wcs); + + // First, determine the length of the destination buffer. + size_t wcs_length; + +#if _MSC_VER >= 1400 // MSVC 2005/8 + errno_t err; + if ((err = mbstowcs_s(&wcs_length, NULL, 0, mbs.c_str(), _TRUNCATE)) != 0) { + return false; + } + assert(wcs_length > 0); +#else // _MSC_VER >= 1400 + if ((wcs_length = mbstowcs(NULL, mbs.c_str(), mbs.length())) == (size_t)-1) { + return false; + } + + // Leave space for the 0-terminator. + ++wcs_length; +#endif // _MSC_VER >= 1400 + + std::vector<wchar_t> wcs_v(wcs_length); + + // Now, convert. +#if _MSC_VER >= 1400 // MSVC 2005/8 + if ((err = mbstowcs_s(NULL, &wcs_v[0], wcs_length, mbs.c_str(), + _TRUNCATE)) != 0) { + return false; + } +#else // _MSC_VER >= 1400 + if (mbstowcs(&wcs_v[0], mbs.c_str(), mbs.length()) == (size_t)-1) { + return false; + } + + // Ensure presence of 0-terminator. + wcs_v[wcs_length - 1] = '\0'; +#endif // _MSC_VER >= 1400 + + *wcs = &wcs_v[0]; + return true; +} + +// static +bool WindowsStringUtils::safe_wcstombs(const wstring &wcs, string *mbs) { + assert(mbs); + + // First, determine the length of the destination buffer. + size_t mbs_length; + +#if _MSC_VER >= 1400 // MSVC 2005/8 + errno_t err; + if ((err = wcstombs_s(&mbs_length, NULL, 0, wcs.c_str(), _TRUNCATE)) != 0) { + return false; + } + assert(mbs_length > 0); +#else // _MSC_VER >= 1400 + if ((mbs_length = wcstombs(NULL, wcs.c_str(), wcs.length())) == (size_t)-1) { + return false; + } + + // Leave space for the 0-terminator. + ++mbs_length; +#endif // _MSC_VER >= 1400 + + std::vector<char> mbs_v(mbs_length); + + // Now, convert. +#if _MSC_VER >= 1400 // MSVC 2005/8 + if ((err = wcstombs_s(NULL, &mbs_v[0], mbs_length, wcs.c_str(), + _TRUNCATE)) != 0) { + return false; + } +#else // _MSC_VER >= 1400 + if (wcstombs(&mbs_v[0], wcs.c_str(), wcs.length()) == (size_t)-1) { + return false; + } + + // Ensure presence of 0-terminator. + mbs_v[mbs_length - 1] = '\0'; +#endif // _MSC_VER >= 1400 + + *mbs = &mbs_v[0]; + return true; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/symbol_collector_client.cc b/toolkit/crashreporter/google-breakpad/src/common/windows/symbol_collector_client.cc new file mode 100644 index 0000000000..30c663ed37 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/symbol_collector_client.cc @@ -0,0 +1,155 @@ +#include "common/windows/symbol_collector_client.h" + +#include <stdio.h> + +#include <regex> + +#include "common/windows/http_upload.h" + +namespace google_breakpad { + + // static + bool SymbolCollectorClient::CreateUploadUrl( + wstring& api_url, + wstring& api_key, + UploadUrlResponse *uploadUrlResponse) { + wstring url = api_url + + L"/v1/uploads:create" + L"?key=" + api_key; + wstring response; + int response_code; + + if (!HTTPUpload::SendSimplePostRequest( + url, + L"", + L"", + NULL, + &response, + &response_code)) { + wprintf(L"Failed to create upload url.\n"); + wprintf(L"Response code: %ld\n", response_code); + wprintf(L"Response:\n"); + wprintf(L"%s\n", response.c_str()); + return false; + } + + // Note camel-case rather than underscores. + std::wregex upload_url_regex(L"\"uploadUrl\": \"([^\"]+)\""); + std::wregex upload_key_regex(L"\"uploadKey\": \"([^\"]+)\""); + + std::wsmatch upload_url_match; + if (!std::regex_search(response, upload_url_match, upload_url_regex) || + upload_url_match.size() != 2) { + wprintf(L"Failed to parse create url response."); + wprintf(L"Response:\n"); + wprintf(L"%s\n", response.c_str()); + return false; + } + wstring upload_url = upload_url_match[1].str(); + + std::wsmatch upload_key_match; + if (!std::regex_search(response, upload_key_match, upload_key_regex) || + upload_key_match.size() != 2) { + wprintf(L"Failed to parse create url response."); + wprintf(L"Response:\n"); + wprintf(L"%s\n", response.c_str()); + return false; + } + wstring upload_key = upload_key_match[1].str(); + + uploadUrlResponse->upload_url = upload_url; + uploadUrlResponse->upload_key = upload_key; + return true; + } + + // static + CompleteUploadResult SymbolCollectorClient::CompleteUpload( + wstring& api_url, + wstring& api_key, + const wstring& upload_key, + const wstring& debug_file, + const wstring& debug_id) { + wstring url = api_url + + L"/v1/uploads/" + upload_key + L":complete" + L"?key=" + api_key; + wstring body = + L"{ symbol_id: {" + L"debug_file: \"" + debug_file + L"\", " + L"debug_id: \"" + debug_id + L"\" " + L"} }"; + wstring response; + int response_code; + + if (!HTTPUpload::SendSimplePostRequest( + url, + body, + L"application/json", + NULL, + &response, + &response_code)) { + wprintf(L"Failed to complete upload.\n"); + wprintf(L"Response code: %ld\n", response_code); + wprintf(L"Response:\n"); + wprintf(L"%s\n", response.c_str()); + return CompleteUploadResult::Error; + } + + std::wregex result_regex(L"\"result\": \"([^\"]+)\""); + std::wsmatch result_match; + if (!std::regex_search(response, result_match, result_regex) || + result_match.size() != 2) { + wprintf(L"Failed to parse complete upload response."); + wprintf(L"Response:\n"); + wprintf(L"%s\n", response.c_str()); + return CompleteUploadResult::Error; + } + wstring result = result_match[1].str(); + + if (result.compare(L"DUPLICATE_DATA") == 0) { + return CompleteUploadResult::DuplicateData; + } + + return CompleteUploadResult::Ok; + } + + // static + SymbolStatus SymbolCollectorClient::CheckSymbolStatus( + wstring& api_url, + wstring& api_key, + const wstring& debug_file, + const wstring& debug_id) { + wstring response; + int response_code; + wstring url = api_url + + L"/v1/symbols/" + debug_file + L"/" + debug_id + L":checkStatus" + L"?key=" + api_key; + + if (!HTTPUpload::SendGetRequest( + url, + NULL, + &response, + &response_code)) { + wprintf(L"Failed to check symbol status.\n"); + wprintf(L"Response code: %ld\n", response_code); + wprintf(L"Response:\n"); + wprintf(L"%s\n", response.c_str()); + return SymbolStatus::Unknown; + } + + std::wregex status_regex(L"\"status\": \"([^\"]+)\""); + std::wsmatch status_match; + if (!std::regex_search(response, status_match, status_regex) || + status_match.size() != 2) { + wprintf(L"Failed to parse check symbol status response."); + wprintf(L"Response:\n"); + wprintf(L"%s\n", response.c_str()); + return SymbolStatus::Unknown; + } + wstring status = status_match[1].str(); + + return (status.compare(L"FOUND") == 0) ? + SymbolStatus::Found : + SymbolStatus::Missing; + } + +} // namespace google_breakpad
\ No newline at end of file diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/symbol_collector_client.h b/toolkit/crashreporter/google-breakpad/src/common/windows/symbol_collector_client.h new file mode 100644 index 0000000000..30e0cb3230 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/symbol_collector_client.h @@ -0,0 +1,89 @@ +// Copyright (c) 2019, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// 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. + +#ifndef COMMON_WINDOWS_SYMBOL_COLLECTOR_CLIENT_H_ +#define COMMON_WINDOWS_SYMBOL_COLLECTOR_CLIENT_H_ + +#include <string> + +namespace google_breakpad { + + using std::wstring; + + struct UploadUrlResponse { + // URL at which to HTTP PUT symbol file. + wstring upload_url; + // Unique key used to complete upload of symbol file. + wstring upload_key; + }; + + enum SymbolStatus { + Found, + Missing, + Unknown + }; + + enum CompleteUploadResult { + Ok, + DuplicateData, + Error + }; + + // Client to interact with sym-upload-v2 API server via HTTP/REST. + class SymbolCollectorClient { + public: + // Returns a URL at which a symbol file can be HTTP PUT without + // authentication, along with an upload key that can be used to + // complete the upload process with CompleteUpload. + static bool CreateUploadUrl( + wstring& api_url, + wstring& api_key, + UploadUrlResponse *uploadUrlResponse); + + // Notify the API that symbol file upload is finished and its contents + // are ready to be read and/or used for further processing. + static CompleteUploadResult CompleteUpload( + wstring& api_url, + wstring& api_key, + const wstring& upload_key, + const wstring& debug_file, + const wstring& debug_id); + + // Returns whether or not a symbol file corresponding to the debug_file/ + // debug_id pair is already present in symbol storage. + static SymbolStatus CheckSymbolStatus( + wstring& api_url, + wstring& api_key, + const wstring& debug_file, + const wstring& debug_id); + }; + +} // namespace google_breakpad + +#endif // COMMON_WINDOWS_SYMBOL_COLLECTOR_CLIENT_H_ |